cancel
Showing results for 
Search instead for 
Did you mean: 

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

TheaArt
Associate III

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.

8 REPLIES 8
FBL
ST Employee

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 Accept as Solution on the reply which solved your issue or answered your question.

TheaArt
Associate III

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

TheaArt
Associate III

STM32CubeMX: 6.5.0-RC4 Build: 20220225-1913 (UTC)

Hi again @Wolfgang Rißler​ ,

First issue is fixed. At least, the sequence is correct in the latest CubeMX version 6.7.0

Second, I can see that UARTEx_RxEventCallback is not present in L0 driver's user manual. However, for F4 for example, it is available. So, it is considered not supported for L0.

To give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.

Yes, I see, that it's not documented in manual.

But why is it included in stm32l0xx_hal_uart.h (which is integrated to the project, when I start a new priject for my STM32 L051R6T6)?

And however, as I experienced, It just works.

I have a Nucleo Board with L4 MCU here too. If I will find the time, I will test, if the same issues also will happen there.

Hello @Wolfgang Rißler​,

The documentation should be updated.

An internal ticket has been submitted for documentation review.

Internal ticket number: 140620 (This is an internal tracking number and is not accessible or usable by customers).

Second, in case of Circular DMA, reception could go on, and next reception data will be stored in index 0 of reception buffer by DMA.

To give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.

Hello @F.Belaid​ ,

I know, that on circular DMA configuration data also will be stored in byte with index 0 again. But what I tried to say is, for an example:

If this first byte (index 0) is the last one, that was written before idle interrupt happens, the HAL calls UARTEx_RxEventCallback(...) with Size is 1, not 0.

Or other example, if the last DMA byte was written before Idle, then Size is LengthOfDMA, not LengthOfDMA-1. So Size does not deliver the last written position (as described in the documentation) but it delivers the first position in DMA what is not written. So you will never get a 0 as Size but you can get LengthOfDMA, which is not a valid position in DMA.

This is my experience, and if I reduce Size by 1, my callback works like a charm. Maybe you could test and confirm this. I dont know, if this affects all MCU HAL Drivers or only L0 series.

Have a nice advent time.

Stanford
Associate II

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.