diff --git a/src/rp2040_arduino_platform.cpp b/src/rp2040_arduino_platform.cpp index dce06e8..691d768 100644 --- a/src/rp2040_arduino_platform.cpp +++ b/src/rp2040_arduino_platform.cpp @@ -4,7 +4,7 @@ Plattform for Raspberry Pi Pico and other RP2040 boards by SirSydom 2021-2022 made to work with arduino-pico - "Raspberry Pi Pico Arduino core, for all RP2040 boards" -by Earl E. Philhower III https://github.com/earlephilhower/arduino-pico +by Earl E. Philhower III https://github.com/earlephilhower/arduino-pico RTTI must be set to enabled in the board options @@ -31,20 +31,77 @@ For usage of KNX-IP you have to define either #include -//Pi Pico specific libs -#include // EEPROM emulation in flash, part of Earl E Philhowers Pi Pico Arduino support -#include // from Pico SDK -#include // from Pico SDK -#include // from Pico SDK +// Pi Pico specific libs +#include // EEPROM emulation in flash, part of Earl E Philhowers Pi Pico Arduino support +#include // from Pico SDK +#include // from Pico SDK +#include // from Pico SDK + +#ifdef USE_KNX_DMA_UART +#include +// constexpr uint32_t uartDmaTransferCount = 0b1111111111; +constexpr uint32_t uartDmaTransferCount = UINT32_MAX; +constexpr uint8_t uartDmaBufferExp = 8u; // 2**BufferExp +constexpr uint16_t uartDmaBufferSize = (1u << uartDmaBufferExp); +int8_t uartDmaChannel = -1; +volatile uint8_t __attribute__((aligned(uartDmaBufferSize))) uartDmaBuffer[uartDmaBufferSize] = {}; +volatile uint32_t uartDmaReadCount = 0; +volatile uint16_t uartDmaRestartCount = 0; +volatile uint32_t uartDmaWriteCount2 = 0; +volatile uint32_t uartDmaAvail = 0; + +// Liefert die Zahl der gelesenen Bytes seit dem DMA Transferstart +inline uint32_t uartDmaWriteCount() +{ + uartDmaWriteCount2 = uartDmaTransferCount - dma_channel_hw_addr(uartDmaChannel)->transfer_count; + return uartDmaWriteCount2; +} + +// Liefert die aktuelle Schreibposition im DMA Buffer +inline uint16_t uartDmaWriteBufferPosition() +{ + return uartDmaWriteCount() % uartDmaBufferSize; +} + +// Liefert die aktuelle Leseposition im DMA Buffer +inline uint16_t uartDmaReadBufferPosition() +{ + return uartDmaReadCount % uartDmaBufferSize; +} + +// Liefert die aktuelle Leseposition als Pointer +inline uint8_t* uartDmaReadAddr() +{ + return ((uint8_t*)uartDmaBuffer + uartDmaReadBufferPosition()); +} + +// Startet den Transfer nach Abschluss neu. +void __time_critical_func(uartDmaRestart)() +{ + // println("Restart"); + uartDmaRestartCount = uartDmaWriteBufferPosition() - uartDmaReadBufferPosition(); + + // wenn uartDmaRestartCount == 0 ist, wurde alles verarbeitet und der read count kann mit dem neustart wieder auf 0 gesetzt werden. + if (uartDmaRestartCount == 0) + { + uartDmaReadCount = 0; + } + + asm volatile("" ::: "memory"); + dma_hw->ints0 = 1u << uartDmaChannel; // clear DMA IRQ0 flag + asm volatile("" ::: "memory"); + dma_channel_set_write_addr(uartDmaChannel, uartDmaBuffer, true); +} +#endif #define FLASHPTR ((uint8_t*)XIP_BASE + KNX_FLASH_OFFSET) #ifndef USE_RP2040_EEPROM_EMULATION -#if KNX_FLASH_SIZE%4096 +#if KNX_FLASH_SIZE % 4096 #error "KNX_FLASH_SIZE must be multiple of 4096" #endif -#if KNX_FLASH_OFFSET%4096 +#if KNX_FLASH_OFFSET % 4096 #error "KNX_FLASH_OFFSET must be multiple of 4096" #endif #endif @@ -57,26 +114,27 @@ extern Wiznet5500lwIP KNX_NETIF; #endif RP2040ArduinoPlatform::RP2040ArduinoPlatform() -#ifndef KNX_NO_DEFAULT_UART +#if !defined(KNX_NO_DEFAULT_UART) && !defined(USE_KNX_DMA_UART) : ArduinoPlatform(&KNX_SERIAL) #endif { - #ifdef KNX_UART_RX_PIN +#ifdef KNX_UART_RX_PIN _rxPin = KNX_UART_RX_PIN; - #endif - #ifdef KNX_UART_TX_PIN +#endif +#ifdef KNX_UART_TX_PIN _txPin = KNX_UART_TX_PIN; - #endif - #ifndef USE_RP2040_EEPROM_EMULATION +#endif +#ifndef USE_RP2040_EEPROM_EMULATION _memoryType = Flash; - #endif +#endif } -RP2040ArduinoPlatform::RP2040ArduinoPlatform( HardwareSerial* s) : ArduinoPlatform(s) +RP2040ArduinoPlatform::RP2040ArduinoPlatform(HardwareSerial* s) + : ArduinoPlatform(s) { - #ifndef USE_RP2040_EEPROM_EMULATION +#ifndef USE_RP2040_EEPROM_EMULATION _memoryType = Flash; - #endif +#endif } void RP2040ArduinoPlatform::knxUartPins(pin_size_t rxPin, pin_size_t txPin) @@ -85,36 +143,162 @@ void RP2040ArduinoPlatform::knxUartPins(pin_size_t rxPin, pin_size_t txPin) _txPin = txPin; } -bool RP2040ArduinoPlatform::overflowUart() { +bool RP2040ArduinoPlatform::overflowUart() +{ +#ifdef USE_KNX_DMA_UART + // during dma restart + bool ret; + const uint32_t writeCount = uartDmaWriteCount(); + if (uartDmaRestartCount > 0) + ret = writeCount >= (uartDmaBufferSize - uartDmaRestartCount - 1); + else + ret = (writeCount - uartDmaReadCount) > uartDmaBufferSize; + + // if (ret) + // { + // println(uartDmaWriteBufferPosition()); + // println(uartDmaReadBufferPosition()); + // println(uartDmaWriteCount()); + // println(uartDmaReadCount); + // println(uartDmaRestartCount); + // printHex("BUF: ", (const uint8_t *)uartDmaBuffer, uartDmaBufferSize); + // println("OVERFLOW"); + // while (true) + // ; + // } + return ret; +#else SerialUART* serial = dynamic_cast(_knxSerial); return serial->overflow(); +#endif } void RP2040ArduinoPlatform::setupUart() { +#ifdef USE_KNX_DMA_UART + if (uartDmaChannel == -1) + { + // configure uart0 + gpio_set_function(_rxPin, GPIO_FUNC_UART); + gpio_set_function(_txPin, GPIO_FUNC_UART); + uart_init(KNX_DMA_UART, 19200); + uart_set_hw_flow(KNX_DMA_UART, false, false); + uart_set_format(KNX_DMA_UART, 8, 1, UART_PARITY_EVEN); + uart_set_fifo_enabled(KNX_DMA_UART, false); + + // configure uart0 + uartDmaChannel = dma_claim_unused_channel(true); // get free channel for dma + dma_channel_config dmaConfig = dma_channel_get_default_config(uartDmaChannel); + channel_config_set_transfer_data_size(&dmaConfig, DMA_SIZE_8); + channel_config_set_read_increment(&dmaConfig, false); + channel_config_set_write_increment(&dmaConfig, true); + channel_config_set_high_priority(&dmaConfig, true); + channel_config_set_ring(&dmaConfig, true, uartDmaBufferExp); + channel_config_set_dreq(&dmaConfig, KNX_DMA_UART_DREQ); + dma_channel_set_read_addr(uartDmaChannel, &uart_get_hw(uart0)->dr, false); + dma_channel_set_write_addr(uartDmaChannel, uartDmaBuffer, false); + dma_channel_set_trans_count(uartDmaChannel, uartDmaTransferCount, false); + dma_channel_set_config(uartDmaChannel, &dmaConfig, true); + dma_channel_set_irq1_enabled(uartDmaChannel, true); + // irq_add_shared_handler(KNX_DMA_IRQ, uartDmaRestart, PICO_SHARED_IRQ_HANDLER_HIGHEST_ORDER_PRIORITY); + irq_set_exclusive_handler(KNX_DMA_IRQ, uartDmaRestart); + irq_set_enabled(KNX_DMA_IRQ, true); + } +#else SerialUART* serial = dynamic_cast(_knxSerial); - if(serial) + if (serial) { if (_rxPin != UART_PIN_NOT_DEFINED) serial->setRX(_rxPin); if (_txPin != UART_PIN_NOT_DEFINED) serial->setTX(_txPin); serial->setPollingMode(); + serial->setFIFOSize(64); } _knxSerial->begin(19200, SERIAL_8E1); - while (!_knxSerial) + while (!_knxSerial) ; +#endif } +#ifdef USE_KNX_DMA_UART +int RP2040ArduinoPlatform::uartAvailable() +{ + if (uartDmaChannel == -1) + return 0; + + if (uartDmaRestartCount > 0) + { + return uartDmaRestartCount; + } + else + { + uint32_t tc = dma_channel_hw_addr(uartDmaChannel)->transfer_count; + uartDmaAvail = tc; + int test = uartDmaTransferCount - tc - uartDmaReadCount; + return test; + } +} + +int RP2040ArduinoPlatform::readUart() +{ + if (!uartAvailable()) + return -1; + + int ret = uartDmaReadAddr()[0]; + // print("< "); + // println(ret, HEX); + uartDmaReadCount++; + + if (uartDmaRestartCount > 0) + { + // process previouse buffer + uartDmaRestartCount--; + + // last char, then reset read count to start at new writer position + if (uartDmaRestartCount == 0) + uartDmaReadCount = 0; + } + + return ret; +} + +size_t RP2040ArduinoPlatform::writeUart(const uint8_t data) +{ + if (uartDmaChannel == -1) + return 0; + + // print("> "); + // println(data, HEX); + while (!uart_is_writable(uart0)) + ; + uart_putc_raw(uart0, data); + return 1; +} + +void RP2040ArduinoPlatform::closeUart() +{ + if (uartDmaChannel >= 0) + { + dma_channel_cleanup(uartDmaChannel); + irq_set_enabled(DMA_IRQ_0, false); + uart_deinit(uart0); + uartDmaChannel = -1; + uartDmaReadCount = 0; + uartDmaRestartCount = 0; + } +} +#endif + uint32_t RP2040ArduinoPlatform::uniqueSerialNumber() { - pico_unique_board_id_t id; // 64Bit unique serial number from the QSPI flash + pico_unique_board_id_t id; // 64Bit unique serial number from the QSPI flash noInterrupts(); rp2040.idleOtherCore(); - flash_get_unique_id(id.id); //pico_get_unique_board_id(&id); + flash_get_unique_id(id.id); // pico_get_unique_board_id(&id); rp2040.resumeOtherCore(); interrupts(); @@ -128,7 +312,7 @@ uint32_t RP2040ArduinoPlatform::uniqueSerialNumber() void RP2040ArduinoPlatform::restart() { println("restart"); - watchdog_reboot(0,0,0); + watchdog_reboot(0, 0, 0); } #ifdef USE_RP2040_EEPROM_EMULATION @@ -137,20 +321,20 @@ void RP2040ArduinoPlatform::restart() #ifdef USE_RP2040_LARGE_EEPROM_EMULATION -uint8_t * RP2040ArduinoPlatform::getEepromBuffer(uint32_t size) +uint8_t* RP2040ArduinoPlatform::getEepromBuffer(uint32_t size) { - if(size%4096) + if (size % 4096) { println("KNX_FLASH_SIZE must be a multiple of 4096"); fatalError(); } - - if(!_rambuff_initialized) + + if (!_rambuff_initialized) { memcpy(_rambuff, FLASHPTR, KNX_FLASH_SIZE); _rambuff_initialized = true; } - + return _rambuff; } @@ -159,10 +343,10 @@ void RP2040ArduinoPlatform::commitToEeprom() noInterrupts(); rp2040.idleOtherCore(); - //ToDo: write block-by-block to prevent writing of untouched blocks - if(memcmp(_rambuff, FLASHPTR, KNX_FLASH_SIZE)) + // ToDo: write block-by-block to prevent writing of untouched blocks + if (memcmp(_rambuff, FLASHPTR, KNX_FLASH_SIZE)) { - flash_range_erase (KNX_FLASH_OFFSET, KNX_FLASH_SIZE); + flash_range_erase(KNX_FLASH_OFFSET, KNX_FLASH_SIZE); flash_range_program(KNX_FLASH_OFFSET, _rambuff, KNX_FLASH_SIZE); } @@ -172,22 +356,22 @@ void RP2040ArduinoPlatform::commitToEeprom() #else -uint8_t * RP2040ArduinoPlatform::getEepromBuffer(uint32_t size) +uint8_t* RP2040ArduinoPlatform::getEepromBuffer(uint32_t size) { - if(size > 4096) + if (size > 4096) { println("KNX_FLASH_SIZE to big for EEPROM emulation (max. 4kB)"); fatalError(); } - - uint8_t * eepromptr = EEPROM.getDataPtr(); - if(eepromptr == nullptr) + uint8_t* eepromptr = EEPROM.getDataPtr(); + + if (eepromptr == nullptr) { EEPROM.begin(4096); eepromptr = EEPROM.getDataPtr(); } - + return eepromptr; } @@ -217,10 +401,10 @@ uint8_t* RP2040ArduinoPlatform::userFlashStart() size_t RP2040ArduinoPlatform::userFlashSizeEraseBlocks() { - if(KNX_FLASH_SIZE <= 0) + if (KNX_FLASH_SIZE <= 0) return 0; else - return ( (KNX_FLASH_SIZE - 1) / (flashPageSize() * flashEraseBlockSize())) + 1; + return ((KNX_FLASH_SIZE - 1) / (flashPageSize() * flashEraseBlockSize())) + 1; } void RP2040ArduinoPlatform::flashErase(uint16_t eraseBlockNum) @@ -228,7 +412,7 @@ void RP2040ArduinoPlatform::flashErase(uint16_t eraseBlockNum) noInterrupts(); rp2040.idleOtherCore(); - flash_range_erase (KNX_FLASH_OFFSET + eraseBlockNum * flashPageSize() * flashEraseBlockSize(), flashPageSize() * flashEraseBlockSize()); + flash_range_erase(KNX_FLASH_OFFSET + eraseBlockNum * flashPageSize() * flashEraseBlockSize(), flashPageSize() * flashEraseBlockSize()); rp2040.resumeOtherCore(); interrupts(); @@ -247,12 +431,12 @@ void RP2040ArduinoPlatform::flashWritePage(uint16_t pageNumber, uint8_t* data) void RP2040ArduinoPlatform::writeBufferedEraseBlock() { - if(_bufferedEraseblockNumber > -1 && _bufferedEraseblockDirty) + if (_bufferedEraseblockNumber > -1 && _bufferedEraseblockDirty) { noInterrupts(); rp2040.idleOtherCore(); - flash_range_erase (KNX_FLASH_OFFSET + _bufferedEraseblockNumber * flashPageSize() * flashEraseBlockSize(), flashPageSize() * flashEraseBlockSize()); + flash_range_erase(KNX_FLASH_OFFSET + _bufferedEraseblockNumber * flashPageSize() * flashEraseBlockSize(), flashPageSize() * flashEraseBlockSize()); flash_range_program(KNX_FLASH_OFFSET + _bufferedEraseblockNumber * flashPageSize() * flashEraseBlockSize(), _eraseblockBuffer, flashPageSize() * flashEraseBlockSize()); rp2040.resumeOtherCore(); @@ -281,7 +465,7 @@ void RP2040ArduinoPlatform::macAddress(uint8_t* addr) #if defined(KNX_IP_W5500) addr = KNX_NETIF.getNetIf()->hwaddr; #elif defined(KNX_IP_WIFI) - uint8_t macaddr[6] = {0,0,0,0,0,0}; + uint8_t macaddr[6] = {0, 0, 0, 0, 0, 0}; addr = KNX_NETIF.macAddress(macaddr); #elif defined(KNX_IP_GENERIC) KNX_NETIF.MACAddress(addr); @@ -294,12 +478,12 @@ void RP2040ArduinoPlatform::setupMultiCast(uint32_t addr, uint16_t port) mcastaddr = IPAddress(htonl(addr)); _port = port; uint8_t result = _udp.beginMulticast(mcastaddr, port); - (void) result; + (void)result; - #ifdef KNX_IP_GENERIC - //if(!_unicast_socket_setup) - // _unicast_socket_setup = UDP_UNICAST.begin(3671); - #endif +#ifdef KNX_IP_GENERIC +// if(!_unicast_socket_setup) +// _unicast_socket_setup = UDP_UNICAST.begin(3671); +#endif // print("Setup Mcast addr: "); // print(mcastaddr.toString().c_str()); @@ -318,7 +502,7 @@ bool RP2040ArduinoPlatform::sendBytesMultiCast(uint8_t* buffer, uint16_t len) { // printHex("<- ",buffer, len); - //ToDo: check if Ethernet is able to receive, return false if not + // ToDo: check if Ethernet is able to receive, return false if not _udp.beginPacket(mcastaddr, _port); _udp.write(buffer, len); _udp.endPacket(); @@ -353,12 +537,12 @@ int RP2040ArduinoPlatform::readBytesMultiCast(uint8_t* buffer, uint16_t maxLen) bool RP2040ArduinoPlatform::sendBytesUniCast(uint32_t addr, uint16_t port, uint8_t* buffer, uint16_t len) { IPAddress ucastaddr(htonl(addr)); - + // print("sendBytesUniCast to:"); // println(ucastaddr.toString().c_str()); #ifdef KNX_IP_GENERIC - if(!_unicast_socket_setup) + if (!_unicast_socket_setup) _unicast_socket_setup = UDP_UNICAST.begin(3671); #endif @@ -376,5 +560,3 @@ bool RP2040ArduinoPlatform::sendBytesUniCast(uint32_t addr, uint16_t port, uint8 #endif #endif - - diff --git a/src/rp2040_arduino_platform.h b/src/rp2040_arduino_platform.h index 4d71862..c02c2a7 100644 --- a/src/rp2040_arduino_platform.h +++ b/src/rp2040_arduino_platform.h @@ -58,6 +58,22 @@ #endif +#if USE_KNX_DMA_UART == 1 +#define KNX_DMA_UART uart1 +#define KNX_DMA_UART_IRQ UART1_IRQ +#define KNX_DMA_UART_DREQ DREQ_UART1_RX +#else +#define KNX_DMA_UART uart0 +#define KNX_DMA_UART_IRQ UART0_IRQ +#define KNX_DMA_UART_DREQ DREQ_UART0_RX +#endif + +#if USE_KNX_DMA_IRQ == 1 +#define KNX_DMA_IRQ DMA_IRQ_1 +#else +#define KNX_DMA_IRQ DMA_IRQ_0 +#endif + class RP2040ArduinoPlatform : public ArduinoPlatform { @@ -67,9 +83,20 @@ public: // uart void knxUartPins(pin_size_t rxPin, pin_size_t txPin); - void setupUart(); - virtual bool overflowUart(); - + void setupUart() override; + bool overflowUart() override; + #ifdef USE_KNX_DMA_UART + int uartAvailable() override; + void closeUart() override; + void knxUart( HardwareSerial* serial) override {}; + HardwareSerial* knxUart() override { return nullptr; }; + size_t writeUart(const uint8_t data) override; + size_t writeUart(const uint8_t* buffer, size_t size) override { return 0; }; + int readUart() override; + size_t readBytesUart(uint8_t* buffer, size_t length) override { return 0; }; + void flushUart() override {}; + #endif + // unique serial number uint32_t uniqueSerialNumber() override;