diff --git a/cores/esp32/HardwareSerial.cpp b/cores/esp32/HardwareSerial.cpp index 3638b6768d0..adca2ffb6aa 100644 --- a/cores/esp32/HardwareSerial.cpp +++ b/cores/esp32/HardwareSerial.cpp @@ -22,76 +22,15 @@ #define ARDUINO_SERIAL_EVENT_TASK_RUNNING_CORE -1 #endif -#ifndef SOC_RX0 -#if CONFIG_IDF_TARGET_ESP32 -#define SOC_RX0 3 -#elif CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 -#define SOC_RX0 44 -#elif CONFIG_IDF_TARGET_ESP32C3 -#define SOC_RX0 20 -#endif -#endif - -#ifndef SOC_TX0 -#if CONFIG_IDF_TARGET_ESP32 -#define SOC_TX0 1 -#elif CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 -#define SOC_TX0 43 -#elif CONFIG_IDF_TARGET_ESP32C3 -#define SOC_TX0 21 -#endif -#endif - void serialEvent(void) __attribute__((weak)); void serialEvent(void) {} #if SOC_UART_NUM > 1 - -#ifndef RX1 -#if CONFIG_IDF_TARGET_ESP32 -#define RX1 9 -#elif CONFIG_IDF_TARGET_ESP32S2 -#define RX1 18 -#elif CONFIG_IDF_TARGET_ESP32C3 -#define RX1 18 -#elif CONFIG_IDF_TARGET_ESP32S3 -#define RX1 15 -#endif -#endif - -#ifndef TX1 -#if CONFIG_IDF_TARGET_ESP32 -#define TX1 10 -#elif CONFIG_IDF_TARGET_ESP32S2 -#define TX1 17 -#elif CONFIG_IDF_TARGET_ESP32C3 -#define TX1 19 -#elif CONFIG_IDF_TARGET_ESP32S3 -#define TX1 16 -#endif -#endif - void serialEvent1(void) __attribute__((weak)); void serialEvent1(void) {} #endif /* SOC_UART_NUM > 1 */ #if SOC_UART_NUM > 2 -#ifndef RX2 -#if CONFIG_IDF_TARGET_ESP32 -#define RX2 16 -#elif CONFIG_IDF_TARGET_ESP32S3 -#define RX2 19 -#endif -#endif - -#ifndef TX2 -#if CONFIG_IDF_TARGET_ESP32 -#define TX2 17 -#elif CONFIG_IDF_TARGET_ESP32S3 -#define TX2 20 -#endif -#endif - void serialEvent2(void) __attribute__((weak)); void serialEvent2(void) {} #endif /* SOC_UART_NUM > 2 */ @@ -111,11 +50,14 @@ HardwareSerial Serial2(2); void serialEventRun(void) { -#if ARDUINO_USB_CDC_ON_BOOT //Serial used for USB CDC - if(Serial0.available()) serialEvent(); -#else +#if HWCDC_SERIAL_IS_DEFINED == 1 // Hardware JTAG CDC Event + if(HWCDCSerial.available()) HWCDCSerialEvent(); +#endif +#if USB_SERIAL_IS_DEFINED == 1 // Native USB CDC Event + if(USBSerial.available()) USBSerialEvent(); +#endif + // UART0 is default serialEvent() if(Serial.available()) serialEvent(); -#endif #if SOC_UART_NUM > 1 if(Serial1.available()) serialEvent1(); #endif @@ -129,16 +71,16 @@ void serialEventRun(void) #define HSERIAL_MUTEX_LOCK() do {} while (xSemaphoreTake(_lock, portMAX_DELAY) != pdPASS) #define HSERIAL_MUTEX_UNLOCK() xSemaphoreGive(_lock) #else -#define HSERIAL_MUTEX_LOCK() -#define HSERIAL_MUTEX_UNLOCK() +#define HSERIAL_MUTEX_LOCK() +#define HSERIAL_MUTEX_UNLOCK() #endif -HardwareSerial::HardwareSerial(uint8_t uart_nr) : -_uart_nr(uart_nr), +HardwareSerial::HardwareSerial(uint8_t uart_nr) : +_uart_nr(uart_nr), _uart(NULL), _rxBufferSize(256), -_txBufferSize(0), -_onReceiveCB(NULL), +_txBufferSize(0), +_onReceiveCB(NULL), _onReceiveErrorCB(NULL), _onReceiveTimeout(false), _rxTimeout(2), @@ -147,8 +89,6 @@ _eventTask(NULL) #if !CONFIG_DISABLE_HAL_LOCKS ,_lock(NULL) #endif -,_ctsPin(-1) -,_rtsPin(-1) { #if !CONFIG_DISABLE_HAL_LOCKS if(_lock == NULL){ @@ -159,19 +99,11 @@ _eventTask(NULL) } } #endif - // sets UART0 (default console) RX/TX pins as already configured in boot - if (uart_nr == 0) { - _rxPin = SOC_RX0; - _txPin = SOC_TX0; - } else { - _rxPin = -1; - _txPin = -1; - } } HardwareSerial::~HardwareSerial() { - end(); + end(); // explicit Full UART termination #if !CONFIG_DISABLE_HAL_LOCKS if(_lock != NULL){ vSemaphoreDelete(_lock); @@ -197,10 +129,10 @@ void HardwareSerial::_destroyEventTask(void) } } -void HardwareSerial::onReceiveError(OnReceiveErrorCb function) +void HardwareSerial::onReceiveError(OnReceiveErrorCb function) { HSERIAL_MUTEX_LOCK(); - // function may be NULL to cancel onReceive() from its respective task + // function may be NULL to cancel onReceive() from its respective task _onReceiveErrorCB = function; // this can be called after Serial.begin(), therefore it shall create the event task if (function != NULL && _uart != NULL && _eventTask == NULL) { @@ -212,7 +144,7 @@ void HardwareSerial::onReceiveError(OnReceiveErrorCb function) void HardwareSerial::onReceive(OnReceiveCb function, bool onlyOnTimeout) { HSERIAL_MUTEX_LOCK(); - // function may be NULL to cancel onReceive() from its respective task + // function may be NULL to cancel onReceive() from its respective task _onReceiveCB = function; // setting the callback to NULL will just disable it @@ -260,14 +192,14 @@ bool HardwareSerial::setRxFIFOFull(uint8_t fifoBytes) bool HardwareSerial::setRxTimeout(uint8_t symbols_timeout) { HSERIAL_MUTEX_LOCK(); - + // Zero disables timeout, thus, onReceive callback will only be called when RX FIFO reaches 120 bytes - // Any non-zero value will activate onReceive callback based on UART baudrate with about 11 bits per symbol - _rxTimeout = symbols_timeout; - if (!symbols_timeout) _onReceiveTimeout = false; // only when RX timeout is disabled, we also must disable this flag + // Any non-zero value will activate onReceive callback based on UART baudrate with about 11 bits per symbol + _rxTimeout = symbols_timeout; + if (!symbols_timeout) _onReceiveTimeout = false; // only when RX timeout is disabled, we also must disable this flag bool retCode = uartSetRxTimeout(_uart, _rxTimeout); // Set new timeout - + HSERIAL_MUTEX_UNLOCK(); return retCode; } @@ -276,7 +208,7 @@ void HardwareSerial::eventQueueReset() { QueueHandle_t uartEventQueue = NULL; if (_uart == NULL) { - return; + return; } uartGetEventQueue(_uart, &uartEventQueue); if (uartEventQueue != NULL) { @@ -293,12 +225,12 @@ void HardwareSerial::_uartEventTask(void *args) if (uartEventQueue != NULL) { for(;;) { //Waiting for UART event. - if(xQueueReceive(uartEventQueue, (void * )&event, (portTickType)portMAX_DELAY)) { + if(xQueueReceive(uartEventQueue, (void * )&event, (TickType_t)portMAX_DELAY)) { hardwareSerial_error_t currentErr = UART_NO_ERROR; switch(event.type) { case UART_DATA: - if(uart->_onReceiveCB && uart->available() > 0 && - ((uart->_onReceiveTimeout && event.timeout_flag) || !uart->_onReceiveTimeout) ) + if(uart->_onReceiveCB && uart->available() > 0 && + ((uart->_onReceiveTimeout && event.timeout_flag) || !uart->_onReceiveTimeout) ) uart->_onReceiveCB(); break; case UART_FIFO_OVF: @@ -337,7 +269,7 @@ void HardwareSerial::_uartEventTask(void *args) void HardwareSerial::begin(unsigned long baud, uint32_t config, int8_t rxPin, int8_t txPin, bool invert, unsigned long timeout_ms, uint8_t rxfifo_full_thrhd) { if(_uart_nr >= SOC_UART_NUM) { - log_e("Serial number is invalid, please use numers from 0 to %u", SOC_UART_NUM - 1); + log_e("Serial number is invalid, please use a number from 0 to %u", SOC_UART_NUM - 1); return; } @@ -351,20 +283,23 @@ void HardwareSerial::begin(unsigned long baud, uint32_t config, int8_t rxPin, in HSERIAL_MUTEX_LOCK(); // First Time or after end() --> set default Pins if (!uartIsDriverInstalled(_uart)) { + // get previously used RX/TX pins, if any. + int8_t _rxPin = uart_get_RxPin(_uart_nr); + int8_t _txPin = uart_get_TxPin(_uart_nr); switch (_uart_nr) { case UART_NUM_0: if (rxPin < 0 && txPin < 0) { // do not change RX0/TX0 if it has already been set before - rxPin = _rxPin < 0 ? SOC_RX0 : _rxPin; - txPin = _txPin < 0 ? SOC_TX0 : _txPin; + rxPin = _rxPin < 0 ? (int8_t)SOC_RX0 : _rxPin; + txPin = _txPin < 0 ? (int8_t)SOC_TX0 : _txPin; } break; #if SOC_UART_NUM > 1 // may save some flash bytes... case UART_NUM_1: if (rxPin < 0 && txPin < 0) { // do not change RX1/TX1 if it has already been set before - rxPin = _rxPin < 0 ? RX1 : _rxPin; - txPin = _txPin < 0 ? TX1 : _txPin; + rxPin = _rxPin < 0 ? (int8_t)RX1 : _rxPin; + txPin = _txPin < 0 ? (int8_t)TX1 : _txPin; } break; #endif @@ -372,8 +307,8 @@ void HardwareSerial::begin(unsigned long baud, uint32_t config, int8_t rxPin, in case UART_NUM_2: if (rxPin < 0 && txPin < 0) { // do not change RX2/TX2 if it has already been set before - rxPin = _rxPin < 0 ? RX2 : _rxPin; - txPin = _txPin < 0 ? TX2 : _txPin; + rxPin = _rxPin < 0 ? (int8_t)RX2 : _rxPin; + txPin = _txPin < 0 ? (int8_t)TX2 : _txPin; } break; #endif @@ -383,15 +318,22 @@ void HardwareSerial::begin(unsigned long baud, uint32_t config, int8_t rxPin, in // map logical pins to GPIO numbers rxPin = digitalPinToGPIONumber(rxPin); txPin = digitalPinToGPIONumber(txPin); + // IDF UART driver keeps Pin setting on restarting. Negative Pin number will keep it unmodified. + // it will detach previous UART attached pins - if(_uart) { - // in this case it is a begin() over a previous begin() - maybe to change baud rate - // thus do not disable debug output - end(false); + // indicates that uartbegin() has to initilize a new IDF driver + if (_testUartBegin(_uart_nr, baud ? baud : 9600, config, rxPin, txPin, _rxBufferSize, _txBufferSize, invert, rxfifo_full_thrhd)) { + _destroyEventTask(); // when IDF uart driver must be restarted, _eventTask must finish too } // IDF UART driver keeps Pin setting on restarting. Negative Pin number will keep it unmodified. + // it will detach previous UART attached pins _uart = uartBegin(_uart_nr, baud ? baud : 9600, config, rxPin, txPin, _rxBufferSize, _txBufferSize, invert, rxfifo_full_thrhd); + if (_uart == NULL) { + log_e("UART driver failed to start. Please check the logs."); + HSERIAL_MUTEX_UNLOCK(); + return; + } if (!baud) { // using baud rate as zero, forces it to try to detect the current baud rate in place uartStartDetectBaudrate(_uart); @@ -401,8 +343,6 @@ void HardwareSerial::begin(unsigned long baud, uint32_t config, int8_t rxPin, in yield(); } - end(false); - if(detectedBaudRate) { delay(100); // Give some time... _uart = uartBegin(_uart_nr, detectedBaudRate, config, rxPin, txPin, _rxBufferSize, _txBufferSize, invert, rxfifo_full_thrhd); @@ -410,13 +350,13 @@ void HardwareSerial::begin(unsigned long baud, uint32_t config, int8_t rxPin, in // S3 and C3 have a limitation and both can't detect a baud rate lower than 9600 if (detectedBaudRate == 9600) log_w("The baud detected, as 9600, may be wrong. ESP32-C3 and ESP32-S3 can't detect a baud rate under 9600."); #endif - } else { + } else { log_e("Could not detect baudrate. Serial data at the port must be present within the timeout for detection to be possible"); _uart = NULL; } } // create a task to deal with Serial Events when, for example, calling begin() twice to change the baudrate, - // or when setting the callback before calling begin() + // or when setting the callback before calling begin() if (_uart != NULL && (_onReceiveCB != NULL || _onReceiveErrorCB != NULL) && _eventTask == NULL) { _createEventTask(this); } @@ -424,10 +364,10 @@ void HardwareSerial::begin(unsigned long baud, uint32_t config, int8_t rxPin, in // Set UART RX timeout uartSetRxTimeout(_uart, _rxTimeout); - // Set UART FIFO Full depending on the baud rate. + // Set UART FIFO Full depending on the baud rate. // Lower baud rates will force to emulate byte-by-byte reading // Higher baud rates will keep IDF default of 120 bytes for FIFO FULL Interrupt - // It can also be changed by the application at any time + // It can also be changed by the application at any time if (!_rxFIFOFull) { // it has not being changed before calling begin() // set a default FIFO Full value for the IDF driver uint8_t fifoFull = 1; @@ -437,47 +377,28 @@ void HardwareSerial::begin(unsigned long baud, uint32_t config, int8_t rxPin, in uartSetRxFIFOFull(_uart, fifoFull); _rxFIFOFull = fifoFull; } - // detach previous attached RX/TX pins when it has changed - if (_uart != NULL) { - if (rxPin >= 0 && rxPin != _rxPin) { - uartDetachPins(_uart_nr, _rxPin, -1, -1, -1); - _rxPin = rxPin; - } - if (txPin >= 0 && txPin != _txPin) { - uartDetachPins(_uart_nr, -1, _txPin, -1, -1); - _txPin = txPin; - } - } HSERIAL_MUTEX_UNLOCK(); } void HardwareSerial::updateBaudRate(unsigned long baud) { - uartSetBaudRate(_uart, baud); + uartSetBaudRate(_uart, baud); } -void HardwareSerial::end(bool fullyTerminate) +void HardwareSerial::end() { - // default Serial.end() will completely disable HardwareSerial, + // default Serial.end() will completely disable HardwareSerial, // including any tasks or debug message channel (log_x()) - but not for IDF log messages! - if(fullyTerminate) { - _onReceiveCB = NULL; - _onReceiveErrorCB = NULL; - if (uartGetDebug() == _uart_nr) { - uartSetDebug(0); - } - - _rxFIFOFull = 0; - - uartDetachPins(_uart_nr, _rxPin, _txPin, _ctsPin, _rtsPin); - _rxPin = _txPin = _ctsPin = _rtsPin = -1; - + _onReceiveCB = NULL; + _onReceiveErrorCB = NULL; + if (uartGetDebug() == _uart_nr) { + uartSetDebug(0); } - - uartEnd(_uart); - _uart = 0; - _destroyEventTask(); + _rxFIFOFull = 0; + uartEnd(_uart_nr); // fully detach all pins and delete the UART driver + _destroyEventTask(); // when IDF uart driver is deleted, _eventTask must finish too + _uart = NULL; } void HardwareSerial::setDebugOutput(bool en) @@ -557,10 +478,10 @@ size_t HardwareSerial::write(const uint8_t *buffer, size_t size) uartWriteBuf(_uart, buffer, size); return size; } -uint32_t HardwareSerial::baudRate() +uint32_t HardwareSerial::baudRate() { - return uartGetBaudRate(_uart); + return uartGetBaudRate(_uart); } HardwareSerial::operator bool() const { @@ -573,6 +494,7 @@ void HardwareSerial::setRxInvert(bool invert) } // negative Pin value will keep it unmodified +// can be called after or before begin() bool HardwareSerial::setPins(int8_t rxPin, int8_t txPin, int8_t ctsPin, int8_t rtsPin) { // map logical pins to GPIO numbers @@ -581,65 +503,59 @@ bool HardwareSerial::setPins(int8_t rxPin, int8_t txPin, int8_t ctsPin, int8_t r ctsPin = digitalPinToGPIONumber(ctsPin); rtsPin = digitalPinToGPIONumber(rtsPin); - // uartSetPins() checks if pins are valid for each function and for the SoC - bool retCode = uartSetPins(_uart_nr, rxPin, txPin, ctsPin, rtsPin); - if (retCode) { - // detach previous attached UART pins if not set as same as before - if (_rxPin >= 0 && rxPin >= 0 &&_rxPin != rxPin) uartDetachPins(_uart_nr, _rxPin, -1, -1, -1); - if (_txPin >= 0 && txPin >= 0 && _txPin != txPin) uartDetachPins(_uart_nr, -1, _txPin, -1, -1); - if (_ctsPin >= 0 && ctsPin >= 0 && _ctsPin != ctsPin) uartDetachPins(_uart_nr, -1, -1, _ctsPin, -1); - if (_rtsPin >= 0 && rtsPin >= 0 &&_rtsPin != rtsPin) uartDetachPins(_uart_nr, -1, -1, -1, _rtsPin); - // set new pins for a future end() or a setPins() - _txPin = txPin >= 0 ? txPin : _txPin; - _rxPin = rxPin >= 0 ? rxPin : _rxPin; - _rtsPin = rtsPin >= 0 ? rtsPin : _rtsPin; - _ctsPin = ctsPin >= 0 ? ctsPin : _ctsPin; - } else { - log_e("Error when setting Serial port Pins. Invalid Pin.\n"); - } - return retCode; + // uartSetPins() checks if pins are valid and, if necessary, detaches the previous ones + return uartSetPins(_uart_nr, rxPin, txPin, ctsPin, rtsPin); } -// Enables or disables Hardware Flow Control using RTS and/or CTS pins (must use setAllPins() before) -bool HardwareSerial::setHwFlowCtrlMode(uint8_t mode, uint8_t threshold) +// Enables or disables Hardware Flow Control using RTS and/or CTS pins +// must use setAllPins() in order to set RTS/CTS pins +// SerialHwFlowCtrl = UART_HW_FLOWCTRL_DISABLE, UART_HW_FLOWCTRL_RTS, +// UART_HW_FLOWCTRL_CTS, UART_HW_FLOWCTRL_CTS_RTS +bool HardwareSerial::setHwFlowCtrlMode(SerialHwFlowCtrl mode, uint8_t threshold) { return uartSetHwFlowCtrlMode(_uart, mode, threshold); } -// Sets the uart mode in the esp32 uart for use with RS485 modes (HwFlowCtrl must be disabled and RTS pin set) -bool HardwareSerial::setMode(uint8_t mode) +// Sets the uart mode in the esp32 uart for use with RS485 modes +// HwFlowCtrl must be disabled and RTS pin set +// SerialMode = UART_MODE_UART, UART_MODE_RS485_HALF_DUPLEX, UART_MODE_IRDA, +// or testing mode: UART_MODE_RS485_COLLISION_DETECT, UART_MODE_RS485_APP_CTRL +bool HardwareSerial::setMode(SerialMode mode) { return uartSetMode(_uart, mode); } +// minimum total RX Buffer size is the UART FIFO space (128 bytes for most SoC) + 1. IDF imposition. size_t HardwareSerial::setRxBufferSize(size_t new_size) { if (_uart) { - log_e("RX Buffer can't be resized when Serial is already running.\n"); + log_e("RX Buffer can't be resized when Serial is already running. Set it before calling begin()."); return 0; } if (new_size <= SOC_UART_FIFO_LEN) { - log_e("RX Buffer must be higher than %d.\n", SOC_UART_FIFO_LEN); // ESP32, S2, S3 and C3 means higher than 128 - return 0; + log_w("RX Buffer set to minimum value: %d.", SOC_UART_FIFO_LEN + 1); // ESP32, S2, S3 and C3 means higher than 128 + new_size = SOC_UART_FIFO_LEN + 1; } _rxBufferSize = new_size; return _rxBufferSize; } +// minimum total TX Buffer size is the UART FIFO space (128 bytes for most SoC). size_t HardwareSerial::setTxBufferSize(size_t new_size) { if (_uart) { - log_e("TX Buffer can't be resized when Serial is already running.\n"); + log_e("TX Buffer can't be resized when Serial is already running. Set it before calling begin()."); return 0; } if (new_size <= SOC_UART_FIFO_LEN) { - log_e("TX Buffer must be higher than %d.\n", SOC_UART_FIFO_LEN); // ESP32, S2, S3 and C3 means higher than 128 - return 0; + log_w("TX Buffer set to minimum value: %d.", SOC_UART_FIFO_LEN); // ESP32, S2, S3 and C3 means higher than 128 + _txBufferSize = 0; // it will use just UART FIFO with SOC_UART_FIFO_LEN bytes (128 for most SoC) + return SOC_UART_FIFO_LEN; } - + // if new_size is higher than SOC_UART_FIFO_LEN, TX Ringbuffer will be active and it will be used to report back "availableToWrite()" _txBufferSize = new_size; return _txBufferSize; } diff --git a/cores/esp32/HardwareSerial.h b/cores/esp32/HardwareSerial.h index 37d1fedd9bc..c3ccf8d5c48 100644 --- a/cores/esp32/HardwareSerial.h +++ b/cores/esp32/HardwareSerial.h @@ -49,13 +49,45 @@ #include #include "Stream.h" #include "esp32-hal.h" +#include "hal/uart_types.h" #include "soc/soc_caps.h" #include "HWCDC.h" +#include "USBCDC.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "freertos/semphr.h" +enum SerialConfig { + SERIAL_5N1 = 0x8000010, + SERIAL_6N1 = 0x8000014, + SERIAL_7N1 = 0x8000018, + SERIAL_8N1 = 0x800001c, + SERIAL_5N2 = 0x8000030, + SERIAL_6N2 = 0x8000034, + SERIAL_7N2 = 0x8000038, + SERIAL_8N2 = 0x800003c, + SERIAL_5E1 = 0x8000012, + SERIAL_6E1 = 0x8000016, + SERIAL_7E1 = 0x800001a, + SERIAL_8E1 = 0x800001e, + SERIAL_5E2 = 0x8000032, + SERIAL_6E2 = 0x8000036, + SERIAL_7E2 = 0x800003a, + SERIAL_8E2 = 0x800003e, + SERIAL_5O1 = 0x8000013, + SERIAL_6O1 = 0x8000017, + SERIAL_7O1 = 0x800001b, + SERIAL_8O1 = 0x800001f, + SERIAL_5O2 = 0x8000033, + SERIAL_6O2 = 0x8000037, + SERIAL_7O2 = 0x800003b, + SERIAL_8O2 = 0x800003f +}; + +typedef uart_mode_t SerialMode; +typedef uart_hw_flowcontrol_t SerialHwFlowCtrl; + typedef enum { UART_NO_ERROR, UART_BREAK_ERROR, @@ -65,6 +97,91 @@ typedef enum { UART_PARITY_ERROR } hardwareSerial_error_t; + +#ifndef ARDUINO_SERIAL_EVENT_TASK_STACK_SIZE + #define ARDUINO_SERIAL_EVENT_TASK_STACK_SIZE 2048 +#endif + +#ifndef ARDUINO_SERIAL_EVENT_TASK_PRIORITY + #define ARDUINO_SERIAL_EVENT_TASK_PRIORITY (configMAX_PRIORITIES-1) +#endif + +#ifndef ARDUINO_SERIAL_EVENT_TASK_RUNNING_CORE + #define ARDUINO_SERIAL_EVENT_TASK_RUNNING_CORE -1 +#endif + +// UART0 pins are defined by default by the bootloader. +// The definitions for SOC_* should not be changed unless the bootloader pins +// have changed and you know what you are doing. + +#ifndef SOC_RX0 + #if CONFIG_IDF_TARGET_ESP32 + #define SOC_RX0 (gpio_num_t)3 + #elif CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 + #define SOC_RX0 (gpio_num_t)44 + #elif CONFIG_IDF_TARGET_ESP32C3 + #define SOC_RX0 (gpio_num_t)20 + #endif +#endif + +#ifndef SOC_TX0 + #if CONFIG_IDF_TARGET_ESP32 + #define SOC_TX0 (gpio_num_t)1 + #elif CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 + #define SOC_TX0 (gpio_num_t)43 + #elif CONFIG_IDF_TARGET_ESP32C3 + #define SOC_TX0 (gpio_num_t)21 + #endif +#endif + +// Default pins for UART1 are arbitrary, and defined here for convenience. + +#if SOC_UART_NUM > 1 + #ifndef RX1 + #if CONFIG_IDF_TARGET_ESP32 + #define RX1 (gpio_num_t)26 + #elif CONFIG_IDF_TARGET_ESP32S2 + #define RX1 (gpio_num_t)4 + #elif CONFIG_IDF_TARGET_ESP32C3 + #define RX1 (gpio_num_t)18 + #elif CONFIG_IDF_TARGET_ESP32S3 + #define RX1 (gpio_num_t)15 + #endif + #endif + + #ifndef TX1 + #if CONFIG_IDF_TARGET_ESP32 + #define TX1 (gpio_num_t)27 + #elif CONFIG_IDF_TARGET_ESP32S2 + #define TX1 (gpio_num_t)5 + #elif CONFIG_IDF_TARGET_ESP32C3 + #define TX1 (gpio_num_t)19 + #elif CONFIG_IDF_TARGET_ESP32S3 + #define TX1 (gpio_num_t)16 + #endif + #endif +#endif /* SOC_UART_NUM > 1 */ + +// Default pins for UART2 are arbitrary, and defined here for convenience. + +#if SOC_UART_NUM > 2 + #ifndef RX2 + #if CONFIG_IDF_TARGET_ESP32 + #define RX2 (gpio_num_t)4 + #elif CONFIG_IDF_TARGET_ESP32S3 + #define RX2 (gpio_num_t)19 + #endif + #endif + + #ifndef TX2 + #if CONFIG_IDF_TARGET_ESP32 + #define TX2 (gpio_num_t)25 + #elif CONFIG_IDF_TARGET_ESP32S3 + #define TX2 (gpio_num_t)20 + #endif + #endif +#endif /* SOC_UART_NUM > 2 */ + typedef std::function OnReceiveCb; typedef std::function OnReceiveErrorCb; @@ -106,8 +223,13 @@ class HardwareSerial: public Stream // eventQueueReset clears all events in the queue (the events that trigger onReceive and onReceiveError) - maybe usefull in some use cases void eventQueueReset(); + // When pins are changed, it will detach the previous ones + // if pin is negative, it won't be set/changed and will be kept as is + // timeout_ms is used in baudrate detection (ESP32, ESP32S2 only) + // invert will invert RX/TX polarity + // rxfifo_full_thrhd if the UART Flow Control Threshold in the UART FIFO (max 127) void begin(unsigned long baud, uint32_t config=SERIAL_8N1, int8_t rxPin=-1, int8_t txPin=-1, bool invert=false, unsigned long timeout_ms = 20000UL, uint8_t rxfifo_full_thrhd = 112); - void end(bool fullyTerminate = true); + void end(void); void updateBaudRate(unsigned long baud); int available(void); int availableForWrite(void); @@ -160,12 +282,22 @@ class HardwareSerial: public Stream void setRxInvert(bool); // Negative Pin Number will keep it unmodified, thus this function can set individual pins - // SetPins shall be called after Serial begin() + // setPins() can be called after or before begin() + // When pins are changed, it will detach the previous ones bool setPins(int8_t rxPin, int8_t txPin, int8_t ctsPin = -1, int8_t rtsPin = -1); // Enables or disables Hardware Flow Control using RTS and/or CTS pins (must use setAllPins() before) - bool setHwFlowCtrlMode(uint8_t mode = HW_FLOWCTRL_CTS_RTS, uint8_t threshold = 64); // 64 is half FIFO Length + // UART_HW_FLOWCTRL_DISABLE = 0x0 disable hardware flow control + // UART_HW_FLOWCTRL_RTS = 0x1 enable RX hardware flow control (rts) + // UART_HW_FLOWCTRL_CTS = 0x2 enable TX hardware flow control (cts) + // UART_HW_FLOWCTRL_CTS_RTS = 0x3 enable hardware flow control + bool setHwFlowCtrlMode(SerialHwFlowCtrl mode = UART_HW_FLOWCTRL_CTS_RTS, uint8_t threshold = 64); // 64 is half FIFO Length // Used to set RS485 modes such as UART_MODE_RS485_HALF_DUPLEX for Auto RTS function on ESP32 - bool setMode(uint8_t mode); + // UART_MODE_UART = 0x00 mode: regular UART mode + // UART_MODE_RS485_HALF_DUPLEX = 0x01 mode: half duplex RS485 UART mode control by RTS pin + // UART_MODE_IRDA = 0x02 mode: IRDA UART mode + // UART_MODE_RS485_COLLISION_DETECT = 0x03 mode: RS485 collision detection UART mode (used for test purposes) + // UART_MODE_RS485_APP_CTRL = 0x04 mode: application control RS485 UART mode (used for test purposes) + bool setMode(SerialMode mode); size_t setRxBufferSize(size_t new_size); size_t setTxBufferSize(size_t new_size); @@ -183,7 +315,6 @@ class HardwareSerial: public Stream #if !CONFIG_DISABLE_HAL_LOCKS SemaphoreHandle_t _lock; #endif - int8_t _rxPin, _txPin, _ctsPin, _rtsPin; void _createEventTask(void *args); void _destroyEventTask(void); @@ -197,10 +328,6 @@ extern void serialEventRun(void) __attribute__((weak)); #define ARDUINO_USB_CDC_ON_BOOT 0 #endif #if ARDUINO_USB_CDC_ON_BOOT //Serial used for USB CDC -#if !ARDUINO_USB_MODE -#include "USB.h" -#include "USBCDC.h" -#endif extern HardwareSerial Serial0; #else extern HardwareSerial Serial; @@ -211,6 +338,5 @@ extern HardwareSerial Serial1; #if SOC_UART_NUM > 2 extern HardwareSerial Serial2; #endif -#endif - +#endif // !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_SERIAL) #endif // HardwareSerial_h diff --git a/cores/esp32/esp32-hal-uart.c b/cores/esp32/esp32-hal-uart.c index eae0f720165..35573370902 100644 --- a/cores/esp32/esp32-hal-uart.c +++ b/cores/esp32/esp32-hal-uart.c @@ -1,4 +1,4 @@ -// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// Copyright 2015-2024 Espressif Systems (Shanghai) PTE LTD // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "esp32-hal-uart.h" #include "esp32-hal.h" #include "freertos/FreeRTOS.h" @@ -23,23 +22,32 @@ #include "soc/soc_caps.h" #include "soc/uart_struct.h" #include "soc/uart_periph.h" +#include "rom/ets_sys.h" +#include "rom/gpio.h" #include "driver/gpio.h" #include "hal/gpio_hal.h" #include "esp_rom_gpio.h" -static int s_uart_debug_nr = 0; +static int s_uart_debug_nr = 0; // UART number for debug output struct uart_struct_t { #if !CONFIG_DISABLE_HAL_LOCKS - xSemaphoreHandle lock; + SemaphoreHandle_t lock; // UART lock #endif - uint8_t num; - bool has_peek; - uint8_t peek_byte; - QueueHandle_t uart_event_queue; // export it by some uartGetEventQueue() function + uint8_t num; // UART number for IDF driver API + bool has_peek; // flag to indicate that there is a peek byte pending to be read + uint8_t peek_byte; // peek byte that has been read but not consumed + QueueHandle_t uart_event_queue; // export it by some uartGetEventQueue() function + // configuration data:: Arduino API tipical data + int8_t _rxPin, _txPin, _ctsPin, _rtsPin; // UART GPIOs + uint32_t _baudrate, _config; // UART baudrate and config + // UART ESP32 specific data + uint16_t _rx_buffer_size, _tx_buffer_size; // UART RX and TX buffer sizes + bool _inverted; // UART inverted signal + uint8_t _rxfifo_full_thrhd; // UART RX FIFO full threshold }; #if CONFIG_DISABLE_HAL_LOCKS @@ -48,63 +56,142 @@ struct uart_struct_t { #define UART_MUTEX_UNLOCK() static uart_t _uart_bus_array[] = { - {0, false, 0, NULL}, + {0, false, 0, NULL, -1, -1, -1, -1, 0, 0, 0, 0, false, 0}, #if SOC_UART_NUM > 1 - {1, false, 0, NULL}, + {1, false, 0, NULL, -1, -1, -1, -1, 0, 0, 0, 0, false, 0}, #endif #if SOC_UART_NUM > 2 - {2, false, 0, NULL}, + {2, false, 0, NULL, -1, -1, -1, -1, 0, 0, 0, 0, false, 0}, #endif }; #else -#define UART_MUTEX_LOCK() do {} while (xSemaphoreTake(uart->lock, portMAX_DELAY) != pdPASS) -#define UART_MUTEX_UNLOCK() xSemaphoreGive(uart->lock) +#define UART_MUTEX_LOCK() if(uart->lock != NULL) do {} while (xSemaphoreTake(uart->lock, portMAX_DELAY) != pdPASS) +#define UART_MUTEX_UNLOCK() if(uart->lock != NULL) xSemaphoreGive(uart->lock) static uart_t _uart_bus_array[] = { - {NULL, 0, false, 0, NULL}, + {NULL, 0, false, 0, NULL, -1, -1, -1, -1, 0, 0, 0, 0, false, 0}, #if SOC_UART_NUM > 1 - {NULL, 1, false, 0, NULL}, + {NULL, 1, false, 0, NULL, -1, -1, -1, -1, 0, 0, 0, 0, false, 0}, #endif #if SOC_UART_NUM > 2 - {NULL, 2, false, 0, NULL}, + {NULL, 2, false, 0, NULL, -1, -1, -1, -1, 0, 0, 0, 0, false, 0}, #endif }; #endif -// IDF UART has no detach function. As consequence, after ending a UART, the previous pins continue -// to work as RX/TX. It can be verified by changing the UART pins and writing to the UART. Output can -// be seen in the previous pins and new pins as well. -// Valid pin UART_PIN_NO_CHANGE is defined to (-1) // Negative Pin Number will keep it unmodified, thus this function can detach individual pins -void uartDetachPins(uint8_t uart_num, int8_t rxPin, int8_t txPin, int8_t ctsPin, int8_t rtsPin) +// This function will set the pin to -1 after detaching +static bool _uartDetachPins(uint8_t uart_num, int8_t rxPin, int8_t txPin, int8_t ctsPin, int8_t rtsPin) { if(uart_num >= SOC_UART_NUM) { - log_e("Serial number is invalid, please use numers from 0 to %u", SOC_UART_NUM - 1); - return; + log_e("Serial number is invalid, please use number from 0 to %u", SOC_UART_NUM - 1); + return false; } - - if (txPin >= 0) { + // get UART information + uart_t* uart = &_uart_bus_array[uart_num]; + bool retCode = true; + //log_v("detaching UART%d pins: prev,pin RX(%d,%d) TX(%d,%d) CTS(%d,%d) RTS(%d,%d)", uart_num, + // uart->_rxPin, rxPin, uart->_txPin, txPin, uart->_ctsPin, ctsPin, uart->_rtsPin, rtsPin); vTaskDelay(10); + + // detaches pins and sets UART information + if (rxPin >= 0 && uart->_rxPin == rxPin) { + gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[rxPin], PIN_FUNC_GPIO); + // avoids causing BREAK in the UART line + if (uart->_inverted) { + esp_rom_gpio_connect_in_signal(GPIO_FUNC_IN_LOW, UART_PERIPH_SIGNAL(uart_num, SOC_UART_RX_PIN_IDX), false); + } else { + esp_rom_gpio_connect_in_signal(GPIO_FUNC_IN_HIGH, UART_PERIPH_SIGNAL(uart_num, SOC_UART_RX_PIN_IDX), false); + } + uart->_rxPin = -1; // -1 means unassigned/detached + } + if (txPin >= 0 && uart->_txPin == txPin) { gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[txPin], PIN_FUNC_GPIO); esp_rom_gpio_connect_out_signal(txPin, SIG_GPIO_OUT_IDX, false, false); + uart->_txPin = -1; // -1 means unassigned/detached } - - if (rxPin >= 0) { - gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[rxPin], PIN_FUNC_GPIO); - esp_rom_gpio_connect_in_signal(GPIO_FUNC_IN_LOW, UART_PERIPH_SIGNAL(uart_num, SOC_UART_RX_PIN_IDX), false); + if (ctsPin >= 0 && uart->_ctsPin == ctsPin) { + gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[ctsPin], PIN_FUNC_GPIO); + esp_rom_gpio_connect_in_signal(GPIO_FUNC_IN_LOW, UART_PERIPH_SIGNAL(uart_num, SOC_UART_CTS_PIN_IDX), false); + uart->_ctsPin = -1; // -1 means unassigned/detached } - - if (rtsPin >= 0) { + if (rtsPin >= 0 && uart->_rtsPin == rtsPin) { gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[rtsPin], PIN_FUNC_GPIO); esp_rom_gpio_connect_out_signal(rtsPin, SIG_GPIO_OUT_IDX, false, false); + uart->_rtsPin = -1; // -1 means unassigned/detached + } + return retCode; +} + +// Attach function for UART +// connects the IO Pad, set internal UART structure data +static bool _uartAttachPins(uint8_t uart_num, int8_t rxPin, int8_t txPin, int8_t ctsPin, int8_t rtsPin) +{ + if(uart_num >= SOC_UART_NUM) { + log_e("Serial number is invalid, please use number from 0 to %u", SOC_UART_NUM - 1); + return false; } + // get UART information + uart_t* uart = &_uart_bus_array[uart_num]; + //log_v("attaching UART%d pins: prev,new RX(%d,%d) TX(%d,%d) CTS(%d,%d) RTS(%d,%d)", uart_num, + // uart->_rxPin, rxPin, uart->_txPin, txPin, uart->_ctsPin, ctsPin, uart->_rtsPin, rtsPin); vTaskDelay(10); + + bool retCode = true; + if (rxPin >= 0) { + // connect RX Pad + bool ret = ESP_OK == uart_set_pin(uart->num, UART_PIN_NO_CHANGE, rxPin, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE); + if (ret) { + uart->_rxPin = rxPin; + } else { + log_e("UART%d failed to attach RX pin %d", uart_num, rxPin); + } + retCode &= ret; + } + if (txPin >= 0) { + // connect TX Pad + bool ret = ESP_OK == uart_set_pin(uart->num, txPin, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE); + if (ret) { + if (ret) uart->_txPin = txPin; + } else { + log_e("UART%d failed to attach TX pin %d", uart_num, txPin); + } + retCode &= ret; + } if (ctsPin >= 0) { - gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[ctsPin], PIN_FUNC_GPIO); - esp_rom_gpio_connect_in_signal(GPIO_FUNC_IN_LOW, UART_PERIPH_SIGNAL(uart_num, SOC_UART_CTS_PIN_IDX), false); + // connect CTS Pad + bool ret = ESP_OK == uart_set_pin(uart->num, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, ctsPin); + if (ret) { + if (ret) uart->_ctsPin = ctsPin; + } else { + log_e("UART%d failed to attach CTS pin %d", uart_num, ctsPin); + } + retCode &= ret; } + if (rtsPin >= 0) { + // connect RTS Pad + bool ret = ESP_OK == uart_set_pin(uart->num, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, rtsPin, UART_PIN_NO_CHANGE); + if (ret) { + if (ret) uart->_rtsPin = rtsPin; + } else { + log_e("UART%d failed to attach RTS pin %d", uart_num, rtsPin); + } + retCode &= ret; + } + return retCode; +} + +// just helper functions +int8_t uart_get_RxPin(uint8_t uart_num) +{ + return _uart_bus_array[uart_num]._rxPin; +} + +int8_t uart_get_TxPin(uint8_t uart_num) +{ + return _uart_bus_array[uart_num]._txPin; } // solves issue https://github.com/espressif/arduino-esp32/issues/6032 @@ -144,57 +231,190 @@ bool uartIsDriverInstalled(uart_t* uart) return false; } -// Valid pin UART_PIN_NO_CHANGE is defined to (-1) // Negative Pin Number will keep it unmodified, thus this function can set individual pins +// When pins are changed, it will detach the previous one bool uartSetPins(uint8_t uart_num, int8_t rxPin, int8_t txPin, int8_t ctsPin, int8_t rtsPin) { if(uart_num >= SOC_UART_NUM) { - log_e("Serial number is invalid, please use numers from 0 to %u", SOC_UART_NUM - 1); + log_e("Serial number is invalid, please use number from 0 to %u", SOC_UART_NUM - 1); return false; } + // get UART information + uart_t* uart = &_uart_bus_array[uart_num]; + + bool retCode = true; + UART_MUTEX_LOCK(); + + //log_v("setting UART%d pins: prev->new RX(%d->%d) TX(%d->%d) CTS(%d->%d) RTS(%d->%d)", uart_num, + // uart->_rxPin, rxPin, uart->_txPin, txPin, uart->_ctsPin, ctsPin, uart->_rtsPin, rtsPin); vTaskDelay(10); - // IDF uart_set_pin() will issue necessary Error Message and take care of all GPIO Number validation. - bool retCode = uart_set_pin(uart_num, txPin, rxPin, rtsPin, ctsPin) == ESP_OK; + // First step: detachs all previous UART pins + bool rxPinChanged = rxPin >= 0 && rxPin != uart->_rxPin; + if (rxPinChanged) { + retCode &= _uartDetachPins(uart_num, uart->_rxPin, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE); + } + bool txPinChanged = txPin >= 0 && txPin != uart->_txPin; + if (txPinChanged) { + retCode &= _uartDetachPins(uart_num, UART_PIN_NO_CHANGE, uart->_txPin, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE); + } + bool ctsPinChanged = ctsPin >= 0 && ctsPin != uart->_ctsPin; + if (ctsPinChanged) { + retCode &= _uartDetachPins(uart_num, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, uart->_ctsPin, UART_PIN_NO_CHANGE); + } + bool rtsPinChanged = rtsPin >= 0 && rtsPin != uart->_rtsPin; + if (rtsPinChanged) { + retCode &= _uartDetachPins(uart_num, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, uart->_rtsPin); + } + + // Second step: attach all UART new pins + if (rxPinChanged) { + retCode &= _uartAttachPins(uart_num, rxPin, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE); + } + if (txPinChanged) { + retCode &= _uartAttachPins(uart_num, UART_PIN_NO_CHANGE, txPin, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE); + } + if (ctsPinChanged) { + retCode &= _uartAttachPins(uart->num, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, ctsPin, UART_PIN_NO_CHANGE); + } + if (rtsPinChanged) { + retCode &= _uartAttachPins(uart->num, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, rtsPin); + } + UART_MUTEX_UNLOCK(); + + if (!retCode) { + log_e("UART%d set pins failed.", uart_num); + } return retCode; } // -bool uartSetHwFlowCtrlMode(uart_t *uart, uint8_t mode, uint8_t threshold) { +bool uartSetHwFlowCtrlMode(uart_t *uart, uart_hw_flowcontrol_t mode, uint8_t threshold) { if(uart == NULL) { return false; } // IDF will issue corresponding error message when mode or threshold are wrong and prevent crashing // IDF will check (mode > HW_FLOWCTRL_CTS_RTS || threshold >= SOC_UART_FIFO_LEN) UART_MUTEX_LOCK(); - bool retCode = (ESP_OK == uart_set_hw_flow_ctrl(uart->num, (uart_hw_flowcontrol_t) mode, threshold)); + bool retCode = (ESP_OK == uart_set_hw_flow_ctrl(uart->num, mode, threshold)); UART_MUTEX_UNLOCK(); return retCode; } - -uart_t* uartBegin(uint8_t uart_nr, uint32_t baudrate, uint32_t config, int8_t rxPin, int8_t txPin, uint16_t rx_buffer_size, uint16_t tx_buffer_size, bool inverted, uint8_t rxfifo_full_thrhd) +// This helper function will return true if a new IDF UART driver needs to be restarted and false if the current one can continue its execution +bool _testUartBegin(uint8_t uart_nr, uint32_t baudrate, uint32_t config, int8_t rxPin, int8_t txPin, uint16_t rx_buffer_size, uint16_t tx_buffer_size, bool inverted, uint8_t rxfifo_full_thrhd) { if(uart_nr >= SOC_UART_NUM) { - return NULL; + return false; // no new driver has to be installed } - uart_t* uart = &_uart_bus_array[uart_nr]; - + // verify if it is necessary to restart the UART driver if (uart_is_driver_installed(uart_nr)) { - uartEnd(uart); + // some parameters can't be changed unless we end the UART driver + if ( uart->_rx_buffer_size != rx_buffer_size || uart->_tx_buffer_size != tx_buffer_size || uart->_inverted != inverted || uart->_rxfifo_full_thrhd != rxfifo_full_thrhd) { + return true; // the current IDF UART driver must be terminated and a new driver shall be installed + } else { + return false; // The current IDF UART driver can continue its execution + } + } else { + return true; // no IDF UART driver is running and a new driver shall be installed + } +} + +uart_t* uartBegin(uint8_t uart_nr, uint32_t baudrate, uint32_t config, int8_t rxPin, int8_t txPin, uint16_t rx_buffer_size, uint16_t tx_buffer_size, bool inverted, uint8_t rxfifo_full_thrhd) +{ + if(uart_nr >= SOC_UART_NUM) { + log_e("UART number is invalid, please use number from 0 to %u", SOC_UART_NUM - 1); + return NULL; // no new driver was installed } + uart_t* uart = &_uart_bus_array[uart_nr]; + log_v("UART%d baud(%ld) Mode(%x) rxPin(%d) txPin(%d)", uart_nr, baudrate, config, rxPin, txPin); #if !CONFIG_DISABLE_HAL_LOCKS if(uart->lock == NULL) { uart->lock = xSemaphoreCreateMutex(); if(uart->lock == NULL) { - return NULL; + log_e("Lock (Mutex) creation error."); + return NULL; // no new driver was installed } } #endif - UART_MUTEX_LOCK(); - + if (uart_is_driver_installed(uart_nr)) { + log_v("UART%d Driver already installed.", uart_nr); + // some parameters can't be changed unless we end the UART driver + if ( uart->_rx_buffer_size != rx_buffer_size || uart->_tx_buffer_size != tx_buffer_size || uart->_inverted != inverted || uart->_rxfifo_full_thrhd != rxfifo_full_thrhd) { + log_v("UART%d changing buffer sizes or inverted signal or rxfifo_full_thrhd. IDF driver will be restarted", uart_nr); + uartEnd(uart_nr); + } else { + bool retCode = true; + UART_MUTEX_LOCK(); + //User may just want to change some parameters, such as baudrate, data length, parity, stop bits or pins + if (uart->_baudrate != baudrate) { + if (ESP_OK != uart_set_baudrate(uart_nr, baudrate)) { + log_e("UART%d changing baudrate failed.", uart_nr); + retCode = false; + } else { + log_v("UART%d changed baudrate to %d", uart_nr, baudrate); + uart->_baudrate = baudrate; + } + } + uart_word_length_t data_bits = (config & 0xc) >> 2; + uart_parity_t parity = config & 0x3; + uart_stop_bits_t stop_bits = (config & 0x30) >> 4; + if (retCode && (uart->_config & 0xc) >> 2 != data_bits) { + if (ESP_OK != uart_set_word_length(uart_nr, data_bits)) { + log_e("UART%d changing data length failed.", uart_nr); + retCode = false; + } else { + log_v("UART%d changed data length to %d", uart_nr, data_bits + 5); + } + } + if (retCode && (uart->_config & 0x3) != parity) { + if (ESP_OK != uart_set_parity(uart_nr, parity)) { + log_e("UART%d changing parity failed.", uart_nr); + retCode = false; + } else { + log_v("UART%d changed parity to %s", uart_nr, parity == 0 ? "NONE" : parity == 2 ? "EVEN" : "ODD"); + } + } + if (retCode && (uart->_config & 0xc30) >> 4 != stop_bits) { + if (ESP_OK != uart_set_stop_bits(uart_nr, stop_bits)) { + log_e("UART%d changing stop bits failed.", uart_nr); + retCode = false; + } else { + log_v("UART%d changed stop bits to %d", uart_nr, stop_bits == 3 ? 2 : 1); + } + } + if (retCode) uart->_config = config; + if (retCode && rxPin > 0 && uart->_rxPin != rxPin) { + retCode &= _uartDetachPins(uart_nr, uart->_rxPin, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE); + retCode &= _uartAttachPins(uart_nr, rxPin, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE); + if (!retCode) { + log_e("UART%d changing RX pin failed.", uart_nr); + } else { + log_v("UART%d changed RX pin to %d", uart_nr, rxPin); + } + } + if (retCode && txPin > 0 && uart->_txPin != txPin) { + retCode &= _uartDetachPins(uart_nr, UART_PIN_NO_CHANGE, uart->_txPin, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE); + retCode &= _uartAttachPins(uart_nr, UART_PIN_NO_CHANGE, txPin, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE); + if (!retCode) { + log_e("UART%d changing TX pin failed.", uart_nr); + } else { + log_v("UART%d changed TX pin to %d", uart_nr, txPin); + } + } + UART_MUTEX_UNLOCK(); + if (retCode) { + // UART driver was already working, just return the uart_t structure, saying that no new driver was installed + return uart; + } + // if we reach this point, it means that we need to restart the UART driver + uartEnd(uart_nr); + } + } else { + log_v("UART%d not installed. Starting installation", uart_nr); + } uart_config_t uart_config; uart_config.data_bits = (config & 0xc) >> 2; uart_config.parity = (config & 0x3); @@ -209,20 +429,40 @@ uart_t* uartBegin(uint8_t uart_nr, uint32_t baudrate, uint32_t config, int8_t rx uart_config.source_clk = UART_SCLK_APB; // ESP32, ESP32S2 uart_config.baud_rate = _get_effective_baudrate(baudrate); #endif - ESP_ERROR_CHECK(uart_driver_install(uart_nr, rx_buffer_size, tx_buffer_size, 20, &(uart->uart_event_queue), 0)); - ESP_ERROR_CHECK(uart_param_config(uart_nr, &uart_config)); - ESP_ERROR_CHECK(uart_set_pin(uart_nr, txPin, rxPin, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE)); + UART_MUTEX_LOCK(); + bool retCode = ESP_OK == uart_driver_install(uart_nr, rx_buffer_size, tx_buffer_size, 20, &(uart->uart_event_queue), 0); + + if (retCode) retCode &= ESP_OK == uart_param_config(uart_nr, &uart_config); // Is it right or the idea is to swap rx and tx pins? - if (inverted) { + if (retCode && inverted) { // invert signal for both Rx and Tx - ESP_ERROR_CHECK(uart_set_line_inverse(uart_nr, UART_SIGNAL_TXD_INV | UART_SIGNAL_RXD_INV)); + retCode &= ESP_OK == uart_set_line_inverse(uart_nr, UART_SIGNAL_TXD_INV | UART_SIGNAL_RXD_INV); } + if (retCode) { + uart->_baudrate = baudrate; + uart->_config = config; + uart->_inverted = inverted; + uart->_rxfifo_full_thrhd = rxfifo_full_thrhd; + uart->_rx_buffer_size = rx_buffer_size; + uart->_tx_buffer_size = tx_buffer_size; + uart->has_peek = false; + uart->peek_byte = 0; + } UART_MUTEX_UNLOCK(); - uartFlush(uart); - return uart; + // uartSetPins detaches previous pins if new ones are used over a previous begin() + if (retCode) retCode &= uartSetPins(uart_nr, rxPin, txPin, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE); + if (!retCode) { + uartEnd(uart_nr); + uart = NULL; + log_e("UART%d initialization error.", uart->num); + } else { + uartFlush(uart); + log_v("UART%d initialization done.", uart->num); + } + return uart; // a new driver was installed } // This function code is under testing - for now just keep it here @@ -270,14 +510,21 @@ bool uartSetRxFIFOFull(uart_t* uart, uint8_t numBytesFIFOFull) return retCode; } -void uartEnd(uart_t* uart) + +void uartEnd(uint8_t uart_num) { - if(uart == NULL) { + if(uart_num >= SOC_UART_NUM) { + log_e("Serial number is invalid, please use number from 0 to %u", SOC_UART_NUM - 1); return; } + // get UART information + uart_t* uart = &_uart_bus_array[uart_num]; UART_MUTEX_LOCK(); - uart_driver_delete(uart->num); + _uartDetachPins(uart_num, uart->_rxPin, uart->_txPin, uart->_ctsPin, uart->_rtsPin); + if(uart_is_driver_installed(uart_num)) { + uart_driver_delete(uart_num); + } UART_MUTEX_UNLOCK(); } @@ -302,7 +549,7 @@ void uartSetRxInvert(uart_t* uart, bool invert) hw->conf0.rxd_inv = 1; else hw->conf0.rxd_inv = 0; -#endif +#endif } @@ -331,7 +578,7 @@ uint32_t uartAvailableForWrite(uart_t* uart) uint32_t available = uart_ll_get_txfifo_len(UART_LL_GET_HW(uart->num)); size_t txRingBufferAvailable = 0; if (ESP_OK == uart_get_tx_buffer_free_size(uart->num, &txRingBufferAvailable)) { - available += txRingBufferAvailable; + available = txRingBufferAvailable == 0 ? available : txRingBufferAvailable; } UART_MUTEX_UNLOCK(); return available; @@ -381,7 +628,7 @@ uint8_t uartRead(uart_t* uart) c = uart->peek_byte; } else { - int len = uart_read_bytes(uart->num, &c, 1, 20 / portTICK_RATE_MS); + int len = uart_read_bytes(uart->num, &c, 1, 20 / portTICK_PERIOD_MS); if (len <= 0) { // includes negative return from IDF in case of error c = 0; } @@ -403,7 +650,7 @@ uint8_t uartPeek(uart_t* uart) if (uart->has_peek) { c = uart->peek_byte; } else { - int len = uart_read_bytes(uart->num, &c, 1, 20 / portTICK_RATE_MS); + int len = uart_read_bytes(uart->num, &c, 1, 20 / portTICK_PERIOD_MS); if (len <= 0) { // includes negative return from IDF in case of error c = 0; } else { @@ -467,6 +714,7 @@ void uartSetBaudRate(uart_t* uart, uint32_t baud_rate) #else uart_ll_set_baudrate(UART_LL_GET_HW(uart->num), baud_rate); #endif + uart->_baudrate = baud_rate; UART_MUTEX_UNLOCK(); } @@ -534,7 +782,7 @@ void uart_install_putc() // Routines that take care of UART mode in the HardwareSerial Class code // used to set UART_MODE_RS485_HALF_DUPLEX auto RTS for TXD for ESP32 chips -bool uartSetMode(uart_t *uart, uint8_t mode) +bool uartSetMode(uart_t *uart, uart_mode_t mode) { if (uart == NULL || uart->num >= SOC_UART_NUM) { @@ -577,23 +825,29 @@ int log_printfv(const char *format, va_list arg) return 0; } } +/* +// This causes dead locks with logging in specific cases and also with C++ constructors that may send logs #if !CONFIG_DISABLE_HAL_LOCKS if(s_uart_debug_nr != -1 && _uart_bus_array[s_uart_debug_nr].lock){ xSemaphoreTake(_uart_bus_array[s_uart_debug_nr].lock, portMAX_DELAY); } #endif - +*/ vsnprintf(temp, len+1, format, arg); ets_printf("%s", temp); - +/* +// This causes dead locks with logging and also with constructors that may send logs #if !CONFIG_DISABLE_HAL_LOCKS if(s_uart_debug_nr != -1 && _uart_bus_array[s_uart_debug_nr].lock){ xSemaphoreGive(_uart_bus_array[s_uart_debug_nr].lock); } #endif +*/ if(len >= sizeof(loc_buf)){ free(temp); } + // flushes TX - try to assure that the log message is completely sent. + if(s_uart_debug_nr != -1) while(!uart_ll_is_tx_idle(UART_LL_GET_HW(s_uart_debug_nr))); return len; } @@ -727,7 +981,7 @@ unsigned long uartDetectBaudrate(uart_t *uart) } /* - These functions are for testing puspose only and can be used in Arduino Sketches + These functions are for testing purpose only and can be used in Arduino Sketches Those are used in the UART examples */ @@ -737,15 +991,17 @@ unsigned long uartDetectBaudrate(uart_t *uart) This code "replaces" the physical wiring for connecting TX <--> RX in a loopback */ -// gets the right TX SIGNAL, based on the UART number +// gets the right TX or RX SIGNAL, based on the UART number from gpio_sig_map.h #if SOC_UART_NUM > 2 -#define UART_TX_SIGNAL(uartNumber) (uartNumber == UART_NUM_0 ? U0TXD_OUT_IDX : (uartNumber == UART_NUM_1 ? U1TXD_OUT_IDX : U2TXD_OUT_IDX)) + #define UART_TX_SIGNAL(uartNumber) (uartNumber == UART_NUM_0 ? U0TXD_OUT_IDX : (uartNumber == UART_NUM_1 ? U1TXD_OUT_IDX : U2TXD_OUT_IDX)) + #define UART_RX_SIGNAL(uartNumber) (uartNumber == UART_NUM_0 ? U0RXD_IN_IDX : (uartNumber == UART_NUM_1 ? U1RXD_IN_IDX : U2RXD_IN_IDX)) #else -#define UART_TX_SIGNAL(uartNumber) (uartNumber == UART_NUM_0 ? U0TXD_OUT_IDX : U1TXD_OUT_IDX) + #define UART_TX_SIGNAL(uartNumber) (uartNumber == UART_NUM_0 ? U0TXD_OUT_IDX : U1TXD_OUT_IDX) + #define UART_RX_SIGNAL(uartNumber) (uartNumber == UART_NUM_0 ? U0RXD_IN_IDX : U1RXD_IN_IDX) #endif /* - Make sure UART's RX signal is connected to TX pin - This creates a loop that lets us receive anything we send on the UART + This function internally binds defined UARTs TX signal with defined RX pin of any UART (same or different). + This creates a loop that lets us receive anything we send on the UART without external wires. */ void uart_internal_loopback(uint8_t uartNum, int8_t rxPin) { @@ -766,7 +1022,7 @@ void uart_send_break(uint8_t uartNum) // This is very sensetive timing... it works fine for SERIAL_8N1 uint32_t breakTime = (uint32_t) (10.0 * (1000000.0 / currentBaudrate)); uart_set_line_inverse(uartNum, UART_SIGNAL_TXD_INV); - ets_delay_us(breakTime); + esp_rom_delay_us(breakTime); uart_set_line_inverse(uartNum, UART_SIGNAL_INV_DISABLE); } diff --git a/cores/esp32/esp32-hal-uart.h b/cores/esp32/esp32-hal-uart.h index 886142ce23b..12da8c8cd84 100644 --- a/cores/esp32/esp32-hal-uart.h +++ b/cores/esp32/esp32-hal-uart.h @@ -1,4 +1,4 @@ -// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// Copyright 2015-2024 Espressif Systems (Shanghai) PTE LTD // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -24,83 +24,14 @@ extern "C" { #include #include "freertos/FreeRTOS.h" #include "freertos/queue.h" - -#ifdef __cplusplus -enum SerialConfig { -SERIAL_5N1 = 0x8000010, -SERIAL_6N1 = 0x8000014, -SERIAL_7N1 = 0x8000018, -SERIAL_8N1 = 0x800001c, -SERIAL_5N2 = 0x8000030, -SERIAL_6N2 = 0x8000034, -SERIAL_7N2 = 0x8000038, -SERIAL_8N2 = 0x800003c, -SERIAL_5E1 = 0x8000012, -SERIAL_6E1 = 0x8000016, -SERIAL_7E1 = 0x800001a, -SERIAL_8E1 = 0x800001e, -SERIAL_5E2 = 0x8000032, -SERIAL_6E2 = 0x8000036, -SERIAL_7E2 = 0x800003a, -SERIAL_8E2 = 0x800003e, -SERIAL_5O1 = 0x8000013, -SERIAL_6O1 = 0x8000017, -SERIAL_7O1 = 0x800001b, -SERIAL_8O1 = 0x800001f, -SERIAL_5O2 = 0x8000033, -SERIAL_6O2 = 0x8000037, -SERIAL_7O2 = 0x800003b, -SERIAL_8O2 = 0x800003f -}; -#else -#define SERIAL_5N1 0x8000010 -#define SERIAL_6N1 0x8000014 -#define SERIAL_7N1 0x8000018 -#define SERIAL_8N1 0x800001c -#define SERIAL_5N2 0x8000030 -#define SERIAL_6N2 0x8000034 -#define SERIAL_7N2 0x8000038 -#define SERIAL_8N2 0x800003c -#define SERIAL_5E1 0x8000012 -#define SERIAL_6E1 0x8000016 -#define SERIAL_7E1 0x800001a -#define SERIAL_8E1 0x800001e -#define SERIAL_5E2 0x8000032 -#define SERIAL_6E2 0x8000036 -#define SERIAL_7E2 0x800003a -#define SERIAL_8E2 0x800003e -#define SERIAL_5O1 0x8000013 -#define SERIAL_6O1 0x8000017 -#define SERIAL_7O1 0x800001b -#define SERIAL_8O1 0x800001f -#define SERIAL_5O2 0x8000033 -#define SERIAL_6O2 0x8000037 -#define SERIAL_7O2 0x800003b -#define SERIAL_8O2 0x800003f -#endif // __cplusplus - -// These are Hardware Flow Contol possible usage -// equivalent to UDF enum uart_hw_flowcontrol_t from -// https://github.com/espressif/esp-idf/blob/master/components/hal/include/hal/uart_types.h#L75-L81 -#define HW_FLOWCTRL_DISABLE 0x0 // disable HW Flow Control -#define HW_FLOWCTRL_RTS 0x1 // use only RTS PIN for HW Flow Control -#define HW_FLOWCTRL_CTS 0x2 // use only CTS PIN for HW Flow Control -#define HW_FLOWCTRL_CTS_RTS 0x3 // use both CTS and RTS PIN for HW Flow Control - -// These are Hardware Uart Modes possible usage -// equivalent to UDF enum uart_mode_t from -// https://github.com/espressif/esp-idf/blob/master/components/hal/include/hal/uart_types.h#L34-L40 -#define MODE_UART 0x00 // mode: regular UART mode -#define MODE_RS485_HALF_DUPLEX 0x01 // mode: half duplex RS485 UART mode control by RTS pin -#define MODE_IRDA 0x02 // mode: IRDA UART mode -#define MODE_RS485_COLLISION_DETECT 0x03 // mode: RS485 collision detection UART mode (used for test purposes) -#define MODE_RS485_APP_CTRL 0x04 +#include "hal/uart_types.h" struct uart_struct_t; typedef struct uart_struct_t uart_t; +bool _testUartBegin(uint8_t uart_nr, uint32_t baudrate, uint32_t config, int8_t rxPin, int8_t txPin, uint16_t rx_buffer_size, uint16_t tx_buffer_size, bool inverted, uint8_t rxfifo_full_thrhd); uart_t* uartBegin(uint8_t uart_nr, uint32_t baudrate, uint32_t config, int8_t rxPin, int8_t txPin, uint16_t rx_buffer_size, uint16_t tx_buffer_size, bool inverted, uint8_t rxfifo_full_thrhd); -void uartEnd(uart_t* uart); +void uartEnd(uint8_t uart_num); // This is used to retrieve the Event Queue pointer from a UART IDF Driver in order to allow user to deal with its events void uartGetEventQueue(uart_t* uart, QueueHandle_t *q); @@ -130,16 +61,30 @@ int uartGetDebug(); bool uartIsDriverInstalled(uart_t* uart); -// Negative Pin Number will keep it unmodified, thus this function can set/reset individual pins +// Negative Pin Number will keep it unmodified, thus this function can set individual pins +// When pins are changed, it will detach the previous ones +// Can be called before or after begin() bool uartSetPins(uint8_t uart_num, int8_t rxPin, int8_t txPin, int8_t ctsPin, int8_t rtsPin); -void uartDetachPins(uint8_t uart_num, int8_t rxPin, int8_t txPin, int8_t ctsPin, int8_t rtsPin); + +// helper functions +int8_t uart_get_RxPin(uint8_t uart_num); +int8_t uart_get_TxPin(uint8_t uart_num); // Enables or disables HW Flow Control function -- needs also to set CTS and/or RTS pins -bool uartSetHwFlowCtrlMode(uart_t *uart, uint8_t mode, uint8_t threshold); +// UART_HW_FLOWCTRL_DISABLE = 0x0 disable hardware flow control +// UART_HW_FLOWCTRL_RTS = 0x1 enable RX hardware flow control (rts) +// UART_HW_FLOWCTRL_CTS = 0x2 enable TX hardware flow control (cts) +// UART_HW_FLOWCTRL_CTS_RTS = 0x3 enable hardware flow control +bool uartSetHwFlowCtrlMode(uart_t *uart, uart_hw_flowcontrol_t mode, uint8_t threshold); // Used to set RS485 function -- needs to disable HW Flow Control and set RTS pin to use // RTS pin becomes RS485 half duplex RE/DE -bool uartSetMode(uart_t *uart, uint8_t mode); +// UART_MODE_UART = 0x00 mode: regular UART mode +// UART_MODE_RS485_HALF_DUPLEX = 0x01 mode: half duplex RS485 UART mode control by RTS pin +// UART_MODE_IRDA = 0x02 mode: IRDA UART mode +// UART_MODE_RS485_COLLISION_DETECT = 0x03 mode: RS485 collision detection UART mode (used for test purposes) +// UART_MODE_RS485_APP_CTRL = 0x04 mode: application control RS485 UART mode (used for test purposes) +bool uartSetMode(uart_t *uart, uart_mode_t mode); void uartStartDetectBaudrate(uart_t *uart); unsigned long uartDetectBaudrate(uart_t *uart);