在工业控制、信号发生等嵌入式开发场景中,常遇到需要 MCU 输出正弦波、固定电平等多种波形并实现交替切换的需求,但若 MCU 内置 DAC 资源不足,可采用PWM + 滤波的方式模拟生成模拟波形。传统方案需使用两个 Timer(一个输出 PWM,一个定时切换波形),而 STM32U5 系列的 GPDMA(通用 DMA)12-15 通道自带2D addressing/Repeat专属功能,配合 Linked List 链表模式,仅需单 Timer + 单 GPDMA 2D 通道即可实现多波形的自动交替输出,不仅节省外设资源,还能实现硬件级的平滑切换,无需 CPU 干预。本文基于 ST 官方 LAT1189 应用笔记,以 STM32U575 NUCLEO 开发板为测试平台,从原理、配置、代码、实测四个维度,详解该方案的实操实现与灵活拓展。
资料获取:【应用笔记】LAT1189 Timer结合DMA 2D通道实现不同波形输出
1. 方案核心原理:PWM 滤波 + GPDMA 2D 硬件功能
本方案的核心是高频 PWM 脉宽实时调整模拟波形+GPDMA 2D 通道的硬件级数据传输与切换,替代传统软件或双 Timer 的切换方式,先明确核心实现逻辑与测试基础参数。
1.1 PWM + 滤波生成模拟波形的基础逻辑
DAC 资源不足时,通过调整 PWM 脉宽的连续序列模拟模拟波形的幅值变化,再经外部无源滤波电路滤除 PWM 高频载波,即可得到平滑的模拟波形:
- 正弦波:按正弦波幅值变化规律,设置一组连续的 PWM 脉宽值,按顺序循环输出,滤波后还原正弦波;
- 固定电平:输出等脉宽的 PWM 波,滤波后得到稳定的直流电平。
本次测试的核心参数(基于 STM32U575,主频 100MHz):
- PWM 频率:2MHz(周期 500ns),脉宽可调范围 0~50 个计数时钟;
- 正弦波:1 个周期 10us,对应 20 个 PWM 脉冲,脉宽序列为 [25,33,40,45,49,50,49,45,40,33,25,17,10,5,1,0,1,5,10,17];
- 波形时长:正弦波序列重复 1000 次,对应 10ms 连续正弦波,后自动切换为固定电平,循环交替。
1.2 GPDMA 2D 通道的专属核心功能
STM32U5 的 GPDMA 共 16 个独立通道,仅 12-15 通道支持 2D addressing/Repeat 功能,搭配 Linked List 链表模式,完美适配多波形交替需求,三个核心功能相互配合:
- 2D addressing(2D 寻址):完成一次指定长度的块数据传输后,源地址自动回滚至数组起点,无需软件重新配置,实现单波形脉宽序列的循环传输;
- Repeat(重复传输):可设置块传输的重复次数,精准控制单个波形的持续时长(如本次设置 1000 次,对应 10ms 正弦波);
- Linked List(链表模式):创建多个波形传输节点(如正弦波节点、固定电平节点),配置为循环模式后,节点可按顺序自动切换,实现多波形的硬件级交替输出,无软件延时。
1.3 整体工作流程
TIM1 输出 2MHz PWM,其更新事件作为 GPDMA 的触发信号;每次 TIM1 更新,GPDMA 从内存脉宽数组中读取一个值,写入 TIM1 的 CCR1 寄存器(PWM 脉宽控制寄存器),实现 PWM 脉宽的实时调整;GPDMA 按链表节点配置,先重复 1000 次正弦波脉宽传输,再自动切换至固定电平脉宽传输,循环往复,最终经滤波得到正弦波与固定电平交替的模拟波形。
2. CubeMX 实操配置:三步完成核心外设配置
基于 STM32CubeMX 生成测试工程,核心配置TIM1 CH1 PWM、GPDMA CH12 2D 通道、Linked List 链表三部分,所有配置均为图形化操作,无需手动修改寄存器,步骤可复现。
前提准备
新建 STM32U575ZGQx 工程,配置系统时钟为 100MHz,开启 ICACHE/DCACHE(提升内存访问效率,保证 DMA 传输速率),配置系统电源管理为正常模式。
步骤 1:TIM1 CH1 PWM 核心配置
TIM1 作为 PWM 输出核心,配置为向上计数、PWM 模式 1,参数与测试需求严格匹配,关键配置如下:
- 模式选择:Channel1 PWM Generation CH1,时钟源为内部时钟;
- 计数器设置:Prescaler(预分频)=0,Counter Period(自动重装值)=50,Counter Mode=Up(向上计数);
- 计算逻辑:TIM1 时钟 = 系统主频 = 100MHz,预分频 0 则计数时钟 100MHz,PWM 周期 = 50/100MHz=500ns,频率 = 2MHz,完美匹配测试需求;
- PWM 配置:PWM Mode=PWM mode1,Output Compare Preload=Disable(禁用输出比较预加载),Fast Mode=Disable;
- 其他:Repetition Counter=0,Break And Dead Time=Disable(无需死区和刹车功能)。
步骤 2:GPDMA CH12 2D 通道配置
选择GPDMA1 Channel12(仅 12-15 通道支持 2D 功能),配置为循环模式,开启 Linked List 功能,关键配置:
- 通道属性:8Words Internal FIFO/2D addressing=Enabled,Linked-List Mode=Enabled;
- 执行模式:Execution Mode=Circular(循环模式),保证 DMA 传输持续进行;
- 传输配置:Allocated Port for Transfer=Port0,Transfer Event Generation=TC/HT(传输完成 / 半完成均触发事件)。
步骤 3:Linked List 链表与节点配置
创建一个循环链表队列,包含正弦波节点(TN1)和固定电平节点(TN2),实现两个波形的自动交替,核心配置:
- 链表队列(TQ1):创建 Queue 命名为 TQ1,关联 GPDMA1 Channel12,Linked List Setting=Circular(循环模式),First Loop Node=TN1(首个执行正弦波节点);
- 正弦波节点(TN1):核心为 “TIM1 更新事件触发 + 2D 寻址 + 重复 1000 次”
- 触发源:Request Configuration=GPDMA_REQUEST_TIM1UP(TIM1 更新事件为 DMA 请求);
- 2D 与 Repeat:2D Addressing=Enabled,Repeat Counter=1000(重复 1000 次);
- 传输参数:Direction=Memory To Peripheral(内存到外设),Data Size=20(对应 20 个脉宽值),Burst Length=1;源地址为正弦波脉宽数组
sine_100k,目的地址为&TIM1->CCR1;地址偏移:源地址偏移 0,目的地址偏移 0,块传输后源地址回滚;
- 固定电平节点(TN2):配置与 TN1 完全一致,仅源地址改为固定电平脉宽数组
bias_100k,实现 PWM 脉宽的固定输出。
关键说明:两个节点的目的地址均为 TIM1->CCR1,DMA 传输的核心是将内存中的脉宽值实时写入 CCR1,实现 PWM 脉宽的硬件级调整,无需 CPU 参与。
3. 核心测试代码解析:简洁高效,无冗余逻辑
CubeMX 生成基础工程后,仅需添加脉宽数组定义和DMA+TIM 关联启动代码,主函数逻辑简洁,无复杂的中断处理或软件切换代码,核心代码如下(基于 HAL 库)。
3.1 脉宽数组定义
在 main.c 中定义正弦波和固定电平的脉宽数组,长度为 20,与 DMA 节点的 Data Size 匹配:
// 正弦波脉宽数组:20个值,对应1个正弦波周期(10us)
uint8_t sine_100k[20] = {25,33,40,45,49,50,49,45,40,33,25,17,10,5,1,0,1,5,10,17};
// 固定电平脉宽数组:20个24,对应稳定PWM脉宽,滤波后为固定直流电平
uint8_t bias_100k[20] = {24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24};
3.2 主函数核心执行流程
系统初始化后,依次完成外设初始化→链表关联→PWM 启动→DMA 启动→TIM 与 DMA 关联,全程无软件循环,硬件自动执行:
int main(void)
{
// 1. 系统基础初始化:HAL库、系统时钟、电源管理、缓存
HAL_Init();
SystemClock_Config();
SystemPower_Config();
MX_ICACHE_Init();
// 2. 外设初始化:GPDMA、GPIO、TIM1
MX_GPDMA1_Init();
MX_GPIO_Init();
MX_TIM1_Init();
// 3. 链表配置:初始化TQ1队列及TN1、TN2节点
MX_TQ1_Config();
// 4. 关联GPDMA12通道与TQ1链表,失败则进入错误处理
if (HAL_DMAEx_List_LinkQ(&handle_GPDMA1_Channel12, &TQ1) != HAL_OK)
{
Error_Handler();
}
// 5. 启动TIM1 CH1 PWM输出
HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);
// 6. 启动GPDMA12链表中断,开启DMA传输
HAL_DMAEx_List_Start_IT(&handle_GPDMA1_Channel12);
// 7. 关联TIM1与GPDMA12:将TIM1 CC1通道DMA句柄与GPDMA12绑定
__HAL_LINKDMA(&htim1, hdma[TIM_DMA_ID_CC1], handle_GPDMA1_Channel12);
// 8. 使能TIM1更新事件DMA请求:触发DMA传输的核心
__HAL_TIM_ENABLE_DMA(&htim1, TIM_DMA_UPDATE);
// 死循环:无需软件操作,硬件自动完成波形交替
while (1)
{
}
}
关键代码说明
HAL_DMAEx_List_LinkQ:将 DMA 通道与链表队列关联,是链表模式执行的基础;__HAL_LINKDMA:实现 TIM1 与 GPDMA12 的硬件绑定,保证 DMA 传输的脉宽值准确写入 TIM1->CCR1;__HAL_TIM_ENABLE_DMA(&htim1, TIM_DMA_UPDATE):开启 TIM1 更新事件的 DMA 请求,是 DMA 传输的触发源,每次 TIM1 计数到 50 产生更新事件,触发一次 DMA 传输。
4. PWM 滤波与实测效果:波形精准,切换平滑
完成硬件配置和代码编写后,在 STM32U575 NUCLEO 板上进行实测,重点验证波形参数准确性和切换平滑性。
4.1 PWM 滤波实现
在 TIM1 CH1 输出引脚(PA8)外接RC 无源滤波电路,滤除 2MHz 的 PWM 高频载波,滤波电路参数需与 PWM 频率匹配:推荐电阻 1kΩ、电容 0.1μF,可根据实际波形平滑度微调。
4.2 实测核心结果
- 波形参数完全符合预期:滤波后的正弦波周期为 10us(频率 100kHz),持续时长 10ms,与配置的 “20 个 PWM 脉冲 / 周期、重复 1000 次” 完全一致;固定电平幅值稳定,无波动;
- 波形切换无延时、超平滑:正弦波与固定电平的交替切换无任何毛刺和延时,远优于传统双 Timer 方案;
- 传统双 Timer 方案:需通过 Timer 中断触发软件修改 CCR1 值,存在 CPU 中断响应延时,切换处易出现毛刺;
- 本方案:GPDMA 链表实现硬件级节点切换,无需 CPU 干预,切换瞬间完成,波形平滑无瑕疵。
5. 方案灵活拓展:适配多场景波形生成需求
本方案的核心优势是配置灵活、可拓展性强,仅需修改链表节点和脉宽数组,即可适配不同的波形生成需求,无需重新配置 TIM 和 GPDMA 基础参数,常见拓展场景如下:
5.1 调整波形持续时长
修改 DMA 节点的 Repeat Counter(重复计数器)值,即可精准控制单个波形的持续时长:
- 如将 Repeat Counter 改为 2000,正弦波持续时长变为 20ms;改为 500,持续时长变为 5ms;
- 若需不同波形的持续时长不同,可分别修改 TN1、TN2 的 Repeat Counter 值(如 TN1=1000,TN2=500,实现 10ms 正弦波 + 5ms 固定电平交替)。
5.2 实现多波形循环输出
在 Linked List 链表中添加更多节点,即可实现 3 种及以上波形的循环交替输出:
- 如新增三角波节点 TN3,定义三角波脉宽数组,按 TN1→TN2→TN3 的顺序配置链表,即可实现正弦波→固定电平→三角波的循环输出;
- 每个节点可独立配置脉宽数组长度、重复次数,实现个性化的波形组合。
5.3 生成其他类型波形
更换脉宽数组的数值序列,即可通过 PWM + 滤波生成三角波、方波、锯齿波等其他模拟波形:
- 三角波:脉宽值按 “递增→递减” 的线性规律设置;
- 方波:脉宽值按 “高值→低值” 的固定规律交替设置,调整占空比即可改变方波幅值。
5.4 提高波形分辨率
增加脉宽数组的长度(即 DMA 节点的 Data Size),即可提高波形的分辨率,让波形更平滑:
- 如将正弦波的脉宽数组长度从 20 增加到 40,一个正弦波周期对应 40 个 PWM 脉冲,幅值变化更细腻,滤波后的正弦波更接近标准波形。
STM32U5 的Timer+GPDMA 2D 通道方案,完美解决了 “DAC 资源不足 + 多波形交替输出” 的开发痛点,相比传统双 Timer 方案,具有节省外设资源、硬件级执行低 CPU 占用、波形切换平滑、配置灵活四大核心优势,可广泛应用于工业信号发生、电机控制、传感器模拟等场景。结合实际开发,给出 5 条实用建议,避免踩坑:
1. 严格选择 GPDMA 通道:仅GPDMA1 12-15 通道支持 2D addressing/Repeat 功能,其他通道无此专属特性,切勿选错通道导致功能失效。
2. 保证参数匹配性:脉宽数组的长度必须与 DMA 节点的Data Size完全一致,Repeat Counter 的数值需根据波形时长需求精准计算,避免传输错误或波形时长偏差。
3. 优化滤波电路参数:RC 滤波电路的电阻、电容值需与PWM 频率匹配:PWM 频率越高,电容可选择更小的值,保证波形的响应速度;若波形出现毛刺,可适当增大电容值。
4. 开启缓存提升传输效率:STM32U5 系列支持 ICACHE/DCACHE,开启后可大幅提升内存访问效率,保证 GPDMA 从内存中读取脉宽值的速率,避免因内存访问过慢导致 PWM 脉宽更新不及时。
5. 开启 DMA 中断实现软件监控:可开启 GPDMA 的传输完成中断(TC),在中断回调函数中实现波形切换的软件监控和干预:如实时统计波形输出次数、根据外部信号手动切换波形节点、异常情况下停止 DMA 传输等。
本方案的本质是充分利用 STM32U5 的 GPDMA 硬件特性,将波形切换的工作从 CPU 和软件转移到硬件,不仅简化了代码逻辑,还提升了系统的稳定性和实时性。对于嵌入式开发而言,这种 “硬件级实现” 的思路是提升系统性能的关键 —— 合理利用 MCU 的专属硬件功能,替代软件循环和中断,既能降低 CPU 占用率,又能避免软件带来的延时和误差,让系统更高效、更稳定。
217