knx
ETS configurable knx-stack
rp2040_arduino_platform.cpp
Go to the documentation of this file.
1 /*-----------------------------------------------------
2 
3 Plattform for Raspberry Pi Pico and other RP2040 boards
4 by SirSydom <com@sirsydom.de> 2021-2022
5 
6 made to work with arduino-pico - "Raspberry Pi Pico Arduino core, for all RP2040 boards"
7 by Earl E. Philhower III https://github.com/earlephilhower/arduino-pico
8 
9 
10 RTTI must be set to enabled in the board options
11 
12 Uses direct flash reading/writing.
13 Size ist defined by KNX_FLASH_SIZE (default 4k) - must be a multiple of 4096.
14 Offset in Flash is defined by KNX_FLASH_OFFSET (default 1,5MiB / 0x180000) - must be a multiple of 4096.
15 
16 EEPROM Emulation from arduino-pico core (max 4k) can be use by defining USE_RP2040_EEPROM_EMULATION
17 
18 A RAM-buffered Flash can be use by defining USE_RP2040_LARGE_EEPROM_EMULATION
19 
20 For usage of KNX-IP you have to define either
21 - KNX_IP_LAN (use the arduino-pico core's w5500 lwip stack)
22 - KNX_IP_WIFI (use the arduino-pico core's PiPicoW lwip stack)
23 
24 ----------------------------------------------------*/
25 
27 
28 #ifdef ARDUINO_ARCH_RP2040
29 #include "knx/bits.h"
30 
31 #include <Arduino.h>
32 
33 // Pi Pico specific libs
34 #include <EEPROM.h> // EEPROM emulation in flash, part of Earl E Philhowers Pi Pico Arduino support
35 #include <hardware/flash.h> // from Pico SDK
36 #include <hardware/watchdog.h> // from Pico SDK
37 #include <pico/unique_id.h> // from Pico SDK
38 
39 #ifdef USE_KNX_DMA_UART
40 #include <hardware/dma.h>
41 // constexpr uint32_t uartDmaTransferCount = 0b1111111111;
42 constexpr uint32_t uartDmaTransferCount = UINT32_MAX;
43 constexpr uint8_t uartDmaBufferExp = 8u; // 2**BufferExp
44 constexpr uint16_t uartDmaBufferSize = (1u << uartDmaBufferExp);
45 int8_t uartDmaChannel = -1;
46 volatile uint8_t __attribute__((aligned(uartDmaBufferSize))) uartDmaBuffer[uartDmaBufferSize] = {};
47 volatile uint32_t uartDmaReadCount = 0;
48 volatile uint16_t uartDmaRestartCount = 0;
49 volatile uint32_t uartDmaWriteCount2 = 0;
50 volatile uint32_t uartDmaAvail = 0;
51 
52 // Returns the number of bytes read since the DMA transfer start
53 inline uint32_t uartDmaWriteCount()
54 {
55  uartDmaWriteCount2 = uartDmaTransferCount - dma_channel_hw_addr(uartDmaChannel)->transfer_count;
56  return uartDmaWriteCount2;
57 }
58 
59 // Returns the current write position in the DMA buffer
60 inline uint16_t uartDmaWriteBufferPosition()
61 {
63 }
64 
65 // Returns the current read position in the DMA buffer
66 inline uint16_t uartDmaReadBufferPosition()
67 {
69 }
70 
71 // Returns the current reading position as a pointer
72 inline uint8_t* uartDmaReadAddr()
73 {
74  return ((uint8_t*)uartDmaBuffer + uartDmaReadBufferPosition());
75 }
76 
77 // Restarts the transfer after completion.
78 void __time_critical_func(uartDmaRestart)()
79 {
80  // println("Restart");
82 
83  // if uartDmaRestartCount == 0, everything has been processed and the read count can be set to 0 again with the restart.
84  if (uartDmaRestartCount == 0)
85  {
86  uartDmaReadCount = 0;
87  }
88 
89  asm volatile("" ::: "memory");
90  dma_hw->ints0 = 1u << uartDmaChannel; // clear DMA IRQ0 flag
91  asm volatile("" ::: "memory");
92  dma_channel_set_write_addr(uartDmaChannel, uartDmaBuffer, true);
93 }
94 #endif
95 
96 #define FLASHPTR ((uint8_t*)XIP_BASE + KNX_FLASH_OFFSET)
97 
98 #ifndef USE_RP2040_EEPROM_EMULATION
99  #if KNX_FLASH_SIZE % 4096
100  #error "KNX_FLASH_SIZE must be multiple of 4096"
101  #endif
102 
103  #if KNX_FLASH_OFFSET % 4096
104  #error "KNX_FLASH_OFFSET must be multiple of 4096"
105  #endif
106 #endif
107 
108 #ifdef KNX_IP_LAN
109  extern Wiznet5500lwIP KNX_NETIF;
110 #elif defined(KNX_IP_WIFI)
111 #elif defined(KNX_IP_GENERIC)
112 
113 #endif
114 
116 #if !defined(KNX_NO_DEFAULT_UART) && !defined(USE_KNX_DMA_UART)
117  : ArduinoPlatform(&KNX_SERIAL)
118 #endif
119 {
120 #ifdef KNX_UART_RX_PIN
121  _rxPin = KNX_UART_RX_PIN;
122 #endif
123 #ifdef KNX_UART_TX_PIN
124  _txPin = KNX_UART_TX_PIN;
125 #endif
126 #ifndef USE_RP2040_EEPROM_EMULATION
127  _memoryType = Flash;
128 #endif
129 }
130 
132  : ArduinoPlatform(s)
133 {
134 #ifndef USE_RP2040_EEPROM_EMULATION
135  _memoryType = Flash;
136 #endif
137 }
138 
139 void RP2040ArduinoPlatform::knxUartPins(pin_size_t rxPin, pin_size_t txPin)
140 {
141  _rxPin = rxPin;
142  _txPin = txPin;
143 }
144 
146 {
147 #ifdef USE_KNX_DMA_UART
148  // during dma restart
149  bool ret;
150  const uint32_t writeCount = uartDmaWriteCount();
151 
152  if (uartDmaRestartCount > 0)
153  ret = writeCount >= (uartDmaBufferSize - uartDmaRestartCount - 1);
154  else
155  ret = (writeCount - uartDmaReadCount) > uartDmaBufferSize;
156 
157  // if (ret)
158  // {
159  // println(uartDmaWriteBufferPosition());
160  // println(uartDmaReadBufferPosition());
161  // println(uartDmaWriteCount());
162  // println(uartDmaReadCount);
163  // println(uartDmaRestartCount);
164  // printHex("BUF: ", (const uint8_t *)uartDmaBuffer, uartDmaBufferSize);
165  // println("OVERFLOW");
166  // while (true)
167  // ;
168  // }
169  return ret;
170 #else
171  SerialUART* serial = dynamic_cast<SerialUART*>(_knxSerial);
172  return serial->overflow();
173 #endif
174 }
175 
177 {
178 #ifdef USE_KNX_DMA_UART
179 
180  if (uartDmaChannel == -1)
181  {
182  // configure uart0
183  gpio_set_function(_rxPin, GPIO_FUNC_UART);
184  gpio_set_function(_txPin, GPIO_FUNC_UART);
185  uart_init(KNX_DMA_UART, 19200);
186  uart_set_hw_flow(KNX_DMA_UART, false, false);
187  uart_set_format(KNX_DMA_UART, 8, 1, UART_PARITY_EVEN);
188  uart_set_fifo_enabled(KNX_DMA_UART, false);
189 
190  // configure uart0
191  uartDmaChannel = dma_claim_unused_channel(true); // get free channel for dma
192  dma_channel_config dmaConfig = dma_channel_get_default_config(uartDmaChannel);
193  channel_config_set_transfer_data_size(&dmaConfig, DMA_SIZE_8);
194  channel_config_set_read_increment(&dmaConfig, false);
195  channel_config_set_write_increment(&dmaConfig, true);
196  channel_config_set_high_priority(&dmaConfig, true);
197  channel_config_set_ring(&dmaConfig, true, uartDmaBufferExp);
198  channel_config_set_dreq(&dmaConfig, KNX_DMA_UART_DREQ);
199  dma_channel_set_read_addr(uartDmaChannel, &uart_get_hw(uart0)->dr, false);
200  dma_channel_set_write_addr(uartDmaChannel, uartDmaBuffer, false);
201  dma_channel_set_trans_count(uartDmaChannel, uartDmaTransferCount, false);
202  dma_channel_set_config(uartDmaChannel, &dmaConfig, true);
203  dma_channel_set_irq1_enabled(uartDmaChannel, true);
204  // irq_add_shared_handler(KNX_DMA_IRQ, uartDmaRestart, PICO_SHARED_IRQ_HANDLER_HIGHEST_ORDER_PRIORITY);
205  irq_set_exclusive_handler(KNX_DMA_IRQ, uartDmaRestart);
206  irq_set_enabled(KNX_DMA_IRQ, true);
207  }
208 
209 #else
210  SerialUART* serial = dynamic_cast<SerialUART*>(_knxSerial);
211 
212  if (serial)
213  {
214  if (_rxPin != UART_PIN_NOT_DEFINED)
215  serial->setRX(_rxPin);
216 
217  if (_txPin != UART_PIN_NOT_DEFINED)
218  serial->setTX(_txPin);
219 
220  serial->setPollingMode();
221  serial->setFIFOSize(64);
222  }
223 
224  _knxSerial->begin(19200, SERIAL_8E1);
225 
226  while (!_knxSerial)
227  ;
228 
229 #endif
230 }
231 
232 #ifdef USE_KNX_DMA_UART
234 {
235  if (uartDmaChannel == -1)
236  return 0;
237 
238  if (uartDmaRestartCount > 0)
239  {
240  return uartDmaRestartCount;
241  }
242  else
243  {
244  uint32_t tc = dma_channel_hw_addr(uartDmaChannel)->transfer_count;
245  uartDmaAvail = tc;
246  int test = uartDmaTransferCount - tc - uartDmaReadCount;
247  return test;
248  }
249 }
250 
252 {
253  if (!uartAvailable())
254  return -1;
255 
256  int ret = uartDmaReadAddr()[0];
257  // print("< ");
258  // println(ret, HEX);
260 
261  if (uartDmaRestartCount > 0)
262  {
263  // process previouse buffer
265 
266  // last char, then reset read count to start at new writer position
267  if (uartDmaRestartCount == 0)
268  uartDmaReadCount = 0;
269  }
270 
271  return ret;
272 }
273 
274 size_t RP2040ArduinoPlatform::writeUart(const uint8_t data)
275 {
276  if (uartDmaChannel == -1)
277  return 0;
278 
279  // print("> ");
280  // println(data, HEX);
281  while (!uart_is_writable(uart0))
282  ;
283 
284  uart_putc_raw(uart0, data);
285  return 1;
286 }
287 
289 {
290  if (uartDmaChannel >= 0)
291  {
292  dma_channel_cleanup(uartDmaChannel);
293  irq_set_enabled(DMA_IRQ_0, false);
294  uart_deinit(uart0);
295  uartDmaChannel = -1;
296  uartDmaReadCount = 0;
298  }
299 }
300 #endif
301 
303 {
304  pico_unique_board_id_t id; // 64Bit unique serial number from the QSPI flash
305 
306  noInterrupts();
307  rp2040.idleOtherCore();
308 
309  flash_get_unique_id(id.id); // pico_get_unique_board_id(&id);
310 
311  rp2040.resumeOtherCore();
312  interrupts();
313 
314  // use lower 4 byte and convert to unit32_t
315  uint32_t uid = ((uint32_t)(id.id[4]) << 24) | ((uint32_t)(id.id[5]) << 16) | ((uint32_t)(id.id[6]) << 8) | (uint32_t)(id.id[7]);
316 
317  return uid;
318 }
319 
321 {
322  println("restart");
323  watchdog_reboot(0, 0, 0);
324 }
325 
326 #ifdef USE_RP2040_EEPROM_EMULATION
327 
328 #pragma warning "Using EEPROM Simulation"
329 
330 #ifdef USE_RP2040_LARGE_EEPROM_EMULATION
331 
333 {
334  if (size % 4096)
335  {
336  println("KNX_FLASH_SIZE must be a multiple of 4096");
337  fatalError();
338  }
339 
341  {
342  memcpy(_rambuff, FLASHPTR, KNX_FLASH_SIZE);
343  _rambuff_initialized = true;
344  }
345 
346  return _rambuff;
347 }
348 
350 {
351  noInterrupts();
352  rp2040.idleOtherCore();
353 
354  // ToDo: write block-by-block to prevent writing of untouched blocks
355  if (memcmp(_rambuff, FLASHPTR, KNX_FLASH_SIZE))
356  {
357  flash_range_erase(KNX_FLASH_OFFSET, KNX_FLASH_SIZE);
358  flash_range_program(KNX_FLASH_OFFSET, _rambuff, KNX_FLASH_SIZE);
359  }
360 
361  rp2040.resumeOtherCore();
362  interrupts();
363 }
364 
365 #else
366 
367 uint8_t* RP2040ArduinoPlatform::getEepromBuffer(uint32_t size)
368 {
369  if (size > 4096)
370  {
371  println("KNX_FLASH_SIZE to big for EEPROM emulation (max. 4kB)");
372  fatalError();
373  }
374 
375  uint8_t* eepromptr = EEPROM.getDataPtr();
376 
377  if (eepromptr == nullptr)
378  {
379  EEPROM.begin(4096);
380  eepromptr = EEPROM.getDataPtr();
381  }
382 
383  return eepromptr;
384 }
385 
387 {
388  EEPROM.commit();
389 }
390 
391 #endif
392 
393 #else
394 
396 {
397  return 16; // 16 pages x 256byte/page = 4096byte
398 }
399 
401 {
402  return 256;
403 }
404 
406 {
407  return (uint8_t*)XIP_BASE + KNX_FLASH_OFFSET;
408 }
409 
411 {
412  if (KNX_FLASH_SIZE <= 0)
413  return 0;
414  else
415  return ((KNX_FLASH_SIZE - 1) / (flashPageSize() * flashEraseBlockSize())) + 1;
416 }
417 
418 void RP2040ArduinoPlatform::flashErase(uint16_t eraseBlockNum)
419 {
420  noInterrupts();
421  rp2040.idleOtherCore();
422 
423  flash_range_erase(KNX_FLASH_OFFSET + eraseBlockNum * flashPageSize() * flashEraseBlockSize(), flashPageSize() * flashEraseBlockSize());
424 
425  rp2040.resumeOtherCore();
426  interrupts();
427 }
428 
429 void RP2040ArduinoPlatform::flashWritePage(uint16_t pageNumber, uint8_t* data)
430 {
431  noInterrupts();
432  rp2040.idleOtherCore();
433 
434  flash_range_program(KNX_FLASH_OFFSET + pageNumber * flashPageSize(), data, flashPageSize());
435 
436  rp2040.resumeOtherCore();
437  interrupts();
438 }
439 
441 {
443  {
444  noInterrupts();
445  rp2040.idleOtherCore();
446 
447  flash_range_erase(KNX_FLASH_OFFSET + _bufferedEraseblockNumber * flashPageSize() * flashEraseBlockSize(), flashPageSize() * flashEraseBlockSize());
449 
450  rp2040.resumeOtherCore();
451  interrupts();
452 
453  _bufferedEraseblockDirty = false;
454  }
455 }
456 #endif
457 
458 #if defined(KNX_NETIF)
460 {
461  return KNX_NETIF.localIP();
462 }
464 {
465  return KNX_NETIF.subnetMask();
466 }
468 {
469  return KNX_NETIF.gatewayIP();
470 }
472 {
473  KNX_NETIF.macAddress(addr);
474 }
475 
476 // multicast
477 void RP2040ArduinoPlatform::setupMultiCast(uint32_t addr, uint16_t port)
478 {
479  mcastaddr = IPAddress(htonl(addr));
480  _port = port;
481  uint8_t result = _udp.beginMulticast(mcastaddr, port);
482  (void)result;
483 
484 #ifdef KNX_IP_GENERIC
485  // if(!_unicast_socket_setup)
486  // _unicast_socket_setup = UDP_UNICAST.begin(3671);
487 #endif
488 
489  // print("Setup Mcast addr: ");
490  // print(mcastaddr.toString().c_str());
491  // print(" on port: ");
492  // print(port);
493  // print(" result ");
494  // println(result);
495 }
496 
498 {
499  _udp.stop();
500 }
501 
502 bool RP2040ArduinoPlatform::sendBytesMultiCast(uint8_t* buffer, uint16_t len)
503 {
504  // printHex("<- ",buffer, len);
505 
506  // ToDo: check if Ethernet is able to receive, return false if not
507  _udp.beginPacket(mcastaddr, _port);
508  _udp.write(buffer, len);
509  _udp.endPacket();
510  return true;
511 }
512 
513 int RP2040ArduinoPlatform::readBytesMultiCast(uint8_t* buffer, uint16_t maxLen, uint32_t& src_addr, uint16_t& src_port)
514 {
515  int len = _udp.parsePacket();
516 
517  if (len == 0)
518  return 0;
519 
520  if (len > maxLen)
521  {
522  println("Unexpected UDP data packet length - drop packet");
523 
524  for (size_t i = 0; i < len; i++)
525  _udp.read();
526 
527  return 0;
528  }
529 
530  _udp.read(buffer, len);
531  _remoteIP = _udp.remoteIP();
532  _remotePort = _udp.remotePort();
533  src_addr = htonl(_remoteIP);
534  src_port = _remotePort;
535 
536  // print("Remote IP: ");
537  // print(_udp.remoteIP().toString().c_str());
538  // printHex("-> ", buffer, len);
539 
540  return len;
541 }
542 
543 // unicast
544 bool RP2040ArduinoPlatform::sendBytesUniCast(uint32_t addr, uint16_t port, uint8_t* buffer, uint16_t len)
545 {
546  IPAddress ucastaddr(htonl(addr));
547 
548  if (!addr)
549  ucastaddr = _remoteIP;
550 
551  if (!port)
552  port = _remotePort;
553 
554  // print("sendBytesUniCast to:");
555  // println(ucastaddr.toString().c_str());
556 
557 #ifdef KNX_IP_GENERIC
558 
559  if (!_unicast_socket_setup)
560  _unicast_socket_setup = UDP_UNICAST.begin(3671);
561 
562 #endif
563 
564  if (UDP_UNICAST.beginPacket(ucastaddr, port) == 1)
565  {
566  UDP_UNICAST.write(buffer, len);
567 
568  if (UDP_UNICAST.endPacket() == 0)
569  println("sendBytesUniCast endPacket fail");
570  }
571  else
572  println("sendBytesUniCast beginPacket fail");
573 
574  return true;
575 }
576 #endif
577 
578 #endif
void println(const char *s)
HardwareSerial * _knxSerial
uint8_t * _eraseblockBuffer
Definition: platform.h:142
NvMemoryType _memoryType
Definition: platform.h:130
int32_t _bufferedEraseblockNumber
Definition: platform.h:143
bool _bufferedEraseblockDirty
Definition: platform.h:144
int readBytesMultiCast(uint8_t *buffer, uint16_t maxLen, uint32_t &src_addr, uint16_t &src_port) override
virtual void flashErase(uint16_t eraseBlockNum)
bool sendBytesUniCast(uint32_t addr, uint16_t port, uint8_t *buffer, uint16_t len) override
void macAddress(uint8_t *addr) override
size_t writeUart(const uint8_t data) override
void knxUartPins(pin_size_t rxPin, pin_size_t txPin)
void setupMultiCast(uint32_t addr, uint16_t port) override
uint32_t uniqueSerialNumber() override
uint8_t * getEepromBuffer(uint32_t size)
virtual void flashWritePage(uint16_t pageNumber, uint8_t *data)
uint8_t _rambuff[KNX_FLASH_SIZE]
uint32_t currentIpAddress() override
virtual uint8_t * userFlashStart()
uint32_t currentSubnetMask() override
virtual size_t userFlashSizeEraseBlocks()
bool sendBytesMultiCast(uint8_t *buffer, uint16_t len) override
uint32_t currentDefaultGateway() override
@ Flash
Definition: platform.h:27
constexpr uint32_t uartDmaTransferCount
Wiznet5500lwIP KNX_NETIF
uint32_t uartDmaWriteCount()
constexpr uint16_t uartDmaBufferSize
constexpr uint8_t uartDmaBufferExp
void __time_critical_func() uartDmaRestart()
int8_t uartDmaChannel
uint16_t uartDmaWriteBufferPosition()
volatile uint32_t uartDmaReadCount
uint16_t uartDmaReadBufferPosition()
uint8_t * uartDmaReadAddr()
volatile uint8_t __attribute__((aligned(uartDmaBufferSize))) uartDmaBuffer[uartDmaBufferSize]
volatile uint32_t uartDmaAvail
volatile uint16_t uartDmaRestartCount
volatile uint32_t uartDmaWriteCount2