cancel
Showing results for 
Search instead for 
Did you mean: 

STM32H563 UART DMA idle not triggering

mipi
Associate

Hello everyone,

I'm working on a custom board with an STM32H563 and using CubeMX, HAL, and ThreadX.

I am trying to receive variable-length UART data via DMA using HAL_UARTEx_ReceiveToIdle_DMA(). My problem is that the HAL_UARTEx_RxEventCallback never triggers for the HAL_UART_RXEVENT_IDLE event. It only triggers for HAL_UART_RXEVENT_HT (Half Transfer) and HAL_UART_RXEVENT_TC (Transfer Complete).

Setup

  • Function Call: HAL_UARTEx_ReceiveToIdle_DMA()
  • RX Buffer Size: 1000 bytes
  • DMA Mode: Circular
  • Data Source: A peripheral sends packets of variable size (approx. 2048 - 4096 bytes) at 1,000,000 bits/s.
  • Idle Time: There is a guaranteed idle time of > 45ms between packets. This should be more than enough to trigger the UART IDLE event.

Here is the .ioc

kasjdlfkjsadfjl.png

Here is the RX line on the scope:

WhatsApp Image 2025-11-08 at 06.23.47.jpeg

Problem

Expected Behavior: Given the long idle time, I expect HAL_UARTEx_RxEventCallback to be called with an event type of HAL_UART_RXEVENT_IDLE after each packet.

Actual Behavior: The IDLE event never fires. I only get Half Transfer and Transfer Complete events (so when the buffer is half full or full).

Here is my RxEventCallback handler:

// This is the callback I EXPECT to fire for IDLE
void Radar_Handler_HandleRxEvent(UART_HandleTypeDef *huart, uint16_t Size)
{
    if (huart->Instance == _radar1_handle.huart_data->Instance)
    {
        switch (HAL_UARTEx_GetRxEventType(huart))
        {
        case HAL_UART_RXEVENT_TC:
            io_handler_send_usb_message("Transmission completed\n");
            break;
        case HAL_UART_RXEVENT_IDLE:
            // THIS CASE NEVER EXECUTES
            io_handler_send_usb_message("Transmission idle\n");
            break;
        case HAL_UART_RXEVENT_HT:
            io_handler_send_usb_message("Transmission half complete\n");
            break;
        default:
            io_handler_send_usb_message("Unknown UART RX event\n");
            break;
        }
        snprintf(msg, sizeof(msg), "%u bytes\r\n", Size);
        io_handler_send_usb_message(msg);
    }
}

This produces the following debug output, showing only HT and TC events:

Transmission half complete
500 bytes
Transmission completed
1000 bytes
Transmission half complete
500 bytes
Transmission completed
1000 bytes
...

Initialization

Here is how I initialize the DMA.

HAL_StatusTypeDef Radar_Handler_Init(UART_HandleTypeDef *huart_control,
                                     UART_HandleTypeDef *huart_data,
                                     GPIO_TypeDef *nreset_port,
                                     uint16_t nreset_pin)
{
    // ... (internal init)

    // Start the DMA reception
    HAL_UARTEx_ReceiveToIdle_DMA(_radar1_handle.huart_data, 
                                 _radar1_handle.data_rx_buffer, 
                                 sizeof(_radar1_handle.data_rx_buffer));

    // This is a workaround but it is commented out.
    // Calling it causes the "Weird Behavior" described below.
    // UART_CheckIdleState(_radar1_handle.huart_data); 

    // ... (semaphore create)
    return status;
}

Callbacks in main.c:

/* USER CODE BEGIN 4 */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
  Radar_Handler_HandleRxInterrupt(huart);
}

void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
{
  Radar_Handler_HandleRxEvent(huart, Size);
}

void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart)
{
  uint32_t error = huart->ErrorCode;
  io_handler_send_usb_message("UART1 error occurred\n");
  if (error & HAL_UART_ERROR_ORE)
    __HAL_UART_CLEAR_OREFLAG(huart);
}

/* USER CODE END 4 */

Weird behavior

This is the part that makes me suspect a HAL or config bug.

If I uncomment the line UART_CheckIdleState(_radar1_handle.huart_data); in my Init function, the entire interrupt behavior changes:

HAL_UARTEx_RxEventCallback() stops triggering entirely.

The HAL_UART_RxCpltCallback() starts triggering instead.

Inside the handler for HAL_UART_RxCpltCallback, I added a test function to manually check the IDLE flag.

Test function called by HAL_UART_RxCpltCallback:

void Radar_Handler_HandleRxInterrupt(UART_HandleTypeDef *huart)
{
    if (huart->Instance == _radar1_handle.huart_data->Instance)
    {
        // Manually check the IDLE flag
        if (__HAL_UART_GET_FLAG(huart, UART_FLAG_IDLE))
        {
            io_handler_send_usb_message("IDLE Interruption (from RxCplt)\n");
        }
    }
}

This produces the following output. It seems the IDLE flag is being set, but only the interruption callback fires, not the rx event one, and it appears to fire only when the buffer is full (at TC).

IDLE Interruption (from RxCplt)
IDLE Interruption (from RxCplt)
...

Simply calling UART_CheckIdleState() (which I assume is a read-only check) should not change which callback (RxEvent vs. RxCplt) is active. This seems to indicate a state problem.

What I have tried

  • Changing GPDMA peripheral and channel: No change.
  • Enabling/Disabling NVIC on the USART1: Enabling it (with or without UART_CheckIdleState) stops all UART interrupts (if the DMA is active, if i turn the DMA off it works normally). Disabling it gives the behavior described above.
  • Disabling Overrun on UART settings in CubeMX: No change.
  • Disabling Circular Mode on GPDMA config in CubeMX: No change, except the DMA stops after the first 1000 bytes (as expected).
  • Handling the interruptions and init directly on main.c: No change.
  • Disabling HT and TC: No change.


I appreciate any tip or advice, I don't think the problem is with my custom board as everything else, I2S, USB, HSE, works without a problem.

 

0 REPLIES 0