Description
Describe the bug
I have been experiencing strange behaviour when using UART Async mode on a STM32 L462 MCU. I am using Zephyr 3.6.0
I have an application communicating via UART (230400 bps, 8N1, with RTS/CTS hw flow control). The MCU is running at 48 MHz. I use the UART Async API.
The application periodically receives messages of 9 bytes. I am supplying buffers of size 13 to the Async API (first with uart_rx_enable
, and afterwards via uart_rx_buf_rsp
). The messages are received as expected: when the message fits entirely within a single buffer I get a UART_RX_RDY
with 9 bytes; if it does not fit into a single buffer (and overflows), then I get for example a UART_RX_RDY
with 5 bytes, then a UART_RX_RDY
with 4 bytes (with some UART_RX_BUF_RELEASED
and UART_RX_BUF_REQUEST
in between).
However, if the message overflows with exactly one byte spilling into the next buffer (i.e 8 bytes fit into the currently active buffer, then the uart driver releases and requests a new buffer, then 1 byte is put into the new active buffer), then the second UART_RX_RDY
(the one that is supposed to have 1 byte) is not triggered. I only receive UART_RX_RDY
for the first chunk of 8 bytes.
This is the corresponding log output (with CONFIG_UART_LOG_LEVEL_DBG=y
)
[00:00:30.675,000] <dbg> uart_stm32: uart_stm32_async_rx_buf_rsp: replace buffer (13)
[00:00:30.675,000] <dbg> uart_stm32: uart_stm32_async_rx_enable: async rx enabled
...
[00:00:45.677,000] <dbg> uart_stm32: uart_stm32_isr: idle interrupt occurred
[00:00:45.677,000] <dbg> uart_stm32: async_evt_rx_rdy: rx_rdy: (0 9)
...
[00:01:00.677,000] <dbg> uart_stm32: async_evt_rx_rdy: rx_rdy: (9 13)
[00:01:00.677,000] <dbg> uart_stm32: uart_stm32_dma_replace_buffer: Replacing RX buffer: 13
[00:01:00.677,000] <dbg> uart_stm32: uart_stm32_async_rx_buf_rsp: replace buffer (13)
[00:01:00.677,000] <dbg> uart_stm32: uart_stm32_isr: idle interrupt occurred
[00:01:00.677,000] <dbg> uart_stm32: async_evt_rx_rdy: rx_rdy: (0 5)
...
[00:01:15.678,000] <dbg> uart_stm32: async_evt_rx_rdy: rx_rdy: (5 13)
[00:01:15.678,000] <dbg> uart_stm32: uart_stm32_dma_replace_buffer: Replacing RX buffer: 13
[00:01:15.678,000] <dbg> uart_stm32: uart_stm32_async_rx_buf_rsp: replace buffer (13)
... <---- no rx_rdy(0 1)
I had a closer look at uart_stm32.c
and in particular the function uart_stm32_dma_replace_buffer
. There is a suspicious line that clears the IDLE flag when requesting a new buffer:
static void uart_stm32_dma_replace_buffer(const struct device *dev)
{
const struct uart_stm32_config *config = dev->config;
struct uart_stm32_data *data = dev->data;
/* Replace the buffer and reload the DMA */
LOG_DBG("Replacing RX buffer: %d", data->rx_next_buffer_len);
/* reload DMA */
data->dma_rx.offset = 0;
data->dma_rx.counter = 0;
data->dma_rx.buffer = data->rx_next_buffer;
data->dma_rx.buffer_length = data->rx_next_buffer_len;
data->dma_rx.blk_cfg.block_size = data->dma_rx.buffer_length;
data->dma_rx.blk_cfg.dest_address = (uint32_t)data->dma_rx.buffer;
data->rx_next_buffer = NULL;
data->rx_next_buffer_len = 0;
dma_reload(data->dma_rx.dma_dev, data->dma_rx.dma_channel,
data->dma_rx.blk_cfg.source_address,
data->dma_rx.blk_cfg.dest_address,
data->dma_rx.blk_cfg.block_size);
dma_start(data->dma_rx.dma_dev, data->dma_rx.dma_channel);
----> LL_USART_ClearFlag_IDLE(config->usart); <----------------------
/* Request next buffer */
async_evt_rx_buf_request(data);
}
What is the intention of clearing the Idle flag when requesting a new buffer? When I remove that line then everything works nicely; I get the UART_RX_RDY
notification even in the 8-1 split case (single byte in the new buffer):
[00:00:33.147,000] <dbg> uart_stm32: uart_stm32_async_rx_buf_rsp: replace buffer (13)
[00:00:33.147,000] <dbg> uart_stm32: uart_stm32_async_rx_enable: async rx enabled
...
[00:00:48.149,000] <dbg> uart_stm32: uart_stm32_isr: idle interrupt occurred
[00:00:48.149,000] <dbg> uart_stm32: async_evt_rx_rdy: rx_rdy: (0 9)
...
[00:01:03.150,000] <dbg> uart_stm32: async_evt_rx_rdy: rx_rdy: (9 13)
[00:01:03.150,000] <dbg> uart_stm32: uart_stm32_dma_replace_buffer: Replacing RX buffer: 13
[00:01:03.150,000] <dbg> uart_stm32: uart_stm32_async_rx_buf_rsp: replace buffer (13)
[00:01:03.150,000] <dbg> uart_stm32: uart_stm32_isr: idle interrupt occurred
[00:01:03.150,000] <dbg> uart_stm32: async_evt_rx_rdy: rx_rdy: (0 2)
[00:01:03.150,000] <dbg> uart_stm32: uart_stm32_isr: idle interrupt occurred
[00:01:03.150,000] <dbg> uart_stm32: async_evt_rx_rdy: rx_rdy: (2 5)
...
[00:01:18.151,000] <dbg> uart_stm32: async_evt_rx_rdy: rx_rdy: (5 13)
[00:01:18.151,000] <dbg> uart_stm32: uart_stm32_dma_replace_buffer: Replacing RX buffer: 13
[00:01:18.151,000] <dbg> uart_stm32: uart_stm32_async_rx_buf_rsp: replace buffer (13)
[00:01:18.151,000] <dbg> uart_stm32: uart_stm32_isr: idle interrupt occurred
[00:01:18.151,000] <dbg> uart_stm32: async_evt_rx_rdy: rx_rdy: (0 1) <------------------ ok
...
[00:01:33.151,000] <dbg> uart_stm32: uart_stm32_isr: idle interrupt occurred
[00:01:33.151,000] <dbg> uart_stm32: async_evt_rx_rdy: rx_rdy: (1 10)
Is it possible that at 230400 bps, receiving the single byte of the second chunk happens so fast that the subsequent setting of the IDLE interrupt flag gets wrongfully cleared by uart_stm32_dma_replace_buffer
(and thus prevents uart_stm32_isr
from properly generating the UART_RX_RDY event?)
Regression
- This is a regression.
Steps to reproduce
No response
Relevant log output
Impact
Annoyance – Minor irritation; no significant impact on usability or functionality.
Environment
No response
Additional Context
No response