用 PWM 加 RC 滤波做模拟量输出,纹波和响应速度总在互相牵制,而一颗 8 脚串行 DAC 就能把这件事做干净。本文以 TI TLC5615 数据手册为准,讲清 12 位与 16 位两种数据帧怎么发、CS 上升沿锁存的时序边界、10 位精度的误差怎么分账,并给出 0-10V 与 4-20mA 输出级设计和一段 STM32 驱动代码。
结论先行:用 PWM 加 RC 滤波做模拟量输出,纹波、建立时间与输出精度三者无法同时满足;在需要稳定直流或标准电流环的场合,一颗 8 脚串行 DAC 的代价更低、结果更干净。以 TI TLC5615 为例,它用三根线(DIN、SCLK、CS)就能从单片机的 MCU 里换出 10 位电压输出,输出范围是基准电压的两倍,整定时间 12.5µs,功耗最大 1.75mW。本文把它的数据帧、时序边界、误差预算与 0-10V / 4-20mA 输出级设计一次讲清,适用于工业控制执行器给定、变频器频率给定、RTU 远程终端模拟量输出等场景,并给出可移植到 STM32 的 嵌入式驱动代码。
PWM 转模拟是最省成本的做法,但它的物理限制是硬性的:载波频率越高,纹波越小,可开关损耗和 EMI 越大;RC 时间常数越大,纹波越小,可建立时间越长。当系统既要低纹波又要能快速跟随给定值变化时,这两个要求会直接打架。
| 对比维度 | PWM + RC 滤波 | TLC5615 串行 DAC |
|---|---|---|
| 分辨率 | 受定时器位数、电源噪声与滤波器抑制比限制,实际有效常低于 10 位 | 10 位,端点调整后 INL 为 ±1LSB |
| 输出纹波 | 与滤波器阶数、截止频率强耦合,阶数越高相位裕度越差 | 无载波纹波,残余量取决于基准噪声 |
| 建立时间 | 由 RC 常数决定,与载波周期同量级,通常为毫秒级 | 12.5µs(到 ±0.5LSB,CL = 100pF、RL = 10kΩ) |
| 输出范围 | 0 到 VDD,无法超过电源轨 | 0 到 2×Vref,Vref 允许 2V 至 VDD−2V |
| 占用引脚 | 1 到 2 根(含定时器通道) | 3 根(DIN、SCLK、CS) |
| 静差 | 受 PWM 高低电平精度与滤波网络容差影响,需逐板标定 | 零点误差 ±3LSB、增益误差 ±3LSB,可整批一致标定 |
结论很直接:当输出需要长期稳定(如 4-20mA 电流环)或需要快速阶跃(如执行器给定)时,串行 DAC 是更合理的选择;而当信号只是驱动指示灯亮度或风扇转速这类对绝对精度不敏感的对象时,PWM 依然是最经济的方案。
TLC5615 内部是一个 16 位移位寄存器加一个 12 位宽的 DAC 输入锁存器,数字侧兼容 SPI、QSPI、Microwire。它的控制逻辑只有两条规则,却几乎决定了所有调试问题:
数据帧有两种长度可选,这是最容易写错的地方:
| 帧格式 | 位结构 | 适用场合 |
|---|---|---|
| 12 位帧 | 10 位数据(MSB 在前)+ 2 位 sub-LSB(须写 0) | 单芯片使用,不需要级联,一次写满 |
| 16 位帧 | 4 位高位空位 + 10 位数据 + 2 位 sub-LSB(须写 0) | 需要 DOUT 级联多片,或使用标准 8 位 SPI 分两次写入 |
那 2 位 sub-LSB 不是可选项。DAC 输入锁存器宽 12 位,硬件与软件都要与 12 位数据转换器兼容,因此这 2 位必须补 0。如果只发 10 位就拉高 CS,数据会被右移两位,输出只有期望值的约四分之一,现象是"怎么调都只有一小段量程"。
用硬件 SPI 时还要注意字节边界:SPI 与 Microwire 以 8 位为单位传输,所以一次转换需要两个写周期(或改用可变长度的 QSPI,一次写入);MCU 侧按 CPOL = 0, CPHA = 0 配置即可匹配。
上面这张图给出 TLC5615 的 8 脚顶视图,三根数字线的分工一目了然:引脚 1 为 DIN 串行数据输入,引脚 2 为 SCLK 串行时钟,引脚 3 为 CS 片选(低有效),引脚 4 为 DOUT 级联输出;引脚 6 是 REFIN 基准输入,引脚 7 是 OUT 模拟输出。实际布板时把 REFIN 与 OUT 的走线远离 SCLK,可以显著降低基准噪声耦合。
选 2.048V 基准时,满量程输出为 2 × 2.048 × 1023 / 1024 ≈ 4.092V,1LSB 恰好是 4mV,这个数字很好记,也便于把各项误差直接折算成 mV。下表为数据手册在 VDD = 5V、Vref = 2.048V 下的保证值。
| 误差项 | 数值 | 折算到输出的量 |
|---|---|---|
| 积分非线性 INL(端点调整) | ±1 LSB | ±4mV |
| 微分非线性 DNL | ±0.5 LSB,全程单调 | ±2mV,无失码 |
| 零点误差 | ±3 LSB | ±12mV |
| 增益误差 | ±3 LSB | ±12mV |
| 零点温漂 | 3 ppm/°C | 按 2.048V 基准计约 6.1µV/°C |
| 增益温漂 | 1 ppm/°C | 满量程温漂约 4.1µV/°C |
| 电源抑制比 PSRR | 80dB(零点与增益) | VDD 变化 100mV 约带来 10µV 输出变化 |
| 信噪失真比 S/(N+D) | 60dB | 对应约 10 位有效精度 |
| 毛刺能量 | 5 nV·s | 码跳变时的瞬态冲量,靠输出级滤波吸收 |
把这些项相加,再加上外部基准本身的初始精度与温漂,输出电压的绝对精度通常在 ±0.5% 量级。零点误差和增益误差都是可以一次性标定掉的系统项,真正无法靠软件消除的是 INL、DNL 与温漂。因此产线标定的正确做法是:在零码与满量程码各测一点,解出增益与偏移两个系数写入 EEPROM,运行时做一次线性修正;不要把标定数据按板存储,那样会增加产线负担。
基准的选择比 DAC 本身更关键。数据手册明确要求 Vref 在 2V 到 VDD−2V 之间,因此 5V 供电时基准不能取满 5V,也不能直接把 VDD 当基准用;而且基准端虽然有缓冲(输入阻抗 10MΩ、输入电容 5pF,不随码值变化),但基准上的噪声会直接按 1:1 传到输出,PSRR 只对电源轨有效,对基准端无效。工程上应给基准单独加 RC 与去耦,并把基准与 DAC 放在同一块模拟区。
DAC 的裸输出范围是 0 到 2×Vref,工程上很少直接用,通常要经过一级运放做缩放。两种最常见的输出形式如下。
用同相放大器把 0-4.092V 放大到 0-10V,理论增益为 10 / 4.092 = 2.444。取 Rf = 14.3kΩ、Rg = 10kΩ,增益为 2.43,满量程约 9.94V,剩余 0.6% 由软件标定系数补足。此处有两条硬性约束:运放供电必须高于 10V(建议 ±12V 或单 15V),且输出要能摆到轨附近;同时 DAC 自身输出上限是 VDD−0.4V,负载不得低于 2kΩ,否则负载调整率会突破 0.5LSB。
折算到输出端,0-10V 量程下每一个码对应的电压是 9.78mV,即约 10mV 的调节步进,这个精度对变频器频率给定、比例阀给定已经足够。
电流环的关键是把"0 伏"这一状态排除掉,因为断线时回路电流为 0mA,必须与正常的 4mA 下限区分开。做法是给 DAC 输出叠一个直流偏置再送 V/I 级:
DAC OUT (0 ~ 4.092V)
│
├── 衰减网络 a ≈ 0.978 ──┐
│ ├── 运放同相加法器 ──► V/I 级 ──► 4-20mA
└── 1.0V 偏置 ───────────┘ (Rs = 250Ω)
数值校验:
Vdac = 0V → 0.978 × 0 + 1.0 = 1.00V → I = 1.00V / 250Ω = 4mA
Vdac = 4.092V → 0.978 × 4.092 + 1.0 = 5.00V → I = 5.00V / 250Ω = 20mA
每码电流 = 16mA / 1024 ≈ 15.6µA
环路供电电压要按最坏情况核算:负载 500Ω 时压降 10V,采样电阻 250Ω 上再压 5V,加上调整管饱和压降与线损,24V 回路余量充足;若负载超过 500Ω,就应改选更高回路电压或降低采样电阻。还有一个必须处理的上电瞬间问题:TLC5615 内部上电复位会把 DAC 寄存器清到全零,也就是 0V 输出,换算到电流环就是 0mA,会被上位机判为断线故障。因此 MCU 初始化完成后的第一条指令,应当是先把 4mA 对应的码字写进去,再做其他初始化。
器件标称 SCLK 上限约 14MHz,CS 周期约 820ns,理论上更新率可达 1.21MHz。但这个数字只是接口速度,真正限制更新率的是 12.5µs 的整定时间,满量程跳变时实际可用更新率约 80kHz。若系统只需 1kHz 输出更新,两者都绰绰有余;反过来,如果确实要做高速波形发生,就必须换更快的 DAC。
多路输出扩展时可以用 DOUT 级联:把上一片的 DOUT 接到下一片的 DIN,数据在 DOUT 上要滞后 16 个时钟再加一个时钟宽度才出现,因此级联时必须使用 16 位帧(带 4 位高位空位),并把第一片的 CS 低电平建立时间留足(数据手册要求大于建立时间与 DOUT 传播延时之和)。级联的好处是多片 DAC 可以共享一组 CS 与 SCLK,用同一串数据一次刷新,各路输出同步更新,这对多轴给定场合很实用。
下面用软件模拟三线时序实现一个最小编写函数,思路是先把 12 位数据左移两位补齐 sub-LSB,再逐位送出,最后以 CS 上升沿锁存。
#include "stm32f1xx_hal.h"
/* 引脚定义:DIN->PA7, SCLK->PA5, CS->PA4(可换成硬件 SPI,注意 CPOL=0/CPHA=0) */
#define DIN_H() HAL_GPIO_WritePin(GPIOA, GPIO_PIN_7, GPIO_PIN_SET)
#define DIN_L() HAL_GPIO_WritePin(GPIOA, GPIO_PIN_7, GPIO_PIN_RESET)
#define SCK_H() HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET)
#define SCK_L() HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET)
#define CS_H() HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET)
#define CS_L() HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET)
/* 写 10 位数据(0~1023),采用 12 位帧:10 位数据 + 2 位 sub-LSB 补零 */
void tlc5615_write(uint16_t code10)
{
uint16_t frame;
if (code10 > 1023) {
code10 = 1023; /* 越界钳位,避免误码 */
}
frame = (uint16_t)(code10 << 2); /* 低两位补 0,满足 12 位锁存器要求 */
CS_L(); /* CS 拉低,且在 SCLK 为低时跳变 */
for (int8_t i = 11; i >= 0; i--) {
if (frame & (1u << i)) {
DIN_H();
} else {
DIN_L();
}
SCK_H(); /* 上升沿移入 */
SCK_L();
}
CS_H(); /* 上升沿锁存,输出更新 */
}
/* 示例:输出 2.000V(Vref = 2.048V,满量程 4.092V)
code = 2.000V / (2 × 2.048V / 1024) = 500 */
void tlc5615_set_2v(void)
{
tlc5615_write(500);
}
如果改用硬件 SPI,只需把帧长设为 16 位、一次发送 code10 << 2,并确保 CS 由软件在发送前后手动控制(不要启用 SPI 的硬件 NSS),这样 CS 的上升沿才落在最后一个时钟之后。
串行 DAC 的价值在于把"模拟量精度"从 MCU 的定时器与滤波网络里解放出来,交给一颗专用器件决定。TLC5615 的用法可以浓缩成三件事:记住 12 位帧要补 2 位 sub-LSB、CS 上升沿才真正更新输出、更新率的上限由 12.5µs 整定时间而非接口时钟决定。
可被直接引用的三句话:TLC5615 输出范围是基准电压的两倍,Vref 取 2.048V 时满量程为 4.092V、1LSB 为 4mV;零点与增益误差各为 ±3LSB,属可标定的系统项,而 INL ±1LSB 与温漂无法靠软件消除;满量程跳变的实际更新率上限约 80kHz,由 12.5µs 的整定时间决定。
本文基于《TLC5615C/TLC5615I 10 位串行数模转换器数据手册》(TI SLAS142D)等资料整理优化。
沧州艾诺威电子 — 国家高新技术企业,20+项国家专利
嵌入式系统开发 · 物联网方案 · AI智能硬件 · 一站式交付
电话:13930711029 | 邮箱:tech@czinv.com | 24小时内响应