
简介面向MSP430与LMP90100传感器AFE接口开发的官方代码库由TI半导体提供适合嵌入式开发者、单片机工程师以及传感器采集系统设计人员尤其利于高精度模拟前端方案的快速原型验证与应用评估。压缩包共37个文件其中包含23个C语言源文件、12个头文件以及2份PDF应用文档源码部分覆盖了LMP90100芯片的多种操作方式文档部分则详细说明不同接口模式、寄存器设置以及数据读取流程两类文件相互补充可从代码和原理两个层面辅助开发。整个资源压缩后仅5.79MB轻量易用目前已获275人学习。代码库内置demo-application-examples示例工程并配有完整的说明文档读者可参考其中的寄存器配置、校准处理与数据转换逻辑快速完成驱动移植和原型验证整体上是一套兼顾工程实践与原理讲解的传感器AFE开发参考非常适用于工业采集、仪器仪表等场景。 做高精度测量设备的朋友应该都有体会传感器信号本身质量很好但后端的ADC和MCU配合起来总是问题不断不是时序对不上就是噪声大得根本没法用。我前两年做一个多通道温度采集模块选型时盯上了TI的LMP90100这是一颗24位Delta-Sigma ADC自带PGA可编程增益放大器、模拟多路复用器、激励电流源特别适合热电偶、RTD这类小信号传感器。主控端选了MSP430系列低功耗、稳定、开发工具链成熟。为了避免每次新项目都重新造轮子我把MSP430与LMP90100之间的接口逻辑整理成了一套可复用的代码库也就是项目名里的TI_MSP430 Interface to LMP90100 Code Library。这篇就围绕这个代码库把硬件连接、初始化流程、底层驱动、数据读取、校准调试这些环节完整过一遍给同样在做高精度采集的朋友一个可以直接抄作业的参考。1. 项目定位为什么是MSP430和LMP90100这对组合1.1 LMP90100到底是什么芯片LMP90100是TI推出的24位Delta-Sigma ADC但不是一颗“裸ADC”那么简单。它在内部集成了模拟多路复用器、PGA可编程增益放大器、两个激励电流源、片上温度传感器、后台校准逻辑和错误检测模块。通俗点说传感器小信号进来之后MUX选择通道PGA先把信号按需要放大再交给24位ADC做转换最后通过SPI接口把结果读走。这颗芯片最有吸引力的点是它的真连续后台校准能力。很多高精度ADC必须定期暂停转换去做校准而LMP90100可以在正常转换的同时持续修正偏移和增益误差。对于工业温度变送器、精密数据采集卡这类需要长期运行的设备来说这个特性可以显著减少维护成本。我用它接热电偶和PT100热电阻测温场景里信号幅度极小往往只有几毫伏到几十毫伏没有PGA的支持根本没法做。加上它内部有冷端补偿用的温度传感器整个前端电路可以省掉不少外部器件。1.2 为什么主控选MSP430MSP430是TI的老牌低功耗MCU选它的原因很简单低功耗性能出色SPI接口成熟稳定开发环境方便。在电池供电或两线制变送器这类功耗敏感的应用里MSP430可以被配置到微安级电流和LMP90100的低功耗特性正好匹配。我自己用的两颗芯片是MSP430F5529和MSP430G2553。F5529用于开发验证阶段因为资源和外设多方便调SPI、调中断、挂调试器等到产品定型想要降成本、降功耗再换到G2553这类小封装芯片。代码库在编写时也刻意避免了依赖特定型号的寄存器换MCU只需要改底层几个宏定义。1.3 这套代码库里有什么代码库并不复杂但分层很明确底层SPI读写模块封装MSP430的eUSCI硬件SPILMP90100寄存器映射、命令字定义和关键配置宏初始化函数包含复位、配置采样率、配置PGA增益、使能后台校准单通道和多通道数据读取接口若干调试辅助函数比如读取设备ID、读取校准状态。这样分层的直接好处是如果以后把LMP90100换到STM32或者其它MCU上只需要重写最底层的SPI接口驱动层和应用层完全不用动。我把这个叫“一次写好到处搬砖”。2. 硬件连接与SPI通信核心特性2.1 最小硬件系统怎么连LMP90100和MSP430之间只需要4根线就能跑起来SCLK、DIN、DOUT、CS。另外我强烈建议把DRDY引脚接到MSP430的一个GPIO上用来判断数据是否准备好。虽然也可以读状态寄存器轮询但用硬件引脚判断更直接也更省主控时间。连接关系如下MSP430引脚LMP90100引脚功能说明P3.0/UCB0CLKSCLKSPI时钟P3.1/UCB0SIMODIN主出从入命令和数据输入P3.2/UCB0SOMIDOUT主入从出转换数据输出P3.3/GPIOCS片选低电平有效P3.4/GPIODRDY数据就绪转换完成后拉低GPIORESET复位引脚低电平有效供电方面要特别注意LMP90100的模拟电源AVDD和数字电源DVDD建议分开处理。我最初参考手册直接把两个引脚并在一起接3.3V结果模拟部分纹波偏大数据最低位一直在跳动。后来在AVDD入口加了一个10欧电阻配合10uF和0.1uF电容做RC滤波再从DVDD单独走线噪声立刻降了一个数量级。2.2 SPI的时钟极性和相位是第一个坑LMP90100的SPI协议比较规矩但和不少MCU上电默认的SPI模式并不一致。如果你直接拿默认配置去读寄存器大概率读到全0xFF或者随机值。坦白说LMP90100具体工作在Mode 0还是Mode 1资料里有明确时序图但不同版本的资料拿到的图可能让人犯迷糊。我的建议是不要猜直接按数据手册的时序图对着调。MSP430的eUSCI模块里UCCKPL和UCCKPH位就是用来配置时钟极性和相位的花十分钟试一下两个组合用逻辑分析仪抓SCLK和DIN上的波形对比手册就能确认。实测下来我最终用的是CPOL0、CPHA1配置SCLK空闲时为低数据在第二个边沿被采样。但这个只能作为参考不能作为标准答案不同批次芯片还是要以实际抓波形为准。SPI速率也建议保守一点。LMP90100的SCLK理论上可以跑到好几兆但在实际PCB上如果走线比较长或者环境有干扰高速时钟容易出错。我把SCLK配置在500kHz左右稳定性和速度的平衡最好一次24位数据加上命令字节也就几十微秒完全不影响整体吞吐率。2.3 寄存器读写协议和命令字格式LMP90100的寄存器读写通过SPI命令字节实现。每次通信开始CS拉低主控先发送一个8位命令字节然后根据命令是读还是写继续发送数据字节或接收转换结果。命令字节的高位区分读写方向低几位包含寄存器地址和突发传输的字节数信息。具体每一位的定义建议直接查数据手册的Command Byte说明这里只说要点写寄存器时命令字节后面跟着要写入的数据读寄存器时命令字节后面需要主控继续发送哑字节来产生SCLK同时从DOUT引脚读出数据。这与很多MCU外设习惯不同很多人第一次接触会忘记“读操作也要发数据”这件事导致读回来的数据永远是0。SPI是全双工总线从机只有在接收到时钟时才会把数据移出所以读操作那部分哑字节是必不可少的。3. 代码库的分层设计与关键实现3.1 为什么不能一个C文件搞定所有事早期做嵌入式驱动我也习惯把寄存器操作直接写在主循环里。那时候代码量小看起来倒是直观可一旦要加功能、换平台或者调试问题就非常痛苦。后来写这个代码库我做了明确的三层划分。最底层是硬件抽象层负责和MSP430的SPI外设打交道只暴露两个接口spi_write_read和gpio_cs_control。中间层是设备驱动层把LMP90100的寄存器读写、初始化、通道配置、数据读取封装成lmp90100_xxx系列函数。最上层是应用层只处理业务逻辑比如“每秒钟读四个通道的温度值并上报”完全不关心SPI时序。这样做的好处我在把代码从F5529移植到G2553时体会特别深。底层只需要改SPI外设的基地址、中断标志位的判断方式驱动层和应用层一行代码都没动。如果从一开始就把这些粘在一起移植时至少要花一整天逐个排查。3.2 底层SPI接口的实现MSP430的eUSCI模块做SPI主模式比较简单。我的底层实现如下void spi_write_read(uint8_t *tx_buf, uint8_t *rx_buf, uint16_t len) { uint16_t i; for (i 0; i len; i) { while (!(UCB0IFG UCTXIFG)); // 等待发送缓冲为空 UCB0TXBUF tx_buf[i]; // 发送数据同时启动时钟 while (!(UCB0IFG UCRXIFG)); // 等待接收完成 rx_buf[i] UCB0RXBUF; // 读取从机返回数据 } }这个函数虽然短但涵盖了SPI通讯的全部逻辑。重点是理解第二行和第三行的配合发送一个字节的同时主机也在产生时钟从机就把一个字节的数据移出来所以每次循环都做了一次全双工交换。读写寄存器时tx_buf和rx_buf可以指向不同数组也可以复用同一个缓冲但要注意读操作时发送的是哑字节内容任意。3.3 设备驱动层的寄存器读写封装有了底层接口驱动层的寄存器读写就清晰了static void lmp90100_cs_low(void) { P3OUT ~BIT3; } static void lmp90100_cs_high(void) { P3OUT | BIT3; } void lmp90100_write_reg(uint8_t reg_addr, uint8_t data) { uint8_t tx[2], rx[2]; tx[0] LMP90100_CMD_WRITE | reg_addr; tx[1] data; lmp90100_cs_low(); spi_write_read(tx, rx, 2); lmp90100_cs_high(); } uint8_t lmp90100_read_reg(uint8_t reg_addr) { uint8_t tx[2] {0, 0}, rx[2] {0, 0}; tx[0] LMP90100_CMD_READ | reg_addr; lmp90100_cs_low(); spi_write_read(tx, rx, 2); lmp90100_cs_high(); return rx[1]; }读操作返回rx[1]而不是rx[0]是因为第一个字节的命令在移入的同时从机移出的还是上一个操作的残余数据第二个字节才是真正的寄存器内容。这个细节代码注释里一定要写清楚否则过三个月自己回来看都可能犯迷糊。3.4 初始化流程按步骤来才能一次跑通LMP90100的初始化顺序很重要不能随便打乱。我的初始化代码分为五个阶段void lmp90100_init(void) { lmp90100_reset(); // 1. 硬件复位 delay_ms(10); // 等待上电稳定 lmp90100_write_reg(LMP90100_SYS_CFG, 0x20); // 2. 系统配置内部时钟、后台校准 lmp90100_write_reg(LMP90100_SAMPLE_RATE, 0x03); // 3. 采样率配置 // 4. 通道A配置AIN0与AIN1差分输入PGA增益32 lmp90100_write_reg(LMP90100_REG_A, 0x18); // 5. 等待后台校准完成 while (lmp90100_read_status() ! LMP90100_STATUS_READY); }这里第2步写入的0x20只是示例值SYS_CFG寄存器里每一位的含义在数据手册里写得很清楚。第4步的0x18同样只是示意REG_A寄存器的高位控制输入选择低位控制PGA增益具体数值要结合自己的传感器类型决定。我吃过一次亏一开始初始化时跳过了等待校准完成的步骤上电后立刻读数据结果前几十个转换值明显偏离真实值。后来加上等待状态判断问题消失。一定要记住LMP90100上电之后需要一点时间完成内部校准这时候读出来的数据是不可以用的。3.5 数据读取24位有符号数的处理LMP90100的转换结果存放在DATA_OUT寄存器里宽度是24位数据格式是有符号数。读取代码如下int32_t lmp90100_read_channel_data(uint8_t channel) { uint8_t tx[4] {0, 0, 0, 0}, rx[4] {0, 0, 0, 0}; uint8_t reg LMP90100_DATA_OUT_A channel * 3; int32_t raw; tx[0] LMP90100_CMD_READ | reg; lmp90100_cs_low(); spi_write_read(tx, rx, 4); lmp90100_cs_high(); raw ((int32_t)rx[1] 16) | ((int32_t)rx[2] 8) | rx[3]; if (raw 0x800000) { raw | 0xFF000000; // 符号扩展把24位转成32位有符号数 } return raw; }这里每读完一个通道的数据DRDY引脚会被自动清除。实际做多通道循环采集时我通常是等待DRDY拉低然后读取当前活跃通道的数据再切换通道。读取函数末尾的符号扩展是个很容易忽略的步骤。如果没有做这一步负半轴的信号会被当成大正数数据曲线就会在零点附近出现一个巨大的跳变。4. 精度调试与校准心得4.1 上电节奏和DRDY信号里的学问LMP90100对电源上电时序有一定要求。如果电源还没稳定就开始配置寄存器芯片可能进入一个异常状态表现为寄存器写不进去或者读出来全是0xFF。解决方法是上电后先等50毫秒以上再拉低RESET引脚做一次硬件复位然后延时再开始配置。DRDY引脚是数据就绪指示器每次新的转换数据准备好之后它会输出一个低脉冲。这个引脚可以由MSP430的外部中断捕获也可以轮询GPIO。我建议用GPIO查询方式因为LMP90100的转换速率本身不高轮询的开销完全可以接受用外部中断反而要处理中断服务函数和主循环之间的竞争问题。4.2 校准不是可选项是必做项LMP90100支持后台校准这个功能不仅仅是为了消除芯片出厂时的偏移误差更重要的是消除系统级的误差。比如外部放大电路、传感器引线电阻、PCB漏电流引入的偏移都可以通过校准来修正。第一次上电我会强制触发一次系统校准。方法是在系统配置寄存器里使能后台校准位然后等待状态寄存器返回就绪标志。校准完成之后再把校准使能位关闭这样芯片就成为一台干净的24位转换器。如果用了PGA放大校准的收益特别明显。我的实测经验是PGA增益设置为32倍时未校准的偏移误差大概会吃掉2到3个最低有效位校准之后基本可以忽略不计。所以省掉校准等于主动把分辨率降了一档不划算。4.3 噪声实测和滤波建议以我的一块四层板为例采样率配置在几十SPS时输入短接参考电压的情况下24位数据码值跳动范围大约在正负十来个LSB折算成有效位约19到20位。如果把增益调高有效位会略微下降这是Delta-Sigma ADC的正常现象。为了进一步提升稳定性我在应用层加了一点软件滤波。对于温度这种变化缓慢的信号用简单的中值平均滤波就够了连续采五次去掉最大值和最小值剩下三个取平均。这个滤波计算量很小MSP430跑起来毫无压力但效果非常明显数据码值跳动可以从十几个LSB降到三四个LSB。另外有一个PCB布局上的建议SCLK和DIN这两根SPI线不要贴着模拟输入走线。我调试时遇到过一次SPI时钟串扰到模拟输入的情况把两根线拉开间距之后噪声就小了很多。这种问题靠软件很难完全消除必须从布局上解决。5. 常见问题速查与排错实录5.1 SPI读回全0xFF或全0x00这是最常遇到的问题。直接说结论90%的情况出在三个方面SPI模式不对、片选逻辑不对、从机没有正常上电。排查方法如下现象可能原因检查方向读回全0xFFSPI模式不对DOUT一直输出高用逻辑分析仪抓SCLK、DIN、DOUT波形对比手册时序读回全0x00CS没有正常拉低检查GPIO配置CS引脚是否有上拉电阻寄存器偶发错误时钟太快或信号线太长降低SCLK频率缩短SPI走线长度所有操作无响应LMP90100未上电或被复位拉死测量AVDD和DVDD电压检查RESET引脚电平5.2 数据跳变严重、有效位明显不够排除接线问题后大概率是前端或参考电压引入的噪声。我遇到过两种情况第一种是AVDD滤波没有做好电源纹波直接进入ADC第二种是参考电压引脚上的去耦电容距离芯片太远等效电感太大。解决方法是先看数据码值跳动的分布。如果跳变是高斯分布说明是随机噪声优先查电源和参考电压如果跳变是固定的几个值来回切换大概率是数字干扰查SPI线的布局和屏蔽。5.3 多通道切换后数据异常LMP90100内部的多路复用器切换后后端滤波器需要一段时间稳定。不能切完通道立刻读数据否则读到的是上一次转换的残余值。在驱动层处理这个问题的标准做法是切换通道后连续丢弃两次转换结果第三次的数据才认为是有效的。我在代码库里专门保留了一个通道稳定计数器目的就是为了防止这类问题。5.4 寄存器写不进去的隐蔽原因这类问题最隐蔽通常是CS拉高时机不对。LMP90100要求在最后一个SCLK边沿之后CS必须继续保持低电平一段时间再拉高结束整个通信周期。如果CS拉高太快或者刚好在SCLK还在翻转的时候拉高寄存器写入就会悄悄失败而且不报任何错误。排查时不要只盯着数据手册的时序图最好用逻辑分析仪同时抓CS和SCLK看看CS拉高的那一刻SCLK是不是已经完全安静下来。把CS拉高的动作推迟一点问题基本就解决了。最后说点个人体会。这套代码库从开始写到现在其实没有特别高深的技术真正花时间的地方都在细节上SPI模式要对着时序图确认、上电顺序要多留一点延时、24位符号扩展不能省、多通道切换要记得丢弃前两次数据。把这些问题逐个踩平之后MSP430和LMP90100的组合在精度和稳定性上都对得起这两颗芯片的定位。如果你们正在做多路高精度采集建议第一步先把单通道跑通确认SPI和寄存器读写全部正确再扩展多通道和后台校准。基础打牢了后面的应用层开发会顺手很多。本文还有配套的精品资源点击获取