前阵子帮朋友调一条产线上的变送器校准工位,看到工人还在用螺丝刀拧机械电位器,拧完还要刷红漆固定,我当时的反应是:这玩意儿早该退休了。后来我们把校准回路换成 AD5293BRUZ-20-RL7 这颗 20kΩ、1024 步的数字电位器,配合 PIC18F4682 单片机在出厂前自动完成增益校准,产线节拍从每台两分钟压到十几秒,一致性还比老师傅手工调出来的好。这篇文章就把这套“可编程电阻 + MCU”的方案从选型、硬件、固件到产线落地的完整过程拆一遍,给做仪器仪表、传感器变送器、电源模块校准的工程师一个可以直接抄作业的参考。
1. 从螺丝刀到固件:校准场景为什么需要一颗可编程电阻
机械电位器在模拟电路里当了这么多年“最后微调”的角色,不是没有原因的:它无源、直观、不依赖软件,拧到位置就不动了。但放到批量生产的校准工位上,问题非常具体。
第一个坑是人工一致性。人的手不是精密仪器,调同一个目标值,十个人能拧出十种结果;就算同一个人上午和下午调,手感和判断也不一样。产线为了保证一致性,往往只能靠“拧到输出刚好进窗口”这种模糊标准,窗口留得越大,产品的实际精度就被牺牲得越多。
第二个坑是点胶固定这个后续工序。机械电位器调完还要点胶或刷漆,防止运输震动把滑片震偏。胶水固化需要时间,也增加了物料和人工成本,遇到返修拆开重调时,点过胶的电位器几乎没法干净地二次使用。
第三个坑是长期漂移。滑片和电阻体的机械接触在温度循环、湿度、氧化作用下会慢慢变化,出厂调好的点在半年一年后可能已经偏出去。对于要求几年内保持精度的工业仪表,这就是设计寿命里的隐患。
数字电位器没有滑片,内部是一串电阻串联,通过 MOS 开关把某个节点接到 wiper 端。调电阻不再靠人手和螺丝刀,而是靠 SPI、I2C 这类接口往寄存器里写一个数。这个东西对校准场景的意义是结构性的:你把“人工手艺”换成了“数字操作”,校准就可以自动化,可以重复,可以记录。
但注意,传统数字电位器在很多工程师心里的印象并不好。像 X9C 系列那种 100 步、±20% 端到端公差的器件,做音量控制可以,做校准根本不够用:步进太粗、公差太离谱、温漂也没谱。这也导致很多老工程师一听“数字电位器”就摇头。AD5293 这类器件出现之后,情况才真正改变,这也是我在这篇文章里想重点讲清楚的事。
AD5293 是一颗单通道、1024 步(10 位)、端到端电阻公差 ±1% 的数字电位器,有 20kΩ 和 50kΩ 两个版本,接口是 SPI。±1% 的端到端公差意味着电阻绝对值的离散性很小,这在数字电位器里属于很高的规格,足以进入校准回路。
PIC18F4682 这边,我最看重的是三样东西:一是 1KB 的片内数据 EEPROM,校准结果可以直接存在单片机里,掉电不丢;二是有 MSSP 模块提供 SPI 主机接口,接 AD5293 不需要额外芯片;三是它有 ECAN 模块,在工业现场组网非常方便。这套组合的经典工作方式是:出厂校准时 MCU 算出最优 wiper 码,存进 EEPROM,每次上电后 MCU 把码写回 AD5293。因为 AD5293 本身是易失性 RDAC(没有 AD5292 那种熔丝编程选项),上电默认在中值,所以“MCU 负责记住、上电负责恢复”这个分工非常自然。
2. AD5293BRUZ-20-RL7:型号里藏着哪些校准指标
2.1 把型号拆开看
很多第一次接触这颗料的人会被 AD5293BRUZ-20-RL7 这么长的料号劝退,其实拆开看非常规整:
- AD5293:基础型号,单通道 10 位数字电位器,SPI 接口;
- 20:端到端电阻 20kΩ;
- BRUZ:封装信息,RU 表示 TSSOP 封装,14 引脚,Z 表示无铅/RoHS;
- RL7:包装方式,7 英寸卷带封装,适合 SMT 贴片产线使用。
这个 20kΩ 版本是最常用的。10 位分辨率对应 1024 个位置,20kΩ 除以 1023 个间隔,每一步大概是 19.55Ω。如果你的电路需要更大的调节范围,可以选 50kΩ 版本;需要更少位数但想省成本的时候,同系列的 AD5291 是 8 位 256 步。选型上我的建议是:校准窗口先算清楚需要多少步进,再倒推电阻值,不要盲目上 50kΩ。
2.2 三个决定“能不能用于校准”的指标
数字电位器那么多,凭什么这颗能进校准回路?看三个数就明白了。
第一个是端到端电阻公差 ±1%。普通数字电位器这个数通常是 ±20% 甚至 ±30%,AD5293 做到 ±1% 意味着同样写一个码,每颗芯片的输出电阻都落在很窄的区间里。这在批量生产时意义重大:不用每台设备单独补偿电阻绝对值,大大简化了标定算法。
第二个是温度系数。手册给出的比例温漂在数个 ppm/℃ 量级,电压分压模式下的表现尤其好。也就是说温度变化时,wiper 在不同位置的输出比例基本稳定,校准点不容易随环境温度跑偏。
第三个是 wiper 导通电阻。这个值在几十欧量级,具体看手册的典型值和最大值。wiper 电阻会叠加在输出电阻上,对“可变电阻”应用有影响,对“电阻分压”应用影响很小。后面第五节我会专门算这笔账。
此外,AD5293 的 INL/DNL 都在亚 LSB 量级,单调性有保证。单调性这个特性很容易被忽略,但对校准非常关键:你写码二分查找时,如果电阻不能保证单调,找出来的解可能是个假收敛点。
2.3 和常用数字电位器的直观对比
| 器件 | 步数 | 接口 | 端到端公差 | 温漂 | 适合场合 |
|---|---|---|---|---|---|
| X9C103 | 100 | 三线增减 | ±20% | 较大 | 音量、粗调 |
| MCP4131 | 128 | SPI | ±20% | 中等 | 消费级微调 |
| AD5293 | 1024 | SPI | ±1% | 35ppm/℃ 量级 | 校准、精密微调 |
这不是说其它器件不好,而是定位不同。如果你只是给一个比较器调个参考阈值,128 步可能够用;但如果你在给一个 0.1% 精度的变送器做增益校准,前面那两位的误差直接就吃掉你一大半指标预算。
3. 硬件连接:电源、SPI 与运放缓冲一个都不能少
3.1 接线与引脚分配
PIC18F4682 的 MSSP 模块做 SPI 主机,最常用的引脚是 RC3(SCK)、RC4(SDI)、RC5(SDO),具体到你用的封装,先查数据手册确认管脚映射。CS 片选可以用任意普通 GPIO,我这里用 RD7。接线关系如下:
| AD5293 信号 | PIC18F4682 | 说明 |
|---|---|---|
| SCLK | RC3 | SPI 时钟,主机输出 |
| SDI | RC5 | 单片机 SDO 输出,接器件的 SDI |
| SDO | RC4 | 器件输出回读,接单片机 SDI |
| SYNC(CS) | RD7 | 片选,低电平有效,一帧数据期间保持低 |
| VLOGIC | MCU 电源 | 逻辑电源,和单片机 I/O 电平一致 |
| VDD/VSS | 模拟电源/地 | 按手册允许范围供电 |
| A / B / W | 模拟信号链 | 电阻两端和滑片输出 |
一个容易犯的错是把 SDO 和 SDI 接反。SPI 主机和从机之间是交叉连接的:PIC 的 SDO 必须接到 AD5293 的 SDI,PIC 的 SDI 必须接到 AD5293 的 SDO。方向搞反的典型现象是数据能发出去但回读全是 0xFF 或 0x00。
3.2 电源与去耦
AD5293 的模拟电源 VDD 和逻辑电源 VLOGIC 是分开的,这对工业应用非常友好。单片机跑 5V,就把 VLOGIC 接到 5V,I/O 电平完全匹配,不需要电平转换。如果单片机是 3.3V 系统,VLOGIC 接 3.3V 同样可以直接驱动。总之一个原则:VLOGIC 跟随 MCU 的 I/O 电源,别跟模拟电源混在一起。
去耦电容我习惯在 VDD 和 VSS 之间放 0.1µF 陶瓷电容,VLOGIC 对地再放一个 0.1µF,两个电容都尽量靠近芯片引脚。如果 VDD 是从 DC-DC 出来的,前面加一级 LC 滤波会更好,因为 AD5293 的输出最终会耦合到信号链里,开关噪声直接落在模拟地上很难看。
3.3 输出端不要舍不得那颗运放
AD5293 的 wiper 电流能力有限,手册会给出最大允许电流,超出会损坏内部开关。典型的用法是让 wiper 只带一个高阻负载,或者干脆接一个运放缓冲器再输出。我在校准电路里最常用的拓扑是:A 端接精密基准电压,B 端接地,W 端进运算放大器跟随器,输出就是一个可编程的基准电压源。
这样接有几个好处:分压模式下 wiper 电阻基本不影响精度;运放缓冲后负载随便带;而且运放输入阻抗极高,几乎不从 wiper 抽电流,器件的长期可靠性也好得多。如果非要让 wiper 直接驱动负载,一定要把负载电流限制在手册允许范围内,并预留串联保护电阻。
3.4 长线先想清楚再说
SPI 是一种短距离协议,PIC18F4682 和 AD5293 之间二十厘米以内随便连,超过半米就要开始担心边沿和串扰。工业现场如果电位器必须放在远端执行器上,我的建议不是拉长 SPI 线,而是在本机用数字隔离器(例如 ISO7741 这类四通道隔离器)把 SPI 隔离出去,隔离器电源端做好,这样既解决电平问题,也把现场的共模干扰挡在外面。更远距离的场景,直接改用 RS-485 或 CAN 这类串行总线,PIC18F4682 本身就有 ECAN,这个优势在组网时非常明显。
4. 固件:20 位 SPI 帧和 RDAC 写的那些细节
4.1 MSSP 配置成 Mode 0
先看 PIC18F4682 这边的 SPI 初始化。AD5293 的时序是典型的 SPI Mode 0:时钟空闲为低,数据在下降沿变化、上升沿被采样。PIC 的 MSSP 对应设置是 CKP=0、CKE=0。时钟分频我习惯选 FOSC/16,在 40MHz 振荡器下就是 2.5MHz 的 SCLK,既不慢到拖累标定速度,又给布线留足了容限。
void SPI1_Init(void) { TRISCbits.TRISC3 = 0; // SCK 输出 TRISCbits.TRISC5 = 0; // SDO 输出 TRISCbits.TRISC4 = 1; // SDI 输入 SSPSTAT = 0x00; // SMP=0, CKE=0, 对应 SPI Mode 0 SSPCON1 = 0x21; // SSPEN=1, CKP=0, 主模式, FOSC/16 }uint8_t SPI1_Transfer(uint8_t dat) { SSPBUF = dat; while (!PIR1bits.SSPIF) {} PIR1bits.SSPIF = 0; return SSPBUF; }如果 AD5293 的 SDO 回读在 SMP=0 时采样不稳,把 SSPSTAT 改成 0x80(SMP=1)再试,边沿裕量会更好一点。写方向基本不受这个位影响。
4.2 一帧 20 位,怎么用 8 位机发出去
AD5293 的一次 SPI 操作需要一帧 20 位数据:前面是命令字,后面是数据。写 RDAC 的命令是 00010(二进制),10 位的 wiper 码放在数据段中间,低 5 位补零。
8 位单片机一次只能移 8 位,最简单的办法是用 24 位(3 字节)发送,把 20 位有效帧左对齐,低 4 位补零。这样芯片移入寄存器后,恰好舍掉开头的补零位,留下的就是完整的 20 位帧。这个“左对齐”的细节是新手最容易踩的坑:如果直接把 16 位或 20 位数据随手塞进 3 个字节,写进去的码值会和预期差一大截。在 2.5MHz 时钟下,一次 24 位帧传输大约 10µs,标定循环里这个开销可以忽略不计。
#define AD5293_CMD_WRITE 0x02 #define AD5293_CMD_READ 0x04 void AD5293_WriteRDAC(uint16_t code) { uint8_t buf[3]; uint32_t frame; // 20 位帧:命令字在 bit19..15,RDAC 码在 bit14..5 frame = ((uint32_t)AD5293_CMD_WRITE << 15) | ((uint32_t)(code & 0x03FF) << 5); frame <<= 4; // 左对齐到 24 位,便于 3 字节发送 buf[0] = (frame >> 16) & 0xFF; buf[1] = (frame >> 8) & 0xFF; buf[2] = frame & 0xFF; DPOT_CS = 0; SPI1_Transfer(buf[0]); SPI1_Transfer(buf[1]); SPI1_Transfer(buf[2]); DPOT_CS = 1; }4.3 片选时序别图省事
CS(SYNC)必须严格要求:在整帧数据还没发送完之前不能拉高。有的工程师为了省一次 GPIO 操作,把 CS 直接接地,让片选一直有效,这在某些从机上是能工作的,但 AD5293 这类以帧为单位解析命令的器件,CS 就是帧同步信号,拉高代表“这一帧结束,开始执行”。一直片选有效会导致芯片在字节间隙错误解析命令,灵异现象特别多。所以每次都老老实实先拉低、发完 3 字节、再拉高。
如果一块板子上有多颗 AD5293,我的做法是每颗单独用 GPIO 做 CS,软件上多一个参数的事,但调试时每个器件的独立性一目了然。菊花链共享 SCLK 和 SDI 能省 IO,不过回读时序处理起来麻烦,除非 IO 实在紧张,否则不推荐。
4.4 校准值的保存与上电恢复
AD5293 没有非易失存储,掉电后 RDAC 回到默认中值。所以校准值必须由 MCU 保存。PIC18F4682 的 1KB 数据 EEPROM 刚好干这件事。我的做法是:码值占 2 字节,加上 CRC 校验字节,按固定地址存两份,读取时先验 CRC,不合法就取中值并进入重新标定流程。
void EEPROM_WriteByte(uint8_t addr, uint8_t val) { EEADR = addr; EEDATA = val; EECON1bits.EEPGD = 0; EECON1bits.WREN = 1; INTCONbits.GIE = 0; // 写 EEPROM 期间关中断 EECON2 = 0x55; // 解锁序列 EECON2 = 0xAA; EECON1bits.WR = 1; INTCONbits.GIE = 1; while (EECON1bits.WR) {} EECON1bits.WREN = 0; }上电恢复的代码要放在 main 里尽量靠前的位置,不要让模拟输出在默认中值停留太久。比如默认中值对应的增益可能是最大的,如果负载对这个增益敏感,上电瞬间就可能出问题。所以我的经验是:完成时钟配置和 GPIO 初始化后,第一件事就是回写 RDAC 到安全码值,然后再去初始化 CAN、ADC 这些外设。
5. 校准精度账本:分辨率、公差和温漂怎么算
5.1 分辨率账
20kΩ 的 AD5293 有 1024 个位置,对应 1023 个电阻间隔,每一步就是:
20kΩ ÷ 1023 ≈ 19.55Ω
如果你的应用是拿它当基准电压的分压器,5V 基准下每步电压就是 5V ÷ 1023 ≈ 4.89mV。这个分辨率对大多数校准窗口都够用。设计时先把需要的调节范围除以每一步的步进,得到的步数必须小于 1024,同时最好留出 10% 以上的余量,因为机械装配、器件老化和温度变化都会吃掉一部分调节范围。如果你想覆盖 0 到 5V 又要 1mV 的步进,4.89mV 就不够,需要配合运放增益或者换更低基准的方案,而不是硬靠这颗器件的位数去扛。
5.2 ±1% 的公差为什么反而不是主要矛盾
看到 ±1% 端到端公差,有人会觉得:那写同样的码,每颗芯片的绝对电阻都不一样,这精度也不行啊。这里要分清“绝对电阻”和“比例精度”。AD5293 的绝对值确实有 ±1% 的离散,但把它接成电阻分压器时,输出的是 A、B、W 三个端点之间的电阻比例。比例是由内部电阻网络决定的,和端到端绝对值关系不大,而 INL/DNL 在亚 LSB 量级,意味着分压比可以精确到大约 0.05% 以内。
更重要的是,校准场景里我们根本不在乎绝对精度。因为标定的本质就是闭环:给一个已知输入,量输出,调 wiper 直到输出进窗口。AD5293 要提供的是“每一步的改变是确定、单调、可重复的”,而不是“绝对电阻有多准”。出厂时每一台都闭环标过一次,这个码本身就是针对当前这颗芯片的个体解。所以在校准回路里,比绝对值更值得看的是分辨率、单调性、重复性和温漂。
5.3 温漂的工程账
温漂是校准设备最大的隐形杀手。手册给的比例温漂在数个 ppm/℃ 量级,拿它算一笔账:如果环境温度从 25℃ 变到 85℃,温差 60℃,系数按 5ppm/℃ 算,比例变化就是 60 乘以 5,等于 300ppm,也就是 0.03%。对 0.1% 精度的校准设备来说,这个漂移可以忽略;但如果是普通数字电位器常见的 100ppm/℃ 甚至更高,60℃ 温差就是 0.6% 的漂移,直接把产品指标打成负的。这就是为什么选型时温漂这条不能省。
5.4 wiper 电阻与负载效应
wiper 导通电阻是串联在输出路径上的。做分压器时,它只在 W 端负载抽取电流时产生误差,接运放缓冲后这个误差趋近于零。做可变电阻器(比如当成某个放大器的反馈电阻)时,wiper 电阻会直接叠加进阻值。解决思路有两种:一种是在设计时就把 wiper 电阻当作固定偏移,在标定环节吃掉它;另一种是用比例拓扑,让 wiper 电阻在两个式子中约掉。这两种方法我都用过,但最省心的还是分压加运放缓冲。
5.5 一段真正在产线上跑的二分标定代码
闭环标定的核心是一个二分查找。把 wiper 码当成自变量,ADC 读数为因变量,目标是在 10 位空间里找出让 ADC 读数最接近目标值的码:
uint16_t CalibrateTo(uint16_t target_adc) { uint16_t lo = 0, hi = 1023, mid; while (lo < hi) { mid = (lo + hi) >> 1; AD5293_WriteRDAC(mid); __delay_ms(1); // 等输出稳定 if (ADC_Read() < target_adc) lo = mid + 1; else hi = mid; } AD5293_WriteRDAC(lo); return lo; }从 0 到 1023 找目标,最多 10 次迭代,加上每次的稳定延时,整个标定过程几十毫秒就完成了。这就是“可编程电阻”对产线效率最直接的贡献:以前两分钟的活,现在连眼睛都不用眨。
6. 产线落地:闭环标定流程与现场问题排查
6.1 一套完整的出厂标定流程
我现在用的流程大致是这样:
- 上电,MCU 从 EEPROM 读出历史标定码并写回 AD5293;
- 进入标定模式,由产线工装给设备施加一个已知的标准激励;
- ADC 读取调理后的信号,调用二分函数收敛到目标码;
- 对收敛结果做一次重复性验证:同一个码写三次、读三次,确认 ADC 读数稳定;
- 把最终码值(带 CRC)写入 EEPROM 的两个备份区;
- 退出标定模式,设备切换到正常运行模式。
这套流程里最有价值的一点是:它把“老师傅的手感”彻底清出了产线。换人、加班、夜班都不影响质量,因为判断标准是 ADC 数据而不是人的眼睛。
6.2 现场问题排查链路
我在调试这种组合时遇到过几次比较典型的问题,按现象和根因整理成一张表:
| 现象 | 最可能的原因 | 排查方向 |
|---|---|---|
| 写入后输出完全不变 | CS 没拉低或 SPI 模式不对 | 用逻辑分析仪看四根线的时序 |
| 输出值只有预期的一半 | 数据位没有左对齐,帧错位 | 检查 24 位发送的字节组合 |
| 回读和写的值对不上 | SDO/SDI 方向接反或回读偏移没算对 | 先量 MISO 静态电平,再抓时钟沿 |
| 上电瞬间输出异常 | RDAC 还是默认中值,MCU 还没写安全码 | 把恢复代码提前到 main 开头 |
| 接近端点时非线性明显 | wiper 电阻和负载电流的影响放大 | 改分压加缓冲拓扑 |
遇到问题第一件事永远是逻辑分析仪抓时序,不要凭感觉改软件。SPI 的东西一旦时序对,剩下基本都是逻辑问题。
6.3 上电安全位置:一个容易忽略的细节
前面提到 AD5293 上电默认中值,这个中值在某些应用里不是安全值。举个例子:如果 AD5293 在控制一个电源模块的反馈分压,默认中值可能让输出电压飙到上限。我的习惯是在原理图阶段就把“安全码”定义清楚——什么状态输出电压最低、增益最小、电流最小——然后保证 MCU 在初始化最早期写入这个安全码,再开后级使能。硬件上如果条件允许,还可以在输出使能脚上加一个默认下拉电阻,让系统默认处于不输出状态,等 MCU 完成 RDAC 初始化后再授权输出。
6.4 焊接与布局提醒
AD5293BRUZ 的 TSSOP-14 封装引脚间距 0.65mm,手工焊接用拖焊法能轻松搞定,但要注意焊盘间距小,连锡后很难发现,上电前用万用表在相邻引脚之间量一遍更稳妥。布局上 A、B、W 三个引脚走线尽量短粗,毕竟它们很可能要走模拟信号;SPI 四根线远离继电器、变压器这类干扰源,必要时在 MCU 端串 33Ω 电阻做阻尼,能显著减少振铃。
还有一个细节:如果用了 SDO 回读功能但平时只在写模式下工作,建议在布局时还是把 SDO 走线留出来,防止后续要加自检测试时没有可用网络。硬件上多引一条线不花钱,后面改板子才花钱。
这套方案我前后在三个产品上落地过,最大的体会是:数字电位器进校准回路,真正的门槛不是硬件也不是代码,而是观念。不要拿它跟机械电位器比“手感”,要拿它跟“数据”比确定性。只要你接受“校准值是一个可以计算、可以存储、可以复查的数字”,整个产线的逻辑都变了——标定记录、追溯、远程维护都成了数据库里的字段,而不是老师傅脑子里的经验。最后再分享一个小习惯:样机阶段一定把 SCLK、SDI、SDO、CS 四根线全引到测试点,逻辑分析仪一挂,什么帧对齐、时序长短全部一目了然。很多在软件里查半天的“写不进去”,其实问题就出在某个字节的移位没对齐上,工具到位了,十分钟就能定位。