Adds dma support for rp2040 uart

This commit is contained in:
Marco Scholl 2024-03-28 08:33:15 +01:00
parent f5724c64d1
commit 6b2ac7e50b
No known key found for this signature in database
2 changed files with 266 additions and 57 deletions

View File

@ -4,7 +4,7 @@ Plattform for Raspberry Pi Pico and other RP2040 boards
by SirSydom <com@sirsydom.de> 2021-2022 by SirSydom <com@sirsydom.de> 2021-2022
made to work with arduino-pico - "Raspberry Pi Pico Arduino core, for all RP2040 boards" 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 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 <Arduino.h> #include <Arduino.h>
//Pi Pico specific libs // Pi Pico specific libs
#include <EEPROM.h> // EEPROM emulation in flash, part of Earl E Philhowers Pi Pico Arduino support #include <EEPROM.h> // EEPROM emulation in flash, part of Earl E Philhowers Pi Pico Arduino support
#include <pico/unique_id.h> // from Pico SDK #include <hardware/flash.h> // from Pico SDK
#include <hardware/watchdog.h> // from Pico SDK #include <hardware/watchdog.h> // from Pico SDK
#include <hardware/flash.h> // from Pico SDK #include <pico/unique_id.h> // from Pico SDK
#ifdef USE_KNX_DMA_UART
#include <hardware/dma.h>
// 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) #define FLASHPTR ((uint8_t*)XIP_BASE + KNX_FLASH_OFFSET)
#ifndef USE_RP2040_EEPROM_EMULATION #ifndef USE_RP2040_EEPROM_EMULATION
#if KNX_FLASH_SIZE%4096 #if KNX_FLASH_SIZE % 4096
#error "KNX_FLASH_SIZE must be multiple of 4096" #error "KNX_FLASH_SIZE must be multiple of 4096"
#endif #endif
#if KNX_FLASH_OFFSET%4096 #if KNX_FLASH_OFFSET % 4096
#error "KNX_FLASH_OFFSET must be multiple of 4096" #error "KNX_FLASH_OFFSET must be multiple of 4096"
#endif #endif
#endif #endif
@ -57,26 +114,27 @@ extern Wiznet5500lwIP KNX_NETIF;
#endif #endif
RP2040ArduinoPlatform::RP2040ArduinoPlatform() RP2040ArduinoPlatform::RP2040ArduinoPlatform()
#ifndef KNX_NO_DEFAULT_UART #if !defined(KNX_NO_DEFAULT_UART) && !defined(USE_KNX_DMA_UART)
: ArduinoPlatform(&KNX_SERIAL) : ArduinoPlatform(&KNX_SERIAL)
#endif #endif
{ {
#ifdef KNX_UART_RX_PIN #ifdef KNX_UART_RX_PIN
_rxPin = KNX_UART_RX_PIN; _rxPin = KNX_UART_RX_PIN;
#endif #endif
#ifdef KNX_UART_TX_PIN #ifdef KNX_UART_TX_PIN
_txPin = KNX_UART_TX_PIN; _txPin = KNX_UART_TX_PIN;
#endif #endif
#ifndef USE_RP2040_EEPROM_EMULATION #ifndef USE_RP2040_EEPROM_EMULATION
_memoryType = Flash; _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; _memoryType = Flash;
#endif #endif
} }
void RP2040ArduinoPlatform::knxUartPins(pin_size_t rxPin, pin_size_t txPin) 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; _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<SerialUART*>(_knxSerial); SerialUART* serial = dynamic_cast<SerialUART*>(_knxSerial);
return serial->overflow(); return serial->overflow();
#endif
} }
void RP2040ArduinoPlatform::setupUart() 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<SerialUART*>(_knxSerial); SerialUART* serial = dynamic_cast<SerialUART*>(_knxSerial);
if(serial) if (serial)
{ {
if (_rxPin != UART_PIN_NOT_DEFINED) if (_rxPin != UART_PIN_NOT_DEFINED)
serial->setRX(_rxPin); serial->setRX(_rxPin);
if (_txPin != UART_PIN_NOT_DEFINED) if (_txPin != UART_PIN_NOT_DEFINED)
serial->setTX(_txPin); serial->setTX(_txPin);
serial->setPollingMode(); serial->setPollingMode();
serial->setFIFOSize(64);
} }
_knxSerial->begin(19200, SERIAL_8E1); _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() 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(); noInterrupts();
rp2040.idleOtherCore(); 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(); rp2040.resumeOtherCore();
interrupts(); interrupts();
@ -128,7 +312,7 @@ uint32_t RP2040ArduinoPlatform::uniqueSerialNumber()
void RP2040ArduinoPlatform::restart() void RP2040ArduinoPlatform::restart()
{ {
println("restart"); println("restart");
watchdog_reboot(0,0,0); watchdog_reboot(0, 0, 0);
} }
#ifdef USE_RP2040_EEPROM_EMULATION #ifdef USE_RP2040_EEPROM_EMULATION
@ -137,20 +321,20 @@ void RP2040ArduinoPlatform::restart()
#ifdef USE_RP2040_LARGE_EEPROM_EMULATION #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"); println("KNX_FLASH_SIZE must be a multiple of 4096");
fatalError(); fatalError();
} }
if(!_rambuff_initialized) if (!_rambuff_initialized)
{ {
memcpy(_rambuff, FLASHPTR, KNX_FLASH_SIZE); memcpy(_rambuff, FLASHPTR, KNX_FLASH_SIZE);
_rambuff_initialized = true; _rambuff_initialized = true;
} }
return _rambuff; return _rambuff;
} }
@ -159,10 +343,10 @@ void RP2040ArduinoPlatform::commitToEeprom()
noInterrupts(); noInterrupts();
rp2040.idleOtherCore(); rp2040.idleOtherCore();
//ToDo: write block-by-block to prevent writing of untouched blocks // ToDo: write block-by-block to prevent writing of untouched blocks
if(memcmp(_rambuff, FLASHPTR, KNX_FLASH_SIZE)) 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); flash_range_program(KNX_FLASH_OFFSET, _rambuff, KNX_FLASH_SIZE);
} }
@ -172,22 +356,22 @@ void RP2040ArduinoPlatform::commitToEeprom()
#else #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)"); println("KNX_FLASH_SIZE to big for EEPROM emulation (max. 4kB)");
fatalError(); fatalError();
} }
uint8_t * eepromptr = EEPROM.getDataPtr();
if(eepromptr == nullptr) uint8_t* eepromptr = EEPROM.getDataPtr();
if (eepromptr == nullptr)
{ {
EEPROM.begin(4096); EEPROM.begin(4096);
eepromptr = EEPROM.getDataPtr(); eepromptr = EEPROM.getDataPtr();
} }
return eepromptr; return eepromptr;
} }
@ -217,10 +401,10 @@ uint8_t* RP2040ArduinoPlatform::userFlashStart()
size_t RP2040ArduinoPlatform::userFlashSizeEraseBlocks() size_t RP2040ArduinoPlatform::userFlashSizeEraseBlocks()
{ {
if(KNX_FLASH_SIZE <= 0) if (KNX_FLASH_SIZE <= 0)
return 0; return 0;
else else
return ( (KNX_FLASH_SIZE - 1) / (flashPageSize() * flashEraseBlockSize())) + 1; return ((KNX_FLASH_SIZE - 1) / (flashPageSize() * flashEraseBlockSize())) + 1;
} }
void RP2040ArduinoPlatform::flashErase(uint16_t eraseBlockNum) void RP2040ArduinoPlatform::flashErase(uint16_t eraseBlockNum)
@ -228,7 +412,7 @@ void RP2040ArduinoPlatform::flashErase(uint16_t eraseBlockNum)
noInterrupts(); noInterrupts();
rp2040.idleOtherCore(); 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(); rp2040.resumeOtherCore();
interrupts(); interrupts();
@ -247,12 +431,12 @@ void RP2040ArduinoPlatform::flashWritePage(uint16_t pageNumber, uint8_t* data)
void RP2040ArduinoPlatform::writeBufferedEraseBlock() void RP2040ArduinoPlatform::writeBufferedEraseBlock()
{ {
if(_bufferedEraseblockNumber > -1 && _bufferedEraseblockDirty) if (_bufferedEraseblockNumber > -1 && _bufferedEraseblockDirty)
{ {
noInterrupts(); noInterrupts();
rp2040.idleOtherCore(); 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()); flash_range_program(KNX_FLASH_OFFSET + _bufferedEraseblockNumber * flashPageSize() * flashEraseBlockSize(), _eraseblockBuffer, flashPageSize() * flashEraseBlockSize());
rp2040.resumeOtherCore(); rp2040.resumeOtherCore();
@ -281,7 +465,7 @@ void RP2040ArduinoPlatform::macAddress(uint8_t* addr)
#if defined(KNX_IP_W5500) #if defined(KNX_IP_W5500)
addr = KNX_NETIF.getNetIf()->hwaddr; addr = KNX_NETIF.getNetIf()->hwaddr;
#elif defined(KNX_IP_WIFI) #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); addr = KNX_NETIF.macAddress(macaddr);
#elif defined(KNX_IP_GENERIC) #elif defined(KNX_IP_GENERIC)
KNX_NETIF.MACAddress(addr); KNX_NETIF.MACAddress(addr);
@ -294,12 +478,12 @@ void RP2040ArduinoPlatform::setupMultiCast(uint32_t addr, uint16_t port)
mcastaddr = IPAddress(htonl(addr)); mcastaddr = IPAddress(htonl(addr));
_port = port; _port = port;
uint8_t result = _udp.beginMulticast(mcastaddr, port); uint8_t result = _udp.beginMulticast(mcastaddr, port);
(void) result; (void)result;
#ifdef KNX_IP_GENERIC #ifdef KNX_IP_GENERIC
//if(!_unicast_socket_setup) // if(!_unicast_socket_setup)
// _unicast_socket_setup = UDP_UNICAST.begin(3671); // _unicast_socket_setup = UDP_UNICAST.begin(3671);
#endif #endif
// print("Setup Mcast addr: "); // print("Setup Mcast addr: ");
// print(mcastaddr.toString().c_str()); // print(mcastaddr.toString().c_str());
@ -318,7 +502,7 @@ bool RP2040ArduinoPlatform::sendBytesMultiCast(uint8_t* buffer, uint16_t len)
{ {
// printHex("<- ",buffer, 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.beginPacket(mcastaddr, _port);
_udp.write(buffer, len); _udp.write(buffer, len);
_udp.endPacket(); _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) bool RP2040ArduinoPlatform::sendBytesUniCast(uint32_t addr, uint16_t port, uint8_t* buffer, uint16_t len)
{ {
IPAddress ucastaddr(htonl(addr)); IPAddress ucastaddr(htonl(addr));
// print("sendBytesUniCast to:"); // print("sendBytesUniCast to:");
// println(ucastaddr.toString().c_str()); // println(ucastaddr.toString().c_str());
#ifdef KNX_IP_GENERIC #ifdef KNX_IP_GENERIC
if(!_unicast_socket_setup) if (!_unicast_socket_setup)
_unicast_socket_setup = UDP_UNICAST.begin(3671); _unicast_socket_setup = UDP_UNICAST.begin(3671);
#endif #endif
@ -376,5 +560,3 @@ bool RP2040ArduinoPlatform::sendBytesUniCast(uint32_t addr, uint16_t port, uint8
#endif #endif
#endif #endif

View File

@ -58,6 +58,22 @@
#endif #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 class RP2040ArduinoPlatform : public ArduinoPlatform
{ {
@ -67,9 +83,20 @@ public:
// uart // uart
void knxUartPins(pin_size_t rxPin, pin_size_t txPin); void knxUartPins(pin_size_t rxPin, pin_size_t txPin);
void setupUart(); void setupUart() override;
virtual bool overflowUart(); 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 // unique serial number
uint32_t uniqueSerialNumber() override; uint32_t uniqueSerialNumber() override;