尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

基于STM32与HX711的智能计价电子秤设计与标定实战

基于STM32与HX711的智能计价电子秤设计与标定实战 1. 项目概述与设计思路拆解1.1 这个项目到底在做什么智能计价电子秤放在嵌入式学习路径里算是一个非常典型的“集合型”项目。它把传感器采集、模拟信号调理、ADC转换、数据处理、显示交互、掉电存储这几个核心环节全部串了起来而且每个环节都有真实物理世界的约束不是那种点几个LED灯就能交差的demo。用STM32来做控制器选型上完全够用既不会因为资源太紧张导致代码写起来抠抠搜搜也不会因为芯片太复杂让新手直接劝退。我见过不少新手一上来就想着搞触摸屏、搞WiFi、搞云端结果项目拖了三个月还在调通信协议。电子秤这种项目核心需求其实非常明确称得准、算得对、显示清楚、数据不丢。听起来简单但真正把精度做到满足实际商用场景还是要踩不少坑的。特别是标定环节很多人觉得传感器买回来接上线就能用了实际上HX711模块的增益、参考电压、滤波算法、零点漂移每一个都直接影响最终读数。这个项目适合谁来看一是准备做嵌入式相关毕设的学生这套东西从硬件到软件都有完整闭环二是想入门传感器信号处理的开发者HX711加上应变片桥路的组合能把仪表放大器、差分采集、数字滤波这些概念全部落地三是电子秤这类产品的行业从业者想了解具体实现细节的也能在这篇文章里找到一些可复用的思路。1.2 系统方案选型与关键取舍整个系统的架构可以分为四层物理层是称重平台和电阻应变式传感器信号层是HX711前端放大和24位ADC处理层是STM32的数据滤波和标定计算交互层是LCD显示和矩阵键盘输入。很多新手会问STM32内部明明有ADC为什么还要外挂HX711这个问题非常关键。STM32F103C8T6内部的ADC是12位的单量程满量程下最小分辨率也就是满量程的1/4096。如果直接采集应变桥的输出信号满量程输出电压通常只有10mV到20mV这个微弱信号不经过高倍数放大根本喂不进MCU的ADC。HX711的作用就是把信号放大128倍或者64倍然后通过24位ADC采样算下来有效分辨率远高于直接用MCU内部ADC。但要注意HX711的24位是Δ-Σ型ADC的过采样结果实际有效位数大概在19到21位之间和参考电压有关。以最常见的HX711模块来说参考电压一般来自稳压后的AVDD通常取4.3V左右那么每个LSB对应的输入电压约是4.3V除以2的24次方再除以128算出来大约0.2uV级别。虽然实际噪声水平达不到理论极限但这个灵敏度对于常见的几十公斤量程的称重传感器来说完全是够用的。2. 硬件设计与电路搭建要点2.1 传感器与前端模块选型细节称重传感器这里用的最常见的就是铝制悬臂梁结构的电阻应变式传感器。量程5kg、10kg、20kg都有家庭电子秤一般用5kg到10kg就足够工业计价秤一般用到30kg。我这里选的是10kg量程的传感器灵敏度在2.0mV/V左右。什么意思呢就是说在满量程10kg时在10V激励电压下输出大约20mV的信号。实际HX711模块一般提供3.3V到5V的激励电压如果用5V激励满量程输出大约10mV。传感器的四根线红色是激励正E黑色是激励负E-白色是信号负S-绿色是信号正S。很多国产传感器颜色可能有差异拿到货第一件事就是用万用表确认电阻关系。电桥的典型阻抗是350欧姆激励端和信号端之间的阻值关系是有规律的查一下数据手册或者用万用表量一下对应关系接反了的话读数会显示负值或者乱跳。HX711模块我建议买那种带屏蔽的版本就是模块本身带一层金属罩的。虽然贵几块钱但抗干扰能力会好很多特别是后面如果打算做USB供电的版本电脑电源的纹波干扰会让读数非常难受。如果条件不允许模块背面自己贴一层铜箔接地也能缓解实测效果比不贴强很多。2.2 STM32最小系统与电源设计主控芯片我用的STM32F103C8T6蓝色Pill小板或者自制最小系统板都可以。这套方案的RAM有20KBFlash有64KB跑个裸机状态机加滤波算法绰绰有余。实际上整个工程编译下来Flash占用大概在20%到30%之间非常轻松。晶体这里特别说一下STM32F103的HSE外部晶振建议用8MHz的方便后面系统时钟倍频到72MHz。有些开发板上用的是其他频率的晶振比如12MHz的也有那样的话PLL配置要对应调整。如果图省事直接用内部HSI时钟跑到64MHz也行但USART的波特率会有一点点偏差高速通信场景下容易出错做电子秤这种低速场景其实没有太大影响。电源部分是这个项目最容易忽略的重点。称重传感器需要稳定的激励电压HX711的参考电压又是从AVDD取的AVDD如果有纹波输出数据就会周期性跳动。我实测用USB直接供电和用稳压模块独立供电两种情况同样的传感器读数稳定性差距非常明显。USB供电下5V电源纹波大约有50mV到80mV的尖峰反映到称重数据上就是末位数字频繁跳动2到3个字。换成AMS1117-3.3再加一个100uF电容滤波后末位跳动控制在1个字以内。至于模拟部分和数字部分要不要分地在这个项目里我认为没有必要做得太复杂。HX711模块本身是单独供电的STM32和模块之间只有两根信号线DT和SCK相连只要确保电源线足够粗、尽量短布线规范一点信号完整性基本不会有问题。2.3 显示与按键交互电路设计显示部分有两种选择LCD1602字符屏或者OLED屏幕。LCD1602的好处是字符大商用计价秤那种风格显示价格、重量、金额各占一行非常直观。缺点是刷新速度稍慢而且模块的驱动需要占用不少IO如果再加上I2C转接板逻辑会简单很多但刷新速度进一步下降。OLED屏幕显示内容灵活可以同时显示更多信息比如还能把单价和金额放大显示但我个人做计价秤产品风格时更倾向于LCD1602因为商超里用的传统计价秤就是这种显示形态。按键这里我用的4x4矩阵键盘。计价秤需要输入单价10个数字键加一个小数点再加一个清零、一个确认、一个删除。很多人做矩阵键盘最喜欢用的就是逐行扫描法但这里我强烈建议用状态机加定时扫描的方式不让按键检测阻塞在主循环里。具体来说就是用一个定时器中断每5ms扫描一次矩阵键盘检测到状态变化后记录事件主循环里再消费事件。这样做的优势是系统在处理HX711数据的时候不会因为等待按键抖动脉冲而卡顿。如果不想占用那么多IO也可以用几个独立按键配合增量编码方式输入单价不过操作效率会低很多商用场景不推荐。矩阵键盘在PCB上布线时要注意行列信号线不能平行走线太长容易形成串扰。实际项目里如果手工做飞线版本建议在行列线上各串联一个100欧姆的电阻对抑制毛刺有帮助。3. 核心代码实现与算法设计3.1 HX711驱动与采样逻辑HX711的通信机制非常简单两根线数据输出DOUT和时钟输入SCK。平时DOUT为高电平当数据准备好后DOUT会拉低这时MCU发送25个以上脉冲来读取数据或配置通道。每次读24位数据最高位在前。发送25个脉冲时第25个脉冲的上升沿会让芯片进入下一次转换同时切换通道和增益设置。需要注意的几个细节DOUT低电平就绪后读取过程中如果中途MCU被高优先级中断打断可能导致时序混乱。所以读取HX711数据的关键代码段最好关中断或者确保中断不会在这个时间段触发。SCK高电平持续时间不能太短HX711的数据手册要求SCK高电平时长至少100ns实际用STM32的GPIO翻转速度完全满足但如果是跑很高主频的芯片建议加几个空操作的延时。24位数据是有符号数最高位是符号位所以读回来之后要判断一下是否超出正数范围。如果第24位是1说明读出来的是负数这时候要手动把它扩展为32位有符号数。否则直接把24位数据赋值给int32_t变量时正数没问题负数会变成很大的正数。基本的读取代码类似这样uint32_t HX711_Read(void) { uint32_t count 0; uint8_t i; // 等待DOUT拉低表示数据就绪 while(HX711_DOUT_Read() 1); // 读取24位数据 for(i 0; i 24; i) { HX711_SCK_High(); if(HX711_DOUT_Read() 1) { count | 0x800000; } HX711_SCK_Low(); count 1; } // 第25个脉冲进入下一次转换 HX711_SCK_High(); HX711_SCK_Low(); return count; }读回来的原始值不能直接用。因为称重传感器桥路的差分信号在空载时也不是绝对的0V加上应变片的初始不平衡、HX711的失调电压空载时读出来的值可能是一个比较大的正数或者负数。这个值就是零点偏移要在标定的时候记录并扣除。3.2 数据滤波与平滑处理HX711输出的数据虽然已经是数字量但依然带有比较明显的噪声。原因有几个一是机械振动和电磁干扰二是传感器桥路本身的温漂和随机噪声三是HX711的Δ-Σ调制器的量化噪声不是完全白噪声。最简单的滤波方式就是平均值滤波连续读取N次然后取平均。但这种方法的缺点是如果某一次读数严重偏离平均值会被拉偏。更稳妥的做法是先去极值再取平均也就是连续读取N组数据去掉最大值和最小值剩下的取平均。这样能有效滤除尖峰脉冲干扰。我实际用的参数是连续读取10次去掉最大和最小剩下8次取平均。在10kg量程的传感器上测试滤波后的数据噪声峰峰值能控制在正负1个字以内对应大概0.1g到0.2g的重量波动范围这对于计价秤来说是完全可接受的。还有一种方案是采用滑动平均滤波就是维护一个环形缓冲区每次读入一个新值就丢掉一个最旧的值然后计算平均值。这种滤波器响应快实时性好不会像每次重新采集10次再去极值那样有较长的滞后。但缺陷是对尖峰脉冲不能有效滤除因为滑动窗口里保留的异常数据会连续影响多个输出。如果要做商用级产品我更推荐先做去极值再做滑动平均两段组合使用。滤波参数的选择直接影响系统响应速度。如果你每次拿到HX711数据后立即更新显示末位跳动会很严重。但如果滤波窗口太长秤台上放了东西之后要等一两秒才能稳定出数用户体验很差。我调下来比较合适的参数是每次读取时采集5次排序后去掉最大最小3次取平均。HX711的转换速率设置在10SPS每秒10次采样这样5次采集耗时约0.5秒显示更新频率约2Hz稳定性和实时性平衡得比较好。3.3 标定算法与重量计算标定是整个电子秤项目中决定精度的核心环节。常见的做法是两点标定记录空载时的ADC原始值作为零点然后放上已知重量的标准砝码记录满量程值通过两点确定一条线性直线斜率就是每克对应的ADC数值。计算公式如下float cal_slope; // 斜率单位ADC值/克 int32_t zero_offset; // 零点偏移 float weight; // 最终计算出的重量 // 标定过程 zero_offset read_filtered_average(); // 空载读数 int32_t cal_value read_filtered_average(); // 放标准砝码后的读数 float standard_weight 1000.0f; // 单位克 cal_slope (float)(cal_value - zero_offset) / standard_weight; // 实时重量计算 weight (float)(read_filtered_average() - zero_offset) / cal_slope;这里有一个细节很多人容易忽略斜率不是永远不变的。温度变化会导致传感器灵敏度漂移长期受力也可能导致传感器出现蠕变。所以商用计价秤都支持定期标定产品设计时通常会在软件里留一个标定模式通过按键组合进入。我这个项目里预留了两个标定档位一个是单点标定只校准零点另一个是两点标定完整校准零点和斜率。两点标定里砝码的重量选择很有讲究。如果你的秤量程是10kg用一个100g的砝码去标定因为传感器在低量程段的线性通常会好一些标出来的斜率可能和满量程段有偏差。比较稳妥的做法是用满量程的50%到75%左右的砝码做标定比如10kg的量程就用5kg或7.5kg的砝码。这样能保证斜率在整个量程范围内的最大偏差最小因为标定点附近的线性度一般是最好的。如果找不到标准砝码家里常见的做法是用已知净含量的矿泉水瓶凑但注意不是所有瓶装水标注的都是精确重量我实测有些瓶装水实际重量和标注差了十几克用来标定精度要求高的秤是完全不行的。最好买的还是那种铁质的标准砝码几十块钱一套精度M2级够用了。3.4 状态机与主循环架构整机功能逻辑我建议用一个简单的状态机来组织。状态包括正常工作、标定零点、标定斜率、参数设置。每个状态对应的按键响应不同显示内容也不同。这样做的好处是代码结构清晰后期加功能不容易把逻辑搞乱。主循环结构大致是while(1) { // 处理按键事件 uint8_t key key_scan(); if(key ! KEY_NONE) { state_machine_handle(key); } // 周期更新重量数据 if(hx711_data_ready_flag) { process_weight_data(); update_display(); } // 可以在这里加一些低功耗或看门狗处理 }HX711数据就绪后用中断或轮询方式置一个标志位主循环检测到标志位后再执行读取、滤波和显示刷新。不要把读取HX711放在按键扫描之前否则扫描按键会被HX711的等待阻塞拖慢。因为HX711读取时如果数据还没准备好会卡在等待DOUT拉低的循环里一次最长可能要几百毫秒这期间按键完全没响应。3.5 EEPROM掉电存储设计计价秤的标定参数和单价信息必须掉电保存。STM32F103C8T6虽然内部没有EEPROM但它的Flash可以被用作数据存储区。最简单的方案是直接用内部Flash的最后一页来存参数每次写入前先擦除再写入。这里有一个寿命问题STM32的Flash擦写寿命一般在1万次左右频繁写入标定参数没问题但如果单价信息每次按键修改都立即写Flash寿命很快耗尽。我采用了一个变通方案单价信息只在“确认”按键触发时写入标定参数只在完成标定流程时写入平时运行中的数据全部在RAM里操作。同时在Flash存储的起始位置放一个魔数标志每次上电读取时先检查标志如果不是预设值就说明是第一次运行或者数据损坏自动恢复默认参数。这个设计能避免数据文件损坏后系统完全无法工作。如果想让Flash寿命更长可以使用磨损均衡算法但在这个项目规模上属于过度设计除非你要做量产产品否则没什么必要。一个折中方案是用外挂AT24C02 EEPROM成本几毛钱I2C接口驱动一个读一个写比操作内部Flash简单得多而且寿命是100万次擦写基本不用考虑磨损。4. 实操过程与标定测试实录4.1 硬件焊接与上电自检我这次做的是手工焊接版本PCB用嘉立创打的样板。整个焊接过程有几个必须注意的地方第一HX711模块上的模拟电源和数字电源要分开布置。虽然模块本身已经做好了滤波但输入输出引脚的焊接引脚尽可能短减少天线效应。第二称重传感器的接线端子要用那种可插拔的绿色端子方便后期换传感器测试不要直接焊死在板上。第三STM32芯片如果手工焊接建议用拖焊法焊完之后用万用表检查相邻引脚之间有没有短路特别是VDD和GND之间如果短路了通电解电容充电瞬间可能直接烧芯片。上电之后先不要着急下载程序先用万用表确认几个关键电压STM32的3.3V是否正常HX711的DVDD是否为3.3VAVDD是否在4.2V到4.5V范围内。再检查一下系统电流整个板子正常情况下电流不超过35mA如果超过100mA说明哪里有问题可能是接线错误或者芯片损坏。然后先烧一个最简单的GPIO测试程序点亮LED确认芯片基本工作正常。如果有串口的话把HX711读取的原始值通过串口打印出来放到秤台上用手按一按观察数据是否随压力变化。这一步能快速排查传感器接线是否正确如果数据反向变化说明S和S-接反了交换一下就好。4.2 标定流程的具体操作步骤标定模式我设计成通过长按“清零”键3秒进入。进入标定模式后显示屏第一行显示提示信息第二行显示当前ADC平均值。具体操作步骤如下确认秤台完全空载没有残留物品记录当前的零点读数。按“确认”键锁定零点值系统会把当前ADC平均值存储为zero_offset。放进标准砝码等待数据稳定。这里要等大概5到10秒让滤波算法完全稳定下来。查看当前ADC读数如果读数方向和压力方向一致按“确认”键锁定砝码重量校准点。输入砝码的实际重量单位克确认后系统自动计算斜率并保存。标定的关键是在稳定后读取数据而不是看数据大致差不多就确认。我测试过程中发现刚放上砝码的瞬间由于冲击力传感器输出会有短暂过冲如果在这个时间点读取会导出一个偏差很大的标定值。标定完成后我通常会在秤台上放几个不同已知重量的物体做验证。比如标准砝码500g、1kg、5kg各测几次记录每次显示重量和实际重量的差值看线性度和重复性。我实测的一组数据如下实际重量(g)显示重量(g)偏差(g)误差百分比00.00.00.00%500500.10.10.02%1000999.8-0.20.02%30003000.30.30.01%50005000.10.10.00%80007999.5-0.50.006%从这个结果看线性度还是可以的。误差主要来源有三个方面一是传感器的非线性特性在两端附近偏差稍大二是滤波造成的量化误差三是HX711的温漂在长时间运行后逐渐显现。如果对精度要求更高可以做多点分段标定比如在20%、50%、75%量程处各取一个点然后对不同的区间分别用不同的斜率做线性插值。这在算法上并不复杂但效果很明显能把全量程最大误差再压缩一半左右。4.3 显示界面与交互逻辑实现LCD1602显示的内容布局我设计成这样第一行左边显示重量值右边显示单位中间显示当前状态第二行左边显示单价右边显示总金额。这样设计更贴近实际商用计价秤的显示习惯。按键交互方面数字键用于输入单价小数点键用于小数位确认键用于计算总价。为了防止误操作我把常用的几个功能分开了短按清零键实现去皮长按清零键进入标定模式。去皮功能的实现逻辑是记录当前重量作为皮重值最终显示重量为当前称重值减去皮重值同时LED指示灯亮起提示正在去皮状态。5. 常见问题与排查技巧实录5.1 数据跳变与不稳定这是被问到最多的一个问题现象是即使空载状态下显示屏上的数字也在不停地跳动有时候跳动的幅度还特别大。排查步骤我建议按顺序来首先是供电问题。用示波器看HX711模块的AVDD电压有没有明显纹波没有示波器的可以尝试换一个供电方式比如用充电宝加稳压模块对比一下数据。我发现USB直接供电加开关电源适配器时HX711的数据稳定性明显比锂电池供电差。其次是接线问题。传感器信号线和电源线是否靠得太近特别是传感器四根线如果捆扎在一起走长线需要注意。信号线尽量独立走线不要和电源线平行走线。如果用的那种四线合一硅胶线里面通常是绞合在一起的问题一般不大但如果自己接了延长线就很容易出问题。然后是滤波参数。如果硬件排查都没问题试试加大滤波的采样次数和去极值强度再观察数据跳动是否降低。如果依然跳动可以用示波器量一下HX711的DOUT引脚波形看是否有异常的窄脉冲毛刺。最后还有一种可能是传感器的量程选小了空载状态已经接近超载。比如用5kg量程的传感器去称10kg的物体空载时电桥输出就异常数据肯定稳定不了。这个问题属于选型错误只能换传感器。5.2 标定数据丢失与参数保存异常标定数据写进Flash后上电再读出来发现重量显示明显不对或者显示全是“请重新标定”。这种情况多半是Flash写入过程中出现了问题。Flash写入前必须保证目标地址已经被擦除否则写入数据是脏的。另外STM32的Flash写入要求使用的地址是半字16位对齐的写入操作必须是以16位为单位直接用字节写入会出错。还有一点要注意如果正在写Flash的时候突然断电Flash里的数据可能处于部分写入状态导致魔数检查失败。我用了一个保护策略写Flash的时候先写一个“更新中”标志全部写完并且校验通过后再把魔数从旧值改成新值。这样哪怕写入中途断电上电检查魔数不对也能恢复到一个已知的旧版本参数。如果用的是内部Flash方案建议每次上电读取参数后在系统运行中进行一次CRC校验避免Flash内容被意外的程序错误篡改。程序里的校验逻辑类似这样uint32_t read_from_flash(uint32_t addr, void *buf, uint32_t len) { uint8_t *p (uint8_t *)buf; uint32_t crc 0; // 读取数据 for(uint32_t i 0; i len; i) { p[i] *(volatile uint8_t *)(addr i); } // 读取CRC值并比对 uint32_t stored_crc *(volatile uint32_t *)(addr len); crc calculate_crc(buf, len); if(crc ! stored_crc) { return 0; // 数据损坏 } return 1; }5.3 按键不灵敏与显示乱码矩阵键盘如果出现按键偶尔不响应或者一次触发多次大概率是按键消抖没做。注意不是软件消抖没做而是硬件上矩阵键盘的行列线如果过长容易引入电磁干扰造成误触发。软件消抖的窗口建议设在10ms到20ms之间同时用状态机记录按键边沿不要在按键按下和释放时都触发事件只在按下沿触发。LCD1602显示乱码最常见的原因是初始化时序不对。LCD1602需要上电后等待一段时间才能初始化一般是15ms以上等待内部复位完成后才能发送指令。初始化指令的每一条后面都要加适当的延时特别是0x38这条设定8位模式、双行显示的指令后面的延时要足够长。如果初始化代码没问题但显示仍然乱码检查一下对比度调节电位器有没有调到合适的位置。用了I2C转接板的版本检查一下地址设置是不是和代码一致默认一般是0x27或者0x3F但有些模块的地址跳线位置不同。最后分享一个我自己调试时经常用的招HX711读回来的数据如果一直是同一个值不随压力变化先检查SCK和DOUT引脚有没有虚焊然后检查传感器端子的E和E-是不是有电压。如果E和E-之间电压是0V说明模块的激励输出有问题或者传感器坏了。用万用表测传感器E和E-之间的电阻350欧姆左右是正常的如果开路或者短路传感器基本可以扔了。这种问题排查起来其实不难按信号流向一个一个节点查过去很快就能定位到具体的故障点。
返回列表