Skip to main content
CharlieMangold
Senior
July 14, 2023
Question

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

  • July 14, 2023
  • 8 replies
  • 3056 views

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.

This topic has been closed for replies.

8 replies

Pavel A.
Super User
July 14, 2023

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

CharlieMangold
Senior
July 14, 2023

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.
Super User
July 14, 2023

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
Senior
July 14, 2023

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
Lead
July 14, 2023

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.

CharlieMangold
Senior
July 17, 2023

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.

Pavel A.
Super User
July 16, 2023

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

 

Tesla DeLorean
Guru
July 16, 2023

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 VenmoUp vote any posts that you find helpful, it shows what's working..
Tesla DeLorean
Guru
July 16, 2023

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 VenmoUp vote any posts that you find helpful, it shows what's working..
CharlieMangold
Senior
July 18, 2023

Still can't get the USART1 RX DMA to work. Current theory is that the DMAMUX is the issue. RM0454 Table 39 shows different Resources and the DMAMUX request inputs and the Low Level drivers USART RX definitions seem to match(LL_DMAMUX_SetRequestID(DMAMUX1, LL_DMA_CHANNEL_2, LL_DMAMUX_REQ_USART1_RX)), so I'm not sure where the misalignment could be. Maybe DMA channels need to be assigned to specific peripherals or in a specific order?

	// ----------------------------------------------------------------------------------------------
	// Init UART1 RX DMA
	LL_DMA_SetPeriphRequest(DMA1, LL_DMA_CHANNEL_2, LL_DMAMUX_REQ_USART1_RX);
	LL_DMA_SetDataTransferDirection(DMA1, LL_DMA_CHANNEL_2, LL_DMA_DIRECTION_PERIPH_TO_MEMORY);
	LL_DMA_SetChannelPriorityLevel(DMA1, LL_DMA_CHANNEL_2, LL_DMA_PRIORITY_HIGH);
	LL_DMA_SetMode(DMA1, LL_DMA_CHANNEL_2, LL_DMA_MODE_CIRCULAR);
	LL_DMA_SetPeriphIncMode(DMA1, LL_DMA_CHANNEL_2, LL_DMA_PERIPH_NOINCREMENT);
	LL_DMA_SetMemoryIncMode(DMA1, LL_DMA_CHANNEL_2, LL_DMA_MEMORY_INCREMENT);
	LL_DMA_SetPeriphSize(DMA1, LL_DMA_CHANNEL_2, LL_DMA_PDATAALIGN_BYTE);
	LL_DMA_SetMemorySize(DMA1, LL_DMA_CHANNEL_2, LL_DMA_PDATAALIGN_BYTE);
	LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_2, 512);

	LL_DMAMUX_SetRequestID(DMAMUX1, LL_DMA_CHANNEL_2, LL_DMAMUX_REQ_USART1_RX);
	LL_DMA_ConfigAddresses(DMA1, LL_DMA_CHANNEL_2, (uint32_t)(&uart1RxBuffer[0][0]),
							LL_USART_DMA_GetRegAddr(USART1, LL_USART_DMA_REG_DATA_RECEIVE),
							LL_DMA_DIRECTION_PERIPH_TO_MEMORY);

	/* Enable the USART1 RX DMA1 requests */
	LL_USART_EnableDMAReq_RX(USART1);

	// Enable transfer complete and transfer error interrupts.
	LL_DMA_EnableIT_TC(DMA1, LL_DMA_CHANNEL_2);
	LL_DMA_EnableIT_TE(DMA1, LL_DMA_CHANNEL_2);
	// ----------------------------------------------------------------------------------------------