第一次接触STM32的DAC功能时,我被它简洁的硬件设计惊艳到了。与外部DAC芯片相比,内置DAC不仅节省电路板空间,还大大简化了编程流程。让我们从最基础的DAC寄存器配置开始,逐步揭开数模转换的神秘面纱。
STM32的DAC模块通常包含几个关键寄存器:DAC控制寄存器(DAC_CR)、数据保持寄存器(DHRx)和数据输出寄存器(DORx)。控制寄存器负责配置DAC的工作模式,数据保持寄存器是我们写入数字量的地方,而数据输出寄存器则直接对应最终的模拟输出电压。
实际配置时有个小技巧:在HAL库环境下,我们不需要直接操作这些寄存器。比如配置DAC通道1输出2V电压,参考电压为3.3V时,可以这样计算数字量:
c复制uint16_t digital_value = (2000 / 3300.0) * 4095; // 2V对应的12位数字量
HAL_DAC_SetValue(&hdac, DAC_CHANNEL_1, DAC_ALIGN_12B_R, digital_value);
这里有个容易踩的坑:DAC输出电压范围完全取决于参考电压VREF+。我在早期项目中曾犯过一个错误,没有检查开发板的VREF+连接,导致输出电压始终不正确。后来发现是参考电压引脚悬空,默认使用了VDDA作为参考,而VDDA存在波动。
DAC的分辨率直接影响输出精度。以STM32F4系列为例,其DAC分辨率为12位,意味着可以输出4096个不同的电压级别。但在实际应用中,有效位数(ENOB)可能更低,这取决于电源噪声、PCB布局等因素。我在一个精密控制项目中,通过以下措施将ENOB从10.5位提升到11.3位:
生成波形是DAC最经典的应用场景之一。不同于简单的直流电压输出,波形生成需要考虑更新时序、数据精度和存储空间等多个维度。
生成三角波的关键在于控制电压升降的线性度。我最开始尝试用for循环逐步增减电压值:
c复制for(int i=0; i<4095; i++) {
HAL_DAC_SetValue(&hdac, DAC_CHANNEL_1, DAC_ALIGN_12B_R, i);
delay_us(10);
}
这种方法简单但有个严重问题:循环体内的延迟不精确,导致波形频率不稳定。后来改用定时器触发DAC更新,波形稳定性大幅提升。
进阶方案是使用DMA自动传输波形数据。预先计算好一个周期的三角波数据点,存储到数组中,然后配置DMA循环模式传输。这种方法几乎不占用CPU资源,适合需要同时处理其他任务的场景。以下是核心代码片段:
c复制uint16_t triangle_wave[100];
// 生成三角波数据
for(int i=0; i<50; i++) {
triangle_wave[i] = i * 4095 / 49;
triangle_wave[99-i] = triangle_wave[i];
}
HAL_DAC_Start_DMA(&hdac, DAC_CHANNEL_1, triangle_wave, 100, DAC_ALIGN_12B_R);
正弦波生成比三角波复杂,因为涉及浮点运算。在资源有限的MCU上,我有三种常用方法:
c复制for(int i=0; i<100; i++) {
float angle = 2 * 3.14159 * i / 100;
uint16_t value = 2047 + 2047 * sin(angle);
HAL_DAC_SetValue(&hdac, DAC_CHANNEL_1, DAC_ALIGN_12B_R, value);
delay_us(100);
}
这种方法灵活但计算量大,在低端MCU上可能导致波形失真。
查表法:预先计算好正弦值表存储在Flash中。以256点正弦表为例,输出频率f与定时器触发频率f_trig的关系为:f = f_trig/256。这种方法效率最高,但会占用Flash空间。
DDS(直接数字合成)技术:通过相位累加器和查找表实现,适合需要频率可调的场合。STM32的DAC结合定时器和DMA可以很好实现DDS。
实际项目经验:在音频信号生成项目中,我发现12位DAC直接输出正弦波时,总谐波失真(THD)较高。通过添加一个简单的RC滤波器(f_c=30kHz),THD从-45dB改善到-55dB。更进一步,使用运放构建的有源滤波器可以将THD优化到-65dB以下。
当项目需要多路DAC而芯片内置DAC数量不足时,PWM DAC是个经济高效的解决方案。我曾在一个需要8路模拟输出的项目中,使用STM32的4个定时器生成8路PWM,配合RC滤波器,成本不到外接DAC芯片的1/3。
PWM DAC的有效分辨率公式为:
code复制分辨率(位) = log2(PWM周期/最小占空比步进)
例如,使用72MHz时钟的定时器,ARR设为255,则PWM频率=72MHz/256=281.25kHz,分辨率为8位。若需要更高分辨率,可以降低PWM频率:
实际取舍:在电机控制项目中,我需要14位分辨率但又不希望PWM频率太低影响响应速度。最终方案是使用72MHz时钟,ARR=16383,得到PWM频率=4.4kHz,14位分辨率,配合二阶滤波器后纹波小于5mV。
PWM DAC性能很大程度上取决于滤波器设计。一阶RC滤波器简单但衰减慢,二阶滤波器性能更好但设计复杂。我常用的设计步骤如下:
实用技巧:在PCB空间允许的情况下,我更喜欢使用多重一阶滤波器级联。比如两个f_c=10kHz的一阶滤波器串联,实际效果接近二阶,但对元件精度要求更低。下图是我在最近项目中使用的滤波器电路:
code复制PWM输出 → 1kΩ → 10nF → 1kΩ → 10nF → 输出
| | |
GND GND GND
实测这个电路在20kHz处衰减达到-42dB,完全满足音频应用需求。需要注意的是,电阻应选用1%精度的金属膜电阻,电容建议使用NPO或C0G材质的陶瓷电容,温度稳定性更好。
在多个项目实践中,我总结出DAC和PWM DAC的选型决策矩阵:
| 特性 | 内置DAC | PWM DAC |
|---|---|---|
| 分辨率 | 12位固定 | 可调(8-16位常见) |
| 建立时间 | 1μs以内 | 取决于滤波器 |
| 输出阻抗 | 低(约50Ω) | 较高(需缓冲) |
| 硬件成本 | 无额外成本 | 需RC滤波器 |
| 适合场景 | 精密快速控制 | 低频高分辨率 |
| 多路扩展成本 | 需外置DAC芯片 | 仅需更多定时器通道 |
典型应用案例:
一个有趣的折中方案是PWM DAC + 跟踪保持电路。在某个数据采集项目中,我使用100kHz 12位PWM DAC配合采样保持放大器,实现了等效400ksps的12位DAC,成本仅为专用DAC芯片的1/5。关键点是保持电容的选择——聚丙烯薄膜电容漏电流最小,保持效果最好。