简介面向STM32单片机开发者压缩包内提供了基于AD7190的24位称重模块读写与TFT液晶显示完整例程。源码采用HAL库编写涵盖系统时钟配置、串口调试、3.5寸液晶初始化、重量采集均值滤波、按键清零与砝码校准等关键流程可直接移植到项目或用于学习高精度ADC与称重方案设计。包内共667个文件以C源码、头文件、启动汇编文件、链接脚本及工程配置文件为主另含Hex固件、说明文档和效果图压缩包整体7.46MB便于快速查看与二次开发。目前已有1148人学习下载适合需要参考24位ADC驱动、称重系统软硬件联调或液晶显示例程的电子工程师与嵌入式学习者。1. 从标题拆解这套 AD7190 称重例程STM32 读写 24 位 ADC 到底在做什么做称重项目的工程师十有八九在“最后几克”上翻过车传感器接好了程序却读不回稳定码值。AD7190 是工业称重里很常见的一颗 24 位 Σ-Δ ADCSTM32 通过 SPI 寄存器读写把称重传感器输出的毫伏级差分信号变成数字码值再换算成重量显示在液晶屏上。标题里这套例程 DEMO 源码价值不在某个函数库有多花哨而在于把“模拟前端 → 数字滤波 → 标定换算 → 屏显示”这条链路完整串起来了。适合手里有 STM32 开发板和 AD7190 模块、想搭高精度称重、又不想从零啃数据手册的人。下面按原理、寄存器、显示和排错的顺序把常见做法和踩过坑讲清楚。2. AD7190 为什么能做称重核心24 位 Σ-Δ 的精度机制与选型理由2.1 为什么称重场景要用 24 位 Σ-Δ而不是片内 12 位 ADCSTM32 片内 ADC 通常是 12 位对 0~3.3V 满量程分成 4096 格。称重传感器输出是毫伏级差分信号就算按灵敏度 2mV/V、激励电压 5V 来算满量程也只有 10mV 左右。把 10mV 分成 4096 格每格大约 2.4µV听起来好像够用但实际电路里随便一点电源纹波、接触热电势、PCB 漏电流都会超过这个量测回来的数据根本没有实用意义。这就是为什么称重仪表里几乎清一色用带 PGA 的高分辨率 Σ-Δ ADC而不是直接拿 MCU 片内 ADC 去采。Σ-Δ 架构的工作方式有点反直觉它不追求单次采样有多准而是用远高于目标信号带宽的采样率反复采样输入再在数字域做噪声整形和低通滤波。换句话说它用时间换精度。AD7190 内部有数字滤波器输出速率设得越低滤波越深等效有效位数越高。把数据速率从 1000Hz 降到 10Hz噪声指标会明显改善这就是为什么例程里的寄存器配置会优先照顾低速率。这里要纠正一个常见误读24 位是输出寄存器宽度不是有效精度。很多人拿到 AD7190 后发现低位码字一直在跳就觉得芯片坏了其实是把“物理分辨率”和“有效分辨率”搞混了。24 位 ADC 的名义分辨率是 2^24但受噪声影响真正可信的有效位数通常远低于 24。学习 AD7190 的过程里你会慢慢理解“速率-增益-滤波”三者怎么组合这比单纯用 STM32 的片内 ADC 切换通道扫多路传感器要深入得多——它逼你把信号链和时序管理放到台面上来。2.2 称重传感器桥路与 AD7190 的信号链匹配称重模块的“模拟前端”一般是一组应变片组成的惠斯通电桥。传感器受力时电桥失去平衡输出一个和应变近似线性的差分电压。这个电压很小常见称重传感器的灵敏度在 1~3mV/V用 5V 激励满量程差分输出也就 5~15mV。所以 AD7190 前端必须带可编程增益放大器 PGA增益从 1 到 128 可编程。接称重传感器时我习惯先把增益设到 64 或 128相当于把桥路输出放大后再进 ADC让毫伏级信号占满码值范围。但增益不是越大越好增益越高允许的差分输入范围越小超出范围会直接饱和读数固定死在满码。例程里如果配的是 128 倍增益记住它对应的满量程电压窗口大概只有几十毫伏校准前务必确认传感器空载输出电压落在这个窗口内。AD7190 有两路差分输入 AIN1± 和 AIN2±。称重接一路另一路可以留作备用或接温度传感器。用差分输入而不是单端是为了把共模噪声抵消掉传感器激励线和信号线在长距离走线时共模干扰同时进入正负输入端差分对会在芯片内部做减法这一招在工业现场特别关键。传感器屏蔽线的接地位置也影响这条路接到模拟地而不是数字地是很多初次布线的人交过学费才记住的。2.3 AD7190 的寄存器访问模型SPI 命令字节是唯一入口AD7190 的 SPI 接口是一个三角环式的寄存器模型——发命令、访问目标寄存器顺序不能乱。例程里第一步通常就是定义寄存器地址和通信位掩码#define AD7190_REG_STAT 0x00 // 状态寄存器 #define AD7190_REG_MODE 0x01 // 模式寄存器 #define AD7190_REG_CONF 0x02 // 配置寄存器 #define AD7190_REG_DATA 0x03 // 数据寄存器 #define AD7190_REG_ID 0x04 // 芯片 ID 寄存器 #define AD7190_COMM_WEN 0x80 // 写使能位最高位必须为 1 #define AD7190_COMM_READ 0x40 // 读操作标志访问任何一个寄存器之前都要先发一个通信命令字节最高位 R/W 指定读还是写最低几位是目标寄存器地址。命令字节不带 WEN 标记芯片会直接忽略。这个“先发命令再传数据”的习惯和普通 SPI 外设不太一样忘记这一层是新手最常见的报错来源。所有配置最终都要落到 MODE 和 CONF 两个 24 位寄存器上MODE 管输出速率、转换模式、校准使能CONF 管通道选择、PGA 增益、单双极性。SPI 极性的选择也在这里定AD7190 支持的模式和大多数 SPI 设备一致但我在项目里固定用时钟空闲为高、第二个边沿采样。如果同一路 SPI 上还挂了液晶屏两边 SPI 模式必须一致否则 LCD 正常时 ADC 就读不对这是一个看似低级、实际很容易被忽略的坑。2.4 为什么是 AD7190 而不是 HX711提到称重芯片很多人第一反应是 HX711。它便宜、集成度高、外围简单做电子秤确实够用。但 HX711 的串行接口更像“边沿握手”要靠 MCU 不断地发脉冲读数据时序窗口很短多路通道扩展和回读配置都不方便。AD7190 是完全的 SPI 寄存器操作可以随时读寄存器确认配置状态有芯片 ID、有状态寄存器、有完整校准命令数据速率范围也更宽。如果你做的称重设备要联网、要对不同量程做动态切换、要把校准参数存进 MCUAD7190 这个方向比 HX711 更值得投入。当然成本也明显高些项目预算敏感时HX711 依然是可用的备选但这不是一篇水文里能解决的问题。3. 用 STM32 把 AD7190 读通SPI 接线、寄存器初始化与校准流程3.1 硬件接线不要只连四根线例程源码里最先能抄的是初始化函数但初始化之前硬件上有几个固定习惯。AD7190 的电源引脚要放一组 10µF 电解电容并联 100nF 陶瓷电容去耦位置尽量靠近芯片供电脚。基准电压 REFIN 和 REFIN- 不能浮空我一般直接外接基准芯片把 2.5V 或 5V 基准送到 REFIN这样温漂可控。DOUT/RDY、SCLK、DIN、CS 四根线接 STM32 的 SPI1CS 用软件控制更灵活。AD7190 的 DOUT 兼作 RDY数据准备好之后会拉低它是开漏输出一般需要一个 10kΩ 上拉电阻。STM32 引脚AD7190 引脚说明PA5 (SPI1_SCK)SCLKSPI 时钟PA6 (SPI1_MISO)DOUT/RDY数据输出兼就绪PA7 (SPI1_MOSI)DIN数据输入PA4 (GPIO)CS片选软件控制3.3V / 5VAVDD / DVDD按模块要求供电GNDAGND / DGND单点共地很多称重模块是 5V 模拟电源STM32 是 3.3V 电平SPI 口不建议直接长时间跨压驱动。最省事的办法是用支持 5V 容忍的引脚或者加一级电平转换。我在自己板子上习惯加转换芯片因为称重模块的 DIN 和 SCLK 线可能走线较长电平裕量不够会导致偶发写寄存器失败这种问题排查起来特别耗时间。如果你习惯用 VSCode 搭 STM32 开发环境这边提醒一句CubeMX 生成的 SPI 句柄名要和例程里的 hspi1 保持一致否则移植到一半会发现 HAL 层函数对不上。3.2 复位与初始化先确认 SPI 链路再写配置AD7190 手册要求复位序列最少 40 个 SCLK 周期。例程里一般用循环翻转时钟来实现比直接发字节更可控。复位完成后 DOUT 会变高然后可以读一次 ID 寄存器确认 SPI 通信链路是通的再继续配置。顺序不能反否则后面写什么寄存器都像石沉大海。void AD7190_Reset(void) { // 手册要求CS 拉低后SCLK 连续翻转至少 40 个周期 AD7190_CS_LOW(); for (int i 0; i 40; i) { AD7190_SCK_HIGH(); AD7190_SCK_LOW(); } AD7190_CS_HIGH(); // 复位后延迟 1ms让内部状态机稳定 HAL_Delay(1); } uint8_t AD7190_ReadID(void) { uint8_t cmd AD7190_COMM_WEN | AD7190_COMM_READ | AD7190_REG_ID; uint8_t rx 0; AD7190_CS_LOW(); // 发命令字节的同时读回 ID 寄存器内容 HAL_SPI_TransmitReceive(hspi1, cmd, rx, 1, 100); AD7190_CS_HIGH(); return rx; }逻辑说明复位循环里的高低电平翻转必须发生在 CS 拉低之后。如果 CS 先拉高再翻转时钟复位信号根本送不进去。ID 寄存器读取用了 TransmitReceive 一次性完成命令字节由 MOSI 发出返回数据从 MISO 读回同一根 SPI 总线上收发同时发生这就是 SPI 全双工的特点。AD7190 的 ID 寄存器一般返回非 0xFF 的固定值具体数值以你手上这颗芯片的数据手册为准。如果读到 0xFF先别怀疑芯片检查 SPI 极性是不是配反了。3.3 读取转换结果轮询 DOUT 还是连续读数据寄存器AD7190 在读数据时CS 必须保持低电平覆盖整个传输过程。我的读取函数一般这样写uint32_t AD7190_ReadData(void) { uint8_t cmd AD7190_COMM_WEN | AD7190_COMM_READ | AD7190_REG_DATA; uint8_t buf[3] {0, 0, 0}; AD7190_CS_LOW(); // 第一步发命令字节后续三个字节接收 24 位转换结果 HAL_SPI_Transmit(hspi1, cmd, 1, 100); HAL_SPI_Receive(hspi1, buf, 3, 100); AD7190_CS_HIGH(); return ((uint32_t)buf[0] 16) | ((uint32_t)buf[1] 8) | buf[2]; }逻辑说明这里分两步做因为命令和数据是“先写后读”的关系。有些工程师习惯用 TransmitReceive 一次发 4 字节也能跑通但要注意命令字节之后紧跟的三个字节必须是接收方向不能把发送缓冲里的垃圾数据混进来。读回来的 24 位数据是偏移二进制格式这个后面换算称重值时再处理。读取时机同样重要单次转换模式下每次读之前要确认 DOUT 已经拉低表示新数据准备好了。连续转换模式下DOUT 在转换结束后拉低读走数据后自动拉高。如果你设定 10Hz 输出速率转换周期是 100ms程序在 1ms 内反复查询 DOUT会发现自己看到的永远是同一个电平这不是芯片卡死而是你比芯片快太多。3.4 校准流程内部零点和系统校准的取舍AD7190 校准命令藏在 MODE 寄存器里。做称重最少要做一次内部零刻度校准把芯片自身的失调电压消掉。更完整的是系统校准传感器空载做零点放上标准砝码做满量程。void AD7190_Calibrate(void) { // 第一遍内部零点校准传感器空载 AD7190_WriteMode(AD7190_MODE_INT_ZERO_CAL); // 等待校准完成轮询 MODE 寄存器中的校准状态标志位 while (AD7190_ReadMode() AD7190_MODE_CAL_BUSY) ; // 第二遍系统满量程校准传感器上放好标准砝码 AD7190_WriteMode(AD7190_MODE_SYS_FULL_CAL); while (AD7190_ReadMode() AD7190_MODE_CAL_BUSY) ; }逻辑说明这个流程里最关键的是“等待”而不是“发送”。校准状态位在 MODE 寄存器里校准完成会自动清零。while 循环就是在等这个位翻转。实际使用中我很少用系统满量程校准原因很现实标准砝码摆放位置稍微偏一点传感器受力就不均匀校准出来的增益系数反而不准。更稳的做法是只用内部零点校准消掉失调然后靠软件标定拟合斜率。参数说明无论是哪种校准传感器在开始校准后必须保持稳定不能有人碰秤台、不能有风压波动。我遇到过一次现场事故启动校准后有人靠了一下桌子整个零点偏了 0.5g。所以例程里如果开放了在线校准功能建议加一道“传感器稳定判定”——连续读几次码值差值小于阈值才允许开始校准。4. 带液晶显示的称重例程怎么改刷新时序、码值换算与滤波设计4.1 液晶屏和 AD7190 共用 SPI 总线的时序冲突很多 DEMO 把 AD7190 和液晶屏挂在同一个 SPI 接口上一组 SPI1ADC 占一个片选液晶占另一个片选。理论上分时操作就行实际踩坑不少。LCD 初始化或刷屏时SPI 波特率往往要拉到 10MHz 以上而 AD7190 的 SPI 时钟不能配太高否则 MISO 上的数据采样时会吃掉沿抖动的边。两边共用一个外设时最稳妥的做法不是取一个折中的波特率而是每次切换片选时重新设置 SPI1 的预分频。片选切换顺序也有讲究先拉高 ADC 的 CS等一个短延迟再拉低液晶的 CS。反过来容易出现瞬间两个设备同时被选中的毛刺。如果嫌频繁重配 SPI 影响性能我一般直接给液晶分配另一个 SPI 外设逻辑上省心很多。例程里通常只写了一种调法你拿到后如果发现液晶显示正常、称重读数偶发错位大概率就是两边的波特率和时序没有完全分开。4.2 从 24 位码值到克数偏移二进制与标定系数AD7190 在双极性模式下输出的是偏移二进制码0x800000 附近对应零点。直接把读回来的 0x000000 到 0xFFFFFF 当无符号数处理无法正确区分正负重量零点也容易被算偏。换算函数应该先把码值扩展成有符号数typedef struct { float zeroCode; // 空载时的平均码值 float slope; // 每克对应的码值变化量 } CalibParam; float AD7190_ToWeight(uint32_t rawCode, const CalibParam *cal) { // 24 位偏移二进制码转有符号第 23 位是符号位 int32_t signedCode (rawCode 0x800000) ? (int32_t)(rawCode | 0xFF000000) : (int32_t)rawCode; float delta (float)signedCode - cal-zeroCode; return delta / cal-slope; }逻辑说明rawCode 是 0~0xFFFFFF 的 24 位值。如果最高位置 1说明是负数把高 8 位补 1 扩展成 32 位有符号数否则直接符号扩展。这就是 C 语言里小技巧的活学活用。换算之后再加一个“死区判断”比如 -0.2g 到 0.2g 之间直接显示 0.0否则空载时屏幕上数字一直跳用户观感很差。标定方法先让传感器空载连续读若干次码值取平均记为 zeroCode。再放一个已知重量的砝码比如 500g记录当前码值slope 就等于 500 除以码值变化量。这个 slope 最好在满量程的 50% 附近标定因为秤的线性误差通常在两端更大。例程如果只给了一个粗略换算公式实际装机前必须用你自己的传感器重新标定否则显示出来的克数只能算参考值。4.3 滤波和刷新频率让读数不要“肉眼可见地跳”称重显示最怕的不是慢而是跳。8 次滑动平均是很稳妥的起步做法#define AD7190_FILTER_LEN 8 uint32_t AD7190_ReadAverage(void) { uint32_t sum 0; for (int i 0; i AD7190_FILTER_LEN; i) { sum AD7190_ReadData(); } return sum / AD7190_FILTER_LEN; }代码很简单背后的时序关系要清楚8 次平均会占用 8 个 AD7190 转换周期。若输出速率是 10Hz完成一次平均要 800ms刷新率大约 1Hz。静态秤台场景够用但如果是动态称重或要求读数快速稳定的场景这个响应速度就会拖后腿。不要只改滤波窗口长度而是先提高 MODE 寄存器里的数据速率再把窗口缩短。比如 50Hz 速率配合 4 次平均响应大概 80ms比原来的方案灵敏得多。液晶屏刷新同样要配合这个节奏。我一般让显示代码每 500ms 刷一次而不是每次读回码值都刷屏。有些 LCD 控制器整屏刷写会有明显闪烁这时可以做成区域刷新只更新重量数值所在的字符区域。例程里如果直接把字符串格式化后整屏 clear 再显示速度快不起来是正常的改成先计算显示位置再局部画数字会顺滑很多。5. 避坑AD7190 称重模块最常见的 5 个翻车现场与排查顺序5.1 读到的码值乱跳零点都不稳现象传感器空载连续读回来几十个数码值跳动几百上千个字显示重量忽正忽负。原因最常见不是芯片坏而是增益和速率不匹配。PGA 开到 128 倍的同时输出速率又配到几百赫兹噪声必然大。其次是基准电压纹波太大或者传感器屏蔽线没接好桥路输出像天线一样把 50Hz 工频干扰收进来。解决先把数据速率降到 4.7Hz 或 10Hz 档重新读数把传感器屏蔽层接到模拟地而不是数字地再不行把 PGA 从 128 降到 64。三步下来码值跳动一般能压到几十个字以内。如果还是压不住就去看板子的 AGND 和 DGND 是不是单点相连布线时模拟地走线太细也会有影响。5.2 校准之后称重不准放 100g 显示 98g现象空载显示 0.0放上标定砝码后显示偏差 1%~2%换一个位置放偏差还不同。原因标定时的受力点跟实际称重不一致。传感器输出对施力点很敏感同一块秤板砝码放左角和放右角应变分布不同单点标定自然有偏差。另一个常见问题是系统满量程校准时传感器还没完全“压稳”弹簧特性还在振荡增益系数本身就偏了。解决改用软件两点或三点标定。空载取零点放 50% 满量程砝码取第二点再放 90% 砝码取第三点。每个点都用滑动平均后的读数记录砝码位置统一放在秤台中心。标定范围要覆盖实际使用量程不能拿 1kg 砝码标定却拿去称 20kg 的料那超范围的部分基本没法看。5.3 读回的数据永远是 0xFFFFFF 或 0x000000现象写寄存器像没写一样读数据寄存器全 1 或全 0改配置没有任何反应。原因第一次发命令字节没带 WEN 写使能位通信寄存器直接忽略。或者 SPI 空闲极性配反MOSI 上的数据按错误的时钟沿采样寄存器收到的都是错位数据。还有一种情况是 CS 片选在 HAL 函数之间被意外拉高导致一次传输被拆成两段。解决用软件控制 CS传输全程保持低电平。检查命令字节最高位是否为 1。然后用 MISO 回读一次 ID 寄存器如果读到非预期值把 SPI 在模式 0 和模式 3 之间各试一次通常很快能定位是时序问题。排除顺序是命令字节 → CS 时序 → SPI 极性 → 硬件连线按这个顺序查比盲目改寄存器配置高效很多。5.4 液晶刷新时称重值偶发跳变现象液晶一刷屏重量瞬间跳 0.5g不刷屏时读数正常。原因片选切换的瞬间MISO 线上残留着液晶驱动的电平。AD7190 和液晶共用 SPI 总线时如果上一个设备还在释放总线下一个设备就启动传输残留位被当成数据读进去了。解决固定片选切换顺序先拉高当前设备的 CS延迟几十微秒再拉低目标设备的 CS。用同一个 SPI 外设时两边波特率和 SPI 模式必须一致。如果分开两个 SPI 外设要注意电源地必须共地否则电位差会通过 MISO 线产生灌电流。另外把 AD7190 的 DOUT 上拉电阻从 10kΩ 改成 4.7kΩ有时候也能把读边末端的尖刺压下去。5.5 冷启动第一次读数异常后面正常现象每次上电第一次读数跳几克第二次开始才正常换一片 AD7190 也一样。原因电源上电瞬间AVDD 和基准电压还没建立完成芯片内部复位也没有结束。第一次读操作落进了未就绪的时间窗读到的自然是复位后的默认值或中间态。解决上电后先延迟 100ms再执行复位序列然后读 ID 确认链路正式读数前丢弃前 2~3 个转换结果。这套流程写成固定的初始化顺序以后换任何板子都按“上电等待 → 复位 → ID 验证 → 丢前三次读数 → 正常采集”来跑。这类玄学问题不像硬件故障那么难查但最容易被忽视因为问题只在冷启动瞬间出现一次。6. 进阶让 AD7190 读数更稳的土办法和长期验证习惯前面把链路搭通之后真正决定称重体验的是滤波和标定这两件小事。我习惯的做法是三层滤波第一层在 ADC 输出速率层面把速率降到能接受的响应范围第二层在码值层面用滑动平均去掉随机噪声第三层在显示层面加死区和轻量的变化率限制防止单次异常值直接跳到屏幕上。三层各司其职比单靠一层猛滤波有效得多。标定方面我个人不太信任一次性的两点标定就交付。称重设备的误差会随温度变化应变片的温漂和 AD7190 基准的温漂会叠加。条件允许的话我会在 20°C 和 40°C 下各做一次零点记录算出一个零点温漂系数存进 Flash。使用时按当前温度补偿零点能让大称台在温差明显的环境里也维持稳定。这不是数据手册里的规定是用了几年后觉得值得做的事。每个项目我都会保留一个长期验证习惯上电后先预热传感器 10 分钟再执行一次内部零点校准然后才开始计量。计量过程中每 30 分钟记录一次空载码值观察零点漂移是否在允许范围。这套流程不花多少时间但能挡掉不少“用了三个月后突然不准”的售后问题。AD7190 是一颗值得投入时间摸透的芯片把寄存器、滤波、标定这几件事做扎实称重项目就不会再被“最后几克”卡住。希望帮到你。本文还有配套的精品资源点击获取