ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

TM4C129通过SPI控制AD5293数字电位器实现可编程电阻校准

TM4C129通过SPI控制AD5293数字电位器实现可编程电阻校准 做校准设备和工业仪表这些年我最大的一个体会就是“电位器不可信”。出厂调好的机械电位器三个月后可能漂得一塌糊涂振动、湿度、温度变化都会让阻值发生变化更别提产线上每一位工程师调出来的值都可能不一样。所以当项目里需要一颗能由MCU直接控制的精确电阻时我几乎没犹豫就选了数字电位器方案。这次要聊的就是用TI的TM4C129XKCZAD通过SPI控制ADI的AD5293BRUZ-20-RL7把它做成一个稳定、可重复、可远程校准的可编程电阻子系统。这套组合我在两个工业项目里跑了一年多从板级调试到产线量产都验证过非常适合校准源、4-20mA变送器、传感器桥路调零这类场景。1. 项目背景与选型逻辑1.1 机械电位器的痛点传统工业仪表里零点、满度、增益这些参数基本都是靠一颗多圈机械电位器来调的。这种方式在样机阶段没什么问题调起来直观但一旦进入量产和长期运行问题就来了。首先是温漂机械电位器的电阻体和滑臂接触点在温度变化时阻值会发生明显变化碳膜电位器尤甚。其次是振动和氧化滑臂接触不良会导致阻值跳变这在现场往往是间歇性故障排查起来非常痛苦。还有一个隐形问题是可追溯性一台设备校准好的参数过段时间变了到底是元器件老化还是上次校准就没调准没人说得清。用数字电位器替代机械电位器本质上就是把“手拧的物理位置”变成了“寄存器里的一组数字”。好处是显而易见的参数可以随时读出来、写进去可以做成系统自校准可以远程更新而且没有机械磨损和接触不良的问题。更重要的是每次上电后电阻值都是确定的不会因为换人操作、拧动圈数不同而产生差异。1.2 为什么选AD5293BRUZ-20-RL7这颗数字电位器市面上的数字电位器非常多普通消费级的几块钱一大把但真要拿到校准和工业场景里用能选的其实没几个。我看中的是AD5293BRUZ-20-RL7这颗料先说名字里的信息AD5293是10位单通道数字电位器1024个抽头BRUZ-20代表TSSOP-20封装20代表端到端电阻是20kΩRL7是卷带包装适合贴片量产。这颗料最吸引我的三点是精度、非易失和工业级可靠性。精度方面普通数字电位器的端到端电阻容差普遍在±20%到±30%而AD5293做到了±1%。这意味着在校准时我可以把设计值算得比较准不需要再用一个并联电阻去“凑”目标值。10位分辨率对应1024个档位对于20kΩ的型号每档理论分辨率大约是19.5Ω在校准应用里这个粒度配合外部运放可以做得很细。非易失方面AD5293支持50次永久编程。普通的数字电位器上电后默认是中位值需要MCU每次都重新写入如果MCU没启动或者程序跑挂了电阻值就不对。AD5293可以把当前RDAC位置“熔断”到芯片内部以后每次上电自动恢复这个值这正好对上了工业设备“上电即用”的需求。虽然50次听起来不多但工厂校准场景下这台设备出厂前最多烧一两次留足余量。1.3 TM4C129XKCZAD在这个系统里负责什么TM4C129XKCZAD是TI Tiva C系列里的Cortex-M4F主控120MHz主频带硬件浮点BGA封装工业级温度范围。它在这套系统里不仅仅是发一串SPI数据还承担了系统校准策略的执行任务。比如在自动校准模式下TM4C129内部的12位2MSPS ADC先把被校准链路的输出读回来然后结合目标值计算出AD5293应该写多少再通过SSI接口写进去形成一个全数字化的闭环校准流程。同时TM4C129的以太网接口可以把校准记录、电阻滑阻值、环境温度这些数据上报配合产线自动化系统做数据追溯。相比用一颗MSP430或者8位单片机硬写TM4C129最大的价值在于它的外设齐全、主频高可以在跑校准算法的同时还有充足资源去处理通信和显示任务。选型的时候也考虑过用STM32或者Kinetis但项目现有的BOM里已经有TM4C129系列在跑其他功能固件基础、调试工具链都是现成的再加一路SPI控制数字电位器几乎不增加任何维护成本所以这个组合是顺理成章的。2. 硬件接口设计与电路要点2.1 SPI连接与引脚分配AD5293的控制接口是标准SPI16位命令字一次传输完成读写操作。我用的TM4C129的SSI0模块引脚分配如下AD5293引脚TM4C129引脚功能说明SCLKPA2 (SSI0Clk)SPI时钟配置为1MHzSDIPA5 (SSI0Tx)数据输入主机发送16位命令SDOPA4 (SSI0Rx)数据输出用于回读RDACCSPA3 (GPIO输出)片选低有效软件控制这里有一个细节值得专门说。TM4C129的SSI0硬件FSS信号也映射在PA3但我没有用它做片选而是把PA3配成普通GPIO用软件拉低拉高。原因有二一是AD5293的CS时序要求每帧之间必须释放片选下一帧再重新拉低硬件FSS在连续传输时可能会产生多余的脉冲不够干净二是后面要讲到的回读操作需要CS保持低电平跨两个16位帧硬件FSS做不到这种自由控制。所以用软件GPIO控制CS时序完全自己说了算。接线方面要注意区分数字口和模拟口。SCLK、SDI、SDO这些数字信号走线尽量短不要和模拟输出线平行走太长距离。AD5293的A、B、W三个端子属于模拟信号通路在板子上要让它们避开SPI时钟线否则SCLK的谐波会耦合到分压输出上影响校准精度。2.2 电源、去耦与电平匹配AD5293的电源引脚有VDD、VSS和VLOGIC三个很多人第一次用会忽略VLOGIC。VDD和VSS决定了A、B、W三个模拟端子可以摆动的电压范围VLOGIC则是数字接口的工作电压专门用来匹配MCU电平的。我这次项目里VDD接5VVSS接地模拟信号范围为0到5VVLOGIC接3.3V和TM4C129的GPIO电平完全匹配。如果你的系统需要处理双极性信号比如±10V的工业信号那VSS要接负电源轨VDD接正电源轨这种情况下更要仔细看数据手册的绝对最大额定值别超过VDD到VSS的极限电压。去耦电容不能省。VDD和VSS之间我放了4.7μF钽电容和0.1μF陶瓷电容并联VLOGIC对地放0.1μF全部尽量靠近芯片引脚。为什么这么强调去耦因为AD5293在电位器分压模式下W端的输出电压本质上是从VDD上直接分出来的如果VDD上带了纹波这些纹波会原封不动出现在输出上校准精度直接就崩了。所以在要求高精度的场合VDD建议用低噪声LDO供电别直接拿开关电源的输出怼上去。还有一个容易踩的坑AD5293的B端和W端关断后并不是完全开路内部的ESD保护和寄生二极管会形成一个不确定的通路。在调试时如果发现后级电路有微小的漏电流先查是不是这个原因不要一上来就怀疑运放。2.3 电位器模式与可变电阻模式怎么接AD5293可以接成两种基本形式对应两种不同的需求。第一种是电位器模式也就是分压器模式。A端接基准电压或者信号高端B端接地或者信号低端W端输出分压后的电压。这种情况下输出的是电压比例VW (VA - VB) × code / 1023 VB。分压模式的优点是输出精度高因为它只取决于内部电阻网络的比例基本不受绝对电阻值温漂影响。校准电压源、增益微调都用这种接法。第二种是可变电阻模式也叫VR模式。这时候只使用两个端子比如把W和B短接作为一个端子A作为另一个端子那么A到WB之间的电阻就是RAB × (1024 - code) / 1024再加上大约几十欧姆的游标电阻。这种模式适合做传感器的桥臂电阻微调、滤波器的截止频率调节这类需要“可变电阻”而不是“可变电压”的场合。但要注意数字电位器的端子电流承受能力有限不能当功率电位器用IR流过大时精度会下降甚至损坏芯片。标题里说的“可编程电阻”两种模式都覆盖。我后面的应用场景里有分压模式也有VR模式接法不同代码逻辑是一样的。3. 固件实现与关键寄存器操作3.1 SSI初始化和SPI模式选择TM4C129的SSI初始化用TivaWare驱动库很方便重点是把数据帧配置成16位。AD5293一个命令字正好是16位帧宽匹配之后一次SSIDataPut就是一条完整命令不需要在软件里拼字节或者移位非常干净。#include stdint.h #include inc/tm4c129xnczad.h #include driverlib/sysctl.h #include driverlib/gpio.h #include driverlib/ssi.h #define AD5293_CS_PORT GPIO_PORTA_BASE #define AD5293_CS_PIN GPIO_PIN_3 void AD5293_SSI_Init(void) { // 使能SSI0和GPIOA外设 SysCtlPeripheralEnable(SYSCTL_PERIPH_SSI0); SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA); while(!SysCtlPeripheralReady(SYSCTL_PERIPH_SSI0)); // 配置PA2为SSI0ClkPA4为SSI0RxPA5为SSI0Tx GPIOPinConfigure(GPIO_PA2_SSI0CLK); GPIOPinConfigure(GPIO_PA4_SSI0RX); GPIOPinConfigure(GPIO_PA5_SSI0TX); GPIOPinTypeSSI(GPIO_PORTA_BASE, GPIO_PIN_2 | GPIO_PIN_4 | GPIO_PIN_5); // PA3配置为普通GPIO输出用作AD5293的CS GPIOPinTypeGPIOOutput(AD5293_CS_PORT, AD5293_CS_PIN); GPIOPinWrite(AD5293_CS_PORT, AD5293_CS_PIN, AD5293_CS_PIN); // CS默认高 // SPI Mode 01MHz16位数据帧 SSIConfigSetExpClk(SSI0_BASE, SysCtlClockGet(), 1000000, SSI_FRF_MOTO_MODE_0, SSI_MODE_MASTER, 16, 0); SSIEnable(SSI0_BASE); }关于SPI模式这里要多说一句。AD5293的时序是SCLK空闲为低数据在上升沿被采样所以SPI Mode 0CPOL0CPHA0是最直接的选择。我用逻辑分析仪验证过Mode 0和Mode 3写命令都能正常工作因为帧时间很短建立保持容忍度足够。但调试时最好固定用Mode 0因为数据手册的时序图就是按这个模式画的回读波形也更容易对着看。时钟速率我选择1MHz。AD5293的SCLK理论上可以跑很高但实际工业板卡走线长、环境噪声大1MHz足够完成所有校准操作而且低速传输时信号边沿更干净抗干扰能力更强。回读操作也是1MHz稳定优先。3.2 AD5293指令编码与读写RDACAD5293的16位命令字结构是最高3位是命令后13位是数据其中只有低10位是RDAC位置有效值。命令编码是这一节的核心必须对着数据手册的寄存器映射表来理解命令编码命令含义操作说明000NOP空操作可用于回读时补时钟001Restore 50-Time Setting恢复50次编程保存的阻值010Read RDAC读取当前RDAC寄存器内容100Write RDAC将10位数据写入RDAC寄存器10150-Time Program将当前RDAC值熔断保存110Reset复位到中位值或编程值实际的16位帧里命令编码要左移13位再和10位数据相或。比如写RDAC命令0x4左移13位就是0x8000加上要写入的0-1023的代码值。下面是我工程里直接用的写函数void AD5293_WriteCode(uint16_t code) { uint16_t frame; uint32_t dummy; if(code 1023) code 1023; frame (0x4 13) | (code 0x03FF); // 0x8000 | code GPIOPinWrite(AD5293_CS_PORT, AD5293_CS_PIN, 0); // CS拉低 SSIDataPut(SSI0_BASE, frame); while(SSIBusy(SSI0_BASE)); // 等待发送完成 GPIOPinWrite(AD5293_CS_PORT, AD5293_CS_PIN, AD5293_CS_PIN); // CS拉高 }代码里那个while(SSIBusy(SSI0_BASE))是必须的。SSIDataPut只保证数据进了发送FIFO不代表移位寄存器已经把所有位发完。如果在这里不等SSIBusy就拉高CS最后一个时钟沿可能还没完全落到芯片上命令就不会被锁存表现为“写进去的寄存器和量到的电阻对不上”。这个坑我调试时花了不少时间才定位到现在写出来提醒各位。回读RDAC是AD5293一个非常实用的功能。校准系统里经常需要确认当前电阻值到底是多少总不能每次都拿万用表去量。回读的时序比较特殊命令本身占一个16位帧但SDO上的数据是在第二个16位帧的时钟周期里送出来的。所以完整操作是CS拉低先发Read RDAC命令帧紧接着再发一个NOP帧提供时钟在第二个帧之后从接收FIFO里取出数据。用软件GPIO控制CS的好处在这里就体现出来了我可以让CS一直保持低电平跨越两个帧。uint16_t AD5293_ReadCode(void) { uint32_t rx1, rx2; GPIOPinWrite(AD5293_CS_PORT, AD5293_CS_PIN, 0); // 第一帧发送Read RDAC命令 SSIDataPut(SSI0_BASE, 0x2 13); while(SSIBusy(SSI0_BASE)); // 第二帧NOP提供时钟SDO同步移出RDAC内容 SSIDataPut(SSI0_BASE, 0x0); while(SSIBusy(SSI0_BASE)); SSIDataGet(SSI0_BASE, rx1); // 第一帧的无效数据 SSIDataGet(SSI0_BASE, rx2); // 第二帧的RDAC数据 GPIOPinWrite(AD5293_CS_PORT, AD5293_CS_PIN, AD5293_CS_PIN); return (uint16_t)(rx2 0x03FF); }注意我调用了两次SSIDataGet第一次读出来的是第一帧期间SDO上的垃圾数据第二次才是真正的RDAC值。如果你只调用一次拿到的数据大概率是零或者上一次的残余值怎么读都不对。这是回读最容易出错的地方。3.3 回读校验与50次烧录的完整流程写操作和读操作都验证通过后就可以做50次烧录了。这一步的作用是把当前RDAC寄存器里的值固化到芯片内部以后每次上电都自动恢复到这个值不需要MCU干预。烧录命令是0x5左移13位也就是0xA000。注意数据位应该保持为0因为熔断操作针对的是当前RDAC值命令字里的数据位没有意义。void AD5293_BurnFuse(void) { GPIOPinWrite(AD5293_CS_PORT, AD5293_CS_PIN, 0); SSIDataPut(SSI0_BASE, 0x5 13); while(SSIBusy(SSI0_BASE)); GPIOPinWrite(AD5293_CS_PORT, AD5293_CS_PIN, AD5293_CS_PIN); // 熔断过程需要时间这里延时约200ms SysCtlDelay(SysCtlClockGet() / 3 / 5); }烧录完成后我强烈建议做两步验证。第一步是回读RDAC确认写进去的值没有因为烧录动作被冲掉。第二步是彻底断电重新上电然后回读一次确认上电自动恢复的值就是烧录值。如果第二步常温下没问题再放到高低温箱里验证一下因为熔丝状态在温度极端情况下偶发不稳定也是可能的虽然概率很低但工业设备不怕一万就怕万一。50次烧录不是50个位置随便写它是芯片生命周期内总共只能触发50次熔断操作每一次都不可逆。设计软件时一定要加保护比如需要长按按键或者发送特定解锁序列才能进入烧录流程防止产线误触发把配额耗尽。3.4 上电防冲击的软件斜坡设计还有一个工程细节值得提就是上电冲击。AD5293如果没有烧录过上电默认RDAC值是中位值也就是0x200。比如我做一个0到5V的校准源上电瞬间W端会直接输出大约2.5V如果后级设备正在初始化这个电压可能会造成误动作。解决思路有两种。一种是在硬件上加模拟开关系统完全初始化后再把W端信号接通到后级。另一种更省成本就是在MCU启动代码里立刻执行AD5293写操作把电阻设置为安全值。但MCU启动到执行SPI写入之间有一个时间窗口几百毫秒到一秒不等这个窗口里输出还是中位值。真正严格的做法是用软件斜坡。不要上电后直接跳到目标值而是让MCU每隔一小段时间步进一次比如每次步进1到4个LSB目标值是校准源需要逐步爬升到最终值。这样即使后级电路没有模拟开关保护也不会出现电压突变。AD5293的写操作本身很快1024个档位全部走完也就几十毫秒斜坡时间只受你设定的步进延时影响可控性很好。4. 三个典型应用场景拆解4.1 可编程校准电压源这个场景最直观也是我第一个落地的应用。用一颗5V基准源给AD5293的A端供电B端接地W端输出分压再接一个运放电压跟随器做缓冲就得到了一个0到5V可编程精密电压源。计算一下指标5V基准除以1023档每档电压步进大约是4.887mV。这个精度对于大多数仪表校准需求已经够用。如果觉得粗可以在W端和运放之间再加一级电阻分压比如分压10倍步进就变成0.4887mV当然输出范围也相应缩小到0到0.5V。为什么一定要加运放跟随因为AD5293的W端等效阻抗是随抽头位置变化的直接拿W端去带负载负载电流会在内部电阻网络上产生压降导致实际输出电压偏离理论值。加一个高输入阻抗的运放跟随器就相当于把W端信号隔离出来了后级负载电流由运放提供不再影响分压精度。在这里TM4C129的12位ADC可以顺便做一个自校准把跟随器输出采回来和目标值比对误差超过几个毫伏就微调AD5293的code形成闭环。这个闭环逻辑跑熟了之后校准源完全可以实现全自动标定不再需要人工介入。4.2 4-20mA变送器的量程微调4-20mA电流环变送器是工业现场最常见的信号传输方式它通常有一个零点校准和一个满度校准。以前都是用两颗电位器慢慢拧现在完全可以用一颗AD5293来完成。我的做法是把AD5293接在V/I转换电路的前级作为DAC输出的精密分压器。DAC给出一个粗略的满量程电压AD5293在很小的范围内做细调比如±5%微调。这样做的优势是DAC的分辨率可以不用太高粗调交给DAC细调交给AD5293两者配合就能覆盖整个量程而且校准参数保存在固件里可以按工单自动切换。实际调试时要注意4-20mA环路对温度漂移非常敏感。AD5293的分压比温漂很小但游标导通电阻的温漂会叠加进去。如果变送器的工作环境温度变化很大建议每10℃做一次温漂补偿把不同温度下的校准参数存成一个查找表运行时根据温度传感器读数切换参数。这个功能配合TM4C129的硬件浮点跑起来毫无压力。4.3 传感器电桥的自动调零很多压力传感器、称重传感器都是惠斯通电桥结构。电桥的自然失调电压会导致零点偏移传统做法是电桥上串联一个可调电阻来平衡。这个可调电阻用机械电位器有两个问题一是调好之后受温度影响会漂二是批量生产中每个传感器的失调不同机械调零没法自动化。用AD5293做电桥调零思路是这样的把数字电位器接成可变电阻模式跨接在电桥的一个桥臂上。上电后TM4C129通过ADC读取电桥的差分输出然后步进调整AD5293的code让差分输出逼近零。整个过程就是一个数字PID或者逐次逼近算法全自动完成。每个传感器模组的失调参数可以单独存储换一个传感器只需要重新执行一次调零流程产线效率提升非常明显。这种应用里AD5293的50次烧录功能反而用得不多因为每次上电MCU都会执行调零流程不是特别依赖掉电保存。但如果系统要求上电瞬间电桥就处于平衡状态避免后级放大电路饱和那就可以把调好的值烧录进去一举两得。5. 调试经验与常见问题排查实录5.1 SPI通信“写不进”的排查流程实际调试中遇到最多的就是“程序跑起来电阻值就是不变”。这种问题90%是时序和片选的问题不是芯片坏了。我自己的排查顺序是固定的各位可以参考。第一步先用万用表量AD5293的VDD、VSS、VLOGIC三个电源引脚确认电压正常。VLOGIC忘了接这种低级错误我在样板调试时真的犯过芯片完全没有反应。第二步用示波器或逻辑分析仪抓CS引脚和SCLK引脚看写函数执行时CS有没有拉低再拉高SCLK有没有16个完整时钟脉冲。第三步看SDI上的数据波形。如果没抓到帧或者波形毛刺严重重点查SPI模式是不是Mode 0以及SCLK极性有没有配反。这里有一个实用技巧如果手头没有逻辑分析仪可以用万用表电阻挡直接量W端和B端之间的电阻然后写不同的code值观察阻值有没有变化。比如20kΩ的AD5293写code 0时W-B间电阻接近0写code 1023时接近20kΩ。这个方法能最快验证通信通路是不是通的不依赖任何调试工具。5.2 电阻精度与温漂实测要点测量AD5293的精度时不要用普通万用表的两线制电阻挡因为表笔本身的接触电阻和导线电阻会进入测量结果造成几欧姆到几百欧姆的误差。要做严格测量用四线制开尔文接法或者直接测电压比。比如在A端加一个精密电压B端接地量W端电压用电压比例来解算电阻值这样可以避免接触电阻的影响。我实测过的经验是室温下AD5293的分压比精度非常不错误差主要来自基准电压本身和游标导通电阻。游标电阻大约几十欧姆在20kΩ量程的端点上影响比较明显但到中间位置基本可以忽略。设计电路时如果端点线性度要求很高可以在校准算法里做一个两点或者三点的线性度修正表。温漂测试建议在恒温箱里做。我测试时发现AD5293在分压模式下输出比例温漂明显好于可变电阻模式因为分压比只取决于内部电阻网络的匹配程度。如果你的应用对温漂特别敏感尽量采用分压模式而不要用VR模式去当绝对电阻用。5.3 烧录失败的处理烧录失败通常表现为烧录后断电重上电RDAC值没有恢复成烧录时的值还是中位值。这种问题我遇到过的原因有两个。第一个是烧录过程中看门狗复位了。工业固件里看门狗到处都有烧录命令执行到一半MCU被复位CS引脚立刻变成高阻态烧录过程中断熔丝状态不确定。所以烧录前务必关闭看门狗或者在整个烧录流程里喂狗确保CS在整个熔断期间一直被固件控制。第二个原因是烧录后没有等待足够长的时间就断电了。熔断操作不是瞬间完成的芯片内部需要一个过程把熔丝真正烧断。如果写完命令立即断电可能实际没有熔断成功。我习惯的做法是烧录命令发出后至少等200ms再断电有时候高温老化验证前还会再多等一会儿。如果50次配额用完了还想改出厂值那就没办法了只能靠MCU每次上电后重新写入。所以在软件流程设计上尽量把烧录操作放到产线最后一步确认所有校准参数都稳定了再烧避免重复烧录浪费配额。5.4 常见问题速查表现象可能原因处理办法写入后电阻不变化CS没有正确锁存命令写完后等SSIBusy再拉高CS回读数据全零或乱码回读时只发了一帧少补NOP时钟发完Read命令后补发0x0000取第二个返回值上电瞬间输出跳到中位值未烧录或MCU写入太晚用50次烧录固化值或优化启动代码尽快写输出端接负载后电压下降W端等效阻抗引起分压误差加运放电压跟随器隔离烧录后掉电不保存烧录过程中断或等待时间不足烧录前关看门狗命令后延时200ms以上高温下阻值偏移明显使用的是可变电阻模式绝对电阻温漂改用分压模式或做温度补偿表这套方案在我手里跑过完整的温度循环和老化测试总体表现稳定。最让我满意的是产线反馈以前校准一台设备要人工拧好几个电位器现在程序一跑几十秒自动完成数据还能上传存档。如果你也正在为设备里的机械电位器头疼不妨按这个路子试一下AD5293加TM4C129的组合把校准这件事真正交还给固件。最后分享一个小经验第一次打板时把AD5293的A、B、W三个引脚都引出测试点调试阶段你会发现这个决定能省下不少麻烦。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进