cancel
Showing results for 
Search instead for 
Did you mean: 

UART_DMARxHalfCplt is bugged for STM32G0

In the stm32g0xx_hal_uart.c file, line 3750 the following function can be found:

/**
  * @brief DMA UART receive process half complete callback.
  * @param hdma DMA handle.
  * @retval None
  */
static void UART_DMARxHalfCplt(DMA_HandleTypeDef *hdma)
{
  UART_HandleTypeDef *huart = (UART_HandleTypeDef *)(hdma->Parent);
 
  /* Check current reception Mode :
     If Reception till IDLE event has been selected : use Rx Event callback */
  if (huart->ReceptionType == HAL_UART_RECEPTION_TOIDLE)
  {
#if (USE_HAL_UART_REGISTER_CALLBACKS == 1)
    /*Call registered Rx Event callback*/
    huart->RxEventCallback(huart, huart->RxXferSize/2U);
#else
    /*Call legacy weak Rx Event callback*/
    HAL_UARTEx_RxEventCallback(huart, huart->RxXferSize/2U);
#endif /* USE_HAL_UART_REGISTER_CALLBACKS */
  }
  else
  {
    /* In other cases : use Rx Half Complete callback */
#if (USE_HAL_UART_REGISTER_CALLBACKS == 1)
    /*Call registered Rx Half complete callback*/
    huart->RxHalfCpltCallback(huart);
#else
    /*Call legacy weak Rx Half complete callback*/
    HAL_UART_RxHalfCpltCallback(huart);
#endif /* USE_HAL_UART_REGISTER_CALLBACKS */
  }
}

It can be noticed that in case the reception mode is set to HAL_UART_RECEPTION_TOIDLE, an incorrect callback function is called (huart->RxEventCallback or HAL_UARTEx_RxEventCallback). The library lacks a dedicated "idle half rx callback" function. Because of this, the reception callback is called twice if the amount of received data exceeds half of the buffer size.

@Imen DAHMEN​ can you please confirm?

7 REPLIES 7
Imen.D
ST Employee

Hello @Community member​ ,

Thanks for pointing out to me this issue.

 I am checking it internally and I will come back to you with update.

Imen

When your question is answered, please close this topic by clicking "Accept as Solution".
Thanks
Imen
Guenael Cadier
ST Employee

Hi @Community member​ 

Yes, I confirm that when using Reception In Idle Mode, the callback expected to be called is huart->RxEventCallback or HAL_UARTEx_RxEventCallback.

This callback is specific to the ReceptionToIdle mode.

The purpose of this ReceptionToIdle feature is to offer the capability to receive data without knowing data length.

In this case, user could start a reception asking for X bytes even if less bytes will be received at the end. He will be notified when either requested nb of bytes are received or when an IDLE event occurs (i.e. less than expected nb of bytes are received).

In DMA mode, this callback will be called on any of the 3 events below :

- DMA Half transfer

- DMA Transfer complete

- Idle Event

That's also why the nb of available bytes in Rx buffer is also provided. Using this data, on user side, it should be possible to retrieve the complete flow of received data.

Use examples :

1) DMA reception of 10 bytes, sent without interruption (no Idle event)

Calls of callback are :

=> when 5 bytes have been received : huart->RxEventCallback(huart, 5)

=> when 10 bytes have been received : huart->RxEventCallback(huart, 10)

2) DMA reception of 10 bytes, sent with some interruption (Idle event between char 6 and 7)

Calls of callback are :

=> when 5 bytes have been received : huart->RxEventCallback(huart, 5)

=> when IDLE event is detected : huart->RxEventCallback(huart, 6)

=> when 10 bytes have been received : huart->RxEventCallback(huart, 10)

3) DMA reception of 21 bytes in a Circular DMA mode (10 bytes buffer), sent with interruption (Idle event between char 13 and 14)

Calls of callback are :

=> when 5 bytes have been received : huart->RxEventCallback(huart, 5)

Bytes 1 to 5 are available in reception buffer

=> when 10 bytes have been received : huart->RxEventCallback(huart, 10)

Bytes 6 to 10 are available in reception buffer

=> when IDLE event is detected : huart->RxEventCallback(huart, 3)

Bytes 11 to 13 are available in reception buffer

=> when 5 bytes have been received : huart->RxEventCallback(huart, 5)

Bytes 14 to 15 are available in reception buffer

=> when 10 bytes have been received : huart->RxEventCallback(huart, 10)

Bytes 16 to 20 are available in reception buffer

=> when IDLE event is detected : huart->RxEventCallback(huart, 1)

Byte 21 is available in reception buffer

You could have a look at example implemented on G0

Projects\STM32G0C1E-EV\Examples\UART\UART_ReceptionToIdle_CircularDMA

Hope this helps.

Guenael

Thank you for answers @Imen DAHMEN​ and @Guenael Cadier​ ,

I understand what is the Idle callback needed for. The problem is that from within user code, one is not able to distinguish whether the HAL_UARTEx_RxEventCallback (lets leave the callback hooks a side for simplification) was called because:

  1. Idle event occurred? Or
  2. Half amount of data was received occurred?

And this problem exists, because the HAL_UARTEx_RxEventCallback is called from both the UART_DMARxHalfCplt and UART_DMAReceiveCplt. In my code, how am I supposed to distinguish between who was the caller? For the case in which idle line detection is not used, the situation is clear:

  1. On complete the HAL_UART_RxCpltCallback is called.
  2. On half-complete the HAL_UART_RxHalfCpltCallback is called.

For idle we do not have the HAL_UART_RxHalfCpltCallback equivalent! So it should be:

  1. HAL_UARTEx_RxEventCallback for complete or idle triggered event (since the value of received bytes is provided in the input parameter thats OK),
  2. HAL_UARTEx_RxHalfEventCallback for when half of the expected data is provided. This function does not exist.

Without this, one has to use workarounds in the reception code to figure out whether the reason for calling was idle/ complete event, or half complete event. So for instance, if one expect 32 bytes of data to be received and he receives only 20, the same HAL_UARTEx_RxEventCallback function will be called 2 times:

  1. first with Size = 16,
  2. second with Size = 20.

Can you see the issue?

Dear @Community member​ 

Yes, I understand your use case.

In implementation of this service, goal was to make it as generic as possible (same mechanism applying for various reception cases) so callback is same for Interrupt, DMA and Circular DMA use. On application side, callback indicates some data might be available in reception buffer (provided index indicates nb of available data). On application side to consider if data is to be saved for application, and if enough data have already received for being processed or more data to be waited for, ...

In your use case (noncircular DMA), reception will stop on either IDLE event occurrence, or complete buffer has been received. In case of HT, reception goes on. If you need to differentiate between HT and IDLE calls, you might use the HAL_UART_StateTypeDef HAL_UART_GetState(UART_HandleTypeDef *huart) function and check UART Handle state value.

In case of HT call, state will be either HAL_UART_STATE_BUSY_TX_RX or HAL_UART_STATE_BUSY_RX (depending if Tx is ongoing or not)

In case of IDLE or TC, state will be either HAL_UART_STATE_BUSY_TX or HAL_UART_STATE_READY (depending if Tx is ongoing or not)

Regards

Guenael

Hi @Guenael Cadier​ , thank you for answer.

I confirm your logic is working- When half callback is called, the status is HAL_UART_STATE_BUSY_RX. When Idle is called, the status is HAL_UART_STATE_READY. It is then possible to derive which event caused the HAL_UARTEx_RxEventCallback call.

What makes me unsatisfied still is the fact that for some reason the API is not compliant between regular RX complete and Idle complete. Since HAL_UART_RxHalfCpltCallback exists, why doesn't the same function exists for the Idle event case and instead the status has to be manually checked?

RetroInTheShade
Associate III

Thanks to @Community member​ for raising and to ST team for response.

I have a similar requirement to discriminate between idle and half transfer. I confirm the solution provided also works on the STM32F4. It is more elegant than bloating with a large buffer (to avoid hitting half mark) but suboptimal compared to a separate callback (or including functionality to disable the half transfer interrupt).

Thomas LB
Associate III

Hello, to avoid HAL_UARTEx_RxEventCallback being triggered by Half Transfert you can also disable the half transfert interrupt using this along DMA setup :

 HAL_UARTEx_ReceiveToIdle_DMA(&huart2, RxBuf, RxBuf_SIZE);

 __HAL_DMA_DISABLE_IT(&hdma_usart2_rx, DMA_IT_HT);