Skip to main content
TheaArt
Associate II
December 5, 2022
Question

Issues using HAL_UARTEx_ReceiveToIdle_DMA(...) and HAL_UARTEx_RxEventCallback(...)

  • December 5, 2022
  • 2 replies
  • 11704 views

Finally I used the HAL_UARTEx_ReceiveToIdle_DMA(...) function and the associated HAL_UARTEx_RxEventCallback(...) Interruptcallback. It works nice now, but I had some issues, which have been not so easy to explore. So I want to share them here. Maybe, it helps someone in the future.

First was: If you use UARTs and DMA with HAL and CubeMX to create the template of your project check the sequence of init functions. CubeMX puts the MX_DMA_Init(); behind the MX_USARTx_UART_Init() functions. This does not work. Manually change this and call MX_DMA_Init() before MX_USARTx_UART_Init(). Else no interrupt callback is triggered. (This is also true for HAL_UART_Receive_DMA(...) and its callbacks)

Second: UARTEx_RxEventCallback(...) is called with a Size parameter. The doc says "(indicates a position in reception buffer until which, data are available)" This is not true. Size is the position of the first byte in DMABuffer, where no data is available. So the position up which data is available is Size - 1. (So also you will never get a 0 as Size but you will get the length of your DMABuffer as a valid value (which was provided by HAL_UARTEx_ReceiveToIdle_DMA))

And also very important Data is not only available from the start of DMAbuffer up to Size-1, but rather from Size of the last call of UARTEx_RxEventCallback up to Size-1. It is like DMA works

Third: HAL avoids to call UARTEx_RxEventCallback(...) in the the situation of DMAComplete, So if Idle happens, if the DMAbuffer is just full, the callback is only called once. But it doesn't avoid to call the callback in DMA HalfComplete situation. So if Idle and HalfComplete coincide, the callback is called twice (once for HalfComplete and once for Idle). This could mislead your evaluation logic to think, the DMA was written once more at it's full length.

IMHO it also shouldn't be called for HalfComplete, or it should be also called twice at DMAComplete. But the another parameter would be nice in order to be informed about the reaseon of the call.

Nevertheless this kind of reading from UART solved a lot of problems for me. Thak you for that.

This topic has been closed for replies.

2 replies

ST Technical Moderator
December 5, 2022

Hello @Wolfgang Rißler​,

Could you please specify the product and the working environment?

To give better visibility on the answered topics, please click on "Best answer" on the reply which solved your issue or answered your question.Best regards,FBL
TheaArt
TheaArtAuthor
Associate II
December 5, 2022

Hello,

of course I can (~_ö):

My MCU is a STM32 L051R6T6 (on a custom board)

CubeIde with CubeMX: Version: 1.9.0 Build: 12015_20220302_0855 (UTC)

Application is bare metal without any OS.

Operating System for IDE: Ubuntu 22.04.1 LTS running in VBox, Host is Win10

Stanford
Associate II
May 10, 2023

This also my experience of HAL_UARTEx_RxEventCallback. It always sends in a value of 1…Size (ie, if Size of buffer is 256 bytes, then the values are 1…256, never sends in a size value of 0). 

This is ok since my callback is a single line:

int tail, head;
uint8_t rxBuf[256];
 
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t size)
{
 tail = size % sizeof(rxBuf);
}

I call HAL_UARTEx_ReceiveToIdle_DMA once at startup and it runs all of the time. The Uart RX DMA channel is configured as CIRCULAR. 

Outside of the callback, in my main code loop, I maintain a “head�? index. If head != tail, then I know there is something in the receive DMA buffer. I extract and parse the bytes one-by-one, advancing head:

HAL_UARTEx_ReceiveToIdle_DMA(&huart2, rxBuf, sizeof(rxBuf));
 
while (head != tail)
{
 ParseByte(rxBuf[head]);
 head = (head + 1) % sizeof(rxBuf);
}

This method is very simple and works very well, no issues, as long as the main code loop can extract and process the bytes fast enough before the DMA receive buffer fills up. In my case, this never happens because the receive buffer is large enough and the incoming messages are relatively short and simple. 

Note: I am communicating over an RS-485 bus, master-slave configuration.

Note: I do not use the STM32's Uart DE peripheral signals for turning on the RS–485 transceiver when responding to the master. Instead, I use an arbitrary GPIO so that I can free up those DE pins for other functions. I turn on the GPIO before calling the DMA transmit function. In the transmit complete callback, I turn off the GPIO. This works perfectly.

Running on an STM32L47x.

Open to hearing feedback on this method.