Issue with UART driver
Posted: Tue Jan 14, 2014 11:56 pm
Hi,
i have written a complex driver which exposes a BaseChannel interface of a half duplex rs485 line using the uart driver.
In this case the uarts rx and tx lines are tied together so when i want to transmit data, i have to stop rx first and
after i have received the tx2 cb (physically sent) i reenable rx.
When looking at the uart_lld implementation for STM32/UARTv1, i can see that the start and stop of rx and tx is
achieved by enabling and disabling the dma channels.
I think there is a race condition which ill try to explain:
Sending data looks like this (simplified):
1.) onotify - uartStopReceiveI(), then uartStartSendI()
2.) txend1_cb - if there is more data to send uartStartSendI()
3.) txend2_cb - uartStartReceiveI()
Receiving data looks like this (simplified):
1.) rxend_cb - uartStartReceiveI()
What happens when i have a lot of direction changes (i.e. a loopback) is, that i get rx callbacks for data which has just been sent before.
I think this happens when the set_rx_idle_loop dma is being stopped in between the reception of a byte at the uart and the restart
of the reception in 3.) which leads to a rxend_cb for this received byte which should have been dropped instead because of the prior call to uartStopReceive().
The normal case is, that the rx_idle_dma consumes the byte. In high load, this fails occasionally and thus i get an rxend_cb.
I can "fix" this by adding
to uart_lld_stop_receive
and
to uart_lld_start_receive
but dont really feel good in patching the uart_lld driver.
From a user perspective i am receiving data although i called uartStopReceive() before.
Please advise.
Thank you in advance and with best regards,
Vinz
i have written a complex driver which exposes a BaseChannel interface of a half duplex rs485 line using the uart driver.
In this case the uarts rx and tx lines are tied together so when i want to transmit data, i have to stop rx first and
after i have received the tx2 cb (physically sent) i reenable rx.
When looking at the uart_lld implementation for STM32/UARTv1, i can see that the start and stop of rx and tx is
achieved by enabling and disabling the dma channels.
I think there is a race condition which ill try to explain:
Sending data looks like this (simplified):
1.) onotify - uartStopReceiveI(), then uartStartSendI()
2.) txend1_cb - if there is more data to send uartStartSendI()
3.) txend2_cb - uartStartReceiveI()
Receiving data looks like this (simplified):
1.) rxend_cb - uartStartReceiveI()
What happens when i have a lot of direction changes (i.e. a loopback) is, that i get rx callbacks for data which has just been sent before.
I think this happens when the set_rx_idle_loop dma is being stopped in between the reception of a byte at the uart and the restart
of the reception in 3.) which leads to a rxend_cb for this received byte which should have been dropped instead because of the prior call to uartStopReceive().
The normal case is, that the rx_idle_dma consumes the byte. In high load, this fails occasionally and thus i get an rxend_cb.
I can "fix" this by adding
Code: Select all
uartp->usart->CR1 &= ~USART_CR1_RE;to uart_lld_stop_receive
and
Code: Select all
uartp->usart->CR1 |= USART_CR1_RE;to uart_lld_start_receive
but dont really feel good in patching the uart_lld driver.
From a user perspective i am receiving data although i called uartStopReceive() before.
Please advise.
Thank you in advance and with best regards,
Vinz