Skip to content

Commit 2dd42c2

Browse files
SuGliderCopilotpre-commit-ci-lite[bot]
authored
fix(uart): terminates uart driver whenever both rx and tx pins are detached (#12080)
* fix(uart): terminates uart driver whenever rx and tx are detached * fix(uart): detaching pin check points * fix(uart): extended lod messages * fix(uart): fixes setPins to keep driver working * fix(uart): peripheral manager CI test adjusting Updated UART test configurations to handle I2C interactions correctly by detaching TX pin and restarting the UART driver when both RX and TX are detached. * fix(uart): Refactor UART test to detach only one pin Updated UART test configuration to detach only one pin instead of both, ensuring the UART driver continues to function. Removed redundant code related to UART driver restart when both pins are detached. * fix(uart_test): do not detach both UART pins Updated UART test configuration to detach TX pin while keeping RX pin active. Adjusted assertions to reflect the new pin settings. * fix(uart_test): formatting * fix(uart_doc): Document RX and TX pin detachment behavior Added note about driver behavior when RX and TX pins are detached. * fix(uart): code formatting Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * fix(uart): code formatting Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * fix(uart): code formatting Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * fix(uart): code formatting Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * feat(uart): C wrapper to end related Serial object * fix(uart): fixes bad case value Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * fix(uart): C wrapper to end related Serial object * fix(docs): adding comma * ci(pre-commit): Apply automatic fixes * fix(uart_ci): typo in UART driver comment --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
1 parent c0395df commit 2dd42c2

File tree

5 files changed

+114
-13
lines changed

5 files changed

+114
-13
lines changed

cores/esp32/HardwareSerial.cpp

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,31 @@ HardwareSerial Serial5(5);
6161
extern void HWCDCSerialEvent(void) __attribute__((weak));
6262
#endif
6363

64+
// C-callable helper used by HAL when pins are detached and the high-level
65+
// HardwareSerial instance must be finalized.
66+
extern "C" void hal_uart_notify_pins_detached(int uart_num) {
67+
log_d("hal_uart_notify_pins_detached: Notifying HardwareSerial for UART%d", uart_num);
68+
switch (uart_num) {
69+
case 0: Serial0.end(); break;
70+
#if SOC_UART_NUM > 1
71+
case 1: Serial1.end(); break;
72+
#endif
73+
#if SOC_UART_NUM > 2
74+
case 2: Serial2.end(); break;
75+
#endif
76+
#if SOC_UART_NUM > 3
77+
case 3: Serial3.end(); break;
78+
#endif
79+
#if SOC_UART_NUM > 4
80+
case 4: Serial4.end(); break;
81+
#endif
82+
#if SOC_UART_NUM > 5
83+
case 5: Serial5.end(); break;
84+
#endif
85+
default: log_e("hal_uart_notify_pins_detached: UART%d not handled!", uart_num); break;
86+
}
87+
}
88+
6489
#if USB_SERIAL_IS_DEFINED == 1 // Native USB CDC Event
6590
// Used by Hardware Serial for USB CDC events
6691
extern void USBSerialEvent(void) __attribute__((weak));
@@ -483,9 +508,6 @@ void HardwareSerial::end() {
483508
// including any tasks or debug message channel (log_x()) - but not for IDF log messages!
484509
_onReceiveCB = NULL;
485510
_onReceiveErrorCB = NULL;
486-
if (uartGetDebug() == _uart_nr) {
487-
uartSetDebug(0);
488-
}
489511
_rxFIFOFull = 0;
490512
uartEnd(_uart_nr); // fully detach all pins and delete the UART driver
491513
_destroyEventTask(); // when IDF uart driver is deleted, _eventTask must finish too

cores/esp32/chip-debug-report.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -291,7 +291,7 @@ static void printPerimanInfo(void) {
291291

292292
void printBeforeSetupInfo(void) {
293293
#if ARDUINO_USB_CDC_ON_BOOT
294-
Serial.begin(0);
294+
Serial.begin();
295295
Serial.setDebugOutput(true);
296296
uint8_t t = 0;
297297
while (!Serial && (t++ < 200)) {

cores/esp32/esp32-hal-uart.c

Lines changed: 73 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@
4242
static int s_uart_debug_nr = 0; // UART number for debug output
4343
#define REF_TICK_BAUDRATE_LIMIT 250000 // this is maximum UART badrate using REF_TICK as clock
4444

45+
/* C prototype for the notifier implemented in HardwareSerial.cpp */
46+
extern void hal_uart_notify_pins_detached(int uart_num);
47+
4548
struct uart_struct_t {
4649

4750
#if !CONFIG_DISABLE_HAL_LOCKS
@@ -282,29 +285,67 @@ static bool _uartDetachPins(uint8_t uart_num, int8_t rxPin, int8_t txPin, int8_t
282285
// Peripheral Manager detach callback for each specific UART PIN
283286
static bool _uartDetachBus_RX(void *busptr) {
284287
// sanity check - it should never happen
285-
assert(busptr && "_uartDetachBus_RX bus NULL pointer.");
288+
if (busptr == NULL) {
289+
log_e("_uartDetachBus_RX: busptr is NULL");
290+
return false;
291+
}
286292
uart_t *bus = (uart_t *)busptr;
293+
if (bus->_rxPin < 0) {
294+
log_d("_uartDetachBus_RX: RX pin already detached for UART%d", bus->num);
295+
return true;
296+
}
297+
if (bus->_txPin < 0) { // both rx and tx pins are detached, terminate the uart driver
298+
log_d("_uartDetachBus_RX: both RX and TX pins detached for UART%d, terminating driver", bus->num);
299+
hal_uart_notify_pins_detached(bus->num);
300+
return true;
301+
}
287302
return _uartDetachPins(bus->num, bus->_rxPin, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
288303
}
289304

290305
static bool _uartDetachBus_TX(void *busptr) {
291306
// sanity check - it should never happen
292-
assert(busptr && "_uartDetachBus_TX bus NULL pointer.");
307+
if (busptr == NULL) {
308+
log_e("_uartDetachBus_TX: busptr is NULL");
309+
return false;
310+
}
293311
uart_t *bus = (uart_t *)busptr;
312+
if (bus->_txPin < 0) {
313+
log_d("_uartDetachBus_TX: TX pin already detached for UART%d", bus->num);
314+
return true;
315+
}
316+
if (bus->_rxPin < 0) { // both rx and tx pins are detached, terminate the uart driver
317+
log_d("_uartDetachBus_TX: both RX and TX pins detached for UART%d, terminating driver", bus->num);
318+
hal_uart_notify_pins_detached(bus->num);
319+
return true;
320+
}
294321
return _uartDetachPins(bus->num, UART_PIN_NO_CHANGE, bus->_txPin, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
295322
}
296323

297324
static bool _uartDetachBus_CTS(void *busptr) {
298325
// sanity check - it should never happen
299-
assert(busptr && "_uartDetachBus_CTS bus NULL pointer.");
326+
if (busptr == NULL) {
327+
log_e("_uartDetachBus_CTS: busptr is NULL");
328+
return false;
329+
}
300330
uart_t *bus = (uart_t *)busptr;
331+
if (bus->_ctsPin < 0) {
332+
log_d("_uartDetachBus_CTS: CTS pin already detached for UART%d", bus->num);
333+
return true;
334+
}
301335
return _uartDetachPins(bus->num, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, bus->_ctsPin, UART_PIN_NO_CHANGE);
302336
}
303337

304338
static bool _uartDetachBus_RTS(void *busptr) {
305339
// sanity check - it should never happen
306-
assert(busptr && "_uartDetachBus_RTS bus NULL pointer.");
340+
if (busptr == NULL) {
341+
log_e("_uartDetachBus_RTS: busptr is NULL");
342+
return false;
343+
}
307344
uart_t *bus = (uart_t *)busptr;
345+
if (bus->_rtsPin < 0) {
346+
log_d("_uartDetachBus_RTS: RTS pin already detached for UART%d", bus->num);
347+
return true;
348+
}
308349
return _uartDetachPins(bus->num, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, bus->_rtsPin);
309350
}
310351

@@ -629,6 +670,16 @@ bool uartSetPins(uint8_t uart_num, int8_t rxPin, int8_t txPin, int8_t ctsPin, in
629670
//log_v("setting UART%d pins: prev->new RX(%d->%d) TX(%d->%d) CTS(%d->%d) RTS(%d->%d)", uart_num,
630671
// uart->_rxPin, rxPin, uart->_txPin, txPin, uart->_ctsPin, ctsPin, uart->_rtsPin, rtsPin); vTaskDelay(10);
631672

673+
// mute bus detaching callbacks to avoid terminating the UART driver when both RX and TX pins are detached
674+
peripheral_bus_deinit_cb_t rxDeinit = perimanGetBusDeinit(ESP32_BUS_TYPE_UART_RX);
675+
peripheral_bus_deinit_cb_t txDeinit = perimanGetBusDeinit(ESP32_BUS_TYPE_UART_TX);
676+
peripheral_bus_deinit_cb_t ctsDeinit = perimanGetBusDeinit(ESP32_BUS_TYPE_UART_CTS);
677+
peripheral_bus_deinit_cb_t rtsDeinit = perimanGetBusDeinit(ESP32_BUS_TYPE_UART_RTS);
678+
perimanClearBusDeinit(ESP32_BUS_TYPE_UART_RX);
679+
perimanClearBusDeinit(ESP32_BUS_TYPE_UART_TX);
680+
perimanClearBusDeinit(ESP32_BUS_TYPE_UART_CTS);
681+
perimanClearBusDeinit(ESP32_BUS_TYPE_UART_RTS);
682+
632683
// First step: detaches all previous UART pins
633684
bool rxPinChanged = rxPin >= 0 && rxPin != uart->_rxPin;
634685
if (rxPinChanged) {
@@ -660,6 +711,21 @@ bool uartSetPins(uint8_t uart_num, int8_t rxPin, int8_t txPin, int8_t ctsPin, in
660711
if (rtsPinChanged) {
661712
retCode &= _uartAttachPins(uart->num, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, rtsPin);
662713
}
714+
715+
// restore bus detaching callbacks
716+
if (rxDeinit != NULL) {
717+
perimanSetBusDeinit(ESP32_BUS_TYPE_UART_RX, rxDeinit);
718+
}
719+
if (txDeinit != NULL) {
720+
perimanSetBusDeinit(ESP32_BUS_TYPE_UART_TX, txDeinit);
721+
}
722+
if (ctsDeinit != NULL) {
723+
perimanSetBusDeinit(ESP32_BUS_TYPE_UART_CTS, ctsDeinit);
724+
}
725+
if (rtsDeinit != NULL) {
726+
perimanSetBusDeinit(ESP32_BUS_TYPE_UART_RTS, rtsDeinit);
727+
}
728+
663729
UART_MUTEX_UNLOCK();
664730

665731
if (!retCode) {
@@ -986,6 +1052,9 @@ void uartEnd(uint8_t uart_num) {
9861052
if (uart_is_driver_installed(uart_num)) {
9871053
uart_driver_delete(uart_num);
9881054
}
1055+
if (uartGetDebug() == uart_num) {
1056+
uartSetDebug(0);
1057+
}
9891058
UART_MUTEX_UNLOCK();
9901059
}
9911060

docs/en/api/serial.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@ with additional features for advanced use cases.
2323
* **Event callbacks**: Receive and error event callbacks
2424
* **Configurable buffers**: Adjustable RX and TX buffer sizes
2525

26+
.. note::
27+
In case that both pins, RX and TX are detached from UART, the driver will be stopped.
28+
Detaching may occur when, for instance, starting another peripheral using RX and TX pins, such as Wire.begin(RX0, TX0).
29+
2630
UART Availability
2731
-----------------
2832

tests/validation/uart/uart.ino

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -377,19 +377,23 @@ void change_pins_test(void) {
377377
UARTTestConfig &config = *uart_test_configs[0];
378378
// pinMode will force enabling the internal pullup resistor (IDF 5.3.2 Change)
379379
pinMode(NEW_RX1, INPUT_PULLUP);
380-
config.serial.setPins(NEW_RX1, NEW_TX1);
380+
// Detaching both pins will result in stopping the UART driver
381+
// Only detach one of the pins
382+
config.serial.setPins(NEW_RX1, /*NEW_TX1*/ -1);
381383
TEST_ASSERT_EQUAL(NEW_RX1, uart_get_RxPin(config.uart_num));
382-
TEST_ASSERT_EQUAL(NEW_TX1, uart_get_TxPin(config.uart_num));
384+
//TEST_ASSERT_EQUAL(NEW_TX1, uart_get_TxPin(config.uart_num));
383385

384386
uart_internal_loopback(config.uart_num, NEW_RX1);
385387
config.transmit_and_check_msg("using new pins");
386388
} else {
387389
for (int i = 0; i < TEST_UART_NUM; i++) {
388390
UARTTestConfig &config = *uart_test_configs[i];
389391
UARTTestConfig &next_uart = *uart_test_configs[(i + 1) % TEST_UART_NUM];
390-
config.serial.setPins(next_uart.default_rx_pin, next_uart.default_tx_pin);
392+
// Detaching both pins will result in stopping the UART driver
393+
// Only detach one of the pins
394+
config.serial.setPins(next_uart.default_rx_pin, /*next_uart.default_tx_pin*/ -1);
391395
TEST_ASSERT_EQUAL(uart_get_RxPin(config.uart_num), next_uart.default_rx_pin);
392-
TEST_ASSERT_EQUAL(uart_get_TxPin(config.uart_num), next_uart.default_tx_pin);
396+
//TEST_ASSERT_EQUAL(uart_get_TxPin(config.uart_num), next_uart.default_tx_pin);
393397

394398
uart_internal_loopback(config.uart_num, next_uart.default_rx_pin);
395399
config.transmit_and_check_msg("using new pins");
@@ -450,7 +454,9 @@ void periman_test(void) {
450454

451455
for (auto *ref : uart_test_configs) {
452456
UARTTestConfig &config = *ref;
453-
Wire.begin(config.default_rx_pin, config.default_tx_pin);
457+
// Detaching both pins will result in stopping the UART driver
458+
// Only detach one of the pins
459+
Wire.begin(config.default_rx_pin, /*config.default_tx_pin*/ -1);
454460
config.recv_msg = "";
455461

456462
log_d("Trying to send message using UART%d with I2C enabled", config.uart_num);

0 commit comments

Comments
 (0)