Example for using UART driver (STM32L432)

Discussions and support about ChibiOS/HAL, the MCU Hardware Abstraction Layer.
eblot
Posts: 18
Joined: Fri Dec 08, 2017 5:33 pm
Been thanked: 1 time

Example for using UART driver (STM32L432)

Postby eblot » Mon Sep 24, 2018 12:28 pm

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.

eblot
Posts: 18
Joined: Fri Dec 08, 2017 5:33 pm
Been thanked: 1 time

Re: Example for using UART driver (STM32L432)

Postby eblot » Mon Sep 24, 2018 4:17 pm

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 :P

faisal
Posts: 374
Joined: Wed Jul 19, 2017 12:44 am
Has thanked: 44 times
Been thanked: 60 times

Re: Example for using UART driver (STM32L432)

Postby faisal » Mon Sep 24, 2018 4:54 pm

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!

eblot
Posts: 18
Joined: Fri Dec 08, 2017 5:33 pm
Been thanked: 1 time

Re: Example for using UART driver (STM32L432)

Postby eblot » Tue Sep 25, 2018 10:36 am

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.

User avatar
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)

Postby alex31 » Tue Sep 25, 2018 12:30 pm

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

User avatar
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)

Postby Giovanni » Tue Sep 25, 2018 2:49 pm

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

User avatar
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)

Postby alex31 » Thu Sep 27, 2018 5:01 pm

Hi,

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


Return to “ChibiOS/HAL”

Who is online

Users browsing this forum: No registered users and 5 guests