
Many STM32 developers often configure and generate initialization code based on the HAL library using STM32CubeMx. When it comes to DMA functionality, they find that it is ineffective, but cannot seem to identify the cause from the configuration operations and the code itself. This situation can often be quite frustrating.
For example, someone reported that while developing a product using the STM32F4 series chips and generating initialization code through STM32CubeMx, they used DMA for UART transmission. However, they found that the DMA was not working at all. Later, they accidentally discovered that it was because they had unintentionally adjusted the order of the UART peripheral and DMA peripheral initialization code in their user code. Once they rearranged the order, everything worked fine [with DMA initialization code first and UART initialization code afterward]. They wanted to know how this order affected the DMA functionality.
I casually took an STM32F334 Nucleo board and enabled the data communication function for UART1/UART3, using DMA for cyclic data transmission. UART1 sends data while UART3 receives it. Based on the configuration generated by STM32CubeMx, I added user code as shown in the image below:

After testing, I found that the DMA transmission function based on UART1/3 was normal.
Combining the feedback from the customer, I swapped the initialization order of DMA and UART, as shown in the image below:

Indeed, I found that the DMA no longer worked, and there was no data communication between UART1 and UART3. The data register contents of UART1/3 remained at 0 without any changes, especially the data register of UART1, the sender, showed no activity.
It seems that the order of the initialization code for DMA and UART does indeed affect their functionality. In other words, if the code is based on the existing CubeMX generated initialization code, the initialization order of the two cannot be adjusted arbitrarily. But what exactly is going on?
First, I examined the content of these two initialization codes in an attempt to find clues. Unfortunately, I did not quickly discover the reason. Later, when I reviewed the specific content of the DMA initialization function MX_DMA_Init();, I found that the code was actually quite simple, consisting of just two actions:

One action is to enable the clock for the DMA peripheral, and the other is to enable the interrupt vector control related to DMA.
Since this is the case, I tried to keep the position of the DMA initialization function body still after the UART initialization code, but I extracted the line of code that enables the DMA peripheral clock from the DMA initialization function and moved it before the UART initialization code for verification. This time, everything worked normally.
It seems that, based on the existing initialization code, the enabling of the DMA clock must be placed before the UART initialization code. But why is that? It feels like the configuration of UART has nothing to do with the DMA clock.
Continuing to dig for the reason!
Looking back at the UART initialization code, I found a clue in a sub-function of the UART initialization function, HAL_UART_MspInit();
MX_USART1_UART_Init() ==> HAL_UART_Init() ==> HAL_UART_MspInit();
Since we enabled the DMA functionality related to UART transmission events, in the HAL_UART_MspInit(); function, not only is there configuration for the multiplexing function of GPIO related to UART, but also configuration related to DMA events for UART. It seems that the UART initialization is indeed associated with DMA.

Combining the line of code that enables the DMA peripheral clock from the above DMA initialization function, I basically understand what is going on.
Because we need to perform configurations related to DMA in the UART initialization code, if we do not first enable the DMA peripheral clock, and since there is no code to enable the DMA peripheral clock in the UART initialization function, then performing configuration operations related to DMA in the UART initialization code cannot be guaranteed to be effective.
At this point, the reason mentioned at the beginning for the impact of the order of DMA and UART initialization code on DMA functionality should be revealed.
In embedded development, many initialization configurations are based on the hardware itself, and some initialization sequences may have timing requirements from a hardware perspective. Generally, the chip manuals will have clear descriptions and explanations regarding these. We must follow the relevant provisions when writing initialization code. Of course, some configuration orders may also need to be flexibly adjusted based on specific applications after practical experience.
Returning to the case in the article, generally speaking, STM32CubeMx has already considered the initialization timing when generating initialization code. It is just that users may have unintentionally adjusted the order of the two during the code organization process without realizing it, compounded by our lack of sufficient understanding of the initialization code itself, which may have led us into difficulties.
From personal experience, when we adjust configurations back and forth based on CubeMx in practical applications, this order may also get disrupted. Please pay attention to this point. To be honest, this is very subtle, and even if we know about it, we may still forget or overlook it. When DMA transmission anomalies occur because of this, it is very difficult to identify the root cause without tracking or reading the code based on the code itself, as the configuration operations and the called library function code itself are not problematic. The core issue is the execution order of the initialization code.
For example, in the past few days, several people have reported that they encountered anomalies in ADC data not being fetched by DMA when using STM32 chips, all due to this reason. In summary, under the current circumstances, ensuring that the DMA initialization code is placed before the initialization of other peripherals related to DMA will avoid similar issues. For example, it should look like the following:

Regarding this topic, I shared it three years ago. Throughout this process, there are still people who encounter this issue, and I feel it is necessary to share it again, so I am reiterating it here as a reminder, hoping to lessen the bumps in your development process.
Three years, it has been almost three years of the ongoing COVID-19 pandemic, it has been tough! I am posting a motivational song below, hoping everything returns to normal soon!
==============================
Previous topic reading links: 【Click to view】
1、Application Example of STM32 Timer Triggering SPI Byte-by-Byte Transmission
2、Synchronization Issues Between High-Precision Timer Timing Units in STM32
3、Common Issues in Getting Started with STM32CubeIDE
4、Two Misunderstandings in Debugging STM32 in KEIL MDK Environment
5、Application Example of STM32 TIM+DMA+DAC
