cancel
Showing results for 
Search instead for 
Did you mean: 

UART/USART DMA IDLE Receive with unknow data

EasyNet
Associate II

Hi,

I'm trying to build a software to retrieve data from an solar inverter.

After I success to send data, it was the time to receive the data. I choose the method with DMA IDLE to receive data in a circular buffer.

In this mode, after a lot of debugging, I notice some issues with HAL functions.

Function void USART2_IRQHandler(void) and void DMA1_Stream5_IRQHandler(void) are calling the same function void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size). Doesn't metter if is a DMA IRQ or UART/USART IRQ, this function is called anyway.

My STM32F411 triggers an IRQ for DMA buffer overflow / reset counter and another IRQ for UART/USART IDLE. Because of this I tried to find out a solution to separate these events to threat the overflow of the DMA buffer and keep a tracking of the receiving data.

I tried with UART flags, getting IRQ triggers for DMA. All of these failed and I couldn't find a way to separate the events.

The idea is that after the DMA buffer overflows and IRQ is triggered I got the UART/USART IRQ afterwards. In this way is impossible to figure out how to threat the event.

There is a way in the HAL drivers to check this and without adding additional codes in void DMA1_Stream5_IRQHandler(void) and void USART2_IRQHandler(void) functions, like creating a flag for each type of IRQ, and then clear the flags in void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size) ?

Example of hack to distinguishing the events:

void DMA1_Stream5_IRQHandler(void)
{
 /* USER CODE BEGIN DMA1_Stream5_IRQn 0 */
 /*
  * 0b01 means HAL_UARTEx_RxEventCallback is called from UART/USART DMA IRQ.
  * 0b10 means HAL_UARTEx_RxEventCallback is called from UART/USART IRQ.
  */
 
 PI30_dev.IRQ_flag = 0b01;
 
 /* USER CODE END DMA1_Stream5_IRQn 0 */
 HAL_DMA_IRQHandler(&hdma_usart2_rx);
 /* USER CODE BEGIN DMA1_Stream5_IRQn 1 */
 
 /* USER CODE END DMA1_Stream5_IRQn 1 */
}
 
void USART2_IRQHandler(void)
{
 /* USER CODE BEGIN USART2_IRQn 0 */
 /*
  * 0b01 means HAL_UARTEx_RxEventCallback is called from UART/USART DMA IRQ.
  * 0b10 means HAL_UARTEx_RxEventCallback is called from UART/USART IRQ.
  */
 
 PI30_dev.IRQ_flag = 0b10;
 
  /* USER CODE END USART2_IRQn 0 */
  HAL_UART_IRQHandler(&huart2);
  /* USER CODE BEGIN USART2_IRQn 1 */
 
  /* USER CODE END USART2_IRQn 1 */
}

PI30_dev.IRQ_flag is an uint8_t, uint16_t, uint32_t global var like. In this way I know for sure which IRQ triggered HAL_UARTEx_RxEventCallback.

4 REPLIES 4
TDK
Guru

What chip are you using? Ah, STM32F411, I see it now.

Fairly sure they are gated behind the appropriate check to determine if the call is interrupt or DMA based.

If you feel a post has answered your question, please click "Accept as Solution".
EasyNet
Associate II

Hi,

For example if you have a DMA buffer of 16 bytes (giving a small amount of circular buffer, intentionally of course) the events are like this:

  1. Sending 10 chars
  2. After finishing the transmission an UART/USART IRQ is fired up and HAL_UARTEx_RxEventCallback is called. Without my hack I have no idea if is UART/USART IRQ.
  3. Ending the processing of IRQ
  4. Wait for more data to receive.
  5. Sending 8 more bytes.
  6. After 6 bytes a DMA IRQ is triggered that DMA buffer will overflow, which is normal.
  7. HAL_UARTEx_RxEventCallback is called. Again I have no idea how to distinguished if is DMA or UART/USART IRQ.
  8. Ending of processing the IRQ.
  9. UART/USART IRQ is triggered and is calling again HAL_UARTEx_RxEventCallback.
  10. Ending of processing of IRQ.
  11. Waiting for more data to come.

And here is the confusion: same function called twice for different events. I can't use point 7 for getting the UART/USART data because received data is incomplete. The complete data will be in step 9.

But also I need the step 7 to process the current buffer position and update some variables.

TDK
Guru

Edit: Oh, okay, so you want to know specifically when IDLE occurs because that indicates to your program that data transfer is done. I see now.

And the IDLE interrupt is masked if no new data is received after HT or TC happens. So yes, that seems to be an issue.

---

Original post:

I don't see why you need to know if this is an IDLE or DMA based interrupt, though. The amount of data received is transferred to the function.

Your scenario functions exactly as the HAL example code indicates it should:

https://github.com/STMicroelectronics/STM32CubeF4/blob/2f3b26f16559f7af495727a98253067a31182cfc/Projects/STM32F429ZI-Nucleo/Examples/UART/UART_ReceptionToIdle_CircularDMA/readme.txt#L80

Example : case of a reception of 22 bytes before Idle event occurs, using Circular DMA and a Rx buffer
of size of 20 bytes.
    - User calls HAL_UARTEx_ReceiveToIdle_DMA() providing buffer address and buffer size (20)
    - HAL_UARTEx_RxEventCallback() will be executed on HT DMA event with Size = 10
      Data in user Rx buffer could be retrieved by application from index 0 to 9
    - HAL_UARTEx_RxEventCallback() will be executed on TC DMA event with Size = 20
      New data in user Rx buffer could be retrieved by application from index 10 to 19
    - HAL_UARTEx_RxEventCallback() will be executed after IDLE event occurs with Size = 2
      New data in user Rx buffer could be retrieved by application from index 0 to 1

You do need to store the current head of the buffer to determine where the data is, but that is easy to do. Here is one implementation:

https://github.com/STMicroelectronics/STM32CubeF4/blob/2f3b26f16559f7af495727a98253067a31182cfc/Projects/STM32F429ZI-Nucleo/Examples/UART/UART_ReceptionToIdle_CircularDMA/Src/main.c#L336

If you feel a post has answered your question, please click "Accept as Solution".
EasyNet
Associate II

Hi TDK,

Thanks for the answer. I read the code and I applied.

Because I'm using RTOS I'm chaning the code for the hard IRQ to add the chunks received in DMA ring buffers (for each USART I have a pair of 2 or more swapped buffers) that I will add them into a queue to exit as fast as I can from this hard IRQ.

Then I'm using a task with high priority in RTOS, which I can named it soft IRQ, to process this queue depending on type of data I need: normal line with <LF> / <CR> or binary mode. Then I will send to another queue / buffer to be picked up by the application task.

I hope this implementation will not have a drastical impact in performance / RAM usage. I know that I have to use plenty of buffers in this case, but at this moment I don't have other ideas.

If you have some sugestions, I'm open to them. I'm will try to create an example of such "driver" for USART and post it on github.