Skip to main content
E P
Associate III
November 8, 2019
Question

HAL_UART_Transmit_DMA() doesn't?

  • November 8, 2019
  • 9 replies
  • 18240 views

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!

9 replies

Tesla DeLorean
Guru
November 8, 2019

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 VenmoUp vote any posts that you find helpful, it shows what's working..
E P
E PAuthor
Associate III
November 8, 2019

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
E PAuthor
Associate III
November 8, 2019

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.

MSavi.1
Visitor II
October 28, 2021

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.

E P
E PAuthor
Associate III
November 8, 2019

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.

waclawek.jan
Super User
November 9, 2019

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

JW

E P
E PAuthor
Associate III
November 11, 2019

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!

MX_Master
Associate
February 3, 2022

This "initialization order" bug is still there. Just checked on STM32CubeMX v6.4.0 with FW_F1 v1.8.4.

PS found this topic while googling

FHarr
Visitor II
March 18, 2022

Yeah, it was driving me nuts trying to figure this one out just to stumble accross this, a post started in 2019...

The issue is the DMA clock (__HAL_RCC_DMA1_CLK_ENABLE()) isnt enabled till after we have started configuring it.

ali_metu
Associate
March 23, 2022

The order of initialization works for me as well. UART, ADC and DAC are running OK with DMA.

GoEk
Associate III
July 4, 2022

This problem is mentioned in the CubeMX manual at section 19.11:

https://www.st.com/resource/en/user_manual/dm00104712-stm32cubemx-for-stm32-configuration-and-initialization-c-code-generation-stmicroelectronics.pdf

Apparently version 6.3.0. introduced this bug and it carries to later versions of MX, requiring a "hack" as the FAQ mentions.

Visitor II
May 23, 2024

I found this old topic when I had the same problem.  However, in my case the initialization order (in my own code) happened to be write.  My issue was that when I enabled DMA in CubeMX, I hadn't enabled the global interrupt.

Just leaving this here in case someone else comes along with the same problem.  The fixed configuration (on my STM32L0) looks like:

AndrewEidsness_0-1716498436897.png

Originally the second checkbox was disabled and DMA would not work.

Explorer
August 26, 2025

Now that concluded my problem as well.

The previous answers about order seems to be fixed by  ST.