From 383ba407baf75f59f20c2ab7981a167ae445fe3f Mon Sep 17 00:00:00 2001 From: Maxint R&D Date: Sun, 28 Jul 2024 08:43:09 +0200 Subject: [PATCH 1/7] Added interrupt handler to fill serial read buffer of Serial1/USART1 --- cores/arduino/HardwareSerial.cpp | 134 ++++++++++++++++++++++++++++++- cores/arduino/HardwareSerial.h | 33 +++++++- 2 files changed, 159 insertions(+), 8 deletions(-) diff --git a/cores/arduino/HardwareSerial.cpp b/cores/arduino/HardwareSerial.cpp index 8554ded7..b55544a7 100644 --- a/cores/arduino/HardwareSerial.cpp +++ b/cores/arduino/HardwareSerial.cpp @@ -57,6 +57,31 @@ void HardwareSerial::init(PinName _rx, PinName _tx, PinName _rts, PinName _cts) } +// Interrupt handler for filling rx buffer ///////////////////////////////////// +#if(OPT_USART1_INT==1) + +#if defined(USART1) + #ifdef __cplusplus + extern "C" { + #endif + void USART1_IRQHandler(void) __attribute__((interrupt("WCH-Interrupt-fast"))); + void USART1_IRQHandler(void) { + USART_ClearITPendingBit(USART1, USART_IT_RXNE); + // Use the proper serial object to fill the RX buffer. Perhaps we should use uart_handlers[] as defined in uart.c + // Serial is most often Serial1, initialized below as HardwareSerial Serial1(USART1); DEBUG_UART may give issues. + // TODO? get_serial_obj(uart_handlers[UART1_INDEX]); + HardwareSerial *obj=&Serial1; + obj->_rx_buffer[obj->_rx_buffer_head] = USART_ReceiveData(USART1); // maybe we should use uart_getc()? + obj->_rx_buffer_head++; + obj->_rx_buffer_head %= SERIAL_RX_BUFFER_SIZE; + } + #ifdef __cplusplus + } + #endif +#endif + +#endif + // Public Methods ////////////////////////////////////////////////////////////// void HardwareSerial::begin(unsigned long baud, byte config) @@ -138,32 +163,133 @@ void HardwareSerial::begin(unsigned long baud, byte config) break; } uart_init(&_serial, (uint32_t)baud, databits, parity, stopbits); + +#if(OPT_USART1_INT==1) + // MMOLE 240619: Enable interrupt handler for filling rx buffer + #if defined(USART1) + USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); + NVIC_SetPriority(USART1_IRQn, UART_IRQ_PRIO); + NVIC_EnableIRQ(USART1_IRQn); + #endif + // MMOLE TODO: I only have CH32V003; only tested USART1, how about others? +#endif } + +#if(OPT_USART1_INT==1) +#else +// MMOLE: reintroduced RX buffer to properly implement read/available/peek methods +void HardwareSerial::fillRxBuffer(void) // private method: read all characters that can be read +{ + // Fill RX buffer during read/available calls + // Newly received characters are added to the buffer + // _rx_buffer_head is the location of the new character + // _rx_buffer_tail is the location of the oldest character that is not read yet + unsigned char c; +/* + while(uart_getc(&_serial, &c) == 0) + { + _rx_buffer[_rx_buffer_head]=c; + _rx_buffer_head = (rx_buffer_index_t)(_rx_buffer_head + 1) % SERIAL_RX_BUFFER_SIZE; + } +*/ + // To avoid buffer underruns, we try to read during at least a few millis. + // Perhaps there is a better way, but for now it works. + // Maybe we should also do something to handle disruption of interrupts +/* + #define SERIAL_WAIT_FOR_RX 5000L + uint32_t uStart=micros(); + while((micros()-uStart) @@ -101,6 +106,9 @@ typedef enum { class HardwareSerial : public Stream { +#if(OPT_USART1_INT==1) +public: +#endif serial_t _serial; public: HardwareSerial(void *peripheral); @@ -109,6 +117,17 @@ class HardwareSerial : public Stream { { begin(baud, SERIAL_8N1); //SERIAL_9E1_5 SERIAL_8N1 } + // MMOLE: reintroduced RX buffer to properly implement read/available/peek methods + volatile rx_buffer_index_t _rx_buffer_head; + volatile rx_buffer_index_t _rx_buffer_tail; + //volatile tx_buffer_index_t _tx_buffer_head; + //volatile tx_buffer_index_t _tx_buffer_tail; + + // Don't put any members after these buffers, since only the first + // 32 bytes of this struct can be accessed quickly using the ldd + // instruction. + unsigned char _rx_buffer[SERIAL_RX_BUFFER_SIZE]; + //unsigned char _tx_buffer[SERIAL_TX_BUFFER_SIZE]; void begin(unsigned long, uint8_t); void end(); @@ -159,6 +178,12 @@ class HardwareSerial : public Stream { uint8_t _config; unsigned long _baud; void init(PinName _rx, PinName _tx, PinName _rts = NC, PinName _cts = NC); + + // MMOLE: reintroduced RX buffer to properly implement read/available/peek methods + #if(OPT_USART1_INT==1) + #else + void fillRxBuffer(void); // read all characters that can be read + #endif }; #if defined(USART1) @@ -173,16 +198,16 @@ class HardwareSerial : public Stream { #if defined(UART4) || defined(USART4) extern HardwareSerial Serial4; #endif -#if defined(UART5) +#if defined(UART5) || defined(USART5) extern HardwareSerial Serial5; #endif -#if defined(UART6) +#if defined(USART6) extern HardwareSerial Serial6; #endif -#if defined(UART7) +#if defined(UART7) || defined(USART7) extern HardwareSerial Serial7; #endif -#if defined(UART8) +#if defined(UART8) || defined(USART8) extern HardwareSerial Serial8; #endif From a7538746f67a260e68167f695c6045039575e733 Mon Sep 17 00:00:00 2001 From: Maxint R&D Date: Sun, 28 Jul 2024 08:53:31 +0200 Subject: [PATCH 2/7] Removed clumsy polling implementation --- cores/arduino/HardwareSerial.h | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/cores/arduino/HardwareSerial.h b/cores/arduino/HardwareSerial.h index 62904abf..000b5feb 100644 --- a/cores/arduino/HardwareSerial.h +++ b/cores/arduino/HardwareSerial.h @@ -19,17 +19,16 @@ Modified 28 September 2010 by Mark Sproul Modified 14 August 2012 by Alarus Modified 3 December 2013 by Matthijs Kooijman - Modified 1 may 2023 by TempersLee - Modified 13 October 2023 by Maxint R&D + Modified 1 May 2023 by TempersLee + Modified 28 July 2024 by Maxint R&D */ #ifndef HardwareSerial_h #define HardwareSerial_h -// MMOLE 240619: set OPT_USART1_INT to 1 if you want to use interrupts for receiving serial data instead of a clumsy polling implementation. +// MMOLE 240619: set OPT_USART1_INT to 1 if you want to use interrupts for receiving serial data. #define OPT_USART1_INT 1 - #if 1 #include @@ -178,12 +177,6 @@ class HardwareSerial : public Stream { uint8_t _config; unsigned long _baud; void init(PinName _rx, PinName _tx, PinName _rts = NC, PinName _cts = NC); - - // MMOLE: reintroduced RX buffer to properly implement read/available/peek methods - #if(OPT_USART1_INT==1) - #else - void fillRxBuffer(void); // read all characters that can be read - #endif }; #if defined(USART1) @@ -201,7 +194,7 @@ class HardwareSerial : public Stream { #if defined(UART5) || defined(USART5) extern HardwareSerial Serial5; #endif -#if defined(USART6) +#if defined(USART6) || defined(USART6) extern HardwareSerial Serial6; #endif #if defined(UART7) || defined(USART7) From d605c9d113b148f33216cc4cc84dcc32b23862e1 Mon Sep 17 00:00:00 2001 From: Maxint R&D Date: Sun, 28 Jul 2024 09:02:37 +0200 Subject: [PATCH 3/7] Removed clumsy polling alternative for interrupts --- cores/arduino/HardwareSerial.cpp | 65 +------------------------------- 1 file changed, 2 insertions(+), 63 deletions(-) diff --git a/cores/arduino/HardwareSerial.cpp b/cores/arduino/HardwareSerial.cpp index b55544a7..235b8b87 100644 --- a/cores/arduino/HardwareSerial.cpp +++ b/cores/arduino/HardwareSerial.cpp @@ -21,8 +21,10 @@ Modified 14 August 2012 by Alarus Modified 3 December 2013 by Matthijs Kooijman Modified 1 may 2023 by TempersLee + Modified 28 July 2024 by Maxint R&D */ + #include #include "Arduino.h" #include "HardwareSerial.h" @@ -175,59 +177,6 @@ void HardwareSerial::begin(unsigned long baud, byte config) #endif } - -#if(OPT_USART1_INT==1) -#else -// MMOLE: reintroduced RX buffer to properly implement read/available/peek methods -void HardwareSerial::fillRxBuffer(void) // private method: read all characters that can be read -{ - // Fill RX buffer during read/available calls - // Newly received characters are added to the buffer - // _rx_buffer_head is the location of the new character - // _rx_buffer_tail is the location of the oldest character that is not read yet - unsigned char c; -/* - while(uart_getc(&_serial, &c) == 0) - { - _rx_buffer[_rx_buffer_head]=c; - _rx_buffer_head = (rx_buffer_index_t)(_rx_buffer_head + 1) % SERIAL_RX_BUFFER_SIZE; - } -*/ - // To avoid buffer underruns, we try to read during at least a few millis. - // Perhaps there is a better way, but for now it works. - // Maybe we should also do something to handle disruption of interrupts -/* - #define SERIAL_WAIT_FOR_RX 5000L - uint32_t uStart=micros(); - while((micros()-uStart) Date: Sun, 28 Jul 2024 09:11:42 +0200 Subject: [PATCH 4/7] merged bugfix #92 --- cores/arduino/HardwareSerial.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cores/arduino/HardwareSerial.cpp b/cores/arduino/HardwareSerial.cpp index 235b8b87..3d55bc44 100644 --- a/cores/arduino/HardwareSerial.cpp +++ b/cores/arduino/HardwareSerial.cpp @@ -337,7 +337,7 @@ void HardwareSerial::setHandler(void *handler) #endif #if defined(HAVE_HWSERIAL6) - HardwareSerial Serial6(USART6); + HardwareSerial Serial6(UART6); #endif #if defined(HAVE_HWSERIAL7) From f15e4f176bdf8723dec5077f38ab585d529d7ef4 Mon Sep 17 00:00:00 2001 From: Maxint R&D Date: Sun, 28 Jul 2024 09:16:30 +0200 Subject: [PATCH 5/7] merged bugfix #92 / # 112 --- cores/arduino/HardwareSerial.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cores/arduino/HardwareSerial.h b/cores/arduino/HardwareSerial.h index 000b5feb..3fefc237 100644 --- a/cores/arduino/HardwareSerial.h +++ b/cores/arduino/HardwareSerial.h @@ -191,16 +191,16 @@ class HardwareSerial : public Stream { #if defined(UART4) || defined(USART4) extern HardwareSerial Serial4; #endif -#if defined(UART5) || defined(USART5) +#if defined(UART5) extern HardwareSerial Serial5; #endif -#if defined(USART6) || defined(USART6) +#if defined(UART6) extern HardwareSerial Serial6; #endif -#if defined(UART7) || defined(USART7) +#if defined(UART7) extern HardwareSerial Serial7; #endif -#if defined(UART8) || defined(USART8) +#if defined(UART8) extern HardwareSerial Serial8; #endif From 306350fc247075c4c40bf9556ba30f4499ef05db Mon Sep 17 00:00:00 2001 From: Maxint R&D Date: Tue, 6 May 2025 10:25:08 +0200 Subject: [PATCH 6/7] Interrupt driven Serial1, Serial2 or both (tested on CH32X033) --- cores/arduino/HardwareSerial.cpp | 73 +++++++++++++++++++++++++------- cores/arduino/HardwareSerial.h | 21 ++++----- 2 files changed, 69 insertions(+), 25 deletions(-) diff --git a/cores/arduino/HardwareSerial.cpp b/cores/arduino/HardwareSerial.cpp index 3d55bc44..2598d375 100644 --- a/cores/arduino/HardwareSerial.cpp +++ b/cores/arduino/HardwareSerial.cpp @@ -21,10 +21,9 @@ Modified 14 August 2012 by Alarus Modified 3 December 2013 by Matthijs Kooijman Modified 1 may 2023 by TempersLee - Modified 28 July 2024 by Maxint R&D + Modified 13 October 2023 by Maxint R&D, latest update 6 May 2025 */ - #include #include "Arduino.h" #include "HardwareSerial.h" @@ -36,10 +35,22 @@ HardwareSerial::HardwareSerial(void *peripheral) { setHandler(peripheral); +#if defined(PIN_SERIAL_RX) && defined(PIN_SERIAL_TX) + // if SERIAL_UART_INSTANCES is defined and has value 1, then PIN_SERIAL_RX and PIN_SERIAL_TX are rx/tx pins of the selected SERIAL_UART_INSTANCE setRx(PIN_SERIAL_RX); - setTx(PIN_SERIAL_TX); - +#endif + +#if defined(USART2) && defined(PIN_SERIAL_RX2) && defined(PIN_SERIAL_TX2) + // if SERIAL_UART_INSTANCES is defined and is 2 or higher, multiple instances can be used simultaneously + // TODO: get pin number from pinmap PinMap_UART_TX and PinMap_UART_RX + if(peripheral==USART2) + { + setRx(PIN_SERIAL_RX2); + setTx(PIN_SERIAL_TX2); + } +#endif + init(_serial.pin_rx, _serial.pin_tx); } @@ -60,12 +71,13 @@ void HardwareSerial::init(PinName _rx, PinName _tx, PinName _rts, PinName _cts) // Interrupt handler for filling rx buffer ///////////////////////////////////// -#if(OPT_USART1_INT==1) - -#if defined(USART1) +#if(OPT_USART_INT==1) #ifdef __cplusplus extern "C" { #endif + +#if defined(USART1) + #if defined(HAVE_HWSERIAL1) void USART1_IRQHandler(void) __attribute__((interrupt("WCH-Interrupt-fast"))); void USART1_IRQHandler(void) { USART_ClearITPendingBit(USART1, USART_IT_RXNE); @@ -77,13 +89,29 @@ void HardwareSerial::init(PinName _rx, PinName _tx, PinName _rts, PinName _cts) obj->_rx_buffer_head++; obj->_rx_buffer_head %= SERIAL_RX_BUFFER_SIZE; } - #ifdef __cplusplus - } - #endif + #endif #endif +#if defined(USART2) + #if defined(HAVE_HWSERIAL2) + void USART2_IRQHandler(void) __attribute__((interrupt("WCH-Interrupt-fast"))); + void USART2_IRQHandler(void) { + USART_ClearITPendingBit(USART2, USART_IT_RXNE); + // Use the proper serial object to fill the RX buffer. Perhaps we should use uart_handlers[] as defined in uart.c + // Second Serial is most often Serial2, initialized below as HardwareSerial Serial2(USART2); DEBUG_UART may give issues. + // TODO? get_serial_obj(uart_handlers[UART2_INDEX]); + HardwareSerial *obj=&Serial2; + obj->_rx_buffer[obj->_rx_buffer_head] = USART_ReceiveData(USART2); // maybe we should use uart_getc()? + obj->_rx_buffer_head++; + obj->_rx_buffer_head %= SERIAL_RX_BUFFER_SIZE; + } + #endif #endif + #ifdef __cplusplus + } + #endif +#endif //if(OPT_USART_INT==1) // Public Methods ////////////////////////////////////////////////////////////// void HardwareSerial::begin(unsigned long baud, byte config) @@ -166,14 +194,19 @@ void HardwareSerial::begin(unsigned long baud, byte config) } uart_init(&_serial, (uint32_t)baud, databits, parity, stopbits); -#if(OPT_USART1_INT==1) +#if(OPT_USART_INT==1) // MMOLE 240619: Enable interrupt handler for filling rx buffer #if defined(USART1) USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); NVIC_SetPriority(USART1_IRQn, UART_IRQ_PRIO); NVIC_EnableIRQ(USART1_IRQn); #endif - // MMOLE TODO: I only have CH32V003; only tested USART1, how about others? + // MMOLE TODO: I only have CH32V003/CH32X033; only tested USART1 and USART2, how about others? + #if defined(USART2) + USART_ITConfig(USART2, USART_IT_RXNE, ENABLE); + NVIC_SetPriority(USART2_IRQn, UART_IRQ_PRIO); + NVIC_EnableIRQ(USART2_IRQn); + #endif #endif } @@ -185,8 +218,8 @@ void HardwareSerial::end() uart_deinit(&_serial); -#if(OPT_USART1_INT==1) - // MMOLE TODO: disable interrupt handler +#if(OPT_USART_INT==1) + // MMOLE TODO: disable interrupt handlers #endif } @@ -234,15 +267,25 @@ int HardwareSerial::read(void) size_t HardwareSerial::write(const uint8_t *buffer, size_t size) { - +#if OPT_PR180 // PR180: HardwareSerial: use correct UART HW for TX + for (size_t i = 0; i < size; i++) { + write(buffer[i]); + } + return size; +#else return uart_debug_write((uint8_t *)buffer, size); +#endif } size_t HardwareSerial::write(uint8_t c) { +#if OPT_PR180 // PR180: HardwareSerial: use correct UART HW for TX + return uart_putc(&_serial, c); +#else uint8_t buff = c; return write(&buff, 1); +#endif } void HardwareSerial::setRx(uint32_t _rx) diff --git a/cores/arduino/HardwareSerial.h b/cores/arduino/HardwareSerial.h index 3fefc237..a52da82d 100644 --- a/cores/arduino/HardwareSerial.h +++ b/cores/arduino/HardwareSerial.h @@ -20,14 +20,15 @@ Modified 14 August 2012 by Alarus Modified 3 December 2013 by Matthijs Kooijman Modified 1 May 2023 by TempersLee - Modified 28 July 2024 by Maxint R&D + Modified 13 October 2023 by Maxint R&D, latest update 6 May 2025 */ #ifndef HardwareSerial_h #define HardwareSerial_h -// MMOLE 240619: set OPT_USART1_INT to 1 if you want to use interrupts for receiving serial data. -#define OPT_USART1_INT 1 +// MMOLE 240619: set OPT_USART_INT to 1 if you want to use interrupts for receiving serial data. +#define OPT_USART_INT 1 +#define OPT_PR180 1 // PR180: HardwareSerial: use correct UART HW for TX #if 1 @@ -105,7 +106,7 @@ typedef enum { class HardwareSerial : public Stream { -#if(OPT_USART1_INT==1) +#if(OPT_USART_INT==1) public: #endif serial_t _serial; @@ -191,22 +192,22 @@ class HardwareSerial : public Stream { #if defined(UART4) || defined(USART4) extern HardwareSerial Serial4; #endif -#if defined(UART5) +#if defined(UART5) || defined(USART5) extern HardwareSerial Serial5; #endif -#if defined(UART6) +#if defined(UART6) || defined(USART6) extern HardwareSerial Serial6; #endif -#if defined(UART7) +#if defined(UART7) || defined(USART7) extern HardwareSerial Serial7; #endif -#if defined(UART8) +#if defined(UART8) || defined(USART8) extern HardwareSerial Serial8; #endif -#else +#else // #if 1 @@ -214,7 +215,7 @@ class HardwareSerial : public Stream { -#endif +#endif // #if 1 From f504b8f0fd0ef950284394d591846e6b81b58e09 Mon Sep 17 00:00:00 2001 From: Maxint R&D Date: Tue, 6 May 2025 10:31:38 +0200 Subject: [PATCH 7/7] Added uart_putc according PR #180 --- cores/arduino/ch32/uart.c | 25 +++++++++++++++++++++++++ cores/arduino/ch32/uart.h | 1 + 2 files changed, 26 insertions(+) diff --git a/cores/arduino/ch32/uart.c b/cores/arduino/ch32/uart.c index 37070727..5e0681be 100644 --- a/cores/arduino/ch32/uart.c +++ b/cores/arduino/ch32/uart.c @@ -528,6 +528,31 @@ int uart_getc(serial_t *obj, unsigned char *c) return 0; } +// PR180: HardwareSerial: use correct UART HW for TX +/** + * @brief Write byte to uart + * @param obj : pointer to serial_t structure + * @retval error status + */ +int uart_putc(serial_t *obj, unsigned char c) +{ + uint32_t tickstart = GetTick(); + + if (obj == NULL) { + return -1; + } + + while (serial_tx_active(obj)) + { + if ((GetTick() - tickstart) >= TX_TIMEOUT) + { + return 0; // 0 means no error? Should be timeout error? + } + } + + USART_SendData(uart_handlers[obj->index]->Instance, c); + return 0; // 0 means no error +} diff --git a/cores/arduino/ch32/uart.h b/cores/arduino/ch32/uart.h index 1a1e9051..3b9883d8 100644 --- a/cores/arduino/ch32/uart.h +++ b/cores/arduino/ch32/uart.h @@ -90,6 +90,7 @@ typedef struct __UART_HandleTypeDef void uart_deinit(serial_t *obj); int uart_getc(serial_t *obj, unsigned char *c); + int uart_putc(serial_t *obj, unsigned char c); // PR180: HardwareSerial: use correct UART HW for TX uint8_t serial_tx_active(serial_t *obj); uint8_t serial_rx_active(serial_t *obj);