别再浪费你的STM32定时器了!用PWM波低成本实现8位DAC(附RC滤波器参数计算)
用PWM波在STM32上实现8位DAC的工程实践指南
在嵌入式开发中,模拟信号输出是一个常见需求,但并非所有微控制器都配备足够的DAC资源。对于成本敏感型项目,特别是智能家居传感器、小型电机控制等应用,利用PWM波配合RC滤波器实现DAC功能是一个极具性价比的解决方案。本文将深入探讨如何通过STM32的定时器PWM功能,配合精心设计的RC滤波器,实现8位分辨率的模拟输出。
1. PWM DAC的核心原理与优势
PWM转DAC的核心思想是利用低通滤波器提取PWM信号中的直流分量。当PWM波的占空比变化时,其平均电压也随之改变,这正是DAC功能的基础实现方式。
PWM DAC相比硬件DAC的三大优势:
- 成本优势:无需额外DAC芯片,仅需几个电阻电容
- 资源复用:充分利用MCU已有的PWM外设
- 灵活性:分辨率可通过软件调整,适应不同需求
在STM32中,PWM波由定时器产生,关键参数由ARR(自动重装载寄存器)和CCRx(捕获/比较寄存器)决定。当计数器值小于CCRx时输出高电平,大于CCRx时输出低电平,达到ARR值时复位重新计数。
提示:8位分辨率需要将ARR值设为255(0-255共256个等级),这样PWM频率=定时器时钟/(ARR+1)
2. STM32 PWM配置实战
以STM32F103系列为例,配置定时器3的通道1产生PWM波:
// PWM初始化代码示例 void PWM_Init(void) { TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_OCInitTypeDef TIM_OCInitStructure; GPIO_InitTypeDef GPIO_InitStructure; // 使能时钟 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); // 配置GPIO GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; // TIM3_CH1 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); // 定时器基础配置 TIM_TimeBaseStructure.TIM_Period = 255; // 8位分辨率 TIM_TimeBaseStructure.TIM_Prescaler = 0; TIM_TimeBaseStructure.TIM_ClockDivision = 0; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); // PWM模式配置 TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; TIM_OCInitStructure.TIM_Pulse = 0; // 初始占空比 TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; TIM_OC1Init(TIM3, &TIM_OCInitStructure); TIM_Cmd(TIM3, ENABLE); }关键参数说明:
| 参数 | 说明 | 8位DAC典型值 |
|---|---|---|
| TIM_Period | 自动重装载值 | 255 |
| TIM_Prescaler | 预分频系数 | 根据时钟频率调整 |
| TIM_Pulse | 初始占空比 | 0-255 |
3. RC滤波器设计精要
RC低通滤波器的设计是PWM DAC成败的关键。滤波器需要满足两个核心要求:
- 足够衰减PWM基频及其谐波
- 保持足够的响应速度
一阶与二阶滤波器对比:
| 特性 | 一阶滤波器 | 二阶滤波器 |
|---|---|---|
| 衰减斜率 | 20dB/十倍频 | 40dB/十倍频 |
| 元件数量 | 1R+1C | 2R+2C |
| 设计复杂度 | 简单 | 中等 |
| 相位延迟 | 较小 | 较大 |
对于8位分辨率,我们需要确保基波谐波造成的纹波小于1LSB(3.3V/256≈12.9mV)。以72MHz时钟、8位分辨率为例:
关键计算步骤:
- PWM频率 = 72MHz / 256 = 281.25kHz
- 基波幅度 = (2×3.3V)/π ≈ 2.1V
- 所需衰减 = 20×log10(2.1V/0.0129V) ≈ 44dB
截止频率计算:
- 一阶滤波器:fc = 281.25kHz / 10^(44/20) ≈ 1.77kHz
- 二阶滤波器:fc = 281.25kHz / 10^(44/40) ≈ 22.34kHz
4. 实际电路实现与优化
基于上述计算,我们给出两种典型电路实现方案:
方案一:一阶RC滤波器
PWM输出 → R1 → 输出 → C1 → GND ↓ 负载元件值计算:
# 一阶滤波器计算示例 fc_desired = 1.77e3 # 目标截止频率 C = 100e-9 # 选用常见100nF电容 R = 1 / (2 * 3.1416 * fc_desired * C) print(f"所需电阻值: {R:.1f} Ω")输出:所需电阻值: 899.7 Ω(可选用900Ω或1kΩ)
方案二:二阶RC滤波器
PWM输出 → R1 → R2 → 输出 → C1 → GND ↓ C2 → GND元件值计算(Butterworth配置):
# 二阶滤波器计算示例 fc_desired = 22.34e3 C = 10e-9 # 选用10nF电容 R = 1 / (2 * 3.1416 * fc_desired * C) print(f"单电阻值: {R:.1f} Ω")输出:单电阻值: 712.8 Ω(可选用715Ω或750Ω)
注意:实际应用中应考虑运放缓冲,避免负载影响滤波特性。若直接驱动负载,建议在输出端添加电压跟随器。
5. 性能测试与校准技巧
实现PWM DAC后,需要进行系统级测试和校准:
测试步骤:
- 输出0%占空比,测量实际输出电压(应为0V)
- 输出100%占空比,测量实际输出电压(应接近VCC)
- 输出50%占空比,验证输出电压是否为中间值
- 用示波器观察纹波电压,确保<1LSB
常见问题及解决方案:
| 问题现象 | 可能原因 | 解决方法 |
|---|---|---|
| 输出非线性 | 滤波器截止频率过高 | 降低fc,增大RC时间常数 |
| 响应速度慢 | 滤波器截止频率过低 | 提高fc,减小RC时间常数 |
| 输出电压不足 | 负载阻抗过低 | 添加运放缓冲 |
| 纹波过大 | 滤波器衰减不足 | 增加滤波器阶数 |
在实际项目中,我发现使用二阶滤波器配合简单的软件校准,可以获得优于±2LSB的线性度。一个实用的校准技巧是在代码中建立查找表,补偿非线性误差:
// 非线性补偿表示例 const uint8_t DAC_LUT[256] = { 0, 1, 2, 3, ..., // 实测校准值 // ... 253, 254, 255 }; void Set_PWM_DAC(uint8_t value) { TIM3->CCR1 = DAC_LUT[value]; }