问题现象:
使用 STM32H533 的 UART2 + GPDMA1 进行 DMA 发送测试,期望通过 DMA 将字符串 STM32H533CODE TEST MODE(33字节)完整发送到串口,实际却只能发送第一个字符 S,后续字符丢失。

调试过程
第一阶段:怀疑代码还原
最初怀疑是代码被 IDE/CubeMX 还原,但检查后发现 USER CODE 区域的自定义代码确实存在。发现 tx_buffer 只初始化了前 9 字节,剩余 24 字节为未初始化内存。
第二阶段:DMA 完成回调不触发
添加 HAL_UART_TxCpltCallback 回调函数用于 DMA 完成通知,但在主循环中 while (dma_tx_complete == 0) 死等,回调从未被调用。
第三阶段:DMA 配置错误 — 根本原因
检查 HAL_UART_MspInit 中的 DMA 初始化代码,发现 TX 通道配置:
handle_GPDMA1_Channel1.Init.SrcInc = DMA_SINC_FIXED; // 错误配置
DMA_SINC_FIXED 表示源地址固定,DMA 控制器始终从同一个内存地址读取数据,导致 UART 始终发送缓冲区第一个字节 S。
修复方案
将 SrcInc 从 DMA_SINC_FIXED 改为 DMA_SINC_INCREMENTED:
handle_GPDMA1_Channel1.Init.SrcInc = DMA_SINC_INCREMENTED; // 源地址递增

修改后 DMA 控制器会依次读取缓冲区的每个字节,完整发送 33 个字符。
关键参数说明

(一)循环配置(Circular configuration)
Circular Mode(循环模式):当前为 Disable(关闭)。
如果开启,DMA 传输完设定的数据量后会自动从头开始(常用于音频/ADC 连续采样);关闭则传输一次即停止,需手动重新触发。
(二)请求配置(Request Configuration)
Request(请求源):当前为 USART2_TX。
指定触发该 DMA 通道的外设请求信号(这里是 USART2 的发送请求)。
DMA Handle in IP Structure(IP结构中的DMA句柄):当前为 hdmatx。
软件代码中用于管理该 DMA 传输的句柄变量名。
Block HW request protocol(块硬件请求协议):当前为 Single/Burst Level。
硬件握手协议类型,决定外设请求 DMA 的方式(单次传输还是突发传输块)。
(三)通道配置(Channel configuration)
Priority(优先级):当前为 Low(低)。
当多个 DMA 通道同时请求时,决定谁先被响应(可设为 Very High/High/Medium/Low)。
Transaction Mode(传输模式):当前为 Normal(正常模式)。
对应循环模式的开关,正常模式即非循环,传完指定数量就停止。
Direction(方向):当前为 Memory To Peripheral(内存到外设)。
数据流动方向(源是内存,目标是 USART 外设)。
(四)源数据设置(Source Data Setting)—— 这里是关键
Source Address Increment Aft(源地址递增):Enabled(已启用)
Data Width(数据宽度):Byte(字节,即 8-bit)。
每次传输的数据大小(可选 Byte/Half-word/Word)。增量步长等于此宽度。
Burst Length(突发长度):64。
一次 DMA 请求连续传输的个数(此处为 64 个字节/次)。
Allocated Port for Transfer(分配传输端口):Port 0。
在支持多端口的总线矩阵中,指定源端走哪个总线端口(用于优化带宽)。
(五)目标数据设置(Destination Data Setting)
Destination Address Increment(目标地址递增):Enabled(已启用)。
目标地址是否自增(如果目标是内存数组,通常开启;如果目标是外设数据寄存器,通常关闭)。
Data Width / Burst Length / Allocated Port:与源端含义相同,分别作用于目标端。
(六)数据处理(Data Handling)
Data Handling Configuration(数据处理配置):当前为 Disable(关闭)。
通常指 FIFO(先进先出)模式、数据打包/解包或半字/字节对齐转换功能。关闭即直通传输。
(七)触发(Trigger)
Trigger Configuration(触发配置):当前为 Disable(关闭)。
额外引入的软件或硬件触发器(如定时器触发 DMA),关闭则仅靠外设请求(USART)触发。
(八)传输事件配置(Transfer Event Configuration)
Transfer Event Generation(传输事件生成):当前为 The TC (and the HT) event is generated...(传输完成 TC 和半传输 HT 事件生成)。
表示当传输完成(TC)或传输到一半(HT)时,会生成中断或事件信号,方便 CPU 做后续处理(如关闭 DMA 或切换缓冲区)。
其他相关修改
缓冲区声明
#define TX_SIZE (33) uint8_t tx_buffer[TX_SIZE] = "STM32H533CODE TEST MODE ";
注意:C 语言字符串字面量若长度不足定义长度,剩余字节为未初始化内存,会导致垃圾数据。
DMA 完成回调
volatile uint8_t dma_tx_complete = 0;
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
if (huart == &huart2)
{
dma_tx_complete = 1;
}
}主循环
while (1)
{
HAL_UART_Transmit_DMA(&huart2, (uint8_t *)tx_buffer, TX_SIZE);
while (dma_tx_complete == 0) { } // 等待 DMA 完成
dma_tx_complete = 0;
HAL_Delay(2000);
}总结
DMA 只发送第一个字符最常见的原因是 SrcInc 配置为 DMA_SINC_FIXED。发送前务必确认:
SrcInc = DMA_SINC_INCREMENTED(内存发送源)
DestInc = DMA_DINC_FIXED(UART 数据寄存器不需要递增)
缓冲区大小与传输长度匹配
缓冲区内容已正确初始化
我要赚赏金
