cancel
Showing results for 
Search instead for 
Did you mean: 

HAL_UART_Transmit_DMA() doesn't?

E P
Associate III

I am porting code from an existing project that runs on an STM32F4 chip onto an STM32H7 chip, and have been losing my sanity for a couple of days trying to get over hurdle #1: I use a UART for communicating with the chip, and have not been able to get DMA transfers to work.

I used CubeMX to create a new project for the new chip. Based on https://community.st.com/s/article/FAQ-DMA-is-not-working-on-STM32H7-devices I have confirmed that the buffer that I've pointed the DMA peripheral at is indeed sitting in SRAM2 (D2 area), which it should have access to. For fun, I'm not enabling the data cache, and am not fiddling with the MPU yet. Using HAL_UART_Transmit() with no DMA works fine, but with DMA the transmit function runs once, no data is transferred, and the port stays busy forever. Resetting the port status will let the function run again, but still doesn't transfer any data. No error flags get set, and no interrupts get called (e.g. TxHalfCplt or DMAError), the transfer just doesn't happen.

My suspicion/hope right now is that either

  1. I still haven't managed to get all of the memory locations right, so the peripheral is waiting forever for the indicated number of bytes, or
  2. There was an important change in the way the UART/DMA are initialized that happened some time in the last 3 years that I'm overlooking (the working codebase I'm porting was generated by a much older CubeMX version, in early 2016).

I've looked through half a dozen or so of the examples that come with the H7 MCU package but haven't found anything especially useful. I'm hoping that someone here has some brainstormy ideas so that I can bark up some different trees next week. What are some reasons that a DMA transfer would fail to start? At the moment I haven't even attempted receiving data, I'm just trying to get bytes out. UART configuration is thus:

  huart4.Instance = UART4;
  huart4.Init.BaudRate = 921600;
  huart4.Init.WordLength = UART_WORDLENGTH_8B;
  huart4.Init.StopBits = UART_STOPBITS_1;
  huart4.Init.Parity = UART_PARITY_NONE;
  huart4.Init.Mode = UART_MODE_TX_RX;
  huart4.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  huart4.Init.OverSampling = UART_OVERSAMPLING_16;
  huart4.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_ENABLE;
  huart4.Init.ClockPrescaler = UART_PRESCALER_DIV1;
  huart4.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_RXOVERRUNDISABLE_INIT|UART_ADVFEATURE_DMADISABLEONERROR_INIT;
  huart4.AdvancedInit.OverrunDisable = UART_ADVFEATURE_OVERRUN_DISABLE;
  huart4.AdvancedInit.DMADisableonRxError = UART_ADVFEATURE_DMA_DISABLEONRXERROR;

... DMA:

    hdma_uart4_tx.Instance = DMA1_Stream4;
    hdma_uart4_tx.Init.Request = DMA_REQUEST_UART4_TX;
    hdma_uart4_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
    hdma_uart4_tx.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_uart4_tx.Init.MemInc = DMA_MINC_ENABLE;
    hdma_uart4_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
    hdma_uart4_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
    hdma_uart4_tx.Init.Mode = DMA_NORMAL;
    hdma_uart4_tx.Init.Priority = DMA_PRIORITY_LOW;
    hdma_uart4_tx.Init.FIFOMode = DMA_FIFOMODE_ENABLE;
    hdma_uart4_tx.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;
    hdma_uart4_tx.Init.MemBurst = DMA_MBURST_SINGLE;
    hdma_uart4_tx.Init.PeriphBurst = DMA_PBURST_SINGLE;

Data to transmit lives in a struct that is __attributed to the SRAM2 address space (0x30020000); at the moment I'm just using memcpy() to stuff some values in there, then use HAL_CRC_Calculate() on it and ship the entire struct out the door (or into oblivion, in this case). Calling

HAL_UART_Transmit(&huart4, (uint8_t *)&sUartTxAssy, tUARTTxSize, 100);

works fine, but

HAL_UART_Transmit_DMA(&huart4, (uint8_t *)&sUartTxAssy, tUARTTxSize);

does nothing.

Any thoughts from anyone? Are there any new tricks to kicking off DMA transfers that I could be overlooking? Maybe a CubeMX-generated function stub that needs filling?

Thanks!

17 REPLIES 17

Does the function return an error?

The DMA registers look to be working, clock on, not reporting a fault condition?

Peripheral address is &UART4->TDR ?

Some DMA / USART structure associations?

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
E P
Associate III

Hi,

HAL_UART_Transmit_DMA() returns HAL_OK the first time it's called, and HAL_BUSY after that. HAL_DMA_Start_IT, called from the UART Transmit function, also returns HAL_OK after the first/only time it's called.

huart->ErrorCode seems to stay HAL_UART_ERROR_NONE and hdma->ErrorCode seems to stay HAL_DMA_ERROR_NONE. I'm not seeing any of the error callbacks being called, for the UART or for DMA. This is why I'm wondering if I'm not actually sending it any data.

I haven't followed the debugger too far into low-level DMA land yet but the Cube-generated MX_DMA_Init() runs __HAL_RCC_DMA1_CLK_ENABLE() and I don't see any occasion that might switch it off.

The business end of the peripheral that I'm talking to is ->TDR (which is new here, in the older code it was ->DR) so I guess that's right? I've been assuming that things at this level are correct since that's supposed to be the point of a HAL and the quality and consistency of the HAL/Cube thing have been legendary. So I probably need to spend more time following execution down the lower level paths, but I'm still assuming that there's a /* USER CODE BEGIN 46 */ somewhere that needs to be filled in.

E P
Associate III

WHOA SUCCESS

So this is neat. CubeMX, when generating code, put this at the beginning of int main(void):

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_ADC1_Init();
  MX_ADC3_Init();
  MX_CRC_Init();
  MX_IWDG1_Init();
  MX_SPI3_Init();
  MX_SPI4_Init();
  MX_TIM1_Init();
  MX_TIM3_Init();
  MX_TIM4_Init();
  MX_UART4_Init();
  MX_UART7_Init();
  MX_USB_DEVICE_Init();
  MX_BDMA_Init();
  MX_DMA_Init();
  MX_MDMA_Init();

I moved MX_UART4_Init() to be after MX_DMA_Init(), which makes sense given that the UART uses DMA, and suddenly it's fine. Yay? Looking at this list, basically everything on it will be using DMA (I just haven't tackled any of the other peripherals; the init functions are mostly commented out at the moment) so maybe the DMA init functions should be first.

E P
Associate III

Side note: I see the same thing now in another thread: https://community.st.com/s/feed/0D50X0000BZFWRISQ5 so maybe this is something to yell at ST about.

It appears ST already heard the yell in that other thread.

JW

Yes, it looks like ST jumped right on that. I kept searching for posts about the UART so I completely missed that discussion, but when I looked for MX_DMA_Init() it was right there. Just shows that I should read the forums more!

Automatic code generation can sometimes be a pain 🙂 it is quite a bug. Changing the init order worked for me. DMA first, everything else after.

I'm having this exact issue with STM32CubeMX Version: 6.3.0-RC5, Build: 20210714-1111 (UTC). The USART is initialized prior to the DMA and the USART DMA transfers don't send anything while not producing errors. Switch the order so that the DMA is initialized before the USART fixed it.

Same here, CubeMX 6.3.0, reordering UART and DMA init in Main.c fixes the problem.