cancel
Showing results for 
Search instead for 
Did you mean: 

Change USART RX data retrieval from Rec Not Empty interrupt to DMA

CharlieMangold
Associate III

STM32G0B0RE, Low Level Drivers(no HAL), USART1

Our past 7-8 projects that use the older STM32F micros have used the USART Receiver Not Empty interrupt to drive reading of the data into a internal buffer. This allowed for the interrupt code to log the time of the byte and size of the current buffer. We are coding up changes to use the DMA engine but have run across questions that do not seem to be covered(or at least that we can find.)

 

	if(LL_USART_IsActiveFlag_RXNE_RXFNE(USART1) != RESET)
	{
		/* Read one byte from the receive data register and log the current time stamp */
		uart1RxBuffer[rxHead][uart1RX[rxHead].rxSize]	= LL_USART_ReceiveData9(USART1);
		uart1RX[rxHead].rxTime[uart1RX[rxHead].rxSize]	= timer3_get_count();
		uart1RX[rxHead].rxSize++;
    }

 

 When using the DMA to move data from the USART RX into memory there does not seem to be a clear way to track the current depth of the buffer. Setting the DMA config requires a size that needs to be set but because reception size is unknown, and can be as deep as 512 bytes, what would be the best method of tracking current depth? Our current non DMA method allows us to trace depth and also allows use of the buffers contents before the full packet has been received. The packet is determined by idle time on the USART RX line.

It does not seem possible to use a Transfer Complete interrupt(like we use in the TX direction) because of the unknown packet size(DMA triggers the TC by the programmed size.) We can use the USART RX Timeout interrupt(LL_USART_EnableRxTimeout(USART1)), but we would not be able to use the packet until the entire thing was received.

Is is possible to set the USART RX DMA size to 1 so that we could track the current buffer size and then use the USART RX Timeout interrupt to move the Rx buffer pointer the the next one?

Suggestions/Comments? Thanks.

10 REPLIES 10
Pavel A.
Evangelist II

You have seen these examples , of course?

https://github.com/MaJerle/stm32-usart-uart-dma-rx-tx/tree/main/projects

The component for ring buffers is also there:

https://github.com/MaJerle/lwrb

I did look at those but they set the DMA size(LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_4, xxx)) to the full length of the receive buffer so I was not sure how the Half Transfer or Transfer Complete would ever be triggered in our case. I did not look too deeply at the ring buffer one as it was using FIFO's and I'm currently not using them(adds another layer to error handling.)

We are going to try using a DMA size of one as soon as we get the DMA engine to transfer USART1 RX data into the receive buffer(It's currently not working.)

Thanks for the info. 

Pavel A.
Evangelist II

Note that some examples use the Receive timeout or (if the UART does not implement it) idle detection interrupt to get remainders of data that do not trigger DMA half or full transfer interrupts. Instead of this you can use, for example. periodic timer. Amount of bytes moved by DMA is always known by reading the registers.

 

 

CharlieMangold
Associate III

I was hoping for some super slick hardware driven event but it might not be possible. The whole reason for going to DMA was that we are seeing overruns on the USART RX(using the old method) and have tried every solution we can think of to fix it(currently having ST look at it.) But couple that with the BACnet stack we feed being able to take packet bytes before the entire packet has been received leads us to a slightly oddball spot. As soon as we get the RX DMA running I'll try a few approaches and post them. Appreciate the feedback.

Michal Dudka
Senior III

What is the problem ? You can use DMA to blindly save all recieved bytes into memory buffer. Whenewer you want, you can look how many DMA transfers (bytes) has been done (and how many bytes was recieved) in register DMA_CNDTRx or using corresponding LL function.

Pavel A.
Evangelist II

Receiver overrun can occur even with DMA (though much less probably) and it can block RX until cleared.

Disable the overrun detection if the UART supports this. Or enable interrupt on errors, clear them and restart the whole thing

 

What resolution of time stamp do you need?

You could have a deep DMA buffer, and sweep that periodically. You can mark the buffer to see what's been written, and also read the DMA address/count registers.

At a DMA size of two words, you'd get interrupts for HT / TC events

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

The G0 USART have a FIFO, should be able to use that. Check interrupt priorities if getting overflow/underflow. Do minimum work in handler, just buffer, not processing that can take multiple byte times.

Check also optimization, the code to manage the sw side buffer could be more efficient. If anything is volatile it will coded very verbosely.

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

My issue with using DMA stemmed from the possibility that a faulty USART(or noise as we have high voltage nearby) would drown the micro with data. This was the reason we went with a interrupt driven grab a byte type data movement. We are still waiting on ST as we should not be getting RX overruns at 38400 baud. We are still trying to get the DMA engine to move RX data but it currently is not working. Could be some dumb config setup issue but it also could have something to do with the DMA channel being used. When we first started using the STM32G0B0RE the USART TX DMA would not work on DMA channel 2. We had to move it to DMA channel 5 and we can't figure out why channel 2 does not work(that's Posted in another thread.)

We have plenty of memory so DMAing RX data it probably the better way to go.