很多人拿到STM32开发板之后,做的第一个项目往往都是流水灯、蜂鸣器、按键点灯。学完这些之后很容易产生一种错觉:单片机好像就这么回事。直到你真正碰上一个带模拟信号采集的项目,才会意识到“能出数”和“数据可信”完全是两码事。智能体重秤就是这样一个很适合用来捅破窗户纸的项目,而我也把这个项目的代码、原理图和仿真工程完整整理成了开源资料放出来。
这篇内容就把我的整套设计逻辑重新捋一遍:从传感器选型、信号链路搭建、STM32固件实现,到原理图细节、仿真调试验证、最终标定方法,全部摊开讲。目标很明确——让你拿这套方案直接落地,并且在落地过程中能看懂每个环节“为什么这么做”。它非常适合有一定C语言基础、想进阶做带模拟采集项目的开发者,也适合课程设计和毕业设计的同学直接参考。硬件上我用的核心是STM32F103C8T6,搭配HX711称重ADC和电阻应变式称重传感器,显示用0.96寸OLED,整条链路紧凑、成本低、资料成熟。
1. 项目全景:拆解智能体重秤要解决的三个核心问题
1.1 功能边界与系统架构
先把话说清楚:我要做的不是一台“秤重玩具”,而是一台能连续多次重复称量、读数稳定的智能体重秤。基础功能集定在下面几条:
- 人站上秤,经过约3~5秒稳定判断后,OLED显示体重值;
- 支持“去皮/置零”,长按按键可以把当前载荷作为零点扣除;
- 支持实时数据输出,通过串口把原始ADC码值、滤波后码值、最终重量一起打印出来,方便调试和做上位机;
- 支持低功耗待机,一段时间无操作后自动休眠,按键唤醒。
这套功能看起来不复杂,但是当我把整个系统拆开之后,发现真正要解决的不是“显示一个数”,而是三个连环问题。
第一个问题,微弱信号的采集。电阻应变式称重传感器在受力时,输出的差分电压通常只有毫伏级别。举个例子:一个满量程10kg、灵敏度1mV/V的传感器,用3.3V供电,10kg满载对应的输出电压才3.3mV左右。你拿STM32片内12位ADC去采,就算参考电压稳定,这个量级的信号对应的量化台阶也太大,再加板上噪声,基本没法用。所以必须在ADC前面加一级高增益、高分辨率的模拟前端——这也是HX711这类称重专用ADC存在的意义。
第二个问题,数据的时域稳定性。人站上秤的瞬间,传感器会经历一个机械震荡过程,ADC读数的波动可能达到几百甚至上千个码值。如果软件不对这些原始数据做处理,直接换算成重量,你会看到显示数字在跳。要解决这个问题,滤波算法和稳定判定逻辑必不可少。
第三个问题,标定和量纲转换。HX711输给STM32的是一个24位整型码值,这个码值是无量纲的,它和重量之间存在一个近似线性的比例关系,但这个比例关系会受传感器灵敏度、供电电压、增益、机械安装方式等因素影响。你不能指望规格书上那个mV/V参数直接换算成代码里的比例系数,必须通过实际标定才能得到。而且标定不是做一次就完事,温漂、机械形变、零点偏移都会慢慢改变映射关系。
整个系统的信号流可以概括为:称重传感器 → 差分毫伏电压 → HX711放大与24位AD转换 → STM32读取码值 → 数字滤波与重量换算 → OLED显示/串口输出。每一级都有具体职责,下面几章逐一拆开讲。
1.2 物料清单与选型理由
这个项目核心物料如下:
| 模块 | 参数/型号 | 数量 | 作用 |
|---|---|---|---|
| MCU | STM32F103C8T6 | 1 | 主控,负责信号读取、滤波、状态机、显示 |
| 称重ADC | HX711 | 1 | 仪表放大器+24位Σ-Δ ADC |
| 称重传感器 | 悬臂梁式,量程10kg,灵敏度约1mV/V | 1 | 将重量转换为差分电压 |
| 显示屏 | 0.96英寸 I2C OLED,SSD1306驱动 | 1 | 显示重量和状态 |
| 按键 | 轻触开关 | 1 | 去皮/标定切换 |
| 电源 | 3.7V锂电池 + 3.3V LDO稳压 | 1 | 整机供电 |
| 外壳/结构件 | 亚克力或小钢板 | 1套 | 固定传感器并承压 |
选型上有几个地方我做过对比。STM32F103C8T6在这个项目里属于典型的“用不完”配置:Cortex-M3内核,64KB Flash,20KB RAM,跑一个带滤波和显示逻辑的固件绰绰有余。同时它的价格、引脚资源、资料丰富度都是教科书级别的,对初学者非常友好。你也完全可以用F407或者G0系列来做,但在这个方案里C8T6是性价比很高的选择。
HX711虽然是个很老的芯片,但在称重领域一直没有被取代。它内部集成了低噪声可编程增益放大器,增益可选128或64,还带24位Σ-Δ ADC,差分输入模式天然适合接全桥传感器。最关键是它功耗极低、价格便宜,一个中断引脚加一个时钟引脚就能和单片机通信,不太占IO资源。
2. 硬件链路:电阻应变传感器如何变成数字信号
2.1 惠斯通电桥与应变片原理
要理解整个项目,最先要理解的就是称重传感器本身。电阻应变式传感器的核心是一小片金属箔应变片,贴在弹性体的受力位置。当弹性体受到外力发生形变时,应变片的电阻值会发生极其微小的变化,变化量往往只有千分之几到万分之几。
问题来了:单片机不读电阻,只读电压。怎么把一个微小的电阻变化变成一个可测量的电压变化?答案是惠斯通电桥。
惠斯通电桥由四个电阻组成菱形结构:上面两个电阻串联,下面两个也串联,然后跨接在激励电源之间,中间抽出两个电压节点作为差分输出。初始状态下电桥平衡,两个节点电压相等,差分输出为零。当某一臂的电阻因为应变而改变时,平衡被打破,两个节点之间出现电压差。
实际称重传感器内部就是把这个原理做成了完整的测量电桥:四片应变片贴在弹性梁的上下表面,受力时两片被拉伸、两片被压缩,分别接入电桥的四臂。这样差分输出信号加倍,同时对温度有一定程度的共模抑制。最终引出四根线:激励正(E+)、激励负(E-)、信号正(S+)、信号负(S-)。
这里有一个非常重要但容易被忽略的点:市面上传感器的线色定义并不统一。红色、黑色、白色、绿色这四种线在不同品牌、不同批次之间代表的意义可能完全不同。我拿到新传感器第一件事不是急着接线,而是用万用表电阻档测四根线之间的阻值关系。同一组应变片的两个引脚之间的电阻值才是阻值匹配的桥臂,桥臂阻值一般在350Ω或1kΩ左右。千万不能盲信网上搜来的色序表,这一步不确认,后面所有标定工作都是白做。
2.2 HX711的关键参数与增益配置
HX711的整体逻辑可以理解为:把传感器送来的毫伏级差分电压放大几十到一百多倍,再用一个24位ADC做数字化。它有两路差分输入通道:通道A支持增益128或64,通道B固定增益32。称重项目里我们接的是通道A,用增益128。
为什么选增益128而不是64?核心原因是分辨率。传感器满量程输出才几毫伏,如果增益不够大,ADC的有效使用范围太小,大量量化位数被浪费。用增益128时,满量程信号对应的码值空间大约能占到24位ADC的两成多,再结合数字滤波,分辨出5~10g的变化基本没有压力。当然增益也不是越大越好,增益128会把输入端的噪声也一并放大,所以后面滤波这步不能省。
HX711的通信协议算是这个项目里第一个“坑”。它不像I2C或SPI那样有标准地址和帧格式,而是非常简单粗暴的一根时钟线加一根数据线,用脉冲计数来读出数据。具体过程是:DOUT引脚为低电平时表示一次AD转换完成,此时主控给PD_SCK引脚发24个时钟脉冲,每来一个脉冲DOUT就输出一位数据,先高位后低位。第25个脉冲用于配置下一次转换的通道和增益。
读出来的24位数据是二进制补码格式,对应双极性差分输入。如果最高位为1,说明当前是负值,需要符号扩展成32位有符号整数。具体实现在下一章给代码。
还有一个细节值得说:HX711有两种输出速率,10SPS和80SPS,可以通过PD_SCK引脚下拉或上拉来选择。体重秤这种静态称重场景,10SPS完全够用,而且10SPS模式内部滤波效果更好、噪声更低。我实测下来同样是空载状态,80SPS模式下的原始码值抖动明显比10SPS大。如果你做的不是高速动态称重,建议老老实实用10SPS。
2.3 信号完整性:屏蔽、滤波与接地
很多刚开始做的人容易忽略一条原则:传感器出来的模拟信号是整个系统里最娇贵的部分,它在被HX711放大和数字化之前,一路都要“小心伺候”。
第一,传感器到HX711的连线要尽量短,最好用屏蔽线或者双绞线。差分传输本身有一定抗共模干扰能力,但长距离平行走线仍然可能把电源噪声、数字开关噪声耦合进来。在我这个布局里,传感器和主控板之间线长控制在20cm以内,而且S+和S-绞在一起走,与电源线保持距离。
第二,HX711的模拟电源和数字电源最好分开处理。有些简化原理图直接把AVDD和DVDD并在一起接3.3V,运行起来常常出现码值周期性波动。我在AVDD入口增加了一级LC滤波,数字部分和模拟部分的地也拆开,最后单点汇聚到电源地。这个改动之后,空载码值的抖动幅度肉眼可见地缩小了。
第三,传感器激励电源的稳定度直接影响输出。如果用电平转换或直接电池供电,电池电压从4.2V一路降到3.6V,传感器桥路的输出电压也会跟着变化,读出来的码值自然漂。所以我整机供电采用了“电池+LDO”的方案,先把电压稳到3.3V,再分别给数字部分和模拟部分供电。至少保证在这个量级的设计下,电源波动不会成为误差主导项。
3. STM32固件实现:从ADC原始值到可靠体重读数
3.1 HX711时序协议与驱动函数
固件开发我基于标准库/LL库的方式实现,原因是这套代码移植方便、依赖少。HX711的驱动核心就是两个GPIO引脚的时序控制,下面是简化过的读取函数,关键路径都保留了。
// 返回HX711一次24位转换结果,失败返回0 int32_t HX711_ReadRaw(void) { int32_t val = 0; uint16_t timeout = 30000; // 等待DOUT拉低,表示转换完成 while (HAL_GPIO_ReadPin(HX711_DOUT_GPIO_Port, HX711_DOUT_Pin) && timeout--) { delay_us(1); } if (timeout == 0) { return 0; // 超时,传感器或接线异常 } // 读取24位数据,高位在前 for (int i = 0; i < 24; i++) { HAL_GPIO_WritePin(HX711_SCK_GPIO_Port, HX711_SCK_Pin, GPIO_PIN_SET); delay_us(1); val <<= 1; if (HAL_GPIO_ReadPin(HX711_DOUT_GPIO_Port, HX711_DOUT_Pin)) { val |= 1; } HAL_GPIO_WritePin(HX711_SCK_GPIO_Port, HX711_SCK_Pin, GPIO_PIN_RESET); delay_us(1); } // 第25个脉冲:配置下一次转换通道A,增益128 HAL_GPIO_WritePin(HX711_SCK_GPIO_Port, HX711_SCK_Pin, GPIO_PIN_SET); delay_us(1); HAL_GPIO_WritePin(HX711_SCK_GPIO_Port, HX711_SCK_Pin, GPIO_PIN_RESET); delay_us(1); // 24位有符号数扩展 if (val & 0x00800000) { val |= 0xFF000000; } return val; }这段代码有几个地方值得展开。第一,超时机制必须加。如果传感器断线、DOUT一直拉高,程序会卡死在等待DOUT拉低的那个循环里,表现为整机“死机”。我最初没加超时,结果有一次传感器插头松了,OLED停在一半不刷新,排查了半天才发现是死循环。第二,时钟脉冲的时序不必掐得太死,HX711对PD_SCK脉冲宽度要求很宽裕,delay_us(1)已经足够,但一定要保证高低电平都稳定后再采样DOUT,否则边缘处的电平不确定会导致读错位。
3.2 数字滤波与重量换算
HX711从原始码值到最终读数,中间必须经过滤波。我用的是移动平均加一阶低通组合:先在一个固定长度的窗口内做移动平均,消除高频随机噪声;再对平均值做一次低通平滑,进一步压低脉冲式干扰。
移动平均的窗口长度我取了16个采样点。10SPS速率下,16个点对应1.6秒数据,人站上去之后大概需要两三秒窗口才能完全充满,加上稳定判定逻辑,总时间大约3~5秒出数,这个体验基本可以接受。窗口再长一点读数更稳,但出数太慢;再短一点又压不住抖动。这个值是实测折中的结果。
滤波和换算的关键代码如下:
#define FILTER_LEN 16 int32_t raw_buf[FILTER_LEN]; int32_t raw_sum = 0; uint8_t filter_index = 0; int32_t offset_zero = 800000; // 空载零点码值,标定时更新 float scale_k = 200000.0f; // 码值/千克换算系数,标定时更新 float Weight_GetKg(void) { int32_t raw = HX711_ReadRaw(); if (raw == 0) return -1.0f; // 读取异常 // 移动平均 raw_sum -= raw_buf[filter_index]; raw_sum += raw; raw_buf[filter_index] = raw; filter_index = (filter_index + 1) % FILTER_LEN; int32_t avg = raw_sum / FILTER_LEN; float task_weight = (float)(avg - offset_zero) / scale_k; // 过滤轻微负值,避免显示“-0.03kg” if (task_weight < 0.005f) { task_weight = 0.0f; } return task_weight; }换算公式本质就是初中一次函数:重量 = (当前码值 - 零点码值)/ 斜率。这里的offset_zero就是空载时滤波后的码值,scale_k就是“每千克对应多少个码值”。这两个参数不是固定的,需要标定,具体标定流程放到第6章讲。
不过移动平均也有它的毛病:面对突然出现的尖峰干扰,比如有人在秤旁边踩了一下地面,传感器会弹一个很大的瞬时值,移动平均会被这个尖峰带动几拍,显示数字短暂跳一下。为了压制这种问题,我在移动平均之前加了一级“采样合理性检查”:如果当前原始值与窗口平均值偏差超过一个阈值,就把这次采样丢弃,说白了就是一个简易的脉冲滤除。这个在代码里实现非常便宜,但对体验提升很明显。
3.3 去皮、稳定判定与低功耗状态
按键处理在体重秤里不是简单的点一下,而需要区分短按和长按。我定义:短按为“去皮”,长按进入“标定模式”。去皮的本质是把当前载荷的滤波码值临时记录为新的零点。比如你放了一个托盘上去,短按之后系统把托盘重量视为零,之后再站人,显示的就是纯人体重量。
去皮代码很简单:
void Tare_Process(void) { offset_zero = GetFilteredRaw(); weight_display = 0.0f; }真正需要仔细设计的是稳定判定。人的身体站在秤上不可能完全静止,呼吸、重心微调都会让读数在小范围内浮动。我的方案是每200ms对滤波后的重量做一次比较,如果连续3次的变化量都不超过0.05kg,就认为称重结果稳定,OLED上的“称重中...”状态切换为“完成”,同时锁定显示值。之后除非重新进入称重状态,否则不再刷新这个数字,避免用户看到小数点后最后一位一直跳。
低功耗这块我用的是一个简单状态机:上电默认状态为待机,按键或传感器检测到重量变化后进入称重状态;称重完成并保持显示30秒后自动回到待机。待机时STM32进入停止模式,HX711通过PD_SCK拉高进入省电模式,OLED关闭,整机功耗可以压到几十微安级别。唤醒方式是用按键外部中断或者传感器码值变化检测。这个设计在电池供电场景下很重要——如果只靠USB供电当然无所谓,但一旦换成锂电池,没做低功耗的秤会非常费电。
4. 原理图反推:每个元件在该位置存在的理由
4.1 电源通路与去耦设计
原理图拿到手之后,我建议你先看电源,而不是先看单片机引脚。很多初学者习惯先数引脚和OLED的接线,但电源设计如果崩了,后面所有模块都跟着遭殃。
这套系统供电分两路:电池或USB 5V进来,先经过一个3.3V LDO稳压,然后分成DVDD和AVDD两路。DVDD供STM32、OLED和HX711数字部分;AVDD单独串一个电阻加电容的滤波网络,再供HX711的模拟电源和传感器激励。
为什么要把模拟和数字电源分开?因为单片机引脚翻转、OLED刷新时会产生高频脉冲电流,这些电流在电源线上会引起毫伏级波动。如果传感器激励和HX711模拟部分共用这一条受污染的电源线,信号链路的参考基准就不干净了。分离电源是在原理图阶段成本最低、收益最高的一步。
去耦电容的布置也要讲究位置而不是数量。STM32的每个电源引脚旁边放一个100nF陶瓷电容,电容尽量靠近引脚;VDD和VDDA之间再放一个4.7μF钽电容,应对低频纹波。HX711的AVDD引脚下放一个1μF再串一个几十欧的电阻,和后续电容构成低通滤波。这些都不是“别人原理图上有所以我也放”的仪式性操作,而是各有针对性的噪声抑制。
4.2 复位、启动配置和接口电路
STM32F103C8T6的启动配置在原理图里很容易被忽略,但搞错一次就够你折腾半天。NRST引脚接一个100nF电容到地,实现上电复位。BOOT0引脚要接10kΩ下拉电阻到地,确保从Flash启动。我之前见过一个很典型的错误:BOOT0悬空,结果芯片上电后有时候能跑程序,有时候跑不了,查到最后是引脚电平不确定导致启动模式不稳定。
OLED用的是I2C接口,两个信号线SCL和SDA都必须接上拉电阻,典型值4.7kΩ到3.3V。I2C是开漏输出,没有上拉电阻的话,通信时总线拉不上去,OLED完全无响应。如果你用的OLED模块板上已经有上拉电阻,那外部可以不再加,但很多模块是不带或只带一组上拉的,这个要确认清楚。
按键电路同样需要注意。轻触开关一端接MCU输入引脚,另一端接地,引脚内部用下拉电阻,按下时为高电平。更稳的做法是外部再加一个10kΩ下拉,防止引脚悬空时受到干扰误触发。我在按键引脚上还并联了一个100nF电容,消除按键机械抖动,这样固件里可以少做一层消抖,按键响应也更干净。
4.3 模拟前端走线与传感器接口
原理图能画对,不代表板子就能用。模拟信号部分的关键在走线,而原理图上最能体现这种设计意图的,是传感器接口和HX711之间的连接方式。
我用了四线制接线端子把传感器引到板上,E+和E-走较宽的电源线,S+和S-走差分对。线长不超过10cm,并且S+和S-尽量避免平行长距离紧贴数字信号线。板子的底层地平面保持完整,不在模拟区域开大面积的洞。这些都是在PCB阶段才能落地的细节,但你在画原理图时就要把“模拟区”和“数字区”的边界大致划分好,等画板的时候才不会手忙脚乱。
HX711的AGND和数字地最终是要共地的,但走线方式有讲究:模拟部分的地和数字部分的地先各自短粗地走回电源地引脚,形成单点汇聚,而不是让模拟器件的地线绕一大圈穿过数字区域再回到电源负极。地环路是模拟信号的大敌,这个原则几乎适用于所有带ADC的项目。
5. 仿真调试路线:在没有实物的环境中验证固件逻辑
5.1 仿真工程结构与虚拟外设
很多人画完原理图、写完代码之后,直接就打板焊接了。但我强烈建议在这之前先跑一遍仿真。这么做不是要替代实物验证,而是为了在一个可控、可观察、可重复的环境里把固件逻辑先跑通。如果仿真阶段就能发现状态机卡死、滤波参数爆掉、按键逻辑错乱,后面调试实物会省一大半时间。
仿真环境我搭的结构和真实硬件一一对应:STM32主控、HX711等效模型、虚拟OLED、虚拟串口终端。其中HX711模型可以用一个简单的受控电压源加ADC模型替代,或者干脆用一个可调电位器分压网络来模拟传感器差分输出。重点在于:验证固件读取码值、滤波、换算这条主线逻辑在代码层面没有硬伤。
要说清楚一点:仿真和实物不可能完全一致,你也不可能指望仿真软件把HX711内部的Σ-Δ调制细节、温漂、噪声都真实复现。但仿真能帮你把“逻辑正确性”和“硬件正确性”分开验证。逻辑错误必须在仿真里先清干净,否则拿到实板上,你很难区分是软件问题还是硬件问题。
5.2 模拟负载变化与波形验证
我在仿真里最常用的手段,是把HX711的差分输入电压从一个已知值切换到另一个已知值,模拟“空载到加载”的过程。比如设置一个可调电阻分压器,通过改变分压比让输入差分电压从0mV变为0.4mV。按照HX711的计算公式,0.4mV差分输入、增益128、参考电压3.3V,理论输出码值大约在十几万这个量级。仿真里串口打印出来的码值如果和理论计算值对得上,说明驱动读取逻辑没问题。
仿真还能做一件实物很难做的事:把时间轴压缩。你可以在仿真环境里把滤波窗口跑完看输出,也可以直接修改窗长参数观察过渡过程,而不用像实物那样每次改参数都要重新站上秤去等。我用这种方式测了移动平均窗长8、16、32三种情况下的稳定时间和抗噪能力,仿真初步筛选后,再上实物确认,效率高很多。
虚拟示波器在这个阶段也能派上用场。我把滤波前后的数据分别通过DAC或串口虚拟引脚输出,用示波器通道对比观察,能很直观地看到滤波算法对原始波动的抑制程度。模拟故障注入也很方便:把输入电压瞬间拉高再拉回正常值,观察固件能否通过采样合理性检查把尖峰拦截下来。这类极端场景在实物测试里很难稳定复现,但在仿真里点一下鼠标就行。
5.3 仿真能测什么、不能测什么
仿真帮我解决的问题很多,但它解决不了所有问题。我整理了一个简单的对照表,方便你自己评估到什么程度就可以停止仿真、转实物调试:
| 验证项 | 仿真环境 | 实物环境 |
|---|---|---|
| 固件状态机和按键逻辑 | 可完整验证 | 可验证,但需要人工操作 |
| HX711读时序 | 取决于模型精度,一般只能验证大概 | 可直接用示波器抓取验证 |
| 数字滤波效果 | 可验证算法逻辑,噪声模型不真实 | 真实噪声下的最终效果 |
| 模拟噪声、温漂、机械回差 | 几乎无法复现 | 真实存在,必须实测 |
| 低功耗电流 | 只能验证是否进入休眠状态 | 必须用电流表实测 |
| 传感器机械结构与偏心负载 | 完全不能替代 | 必须通过实际承重测试 |
从这个表可以看出,仿真和实物是互补关系。仿真负责把“板上逻辑确认没问题”,实物负责把“真实物理世界里的坑踩掉”。很多翻车的项目,恰恰是跳过了仿真,把所有变量混在一起,最后出了问题根本定位不到是哪一层。所以我的建议是:无论如何都先跑一遍仿真,即便模型粗糙,也比直接烧录进真板强。
6. 标定与误差控制:真正让数据可信的实测工作
6.1 标定流程与参数更新
当固件、硬件都跑通之后,最后一道工序就是标定。这一步直接决定了你的秤准不准。标定的目标是把两个参数填准:offset_zero和scale_k。
我的标定流程分三步:
- 空载标定:系统上电,保证秤面没有任何东西,等待10秒让读数完全稳定,按下标定按键,固件把当前滤波码值记录为offset_zero。
- 加载标定:在秤面上放一个已知重量的标准砝码,比如5kg或者至少1kg,再次进入标定模式,输入这个砝码的实际重量值,固件根据当前码值和offset_zero计算出scale_k。
- 校验:加载多个不同重量的砝码,记录显示值和实际值的误差。如果全程线性很好,一对参数就够了;如果出现两头偏差,就要做多点拟合。
某一次我用的10kg悬臂梁传感器,实测空载稳定码值大约在79万附近,放上1kg砝码后码值增加约19.6万个码值,也就是说scale_k大约等于196000 counts/kg。这说明传感器在实际电路里产生的每千克信号约占满量程码值空间的2.3%,比理论估算值略低一点,原因包括机械结构预压、供电电压精度、LDO输出偏差等。如果完全不做标定,直接拿公式推导的系数去算,最终重量误差可能超过5%,根本没法用。
6.2 最小二乘拟合与线性修正
标定一次并不算完,因为我发现大部分称重传感器在低载荷段和满量程段之间会存在一定非线性。也就是说,你拿1kg砝码标出来的scale_k,在7kg的地方可能会偏出二三十克。如果项目对精度要求不高,这可以接受;如果追求更好,就得上多点标定加线性修正。
我的做法是:分别用0、1kg、3kg、5kg、8kg、10kg六组砝码记录码值,然后在电脑上做一次最小二乘拟合。拟合模型是重量 = a × raw + b,也可以用二次项模型重量 = a0 + a1 × raw + a2 × raw² 来匹配非线性。最后把拟合系数写回固件。实测六点二次拟合之后,全量程误差从最大60多克压到了不到15克。
当然这里要说明,精确标定是在“传感器本身质量合格”的前提下才有意义。如果传感器结构松动、安装面不平,或者机械平台上载荷传递路径不对,你的标定做得再漂亮也白搭。标定只能修正电学映射关系,修正不了机械错误。
6.3 实测中最容易遇到的一批“坑”
这个项目做下来,我积累了若干条实测经验,每条都是真金白银换来的。
第一条,传感器接线错误导致读数乱跳。我在前面强调过,线色定义不统一。有一次我把S+和S-接反了,结果系统还能工作,但读数变成负数,而且按压秤面时数字往反方向走。这其实是个好现象,至少说明链路基本通了,只要调换两线即可。但如果两根线被接到激励端,差分信号就完全没了,读出来只剩电源噪声。
第二条,HX711的DOUT引脚悬空导致上电卡死。第一次上电时,如果DOUT引脚没有被拉高或拉低到确定电平,MCU的等待循环可能直接超时。后来我在固件里加了超时退出,同时把DOUT引脚下拉电阻加上,上电稳定了很多。
第三条,OLED和HX711同时抢总线导致显示闪烁。这类现象往往是供电问题而不是逻辑问题。OLED刷新瞬间电流很大,如果在电池供电且电源布线细的场景下,OLED亮度变化会引起AVDD轻微波动,进而影响HX711的码值。我的处理办法是OLED单独走一路电源,并且在OLED电源引脚旁边放大容量电容。改完以后,码值随显示刷新而波动的现象基本消失。
第四条,机身结构对零点的影响。秤的外壳螺丝拧得过紧或者传感器固定面受力不均,空载码值会发生缓慢漂移。这个问题软件没什么好办法,只能在机械装配时注意:传感器弹性梁的固定端要完全贴平,受力端不能干涉。拧螺丝时不要单边用力,要对称逐步拧紧。
还有一条经验关于“负重量显示”。在空载状态下,由于信号噪声和零点漂移,计算出来的重量偶尔会是一个类似-0.03kg的负值。如果不处理,OLED上就会出现负数字,用户看着非常奇怪。我用的办法是在重量计算出来后判断:如果计算结果小于0.005kg,直接显示0.00kg。这个看似微不足道的细节,对产品观感影响其实很大。
6.4 我最后想强调的一个点
如果只让我留一条忠告,那就是别急着加蓝牙、加App、加云端。智能体重秤的第一价值是“称得准”,第二价值才是“称得智能”。一个数据飘忽的秤,就算手机界面做得再漂亮,最终也不会有人愿意每天站上去。先把传感器信号链路调理干净,把滤波和标定做扎实,让同一砝码连续十次读数都稳定在10g以内,然后再考虑怎么把数据传出去。这个顺序颠倒的人,往往到最后要回来重做前端电路。我的开源包里代码、原理图和仿真工程都齐了,剩下的就是你自己上手把这套链路真正跑通一遍——这个过程里踩过的坑,比任何教程都值钱。