Hi,
I'm trying to use the UART driver (not the serial one) with an STM32L432, using the RX way only - no TX.
I've not been able to find many examples about how to use it, and I guess I'm not doing the expected calls.
For testing purposes, UART1 RX receives a constant stream of ASCII char, so it's easy to debug and spot lost bytes. My example code simply copy back the received stream to another UART2 TX which acts as debug port. I know this looks like the serial driver use case, but the goal of the final implementation is to:
- use RX at high speed (8-10Mbps)
- filter data (about every other byte needs to be discarded)
- understand how to use the UART driver
I'm using rxend_cb to push the RX data into a ring buffer, and a RT thread is simply popping the ring buffer to print the data out.
I've configured the RX DMA buffer for 128 byte transfer.
No data is lost in a single buffer, but bytes are lost from one buffer to the next one. I'm not sure how to use the UART API, as I wonder how to properly call the UART driver so that no data is lost between rxend_cb is called, and a new DMA buffer is pushed to the UART driver. I think what I'm missing is what should be done when rxend_cb, i.e. DMA buffer has been filled, to provide a fresh buffer to the UART driver/HW and resume RX immediately.
I would have expected rxchar_cb to be called when data is received and no buffer is available, but it never gets called, so I probably did not get what is is meant for.
The API doc (uartStartReceive*) refers to "data frames" not to bytes. What does it actually mean? BTW, uartStartReceive* refers to data frame to *send*, not to receive.
Thanks for any help/pointers to an example implementation.
Example for using UART driver (STM32L432)
Re: Example for using UART driver (STM32L432)
Replying to self
- data frame seems to be the sequence of bits between the start and stop bit, whose count may differ from a full byte (i.e. 7 or 9 bits for ex.)
- rxchar_cb seems to behave as expected. I'm seeing it called when UART speed is 4Mbps+, and I guess the new DMA buffer is not yet set
- I do not see how to provide DMA buffer fast enough when the last set one has been filled and UART keeps receiving data. I guess it would require to change the hal_uart_ldd API, with some kind of chain list of DMA buffers.
- I'm still looking for some real world examples
- data frame seems to be the sequence of bits between the start and stop bit, whose count may differ from a full byte (i.e. 7 or 9 bits for ex.)
- rxchar_cb seems to behave as expected. I'm seeing it called when UART speed is 4Mbps+, and I guess the new DMA buffer is not yet set
- I do not see how to provide DMA buffer fast enough when the last set one has been filled and UART keeps receiving data. I guess it would require to change the hal_uart_ldd API, with some kind of chain list of DMA buffers.
- I'm still looking for some real world examples
Re: Example for using UART driver (STM32L432)
What you need to do is to use DMA in circular mode. This would probably require modifications to HAL_UART. If I remember correctly, it is setup for fixed transfer sizes. If you use DMA in circular mode, then you can setup the half transfer and transfer complete interrupt so that while you are reading the first half of the buffer, the DMA continues to write received UART data to the second half, and thus no data is lost.
I ran into a similar problem. My method of solving this was to recognize I was doing variable length message-based communication over UART. Meaning, the UART transfers were individual messages with some (small) dead time between messages. COBS is used to encode the data before sending so that the 0 character is escaped from the data sequence. That way, the 0 character can be used as a frame marker. On the receiving side, a character match interrupt is setup to interrupt on 0. Then, when receiving, I set the RX DMA to the maximum message size, and allow the RX character match interrupt to run. When the interrupt occurs, that means the message is complete. The DMA is restarted to a secondary buffer, and then the data is copied out of the first one. There is very little CPU overhead, and dead-time is minimized between messages.
I was able to do the above because I controlled the messaging protocol. If you just have a stream of bytes coming in with no constraints/bounds or assumptions you can make about the format of the data - then you need to either use HAL_SERIAL for character by character interrupts and process each character. Or, you need to use DMA in circular mode. The limitation of the later is that you will not be notified if there is a large gap between characters before the DMA has transfer the programmed length. One work around that would be to enable the IDLE line interrupt - in which you can inspect the DMA transfer, and restart it if necessary. However, there is a probably that you will loose a character while you are restarting the transfer.
Good luck!
I ran into a similar problem. My method of solving this was to recognize I was doing variable length message-based communication over UART. Meaning, the UART transfers were individual messages with some (small) dead time between messages. COBS is used to encode the data before sending so that the 0 character is escaped from the data sequence. That way, the 0 character can be used as a frame marker. On the receiving side, a character match interrupt is setup to interrupt on 0. Then, when receiving, I set the RX DMA to the maximum message size, and allow the RX character match interrupt to run. When the interrupt occurs, that means the message is complete. The DMA is restarted to a secondary buffer, and then the data is copied out of the first one. There is very little CPU overhead, and dead-time is minimized between messages.
I was able to do the above because I controlled the messaging protocol. If you just have a stream of bytes coming in with no constraints/bounds or assumptions you can make about the format of the data - then you need to either use HAL_SERIAL for character by character interrupts and process each character. Or, you need to use DMA in circular mode. The limitation of the later is that you will not be notified if there is a large gap between characters before the DMA has transfer the programmed length. One work around that would be to enable the IDLE line interrupt - in which you can inspect the DMA transfer, and restart it if necessary. However, there is a probably that you will loose a character while you are restarting the transfer.
Good luck!
Re: Example for using UART driver (STM32L432)
Hi Faisal,
I was thinking using the timeout feature of the STM32L4 to get a timeout if no data is sent for a while, so that the buffer can be flushed, and the upper layer notified. So far, I've not been able to trigger such a timeout (RTOF/RTOIE bit, RTOR register).
Thanks for confirming I need to change the implementation of UART LDD.
I've been able to receive bytes sequence @ 4MBps, but the USART stays silent with a 8 MBps bitstream. Did someone manage to use this USART at higher speeds?
Cheers.
I was thinking using the timeout feature of the STM32L4 to get a timeout if no data is sent for a while, so that the buffer can be flushed, and the upper layer notified. So far, I've not been able to trigger such a timeout (RTOF/RTOIE bit, RTOR register).
Thanks for confirming I need to change the implementation of UART LDD.
I've been able to receive bytes sequence @ 4MBps, but the USART stays silent with a 8 MBps bitstream. Did someone manage to use this USART at higher speeds?
Cheers.
- alex31
- Posts: 379
- Joined: Fri May 25, 2012 10:23 am
- Location: toulouse, france
- Has thanked: 38 times
- Been thanked: 62 times
- Contact:
Re: Example for using UART driver (STM32L432)
Hello,
I have been hit by exactly same problem, and can confirm that a rework on UART api to propose half buffer callback would be useful, and more orthogonal with other DMA based drivers that works like that (ADC, DAC, and recently SPI).
For now, the SERIAL driver with a big buffer is the only way to be sure not to miss bytes, but at the cost of high cpu usage, and definitely no suitable for 4 Mb or even 8Mb bandwidth !
As an alternative to idle line detection, i have wrotten a DMA driver with an optional timout parameter :
callback is called on two conditions :
1/ half buffer completion
2/ timout
http://www.chibios.com/forum/viewtopic.php?f=8&t=4596
using it will solve the problem of loosing bytes in dma mode, but the UART registers configuration will need to be done manually.
Alexandre
I have been hit by exactly same problem, and can confirm that a rework on UART api to propose half buffer callback would be useful, and more orthogonal with other DMA based drivers that works like that (ADC, DAC, and recently SPI).
For now, the SERIAL driver with a big buffer is the only way to be sure not to miss bytes, but at the cost of high cpu usage, and definitely no suitable for 4 Mb or even 8Mb bandwidth !
As an alternative to idle line detection, i have wrotten a DMA driver with an optional timout parameter :
callback is called on two conditions :
1/ half buffer completion
2/ timout
http://www.chibios.com/forum/viewtopic.php?f=8&t=4596
using it will solve the problem of loosing bytes in dma mode, but the UART registers configuration will need to be done manually.
Alexandre
- Giovanni
- Site Admin
- Posts: 14455
- Joined: Wed May 27, 2009 8:48 am
- Location: Salerno, Italy
- Has thanked: 1076 times
- Been thanked: 922 times
- Contact:
Re: Example for using UART driver (STM32L432)
Note that the UART driver has a callback for bytes received in between receive operations, the DMA never stops, those bytes are stored in a small dedicated internal buffer.
Giovanni
Giovanni
- alex31
- Posts: 379
- Joined: Fri May 25, 2012 10:23 am
- Location: toulouse, france
- Has thanked: 38 times
- Been thanked: 62 times
- Contact:
Re: Example for using UART driver (STM32L432)
Hi,
I have tried to use it to get uninterrupted stream of bytes at high rate, and have not been able to achieve my goal, there is still a gap where bytes can be lost.
I am still convinced that uart driver should optionally be configured with half buffer ISR, for that kind of use case.
Alexandre
Note that the UART driver has a callback for bytes received in between receive operations, the DMA never stops, those bytes are stored in a small dedicated internal buffer.
I have tried to use it to get uninterrupted stream of bytes at high rate, and have not been able to achieve my goal, there is still a gap where bytes can be lost.
I am still convinced that uart driver should optionally be configured with half buffer ISR, for that kind of use case.
Alexandre
Who is online
Users browsing this forum: No registered users and 5 guests