深入GD32 DMA握手机制:为什么你的DAC正弦波数据传输出错?
2026/5/8 3:58:33 网站建设 项目流程

深入解析GD32 DMA握手机制:DAC正弦波数据传输出错排查指南

当你在GD32平台上尝试用DAC+DMA+TIMER生成正弦波时,是否遇到过波形断裂、数据错位或频率异常的问题?这往往源于对DMA握手机制的理解偏差。本文将从一个实际调试案例切入,带你拆解DMA与DAC的交互细节。

1. 问题现象与根源定位

最近在调试GD32F103的DAC正弦波输出时,遇到了一个典型现象:示波器显示的波形周期性出现"台阶式"跳变,本该平滑的曲线在某些点突然跃升。通过逻辑分析仪抓取DMA传输时序后发现,DAC保持寄存器获取的数据与实际内存中的正弦波数组不符。

常见异常现象对照表

现象描述可能原因验证方法
波形幅值正确但频率减半TIMER重载值计算错误检查ARR寄存器配置
波形出现周期性畸变DMA传输地址未对齐检查内存地址&数组类型
只有固定电平输出DMA通道映射错误核对DMA1_Channel2配置
随机数据点跳变外设地址计算错误检查DAC_DHRx寄存器偏移量

提示:使用J-Link调试时,可在DMA传输完成中断设置断点,通过Memory窗口实时查看DAC_DHR寄存器的值。

2. DMA握手机制深度解析

2.1 从TIMER到DAC的触发链条

整个数据传输的触发源头是TIMER的更新事件。当计数器达到自动重装载值(ARR)时:

  1. TIMERx_CNT == TIMERx_ARR
  2. 产生更新事件(UEV)
  3. 触发主模式输出(TIMERx_TRGO)
  4. DAC检测到外部触发信号
  5. DAC使能DMA请求(DMAEN=1)
// 关键配置代码示例 timer_auto_reload_value_config(TIMER6, 72-1); // 设置ARR值 timer_master_output_trigger_source_select(TIMER6, TIMER_TRI_OUT_SRC_UPDATE); dac_trigger_source_config(DAC0, DAC_TRIGGER_T6_TRGO); dac_trigger_enable(DAC0);

2.2 DMA1_Channel2的独占性

为什么必须是DMA1的Channel2?查看GD32参考手册的DMA映射表会发现:

  • DAC0对应DMA1_Channel2
  • DAC1对应DMA1_Channel3

通道配置要点

  • 外设地址设为DAC_DHRx寄存器地址(如0x40007408)
  • 内存地址指向正弦波数组首地址
  • 传输宽度需匹配数据格式(12位右对齐为16位)
dma_parameter_struct dma_init; dma_struct_para_init(&dma_init); dma_init.periph_addr = (uint32_t)&DAC_R12DH(DAC0); dma_init.memory_addr = (uint32_t)sin_wave; dma_init.direction = DMA_PERIPH; dma_init.number = 256; // 波形点数 dma_init.periph_inc = DMA_PERIPH_INCREASE_DISABLE; dma_init.memory_inc = DMA_MEMORY_INCREASE_ENABLE; dma_init.periph_width = DMA_PERIPHERAL_WIDTH_16BIT; dma_init.memory_width = DMA_MEMORY_WIDTH_16BIT; dma_init.priority = DMA_PRIORITY_HIGH; dma_init.circular_mode = DMA_CIRCULAR_MODE_ENABLE; // 循环模式 dma_init(DMA1, DMA_CH2, &dma_init);

3. 典型错误案例分析

3.1 外设地址计算错误

曾遇到一个案例:工程师直接将0x40007400作为DMA目标地址,导致DAC获取的是控制寄存器值而非波形数据。正确的DHR寄存器地址计算:

DAC0_R12DH地址 = DAC0基地址 + 偏移量 = 0x40007400 + 0x08 = 0x40007408

地址验证技巧

  1. 在调试器中输入(uint32_t)&DAC_R12DH(DAC0)查看实际值
  2. 对比手册中的寄存器映射表
  3. 通过Memory窗口观察写入值

3.2 传输宽度不匹配

当正弦波数组定义为uint8_t类型但DMA配置为16位传输时,会导致相邻数据合并传输。例如:

内存数据:[0x12, 0x34, 0x56, 0x78]实际传输:0x3412,0x7856(小端模式)

解决方案:

  • 确保数组类型与DMA宽度一致
  • 使用__attribute__((aligned(4)))保证内存对齐

4. 高级调试技巧

4.1 使用断点验证传输时序

在DMA传输完成中断(TCIF)设置断点,检查:

  1. DMA_CNDTR寄存器剩余计数
  2. DAC_DOR寄存器当前输出值
  3. 逻辑分析仪抓取TIMER_TRGO和DAC_OUT时序

4.2 频率精度优化公式

实际输出频率计算公式:

f_out = f_timer / (ARR + 1) / N

其中:

  • f_timer:TIMER时钟源频率
  • ARR:自动重装载值
  • N:正弦波一个周期的点数

示例计算: 当f_timer=72MHz,ARR=71,N=256时:

f_out = 72,000,000 / 72 / 256 ≈ 3906.25Hz

4.3 动态波形切换方案

通过双缓冲技术实现波形无缝切换:

  1. 配置两个内存区域:WaveA和WaveB
  2. 在DMA半传输中断(HTIF)和传输完成中断(TCIF)中切换内存地址
  3. 使用DMA_MemoryTargetConfig()函数动态修改目标地址
void DMA1_Channel2_IRQHandler(void) { if(dma_interrupt_flag_get(DMA1, DMA_CH2, DMA_INT_FLAG_HT)) { dma_memory_address_config(DMA1, DMA_CH2, (uint32_t)wave_b); } if(dma_interrupt_flag_get(DMA1, DMA_CH2, DMA_INT_FLAG_TC)) { dma_memory_address_config(DMA1, DMA_CH2, (uint32_t)wave_a); } dma_interrupt_flag_clear(DMA1, DMA_CH2, DMA_INT_FLAG_G); }

调试时发现,在波形切换瞬间偶尔会出现毛刺,通过示波器触发模式捕获到这是由DMA地址重配置期间的短暂延迟导致的。解决方法是在切换前短暂关闭DMA使能:

dma_channel_disable(DMA1, DMA_CH2); dma_memory_address_config(DMA1, DMA_CH2, new_addr); dma_channel_enable(DMA1, DMA_CH2);

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询