ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

STM32+HX711+OLED称重模块实战:从硬件选型到滤波标定

STM32+HX711+OLED称重模块实战:从硬件选型到滤波标定 1. 称重项目为什么值得用STM32加HX711这套组合来做称重这个需求看起来简单但真正落到嵌入式项目里坑比想象中多得多。我最早接触电子秤类项目时以为无非就是传感器读个数、屏幕显示一下就完事结果第一版做出来空载读数在±30克之间乱跳放上500克砝码显示487克换了三块HX711模块才找到问题所在。后来陆续做了配料秤、快递称重台、厨房电子秤几个变种才慢慢把STM32加HX711加OLED这套组合吃透。这套方案的核心价值在于HX711把称重传感器微弱的毫伏级差分信号放大并转换成24位数字量STM32负责采样时序、滤波算法、标定换算和人机交互OLED则把重量、单位、状态实时呈现出来。三者分工明确成本极低一套下来物料成本通常不超过三十块却能做出精度到1克、稳定性不错的成品。适合谁参考正在做毕业设计的学生、想入门嵌入式传感器采集的开发者、需要给设备加称重功能的工程师以及单纯想搞个实用小工具的手工爱好者。关键词里提到的STM32、HX711、称重模块、OLED正好覆盖了这个项目的四个核心环节。我下面会从硬件连接、HX711的采样时序与速率选择、STM32端的驱动与滤波、OLED显示与标定流程、以及实际调试中那些文档里不会写的坑一层层拆开讲。你不需要有很深的嵌入式基础只要会点C语言、能看懂电路图跟着走就能复现。先说清楚一个前提称重项目的精度七分靠机械结构和传感器本身三分才靠电路和代码。如果你的传感器量程选得离谱、安装方式有应力干扰再好的代码也救不回来。所以我会在讲代码之前先把硬件选型和安装的底层逻辑讲透这部分才是决定成败的地方。2. 硬件选型与接线从传感器到HX711再到STM322.1 称重传感器怎么选量程和灵敏度决定了什么称重传感器本质是个惠斯通电桥受力后桥臂电阻变化输出一个和受力成正比的差分电压。常见的悬臂梁式传感器灵敏度标称一般是1mV/V意思是供5V激励电压时满量程输出5mV。这个信号非常微弱STM32自带的12位ADC根本分辨不出来所以才需要HX711这种专用24位ADC来做前端。选传感器时量程要留出余量。你要称的最大重量如果是5公斤别选5公斤量程的选10公斤的。原因有两个一是满量程附近线性度会变差二是留余量能避免偶尔超载损坏传感器。我见过有人用1公斤量程的传感器去称1.2公斤的东西当场就把应变片压变形了读数再也回不到零点。灵敏度方面市面上常见的有1mV/V和2mV/V两种。2mV/V的信号更强对HX711来说更容易分辨但价格稍贵。如果你做的是小量程高精度秤比如0到500克优先选2mV/V的。另外要注意传感器的四线还是五线接法五线多一根反馈线能补偿导线电阻带来的激励电压衰减长距离传输时更准。2.2 HX711模块的引脚定义和与STM32的接线逻辑HX711模块通常有十个引脚分两组。一组是传感器侧E、E-、A、A-、B、B-其中A通道增益128B通道增益32。另一组是MCU侧VCC、GND、DT数据、SCK时钟。我们称重一般用A通道因为增益高、分辨率好。接线时最容易出错的是传感器和HX711之间的四根线。传感器上一般标着E、E-、S、S-对应接到HX711的E、E-、A、A-。这里有个坑不同厂家的传感器线色定义不一样红黑绿白分别代表什么一定要看传感器说明书不能凭经验。我就因为想当然把激励线和信号线接反过结果HX711读数一直是满量程饱和值查了半天才发现。HX711和STM32的连接就简单了DT接任意GPIO输入SCK接任意GPIO输出VCC接3.3V或5V都行模块自带稳压。但要注意如果STM32是3.3V供电HX711的VCC也建议接3.3V这样DT和SCK的电平匹配不会出现3.3V MCU读5V模块时的电平兼容问题。虽然HX711的DT是开漏输出理论上能容忍5V但稳妥起见还是统一电压。传感器引脚HX711引脚说明EE激励正通常红或黑E-E-激励负SA信号正差分输入S-A-信号负差分输入2.3 供电与去耦为什么你的读数总在跳很多人接完线发现读数不稳第一反应是代码滤波没做好其实八成是供电问题。HX711内部有稳压和振荡电路对电源纹波比较敏感。如果你用USB供电电脑USB口的纹波可能就有几十毫伏直接耦合到模拟前端上。我的做法是在HX711的VCC和GND之间并一个10微法和一个0.1微法的电容0.1微法尽量靠近模块引脚。如果条件允许给HX711单独用一个低压差线性稳压器供电和STM32的数字电源分开。另外传感器的屏蔽线要单端接地别两端都接否则形成地环路反而引入干扰。还有一点HX711模块上那个跳线默认是10Hz采样率短接后变80Hz。这个选择后面会详细讲但硬件上先知道有这么个东西。如果你做的是静态称重10Hz足够如果是动态称重比如流水线上快速过秤才需要80Hz。3. HX711的采样时序与速率选择10Hz还是80Hz3.1 读懂HX711的串行通信协议HX711不是标准的I2C或SPI它自己定义了一套简单的两线时序。DT是数据线SCK是时钟线。当DT为高电平时表示转换未完成MCU要等当DT变低表示数据准备好了MCU开始发时钟脉冲读数据。读一次数据的流程是先等DT变低然后发25到27个SCK脉冲。前24个脉冲读出24位数据第25个脉冲用来选择下一次转换的通道和增益。具体来说第25个脉冲的个数决定下一轮用哪个通道25个脉冲选A通道128增益26个选B通道32增益27个选A通道64增益。我们称重固定用A通道128增益所以每次发25个脉冲就行。这里有个细节24位数据是二进制补码格式最高位是符号位。读出来后要先做符号扩展再转成有符号的32位整数。很多人直接当无符号数处理结果负重量显示成一个巨大的正数就是因为没处理补码。// HX711读一次原始数据的典型实现 int32_t HX711_Read(void) { int32_t value 0; uint8_t i; while (HX711_DT_READ() 1); // 等待DT变低 for (i 0; i 24; i) { HX711_SCK_HIGH(); delay_us(1); value value 1; HX711_SCK_LOW(); delay_us(1); if (HX711_DT_READ()) { value; } } // 第25个脉冲选择A通道128增益 HX711_SCK_HIGH(); delay_us(1); HX711_SCK_LOW(); delay_us(1); // 补码转有符号数 if (value 0x800000) { value | 0xFF000000; } return value; }3.2 10Hz和80Hz的取舍不只是快慢问题HX711的采样率由内部振荡器决定默认10Hz短接模块上的RATE引脚后变80Hz。表面看80Hz更快但代价是有效位数下降。10Hz模式下HX711的噪声等效输入大约50nV有效位数能到21位左右80Hz模式下噪声增大有效位数大概降到19位。这意味着什么假设你用5公斤量程、2mV/V灵敏度的传感器5V激励满量程输出10mV。21位有效分辨率下最小可分辨约10mV除以2的21次方大约是4.8纳伏换算成重量约2.4微克当然实际受机械噪声限制远达不到。但19位时分辨率就差了4倍。所以静态称重坚决用10Hz动态快速称重才考虑80Hz。我做过一个快递称重台要求放上去1秒内出稳定读数。用10Hz的话1秒才10个样本滤波后响应偏慢换80Hz后1秒有80个样本做滑动平均后响应快很多精度也够用。所以速率选择要看你的应用场景没有绝对的好坏。3.3 采样时序中的延时与稳定性HX711的SCK高电平持续时间要求最小0.2微秒低电平也是0.2微秒。STM32跑72MHz时一个指令周期约14纳秒所以直接翻转GPIO就够快了但为了保险我还是加了1微秒的延时。这个延时不能省尤其是在长导线连接时信号边沿变缓太快的时钟可能导致数据错位。另外读数据前等待DT变低要有超时机制。正常情况下HX711每100毫秒10Hz或12.5毫秒80Hz拉低一次DT。如果程序卡在while等待里超过200毫秒说明硬件出问题了应该跳出并报错而不是死等。我在实际项目里就遇到过传感器线断了一根DT一直不拉低程序卡死导致看门狗复位加了超时后才定位到是接线问题。4. STM32端的驱动、滤波与标定换算4.1 GPIO模拟时序还是硬件SPI为什么我选模拟HX711的时序虽然像SPI但不是标准SPI因为它的数据线方向是单向的但时钟极性特殊用硬件SPI反而不好控制第25个脉冲的通道选择。所以绝大多数方案都用GPIO模拟时序灵活且可靠。STM32的HAL库提供HAL_GPIO_WritePin和HAL_GPIO_ReadPin直接调用就行性能足够。如果你追求极致速度可以用寄存器操作BSRR和IDR把读一位的时间压到几百纳秒。但对10Hz采样来说完全没必要HAL库的可读性更好维护起来方便。我用STM32F103C8T6实测HAL库模拟时序读一次数据大约几十微秒相对于100毫秒的采样周期可以忽略。4.2 滤波算法滑动平均、中值滤波还是卡尔曼原始数据读出来后不能直接用必须滤波。我试过几种方案各有适用场景。滑动平均最简单维护一个长度为N的数组每次新数据进来替换最旧的输出平均值。N取10到20比较合适太大响应慢太小滤波效果差。缺点是遇到突发干扰比如有人碰了一下秤台会把干扰平均进去恢复慢。中值滤波是取最近N个数据的中位数能有效剔除脉冲干扰。N一般取5或7。缺点是计算量稍大需要排序但对STM32来说小菜一碟。我通常把中值滤波和滑动平均结合先中值滤波去掉突发尖峰再滑动平均平滑随机噪声。卡尔曼滤波理论上最优但对称重这种一维静态信号有点杀鸡用牛刀而且参数调起来麻烦。除非你的应用对动态响应要求极高否则前两种组合就够了。#define FILTER_N 10 int32_t filter_buf[FILTER_N]; uint8_t filter_idx 0; int32_t HX711_GetFiltered(void) { int32_t raw HX711_Read(); filter_buf[filter_idx] raw; filter_idx (filter_idx 1) % FILTER_N; int32_t sum 0; for (uint8_t i 0; i FILTER_N; i) { sum filter_buf[i]; } return sum / FILTER_N; }4.3 标定从原始值到克数的两步法滤波后的原始值还是个没有物理意义的整数要换算成重量需要标定。标准做法是两点标定空载时读一个值叫零点值放上已知重量的砝码读一个值叫满度值然后线性换算。公式是重量 (当前值 - 零点值) / (满度值 - 零点值) * 砝码重量。这里的关键是砝码要选你常用量程附近的值。如果你平时称几十克的东西却用5公斤砝码标定小量程段的误差会被放大。我一般建议用满量程的50%到80%作为标定砝码。比如10公斤量程用5公斤或8公斤砝码标。标定值要存在Flash里掉电不丢失。STM32F103的Flash操作不难HAL库有现成的HAL_FLASH_Program。注意写之前要先擦除整个页STM32F103C8T6的页大小是1KB。我通常把零点值和满度值存在一个专门的页里开机时读出来。typedef struct { int32_t zero; int32_t full; float weight; uint32_t magic; } CalibData; CalibData calib; void LoadCalib(void) { memcpy(calib, (void*)CALIB_ADDR, sizeof(CalibData)); if (calib.magic ! 0x5A5A1234) { calib.zero 0; calib.full 100000; calib.weight 1000.0f; calib.magic 0x5A5A1234; } } float GetWeight(void) { int32_t raw HX711_GetFiltered(); return (float)(raw - calib.zero) / (float)(calib.full - calib.zero) * calib.weight; }4.4 温漂与零点跟踪长时间使用的必修课称重传感器对温度敏感温度变化几度零点可能漂移几十克。如果你的秤要长时间通电必须做零点跟踪。原理是当读数在零点附近小幅波动且持续一段时间比如3秒就认为当前是空载慢慢把零点值往当前值靠。实现上维护一个零点偏移量每次检测到稳定且接近零时让零点值以很小的步长逼近当前读数。步长要小比如每次调整0.1%避免把真实的小重量误判为零点。这个功能在电子秤芯片里是标配自己写也不复杂但能大幅提升长期稳定性。5. OLED显示从驱动到界面的完整实现5.1 0.96寸OLED的I2C驱动要点0.96寸OLED基本都用SSD1306驱动芯片四针I2C接口VCC、GND、SCL、SDA。STM32用硬件I2C或软件模拟都行。硬件I2C速度快但STM32F103的硬件I2C有历史遗留的卡死问题很多人宁愿用软件模拟。我用软件模拟I2CGPIO翻转稳定可靠速率400kHz下刷一屏128x64也就十几毫秒。SSD1306的初始化序列比较长网上有现成的但要注意几个关键命令设置对比度、设置显示模式、设置多路复用率、设置显示偏移、设置时钟分频、设置预充电周期、设置COM引脚配置、设置VCOMH电压。这些参数影响显示效果和寿命不能随便改。特别是对比度设太高OLED老化快设太低看不清一般0xCF左右比较合适。5.2 显示汉字和自定义图形的方法SSD1306本身不带字库显示英文和数字可以用内置的6x8或8x16点阵但汉字必须自己取模。取模软件用PCtoLCD2002设置成阴码、逐列式、顺向生成十六进制数组。一个16x16的汉字占32字节显示时按页写入。显示重量时我一般用大号数字字体比如16x32的数字这样远处也能看清。数字字体可以自己取模也可以用现成的。单位“g”或“kg”用小字体显示在旁边。界面上再留一行显示状态比如“稳定”“标定中”“超载”。// 显示一个16x16汉字 void OLED_ShowCN(uint8_t x, uint8_t page, const uint8_t *cn) { for (uint8_t i 0; i 16; i) { OLED_WriteData(cn[i]); } OLED_SetPos(x, page 1); for (uint8_t i 0; i 16; i) { OLED_WriteData(cn[i 16]); } }5.3 界面刷新策略别让刷屏拖慢采样OLED刷新是I2C通信一屏全刷要传1024字节400kHz下大约20毫秒。如果你每次采样都全屏刷新10Hz采样下CPU有20%时间花在刷屏上而且屏幕会闪。正确做法是局部刷新只更新重量数字所在的区域其他不变。更进一步重量数字变化时才刷新没变化就不刷。我通常把重量值转成字符串和上一次的字符串比较不同才刷。这样静态称重时屏幕几乎不占用CPU时间。另外I2C通信期间如果HX711的DT变低可能错过采样窗口所以刷屏最好放在两次采样之间或者用中断方式读HX711。5.4 常见显示问题花屏、不亮、残影OLED不亮先查供电和I2C地址。SSD1306的地址通常是0x78或0x7A取决于SA0引脚。如果地址不对初始化命令发不进去屏幕自然不亮。用逻辑分析仪抓一下I2C波形看有没有应答最快定位。花屏一般是初始化序列不对或复位不完整。SSD1306上电后需要一段复位时间 datasheet要求至少100毫秒。有些模块没有复位引脚靠上电复位如果STM32启动太快可能在OLED还没准备好时就发命令导致花屏。解决办法是初始化前加200毫秒延时。残影是OLED的固有问题长时间显示固定内容会烧屏。缓解办法是降低对比度、定期移动显示位置、或者设置自动熄屏。如果做产品这些都要考虑。6. 调试中那些文档不会告诉你的坑6.1 读数跳动的排查链路读数跳动是最常见的问题排查要按顺序来。第一步看跳动幅度。如果跳动几十克先查供电纹波用示波器看HX711的VCC。第二步看跳动频率。如果和50Hz工频同步说明有工频干扰检查传感器屏蔽和接地。第三步看是否和触摸有关。手碰秤台读数就变说明机械结构有应力传感器安装螺丝拧太紧或底板不平。我遇到过一次读数每隔几秒跳一次幅度不大但很规律。查了半天发现是旁边有个继电器在周期性吸合电磁干扰耦合进来了。把HX711远离继电器、加屏蔽罩后解决。所以干扰源排查要跳出电路本身看整个系统的电磁环境。6.2 标定后小量程不准的原因用大砝码标定后称小东西不准这是线性度问题。传感器在大量程和小量程的线性度不一样用单点标定无法覆盖。解决办法是做多点标定比如0、25%、50%、75%、100%五个点用分段线性插值。虽然代码复杂点但精度提升明显。另一个原因是零点漂移。标定时的零点和实际使用时的零点可能不同尤其是传感器安装后有个蠕变过程。我的经验是传感器装好后先通电老化24小时再标定这样零点稳定得多。6.3 HX711模块的质量差异市面上HX711模块质量参差不齐便宜的几块钱贵的十几块。差异主要在稳压芯片和PCB布局。便宜模块用的稳压芯片噪声大PCB走线也不讲究模拟地和数字地混在一起。我买过一批便宜模块十个里有三个读数明显偏大。后来换了一家贵点的一致性就好很多。如果你批量做产品建议自己画HX711电路用REF3030之类的基准源模拟部分单独铺地成本增加不多但稳定性提升明显。如果只是自己做着玩买模块时看评价选销量高、有原理图的。6.4 程序卡死与看门狗HX711等待DT变低的while循环是卡死高发区。如果传感器没接好或模块坏了DT永远不拉低程序就死在那里。必须加超时超时后返回错误码让上层处理。同时开独立看门狗万一真卡死了能自动复位。STM32的独立看门狗用LSI时钟配置简单。HAL库的HAL_IWDG_Init和HAL_IWDG_Refresh在主循环里定期喂狗。注意喂狗间隔要小于看门狗溢出时间我一般设1秒溢出主循环200毫秒喂一次。7. 从能用到好用几个提升体验的细节7.1 按键交互与标定流程设计一个完整的称重项目按键是必须的。至少三个键去皮、标定、单位切换。去皮就是把当前重量设为零点标定就是进入标定模式后放砝码、输入砝码重量。按键处理要消抖我用的是定时器20毫秒扫描一次连续两次相同才确认。标定流程我设计成长按标定键3秒进入屏幕提示“空载”稳定后按确认记录零点然后提示“放砝码”输入砝码重量稳定后按确认记录满度值最后保存到Flash重启生效。这个流程用户不容易误操作。7.2 超载与欠载保护传感器有量程限制超过120%可能永久损坏。程序里要判断如果重量超过满量程的110%显示“超载”并报警同时停止更新零点。欠载就是负得太多一般是传感器接反或零点漂移显示“错误”提示检查。7.3 低功耗设计思路如果做电池供电的秤低功耗很重要。STM32可以进Stop模式HX711也有掉电模式。但HX711从掉电唤醒后需要稳定时间不能立即采样。我的做法是无操作30秒后进Stop按键中断唤醒唤醒后延时100毫秒再读HX711。这样一节18650电池能用几个月。7.4 数据记录与串口输出很多场景需要记录重量数据比如配料过程。加一个串口输出把每次稳定的重量和时间戳发到上位机用Excel或Python分析。STM32的UART配置简单printf重定向到UART就行。注意串口输出不要放在中断里会阻塞放在主循环里判断标志位发送。8. 关于这套方案的扩展与个人体会这套STM32加HX711加OLED的框架稍加改动就能扩展出很多变种。比如加个DS3231时钟芯片做成带时间记录的称重终端加个SD卡模块离线存储称重数据加个继电器做成定量控制到重量自动停。我甚至见过用它做鱼缸自动喂食器的称重控制饲料量思路是一样的。我个人在实际操作中的体会是称重项目最花时间的从来不是写代码而是硬件调试和标定。代码部分一天就能写完但让读数稳定、精度达标可能要调一个星期。所以如果你刚开始做别急着写代码先把传感器装好、供电弄干净、用示波器看波形把硬件底子打牢后面会省很多事。最后分享一个小技巧调试阶段在OLED上同时显示原始值、滤波值和换算后的重量这样一眼就能看出问题出在哪一环。原始值跳是硬件问题原始值稳但滤波值跳是滤波参数问题滤波值稳但重量不对是标定问题。这个三行显示法帮我省了无数排查时间。
返回列表