cancel
Showing results for 
Search instead for 
Did you mean: 

read from UART DMA buffer on the fly

Wleon.1
Associate II

Hi, I have the below code where I trigger the UART DMA and write to a buffer, and in the while loop I simply do a read from the buffer 1st element:

MX_UART7_Init();
  Uartret = HAL_UART_Receive_DMA(&huart7, uartbuf, 300);
  while(1){
  
      if(Uartret == HAL_OK)
      {
 
          timeBuf[0] = uartbuf[0];
 
      }
 
  }

here is the uartbuf:

0693W000001sOXLQA2.png

however if I remover the read line the 00s are will with values which I am expected.

Is there a way to read from dma on the fly ?

13 REPLIES 13
KnarfB
Principal III

Its an unusual use of DMA. Usually you wait for DMA (half-)completion interrupts/callbacks indicating that DMA is (half-)done.

But, using __HAL_DMA_GET_COUNTER(huart->hdmarx) you can get the current index into filled DMA buffer.

I met this once in the wild: https://github.com/yoneken/rosserial_stm32/blob/master/src/ros_lib/STM32Hardware.h

There, read() is a polling read reading from a cyclic DMA buffer.

Your buffer looks like NMEA telegrams. They come usually at a slow bitrate and have limited but variable lengths.

In that case, I used single char interrupt-driven read HAL_UART_Receive_IT( &huart1, &ch, 1 );  and assembled whole lines (telegrams) in the HAL_UART_RxCpltCallback completion handler which I sent to a telegram parser.

Piranha
Chief II

I also recommend using only interrupts for a slow protocols, but I also would clearly separate UART from NMEA and make all processing, including the line assembling, a task of NMEA parser.

Wleon.1
Associate II

If i use HAL_UART_Receive() with a long timeout instead will it block other interrupt ? as in the code I didnt see any __disable_irq() they only use __HAL_UNLOCK.

No, it will not block other interrupts. But the problem is variable length of telegrams. What buffer size do you want to HAL_UART_Receive call with? HAL_UART_Receive does only return when the buffer is full.

Therefore the char by char approach.

As an alternative, the is an UART idle interrupt. for details, see https://github.com/MaJerle/stm32-usart-uart-dma-rx-tx.

S.Ma
Principal

This is a valid need, if you use DMA in a rolling buffer fed by USART RX and you want to avoid interrupt and just size the buffer large enough to read with time slice method (the best polling method for USART RX)

Which STM32?

There's nothing unusual in it. Even Tilen Majerle uses exactly this (reading from circular-DMA buffer based on NDTR) in the code you've linked to above. That's how DMA is supposed to be used as receiving interfaces' FIFO, except in the Cube/HAL realm.

Using Cube/HAL also probably results in a slow code enough to cover the problem that NDTR counts the peripheral-side transactions, not the finished transfers, at least in the dual-port DMA (F2/F4/F7).

JW

Caching becomes an issue for CM7 implementation

Describing transient memory buffers as volatile is important for compiler/optimizer issues.

My suggested method is to use uint16_t buffers, where you mark the memory (0xFFFF patterns) before you hand it to the DMA unit so you can see where it has been, and can simply sweep the buffer at a convenient interval. The depth of the buffer, and the sweep period being predicated on the maximum data rate.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..

stm32f73x, if i make a loop to read I can see more zeros.

Where do you see zeros? In the debugger? Isn't that a debugger artefact, i.e. does the program in processor see those zeros? Isn't there some other process running which would write them?

Write a minimal program, which only starts the USART DMA and then goes to an infinite loop (i.e. does nothing), observe. Instead of continuously transmitting device, connect UART to PC and transmit into it manually, character by character, and observe.

JW