STMicroelectronics / STM32CubeF7

STM32Cube MCU Full Package for the STM32F7 series - (HAL + LL Drivers, CMSIS Core, CMSIS Device, MW libraries plus a set of Projects running on all boards provided by ST (Nucleo, Evaluation and Discovery Kits))
Other
322 stars 192 forks source link

HAL_UARTEx_ReceiveToIdle_DMA() doesn't trigger IRQHandler() but manually enable RxDMA and IDLE IT works #119

Open Matrixchung opened 2 weeks ago

Matrixchung commented 2 weeks ago

Describe the set-up

Describe the bug

If initialize the UART Rx DMA process using HAL_UARTEx_ReceiveToIdle_DMA(), we got no USARTx_IRQHandler() triggered (observed by setting a breakpoint) while the related UART interrupt bit does occurs in UART->ISR (IDLE/RXNE), and the UART->RDR remains the first byte we got, which causes another overrun flag. HAL_UARTEx_ReceiveToIdle_DMA() returns HAL_OK, and the huart->hdmarx->State is HAL_DMA_STATE_READY, but the DMA Rx buffer still remain empty.

How To Reproduce

  1. CubeMX config: Rx DMA in Circular Mode, and got UART Global Interrupt and corresponding Rx DMA Interrupt enabled.

  2. Configure the UART by HAL_UARTEx_ReceiveToIdle_DMA() and receive data in HAL_UARTEx_RxEventCallback(), and compile+flash the program.

  3. Send a series of bytes to UART which are shorter than Size passed to HAL_UARTEx_ReceiveToIdle_DMA()

  4. Even USARTx_IRQHandler() is untriggered, let alone HAL_UARTEx_RxEventCallback().

Additional context

  1. First try to add a line after HAL_UARTEx_ReceiveToIdle_DMA(), but ends with the same problem:
    HAL_UARTEx_ReceiveToIdle_DMA();
    __HAL_UART_ENABLE_IT(huart, UART_IT_IDLE);
  2. Then I try to replace the HAL_UARTEx_ReceiveToIdle_DMA() line with the following sequence, initializing the DMA process by directly modifying registers like what I have done in LL lib:
    
    // Initialize Rx DMA first
    WRITE_REG(huart->hdmarx->Instance->PAR, (uint32_t)&(huart->Instance->RDR)); // which equals to LL_DMA_SetPeriphAddress();
    WRITE_REG(huart->hdmarx->Instance->M0AR, (uint32_t)rx_buffer);              // LL_DMA_SetMemoryAddress();
    MODIFY_REG(huart->hdmarx->Instance->NDTR, DMA_SxNDT, rx_size);              // LL_DMA_SetDataLength();
    ATOMIC_SET_BIT(huart->Instance->CR3, USART_CR3_DMAR);                       // LL_USART_EnableDMAReq_RX();
    SET_BIT(huart->hdmarx->Instance->CR, DMA_SxCR_EN);                          // LL_DMA_EnableStream();

// then, to be compatible with HAL_UART_IRQHandler() logic, I added some extra lines: huart->pRxBuffPtr = rx_buffer; huart->RxXferSize = rx_size; huart->ReceptionType = HAL_UART_RECEPTION_TOIDLE; huart->RxEventType = HAL_UART_RXEVENT_TC;

// finally, enable UART __HAL_UART_ENABLE_IT(huart, UART_IT_IDLE); __HAL_UART_ENABLE(huart);


Then, I got everything work as expected. But one thing weird to me is that the sequence above should do mainly the same as HAL function, so I hadn't figured out where's the problem inside `HAL_UARTEx_ReceiveToIdle_DMA()` function.
Matrixchung commented 1 week ago

This issue also happens on STM32H750VBTx. I tried optimize level from -Os to -O3 with same result, and the solution is most likely the same, just replace huart->hdmarx->Instance with ((DMA_Stream_TypeDef*)huart->hdmarx->Instance) because the type of DMA_HandleTypeDef.Instance in stm32h7xx_hal_dma.h is void*.

KRASTM commented 1 week ago

Hello @Matrixchung,

Thank you for the report.

If you may, your project is on custom board or ST board?

Could you please share your File .ioc, or at least an example on ST Board with your config, so we can reproduce it. we will get back to you as soon as we analyze it further.

With regards,

KRASTM commented 1 week ago

Hello @Matrixchung,

I manage to adapt and test an example with HAL_UARTEx_ReceiveToIdle_DMA(), it's work fine, and I didn't get any problem especially overrun. Just for information the parameter size in the function impacts the type of event. Also, there is a post on our community similar to this topic, you can check it, it may help you.

With regards

Matrixchung commented 1 week ago

Hi @KRASTM, Sorry for late response. Firstly, I got my project working on own board and I am using C++ environment. For example define an app entry and call it in main.c, then implement it in .cpp file. Some headers about peripherals are imported through extern "C" like this:

#ifdef __cplusplus
extern "C" {
#endif
#include "stdint-gcc.h"
#include "main.h"
#include "usart.h"
void app_main();
#ifdef __cplusplus
}
#endif

Then, I wrote a UART handler class:

class UART
{
public:
    UART(UART_HandleTypeDef *_huart) : huart(_huart) {};
    void Init();
...
private:
    UART_HandleTypeDef *huart;
...
};

After that, call HAL_UARTEx_ReceiveToIdle_DMA(huart, rx_buffer, rx_stack_size) in void UART::Init(), but with no IRQ response. Neither calling it in class member function nor outside class work. However other HAL functions work well, and for UART when I switched to modifying registers like LL way, the same code runs without problem. Based on the information above, would you let me know on what environment (compiler, optimize level, IDE...) you are using to pass the example test? Thank you again for your continuous support.

KRASTM commented 1 week ago

Hello @Matrixchung,

In fact, I use IAR Embedded Workbench IDE- ARM V9.50.1 and I tested with level High & None for the Optimizations. I forgot to share the link for the post on our Community.

I'm wondering, where and how did you configure the UART and DMA? also, did you enable the USART global interrupt?

With regards,

Matrixchung commented 1 week ago

Hi @KRASTM,

Currently I am using both Visual Studio Code (with CubeMX generating makefile projects) and Keil MDK-ARM, they all got same problems when I switched to C++ scope. For the post you mentioned above, it seems that he just forgot to enable interrupt.

I'm wondering, where and how did you configure the UART and DMA?

I configure the UART and DMA in own defined app entry, just after HAL_xxx_Init();.

also, did you enable the USART global interrupt?

Of course I got USART global interrupt enabled. In the beginning of this issue, I have explained the reproduce steps:

CubeMX config: Rx DMA in Circular Mode, and got UART Global Interrupt and corresponding Rx DMA Interrupt enabled.

Best regards

KRASTM commented 22 hours ago

Hello @Matrixchung,

Could you share an extract from your project (config, etc....), we need more details to confirm the issue. Since there is no problem with the driver in particularly what you have described, and at this level we cannot confirm or denied anything.

In your case, and since you used a C++ environment, we need more information at least to share it with our team.

Otherwise, you can post your thread in one of the forums of our community

With regards.