Skip to main content
Lars Beiderbecke
Senior III
June 17, 2018
Solved

Best practice using DMA to read from UART and write to SD?

  • June 17, 2018
  • 2 replies
  • 4023 views
Posted on June 17, 2018 at 12:09

I'm reading data from a UART with DMA and want to write that data to an SD card (on a F722). Since the data is much longer than my buffer, I chose circular buffer for the DMA.

The problem now is that I need to save the receive buffer to SD occasionally, but how do I know when I need to do this?

Can someone recommend the best approach here, using Cube HAL:

  • Should I chop my Receive_DMA() into smaller chunks of buffer size, and receive them individually?  (I may loose data.)
  • Should I count the number of received bytes (how?) and save the buffer if some threshold is reached?
  • Can I enable direct UART-to-SDMMC DMA?  (I don't even know what that means: Does the data go into an open file, and how can I remove the length header of the data first?)

I'd really appreciate some ideas. The Cube examples are way too simple.

#dma
This topic has been closed for replies.
Best answer by Tilen MAJERLE
Posted on June 17, 2018 at 12:22

Hello,

if you are using DMA in circular mode to receive data over UART, you will not loose your data, if your RX buffer can handle all the bytes received by UART by the time you are saving another part of RX buffer. This depends on UART baudrate and time of saving to SDCARD.

When to save data to card? I would handle 3 different events:

  • Half-transfer complete interrupt, means that first part of buffer is full, now let's save it to card
  • Transfer complete interrupt, means second part of buffer is full, let's save second part to buffer
  • Check some time difference (maybe IDLE line interrupt? Depends on your UART stream) between last sdcard save and current time, then check if there are any data in RX buffer which were not yet processed (like cache) and save it.

You may count number of bytes received by checking stream/channel NDTR register.

Best regards,

Tilen

2 replies

Tilen MAJERLE
Tilen MAJERLEBest answer
ST Employee
June 17, 2018
Posted on June 17, 2018 at 12:22

Hello,

if you are using DMA in circular mode to receive data over UART, you will not loose your data, if your RX buffer can handle all the bytes received by UART by the time you are saving another part of RX buffer. This depends on UART baudrate and time of saving to SDCARD.

When to save data to card? I would handle 3 different events:

  • Half-transfer complete interrupt, means that first part of buffer is full, now let's save it to card
  • Transfer complete interrupt, means second part of buffer is full, let's save second part to buffer
  • Check some time difference (maybe IDLE line interrupt? Depends on your UART stream) between last sdcard save and current time, then check if there are any data in RX buffer which were not yet processed (like cache) and save it.

You may count number of bytes received by checking stream/channel NDTR register.

Best regards,

Tilen

Lars Beiderbecke
Senior III
June 17, 2018
Posted on June 17, 2018 at 12:28

Thanks, Tilen, that is very helpful!

Just a small follow-up question:  What is the HAL way of checking NDTR (assuming there is one)?

Also, how do I check NDTR? In a while loop or in an interrupt? (Does a while loop make sense, because it uses again CPU cycles?)

Tilen MAJERLE
ST Employee
June 17, 2018
Posted on June 17, 2018 at 13:42

Hello Lars,

there are different way how to do it, most of them depends on project complexity. Specifically, if you are using F722 MCU, I would assume that UART2SDCARD is not the only task you have to handle, thus I would go with operating system for sure. MCU has enough memory to handle and CPU is smart enough to do the job using RTOS. ST provides FreeRTOS port as part of STM32Cube software.

Example, how i would do it is like this

  • Create new task (thread), dedicated only for reading UART and saving data to SDCARD
    • FatFS (if you are using it) supports RTOS mode, so you are safe to use it. Please read FatFS documentation what implementation is mandatory for RTOS mode
  • Use single message queue to writenotifications from interrupts for DMA HT or TC interrupts
  • Use LL drivers to configure UART and DMA for RX mode and enable HT and TC interrupts
  • Wait for new event on message queue or timeout value, when you decide to check for new data

Pseudo code example below:

void
uart_to_sdcard_thread(void) {
 last_dma_position = 0;
 queue = create_message_queue(10); //Create message queue for 10 elements
 
 while (1) {
 //Wait for new queue or timeout
 //If there is timeout, you know that neither TC or HT events happened
 //so manually check if there is anything to read
 message_queue_wait_msg(queue, MAX_TIMEOUT_VALUE);
 if (timeout_happened_on_read) {
 //There was no message in queue but it was timeout
 }
 //Remember: NDTR is downcounter and holds number of 'remaining' elements to transfer
 new_position = LL_DMA_GetDataCounter(...)
 //Do some math, and save to card
 }
}
//DMA interrupt handler
void
dma_irq_handler(void) {
 if (tc_irq) {
 message_queue_write(queue, tc);
 }
 if (ht_irq) {
 message_queue_write(queue, ht);
 }
}
�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?

If you need example for message queues on STM32F7, follow this example:STM32Cube_FW_F7_V1.0\Projects\STM32756G_EVAL\Applications\FreeRTOS\FreeRTOS_Queues

Some readings, which may help you to start:

https://community.st.com/thread/42689-efficiently-use-dma-with-uart-rx-on-stm32

T J
Senior III
June 18, 2018
Posted on June 18, 2018 at 21:42

The easiest way forward is to understand the packet size of the SDCard.

lets say you like to use a 2k packet to the SDCard.

then make the DMA circular buffer double that.

use the half interrupt to send a packet to the SDCard, and the Full interrupt to send the next...