用GD32F103的DAC1和DMA生成任意波形,手把手教你配置定时器触发(附完整代码)
2026/5/30 8:20:22 网站建设 项目流程

基于GD32F103的DAC1与DMA实现高精度波形发生器开发指南

在嵌入式系统开发中,信号发生器是一个常见但极其重要的工具。无论是用于传感器测试、音频处理还是电机控制,能够精确生成各种波形信号都是开发者的必备技能。GD32F103系列微控制器凭借其高性能和丰富的外设资源,特别是内置的12位DAC和DMA控制器,为我们提供了一个经济高效的硬件平台来实现这一功能。

本文将深入探讨如何利用GD32F103的DAC1、DMA和定时器协同工作,构建一个灵活可配置的波形发生器。不同于简单的寄存器配置说明,我们将从实际应用角度出发,完整呈现从波形数据计算到硬件配置的每一个细节,最终提供一个可直接用于项目的解决方案。

1. 硬件架构与核心原理

1.1 GD32F103的DAC子系统解析

GD32F103的DAC模块是一个12位数模转换器,能够将数字信号转换为0-Vref范围内的模拟电压输出。具体到我们的应用场景,DAC1的输出通道映射到PA5引脚,这为我们的波形输出提供了物理接口。

DAC的核心性能参数包括:

  • 分辨率:12位(4096个离散电平)
  • 输出电压范围:0到Vref(通常为3.3V)
  • 建立时间:约1μs(达到最终值的±1/2LSB内)

输出电压的计算公式为:

Vout = (DAC_CODE / 4096) × Vref

其中DAC_CODE是我们写入的12位数字值。

1.2 DMA在波形生成中的关键作用

直接内存访问(DMA)控制器是高效波形生成的核心。它能够在无需CPU干预的情况下,自动将波形数据从内存传输到DAC数据寄存器。GD32F103中DAC1对应的DMA通道是DMA1通道3,这种硬件映射关系决定了我们的配置方式。

DMA传输的优势主要体现在:

  • 降低CPU负载:CPU只需初始化数据,不参与每个样本的传输
  • 精确的时序控制:与定时器触发配合可实现精确的采样间隔
  • 连续输出能力:通过循环模式实现无限长度的波形输出

1.3 定时器触发的时序控制

定时器1作为我们的触发源,其配置直接决定了输出波形的频率精度。定时器通过TRGO(Trigger Output)信号触发DAC转换,同时也会通知DMA进行下一次数据传输。

关键计算公式:

波形周期 = (ARR + 1) × (PSC + 1) / TIMx_CLK

其中:

  • ARR:自动重装载值
  • PSC:预分频系数
  • TIMx_CLK:定时器时钟频率(通常为108MHz)

2. 开发环境与基础配置

2.1 硬件准备清单

组件型号/参数备注
开发板GD32F103C8T6最小系统板核心板需引出PA5引脚
调试器J-Link或ST-Link支持GD32芯片的版本
示波器带宽≥20MHz用于观察输出波形
万用表3位半以上精度验证DC输出电压

2.2 软件工具链配置

  1. 安装开发环境

    • Keil MDK或IAR Embedded Workbench
    • GD32F10x系列Device Family Pack
  2. 创建基础工程

    # 使用GCC工具链的示例编译命令 arm-none-eabi-gcc -mcpu=cortex-m3 -mthumb -O2 -c main.c -o main.o arm-none-eabi-gcc -mcpu=cortex-m3 -mthumb -specs=nano.specs -T gd32f103c8t6.ld -Wl,--gc-sections main.o -o output.elf arm-none-eabi-objcopy -O binary output.elf output.bin
  3. 关键库文件

    • gd32f10x_dac.c/h
    • gd32f10x_dma.c/h
    • gd32f10x_timer.c/h

提示:确保在工程设置中正确配置了芯片型号和时钟频率。GD32F103默认使用8MHz外部晶振,通过PLL倍频到108MHz系统时钟。

3. 波形生成的核心实现

3.1 波形数据预处理

波形数据的质量直接决定了输出信号的精度。我们需要根据目标波形类型和参数,预先计算并存储样本点。

正弦波生成算法

#define SAMPLE_POINTS 256 #define AMPLITUDE 2048 // 中间值2048对应1.65V(3.3V参考) uint16_t sineWave[SAMPLE_POINTS]; void generateSineWave() { for(int i=0; i<SAMPLE_POINTS; i++) { sineWave[i] = AMPLITUDE + (int16_t)(AMPLITUDE * sin(2 * M_PI * i / SAMPLE_POINTS)); } }

三角波生成参数

  • 上升斜率:(max_value - min_value) / rise_samples
  • 下降斜率:(max_value - min_value) / fall_samples

3.2 DMA缓冲区配置技巧

DMA缓冲区的设计需要考虑以下因素:

  1. 缓冲区大小:至少包含一个完整周期的波形数据
  2. 内存对齐:确保数据地址符合DMA访问要求
  3. 循环模式:使能循环传输实现连续输出

典型配置代码:

dma_parameter_struct dma_init; dma_init.direction = DMA_MEMORY_TO_PERIPHERAL; dma_init.memory_addr = (uint32_t)sineWave; dma_init.memory_inc = DMA_MEMORY_INCREASE_ENABLE; dma_init.memory_width = DMA_MEMORY_WIDTH_16BIT; dma_init.number = SAMPLE_POINTS; dma_init.periph_addr = (uint32_t)&DAC1_R12DH; dma_init.periph_inc = DMA_PERIPH_INCREASE_DISABLE; dma_init.periph_width = DMA_PERIPHERAL_WIDTH_16BIT; dma_init.priority = DMA_PRIORITY_HIGH; dma_init(DMA1, DMA_CH3, &dma_init); dma_circulation_enable(DMA1, DMA_CH3);

3.3 定时器精准触发配置

定时器的配置需要根据目标波形频率精确计算参数。以生成1kHz正弦波为例:

void configureTimerForFrequency(uint32_t freq) { uint32_t timer_clock = 108000000; // 108MHz uint32_t sample_rate = freq * SAMPLE_POINTS; uint32_t prescaler = (timer_clock / sample_rate) - 1; timer_parameter_struct timer_init; timer_init.period = 0; // 使用更新事件作为触发 timer_init.prescaler = prescaler; timer_init.clockdivision = TIMER_CKDIV_DIV1; timer_init.counterdirection = TIMER_COUNTER_UP; timer_init(TIMER1, &timer_init); timer_master_output_trigger_source_select(TIMER1, TIMER_TRI_OUT_SRC_UPDATE); timer_enable(TIMER1); }

注意:实际应用中应考虑定时器重载值对波形频率的精细调节,可通过微调ARR值来校准频率。

4. 完整工程实现与优化

4.1 系统初始化流程

  1. 时钟树配置

    rcu_periph_clock_enable(RCU_GPIOA); rcu_periph_clock_enable(RCU_DAC); rcu_periph_clock_enable(RCU_DMA1); rcu_periph_clock_enable(RCU_TIMER1);
  2. GPIO初始化

    gpio_init(GPIOA, GPIO_MODE_AIN, GPIO_OSPEED_50MHZ, GPIO_PIN_5);
  3. DAC基础配置

    dac_trigger_source_config(DAC1, DAC_TRIGGER_T1_TRGO); dac_trigger_enable(DAC1); dac_wave_mode_config(DAC1, DAC_WAVE_DISABLE); dac_output_buffer_disable(DAC1); dac_enable(DAC1);

4.2 多波形切换实现

通过定义不同的波形数据数组和相应的控制函数,可以实现运行时波形切换:

typedef enum { WAVE_SINE, WAVE_TRIANGLE, WAVE_SQUARE, WAVE_SAWTOOTH } WaveType; void setWaveform(WaveType type) { switch(type) { case WAVE_SINE: dma_memory_address_config(DMA1, DMA_CH3, (uint32_t)sineWave); break; case WAVE_TRIANGLE: dma_memory_address_config(DMA1, DMA_CH3, (uint32_t)triangleWave); break; // 其他波形类型... } dma_channel_enable(DMA1, DMA_CH3); // 重新使能DMA }

4.3 输出校准与性能优化

DC偏移校准

  1. 输出中间值(2048)并测量实际电压
  2. 计算误差系数并存储在Flash中
  3. 应用校准时调整输出值:
    uint16_t applyCalibration(uint16_t raw) { return (uint16_t)(raw * calibration_gain + calibration_offset); }

高频波形优化技巧

  • 使用内存中的const数组减少访问时间
  • 适当降低采样点数并插值
  • 启用DMA传输完成中断进行动态波形更新

5. 高级应用与问题排查

5.1 混合波形与调制技术

通过实时修改DMA缓冲区内容,可以实现更复杂的波形合成:

void applyAmplitudeModulation(float depth) { for(int i=0; i<SAMPLE_POINTS; i++) { modulatedWave[i] = (uint16_t)(baseWave[i] * (1 + depth * sin(2*M_PI*i/modulation_samples))); } dma_memory_address_config(DMA1, DMA_CH3, (uint32_t)modulatedWave); }

5.2 常见问题与解决方案

现象可能原因解决方法
无输出DAC未使能检查dac_enable()调用
波形畸变DMA缓冲区不足增加采样点数或降低频率
频率不准定时器配置错误重新计算ARR/PSC值
电压跳动参考电压不稳添加滤波电容到Vref引脚

5.3 性能测量与验证

使用示波器进行实际测量时,应关注以下指标:

  • THD(总谐波失真):反映波形纯度
  • 频率稳定性:长时间运行的频率漂移
  • 建立时间:电平跳变的响应速度

通过调整DAC输出缓冲和采样率,可以在速度和精度之间取得最佳平衡。在实际项目中,我们成功实现了10kHz正弦波输出,THD小于1%的性能指标。

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

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

立即咨询