从零构建STM32F103正弦波发生器:DAC与DMA的黄金组合实战指南

当我们需要在嵌入式系统中生成精确的模拟信号时,数字模拟转换器(DAC)是不可或缺的硬件资源。而STM32F103系列微控制器内置的12位DAC,配合直接内存访问(DMA)技术,能够实现高效、精确的波形生成。本文将带你深入理解这一技术组合,并手把手实现一个可调频率的正弦波发生器。

1. 硬件基础与设计思路

STM32F103的DAC模块具有两个独立的12位输出通道,每个通道都能在不需要CPU干预的情况下,通过DMA自动更新输出值。这种架构特别适合周期性波形生成,比如我们要实现的正弦波。

为什么选择DMA+DAC方案?

  • CPU资源零占用 :一旦初始化完成,波形生成完全由硬件自动处理
  • 高精度定时 :利用定时器触发确保采样间隔绝对精确
  • 低抖动输出 :DMA直接搬运数据,避免了软件延迟带来的时序不确定性

关键硬件资源配置:

DAC通道1 -> PA4
DAC通道2 -> PA5
定时器8 -> 触发源
DMA2通道4 -> 数据传输

2. 正弦波表生成方法论

由于STM32的DAC没有内置正弦波发生器,我们需要预先计算一个周期的正弦采样值数组。这个数组的质量直接决定了输出波形的精度和平滑度。

Python生成正弦数组的实用代码:

import numpy as np

def generate_sine_table(length=32, bits=12, vref=3.3):
    amplitude = (2**bits - 1) / 2
    offset = amplitude
    return [int(round(offset + amplitude * np.sin(2*np.pi*i/length))) 
            for i in range(length)]

# 生成12位精度的32点正弦表
sine_table = generate_sine_table(32, 12)
print(sine_table)

关键参数解析:

参数 说明 典型值
表长度 一个周期的采样点数 32-256
分辨率 DAC的位数 12位
幅度 输出电压范围 0-3.3V

提示:表长度越长波形越平滑,但会占用更多内存。32点对于大多数应用已经足够。

3. 完整工程配置详解

让我们拆解初始化代码的每个关键部分,理解各个配置项的作用。

GPIO初始化:

GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;  // 模拟输入模式
GPIO_Init(GPIOA, &GPIO_InitStructure);

注意:DAC输出引脚必须配置为模拟输入模式,即使它们是输出。

定时器配置:

TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_TimeBaseStructure.TIM_Period = 0x19;      // 自动重装载值
TIM_TimeBaseStructure.TIM_Prescaler = 0x00;   // 无分频
TIM_TimeBaseInit(TIM8, &TIM_TimeBaseStructure);
TIM_SelectOutputTrigger(TIM8, TIM_TRGOSource_Update);

定时器频率计算公式: $$ f_{TIM} = \frac{f_{CLK}}{(Prescaler + 1) \times (Period + 1)} $$

DMA关键配置解析:

DMA_InitStructure.DMA_PeripheralBaseAddr = DAC_DHR12RD_Address;
DMA_InitStructure.DMA_MemoryBaseAddr = (u32)&DualSine12bit;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
DMA_InitStructure.DMA_BufferSize = 32;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
  • 循环模式确保波形连续输出
  • 内存地址递增实现自动遍历数组
  • 外设地址固定为DAC数据寄存器

4. 频率调节与性能优化

输出波形频率由三个因素决定:

  1. 定时器触发频率
  2. 正弦表长度
  3. DMA缓冲区大小

频率计算公式: $$ f_{out} = \frac{f_{TIM}}{N} $$ 其中N为正弦表长度。

实际调节技巧:

  • 需要更高频率?减小定时器周期或降低表长度
  • 需要更好波形质量?增加表长度但保持相同频率
  • 动态调节示例:
// 动态改变定时器周期来调整频率
void set_sine_frequency(uint32_t freq) {
    TIM8->ARR = SystemCoreClock / (32 * freq) - 1;
    TIM_SetAutoreload(TIM8, TIM8->ARR);
}

常见问题排查指南:

现象 可能原因 解决方案
无输出 DMA未启动 检查DMA和DAC使能顺序
波形畸变 定时器配置错误 验证定时器时钟和分频
幅度不对 数组值范围错误 确认数组值在0-4095范围内
波形不连续 DMA未循环模式 检查DMA_Mode配置

5. 双通道同步输出进阶技巧

利用STM32的双DAC特性,我们可以实现相位可调的双通道输出:

// 生成相位差90度的双通道数据
for(int i=0; i<32; i++) {
    DualSine12bit[i] = (Sine12bit[i] << 16) | Sine12bit[(i+8)%32];
}

应用场景:

  • 正交信号生成
  • 三相电机驱动
  • 差分信号模拟

6. 实测对比与性能评估

使用示波器观察输出时,重点关注三个指标:

  1. 频率精度(与计算值偏差)
  2. 总谐波失真(THD)
  3. 电压稳定性

典型性能参数:

指标 32点波形 64点波形
THD <5% <2%
最大频率 ~100kHz ~50kHz
内存占用 128字节 256字节

通过合理配置,这个方案可以实现优于1%的频率精度和小于3%的THD,满足大多数测试和测量应用的需求。

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐