Skip to content

Hardware serial ports issue with full-duplex usage #467

@ppescher

Description

@ppescher

The way serial ports are implemented in the Arduino core can lead to a serial port permanently stop receiving under particular conditions.

If the serial port is transmitting some data, the last transmit callback does not re-enable the TX interrupt if the ring buffer is empty. This is done at the end of the HardwareSerial::write() function, outside the IRQ context.

If a receive interrupt fires at this time, inside the Transmit_IT() function, when the receive callback is finally called it will fail to re-enable the RX interrupt, because Receive_IT() finds the handle locked.

You can see my solution in this commit: fortebit@21cbddd

Another issue that happened while I was trying to debug this nightmare was that the USART kept receiving bytes after hitting a breakpoint and eventually signaled an overrun error. I'm not entirely sure how the error callback should be handled, but the current implementation inside the uart driver does not seem correct: the error flags are not cleared correctly for an STM32L4 (reading the data register is not enough) and probably you should re-enable the RX interrupt because the normal receive callback does not get called.

You can see my solution in this other commit: fortebit@6653e31

I'm not sure if the flags are cleared correctly for all STM32 families, as was only debugging on an STM32L4.

My variant is based on an old STM32 core version, I think it was 1.2.0, but I verified the affected code has not changed significantly in the latest official release 1.5.0.

The first bug depends on how the handle locking mechanism is implemented in the HAL, I don't know if this has changed lately but my guess is it has not. If the lock is already acquired, an asynchrounous function (called from an IRQ for example) simply fails.

And when the Receive_IT() function fails there was no special handling and no way to know something went bad. It took me many hours of debugging to catch this and finally spot the __HAL_LOCK(handle) macro was leaving the function with an error code.

In my solution I just disabled the UART IRQ when the HAL Receive_IT/Transmit_IT are called, implementing a true critical section, but only affecting a single IRQ and not global interrupts.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions