STM32+HX711+OLED电子秤开发:硬件设计、驱动与标定全解析
1. 项目需求拆解与硬件方案选型1.1 为什么是STM32HX711OLED这个组合做称重项目市面上的方案其实不少有直接用单片机加电阻分压的有用专用称重仪表的也有用Arduino配上各种模块的。我为什么推荐STM32HX711OLED这个组合三个字性价比。STM32F103C8T6这颗料现在的价格已经跌到几块钱性能却足够跑一个完整的称重系统HX711是专门为称重传感器设计的24位高精度ADC芯片几毛钱一颗外围电路简单到不能再简单OLED屏0.96英寸的价格也就十几块。整套下来成本不到20块而且效果一点不比几百块的商品称差。这个组合能干什么实时采集压力传感器信号经过放大、ADC转换、数字滤波、标定换算最终在OLED屏幕上显示重量。往大了说这就是电子秤的核心方案。家里做烘焙称量、小作坊做定量分装、实验室做力值测量甚至做个智能杯垫提醒自己多喝水都靠这一套逻辑。适合谁来学刚学完STM32基础外设、想找个有点深度但又不至于啃半年的练手项目的人或者正在准备毕业设计、电子竞赛的在校学生再或者单纯想改造生活小物的DIY玩家。这个项目麻雀虽小五脏俱全涉及ADC采集、时序操作、滤波算法、UI显示、标定流程每个环节都是硬货。1.2 三个核心器件各自扮演什么角色先理解这三个东西的分工后面的代码才不会看晕。STM32负责“大脑”的工作控制时序、处理数据、驱动屏幕、响应按键。HX711负责“感知”的工作把传感器产生的微弱毫伏级电压变化放大并转换成数字信号。OLED负责“表达”的工作把处理后的数据变成人眼能直接读懂的重量数值。很多人容易把HX711当成一个普通ADC芯片这是理解上的误区。HX711内置了可编程增益放大器PGA增益分为32倍、64倍和128倍三档。这意味着传感器输出的微小差分信号进到芯片内部后会先被放大128倍最高档再由内部的24位高精度ADC进行模数转换。为什么要这么设计称重传感器的输出信号非常微弱桥式传感器在满量程时的差分输出通常只有10mV到20mV。如果直接用STM32内置的12位ADC去采你算一下5V参考电压下12位ADC的分辨率是1.2mV左右连传感器信号的1/10都分辨不出来。而HX711在128倍增益下配合24位ADC理论上可以分辨出纳伏级别的信号变化实际做不到因为噪声和漂移会限制但量级差距摆在那里。OLED选0.96寸的SSD1306驱动方案I2C接口的只有四个引脚VCC、GND、SCL、SDA接线少、占IO少STM32F103C8T6的资源完全够用。SSD1306的像素分辨率是128x64SPI方式刷屏快但需要额外的DC和RES引脚I2C方式虽然刷新率低一些但显示重量数据这种低频更新场景完全够用。我推荐新手选I2C版本省下来的两个引脚可以留给按键或者其他传感器后面想扩展功能也有余量。2. HX711硬件连接与关键电路设计2.1 HX711模块的引脚定义与接线方法市面上买到的HX711模块基本都是现成的一边接传感器一边接单片机。模块上通常会标注两个排针区物料端的四个孔标着E、E-、A、A-单片机端的排针标着VCC、GND、SCK、DOUT。别小看这四个物料端接口接线错了轻则采集数据全是乱的重则直接把模块烧了。先说传感器侧。称重传感器通常引出四根线红、黑、绿、白不同厂家颜色定义略有差异但绝大多数遵循这个规律。红色接E黑色接E-绿色接A白色接A-。接完之后用万用表量一下传感器输出端绿线和白线之间正常应该有一个毫伏级的电压这个电压随受力大小变化。还有一个判断技巧用电桥模式判断线序传感器空闲时绿白之间的电压接近供电电压的一半左右受力时会在中值附近微小变化。再说单片机侧。VCC接STM32的5V引脚注意很多模块上自带稳压但HX711芯片本身工作电压是2.6V到5.5V接3.3V也能跑接5V的好处是给传感器的激励电压更高输出信号幅度更大信噪比更好。GND接GNDSCK接一个普通的GPIO输出口比如PB13DOUT接一个GPIO输入口比如PB12。DOUT内部没有上拉所以最好在STM32初始化时把PB12配置为带上拉输入或者板载模块已经加了上拉电阻看具体模块而定。这里有一个关键点SCK和DOUT没有用硬件SPI而是用普通的GPIO软件模拟时序。为什么HX711对SCK的频率有要求最低不能低于几十kHz最高不超过几MHz软件模拟完全在范围内。另外软件模拟时序的实现方式简单粗暴——读DOUT电平、翻转SCK引脚——不容易受到SPI配置细节的干扰。我见过的很多新手用硬件SPI去读HX711结果因为SPI主从模式配置不对数据一直读错折腾半天最后换回GPIO模拟就全通了。2.2 供电方案与电源去耦技巧供电是整个称重项目最容易踩坑的地方没有之一。传感器和HX711的模拟部分对电源质量非常敏感。开关电源纹波大直接导致ADC读数跳变单片机瞬间拉高电流也会通过共同的电源轨影响到HX711的参考电压。我的做法是HX711的AVDD模块上一般已经连好和VCC之间加一个100uF的电解电容并联一个0.1uF的瓷片电容尽量靠近模块的电源引脚。这两个电容一个管低频滤波一个管高频去耦配合使用效果远好于单个电容。另外如果用USB供电或者开关电源供电强烈建议先经过一个LDO比如AMS1117-3.3或者更稳的ME6211再给整个系统供电不要直接怼开关电源的输出。还有一个很多人忽视的问题传感器和HX711模块之间的连接线。如果传感器和模块分开放比如传感器放在远处模块在主板附近连接线不要太长超过20cm就要用双绞线或者屏蔽线。因为称重传感器的差分信号是毫伏级的长线引进来之后环境中的工频干扰、电磁干扰都会耦合到导线上导致读数一直在毫米级抖动。我最早做实验时传感器模块之间用了一根50cm的杜邦线读数的跳变幅度大约相当于满量程的千分之几一开始没在意后来换成双绞线后跳变缩小了十倍。这个问题在实验室环境可能不明显在工业现场或者有电机、继电器的地方特明显。2.3 第一脚确认与芯片方向辨识很多从淘宝买STM32最小系统板和HX711模块的朋友拿到手第一件事就是认芯片方向。这个看似基础的问题实际操作中真的会翻车把芯片插反、把引脚定义搞错、照着别人代码接线结果发现板子引脚编号不一样。STM32F103C8T6是第一脚有圆点标记的通常圆点旁边还有一条斜切的凹槽。用万用表量也可以确认第一脚旁边通常连着VDD或者VSS。HX711模块上的芯片同样有圆点标识插接排针时注意模块丝印看排针旁的标注E/E-/A/A-和VCC/GND/SCK/DOUT的位置不同厂家模块可能不同接之前先拍照留底或者直接看模块背面的丝印。3. 软件核心实现HX711驱动与OLED显示3.1 HAL库下HX711的时序驱动代码HX711的数据读取靠的是两根线配合使用时序。整个时序过程不复杂但顺序错一步就前功尽弃。我先把关键代码贴出来然后逐行讲为什么这么写。// hx711.h #define HX711_SCK_PIN GPIO_PIN_13 #define HX711_SCK_PORT GPIOB #define HX711_DOUT_PIN GPIO_PIN_12 #define HX711_DOUT_PORT GPIOB #define HX711_SCK_HIGH() HAL_GPIO_WritePin(HX711_SCK_PORT, HX711_SCK_PIN, GPIO_PIN_SET) #define HX711_SCK_LOW() HAL_GPIO_WritePin(HX711_SCK_PORT, HX711_SCK_PIN, GPIO_PIN_RESET) #define HX711_DOUT_READ() HAL_GPIO_ReadPin(HX711_DOUT_PORT, HX711_DOUT_PIN)// hx711.c uint32_t HX711_ReadRaw(void) { uint32_t data 0; uint8_t i; // 等待DOUT拉低表示数据准备好 uint32_t timeout 100000; while (HX711_DOUT_READ() GPIO_PIN_SET) { if (--timeout 0) { return 0; // 超时退出防止死循环 } } // 读取24位数据MSB先行 for (i 0; i 24; i) { HX711_SCK_HIGH(); delay_us(1); data data 1; if (HX711_DOUT_READ() GPIO_PIN_SET) { data; } HX711_SCK_LOW(); delay_us(1); } // 第25个脉冲选择通道A增益128 HX711_SCK_HIGH(); delay_us(1); HX711_SCK_LOW(); delay_us(1); return data; }首先要理解时序的发起方是谁。HX711在内部完成一次ADC采样后会主动把DOUT引脚拉低告诉单片机“数据准备好了”。单片机检测到DOUT为低电平后开始在SCK上产生脉冲每来一个高电平脉冲DOUT上就移出一位数据。全部24位移完之后SCK上再补一个脉冲第25个告诉HX711“本次数据读完了你准备测下一次”。为什么要加超时退出这是实战中非常关键的一个点。如果传感器断线、模块没供电、或者SCK引脚没配置对DOUT可能永远不拉低程序就会卡死在while循环里。加了超时之后至少MCU不会死机后面接个错误提示还能快速定位问题。再解释一下为什么第25个脉冲是必须的HX711使用不同的脉冲数量来选择通道和增益。24个脉冲读数据第25个脉冲选择“通道A128倍增益”第26个脉冲选择“通道B32倍增益”第27个脉冲选择“通道A64倍增益”。通道A接传感器通道B通常空置不用。如果我们一次采样发25个脉冲下一次采样继续发25个脉冲那系统始终工作在通道A、128倍增益这是称重模式的标准配置。3.2 OLED的I2C驱动直接从底层寄存器聊到HAL封装OLED这边我默认用0.96寸、SSD1306控制器、I2C接口的模块。I2C地址通常是0x3C极个别是0x3D。如果屏幕不亮第一步不是去查代码而是确认地址。可以通过I2C扫描程序打印出总线上所有设备的地址。HAL库操作OLED有两个思路一个是直接用HAL_I2C_Mem_Write函数写数据另一个是自己封装底层的I2C发送函数。我用的是后者核心就这么几个函数// ssd1306.c 核心片段 #define OLED_I2C_ADDR (0x3C 1) // 左移一位因为HAL_I2C_Mem_Write需要8位地址 static void OLED_WriteCommand(uint8_t cmd) { HAL_I2C_Mem_Write(hi2c1, OLED_I2C_ADDR, 0x00, I2C_MEMADD_SIZE_8BIT, cmd, 1, 10); } static void OLED_WriteData(uint8_t data) { HAL_I2C_Mem_Write(hi2c1, OLED_I2C_ADDR, 0x40, I2C_MEMADD_SIZE_8BIT, data, 1, 10); }这里的0x00和0x40是SSD1306的“控制字节”规定的第一个字节最高位是Co位continuation最低位是D/C#位data/command选择。0x00表示后面传的是命令0x40表示后面传的是显存数据。HAL_I2C_Mem_Write的MemAddress参数正好对应这个控制字节所以直接传0x00或0x40就行。理解这一层之后你就能看懂网上各种OLED驱动代码为什么有的传0x00、有的传0x40其实都是在设定这个标志位。初始化的命令序列就不完整贴了网上一搜一大把但有两个命令值得单独说。一个是0xAFDisplay ON一个是0xA8设置多路复用比。如果你发现初始化代码执行完了屏幕还不亮先检查0xAF有没有发出去如果屏幕亮但是显示内容偏移、只显示半屏多半是0xCA设置显示起始行或者0xD3显示偏移设置不对。还有一个容易被忽略的0x8D命令后要跟一个0x14这是开启内部的电荷泵。SSD1306的电荷泵是给OLED面板提供更高驱动电压用的不开这个屏幕什么都不会显示。这个命令漏掉的情况我见到的比地址错误还多。3.3 字模与图像显示汉字是怎么“画”出来的OLED的显存是1KB对应128x64的像素。注意一个细节SSD1306的显存布局不是一行一行顺序排列的而是按页page排列的。128列像素被分成8个水平页每页有8行像素。写入数据时每个字节的每一位对应某一列的纵向8个像素。这就是为什么很多人第一次画图时发现图像是“竖着切成一条条”的。显示汉字的原理本质上就是把汉字按16x16点阵取模然后把每个点阵数据按SSD1306的列扫描格式排列依次写入显存。// 16x16汉字字模显示函数 void OLED_ShowCHinese(uint8_t x, uint8_t y, uint8_t index) { uint8_t i; for (i 0; i 16; i) { // 上半部分16列 OLED_WriteData(Hzk[index][i * 2]); OLED_WriteData(Hzk[index][i * 2 1]); // 下半部分16列从第x2行开始 OLED_WriteData(Hzk[index][i * 2 32]); OLED_WriteData(Hzk[index][i * 2 33]); } }这里的关键逻辑是一个16x16的汉字在取模软件里通常被拆成左右两部分、每部分分上下两半共4个8x8块。显示时不是简单的一行读完再读下一行而是先写上半部分的前两行、再写下半部分的前两行这样对应SSD1306的页结构。如果不理解这个布局写出来的汉字会左右颠倒或者上下分裂。如果你不想折腾字模也可以用U8g2库。U8g2是目前我最推荐的OLED图形库自带大量字体支持中文、英文、数字混合显示还内置了画线、画圆、画矩形等函数。但要注意U8g2在STM32上需要至少20KB左右的Flash存储字库F103C8T6有64KB Flash完全够用。初次配置U8g2时要把I2C通信的回调函数绑定到HAL库的HAL_I2C_Master_Transmit上然后设置正确的时钟频率一般设置400kHz没问题但如果你用的是杜邦线连接且线比较长降到100kHz更稳定。// U8g2对接HAL库的核心配置 uint8_t u8x8_hal_i2c(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr) { switch (msg) { case U8X8_MSG_BYTE_SEND: HAL_I2C_Master_Transmit(hi2c1, u8x8_GetI2CAddress(u8x8), (uint8_t *)arg_ptr, arg_int, 100); break; case U8X8_MSG_BYTE_SET_DC: break; case U8X8_MSG_BYTE_INIT: break; default: return 0; } return 1; }用U8g2的好处是你不需要关心SSD1306的页布局、控制字节这些底层细节直接u8g2_DrawStr()就能显示字符串u8g2_DrawXBMP()就能显示位图。缺点是需要花点时间学习和配置但在项目扩展阶段比如你想在称重界面上画个动态进度条、画个电池图标U8g2会帮你省一大半时间。4. 数据滤波、标定与重量换算的完整流程4.1 原始数据不稳定的根源噪声类型与滤波策略直接读HX711的原始值你会发现它一直在跳动幅度可能从几十到几百个码。这并不意味着硬件坏了而是正常现象。称重系统的噪声主要来自两个方面一个是传感器本身的电阻热噪声这个没法消除只能抑制另一个是电磁干扰主要从电源线和连接线耦合进来。处理的思路分两步硬件上适当降低系统带宽比如在HX711的AVDD上并联一个大电容软件上做数字滤波。数字滤波我推荐滑动平均滤波不要用简单的算术平均。算术平均要攒够N个数据再一次性计算实时性差滑动平均是维护一个N长度的队列新数据进来挤掉最老的数据每次计算当前队列的平均值。这样既能平滑噪声又保证了数据的实时更新。我的参数是采样率10Hz也就是每秒读10次HX711滑动窗口取16次。这样每秒钟能更新两次左右的重量显示响应速度对于静态称重绰绰有余。如果你做的是动态称重比如在传送带上称移动的物体滑动窗口不要超过4次否则响应滞后严重。滤波代码长这样#define FILTER_LEN 16 int32_t filter_buf[FILTER_LEN]; uint8_t filter_index 0; int32_t filter_sum 0; int32_t MovingAverageFilter(int32_t new_value) { filter_sum - filter_buf[filter_index]; filter_buf[filter_index] new_value; filter_sum new_value; filter_index (filter_index 1) % FILTER_LEN; return filter_sum / FILTER_LEN; }这个实现比我见过很多教材上的版本好在哪儿它没有在每次滤波时把整个数组求和而是维护一个累加和每次只做一次减法、一次加法、一次除法时间复杂度是O(1)对单片机来说没有负担。4.2 标定全过程从原始码到克重标定是整个称重项目里最“玄学”的部分但它本质上只有两步测零点、测斜率。HX711返回的原始值是一个24位有符号数用uint32_t读取后需要转换成int32_t范围大约在-8388608到8388607。空秤状态下原始值是一个固定的较大数值取决于传感器和增益常见在几百万左右放上已知重量的砝码后原始值会增大或减小一个量。这个增量和重量之间是线性关系标定的目的就是找出这个线性系数。流程是这样的上电后空秤等待系统稳定记录100次滤波后的平均值作为零点值zero_value。放上一个已知重量的标准砝码我用的是500g标准砝码没有砝码的可以用一瓶500ml的矿泉水代替纯净水的密度接近1g/ml误差在0.5%以内记录滤波后的平均值point_value。那么比例系数scale就等于scale (float)(point_value - zero_value) / 500.0f;这里的单位是“每克的原始码数”。之后任意时刻重量的计算公式是float GetWeight(void) { int32_t raw MovingAverageFilter(HX711_ReadRaw()); float weight (float)(raw - zero_value) / scale; if (weight 0.5f) weight 0; // 零点附近强制归零避免负重量闪烁 return weight; }为什么要在零点附近强制归零因为HX711的数据即使经过滤波也会在零点附近有几个码的波动。这些波动换算成重量可能就是0.1g到0.3g的抖动。当你把东西全部拿走时屏幕上的数字不应该显示-0.2g或0.3g而是干脆利落地归零。0.5g阈值是我实践出来的太大比如1g会影响小物体的测量精度太小比如0.1g起不到防抖效果。4.3 校准漂移问题为什么今天标定的明天就不准了这是称重项目最常见的问题昨天标定得好好的今天一开机发现重量偏了。原因有几个。首先是温漂传感器和HX711都对温度敏感。早上和中午温差超过5度零点就可能偏移几十个码。解决办法是每次上电后重新做一次去皮读取当前的空载值作为零点而不是沿用之前存储的零点。所以我的代码里专门留了一个“去皮”按键每次开机自动去皮一次然后按键可以随时触发去皮。第二个原因是传感器的蠕变和滞后。传感器在持续受力后会慢慢地产生形变导致输出信号缓慢变化。在你放上重物后马上读数和放上几分钟后读数重量可能会有微小差异。这不是硬件坏了是传感器的物理特性。所以高精度的称重系统一般会加上“稳定判断”——连续几次读数的差值小于某个阈值时才认为稳定并显示最终值。我的简单做法是连续5次读数的最大值和最小值之差小于2g时判定为稳定这时候显示的重量才被锁定。第三个原因是机械结构。如果你用的不是成品称重传感器支架而是自己搭的木架、泡沫板、甚至书本垫底那么每次放置的方式不同、重心位置不同测出来的重量自然不同。传感器一定要固定在硬质平面上受力要垂直于传感器形变方向不能有侧向力。很多DIY称重改了代码半天没解决精度问题最后发现是传感器底座没固定好。5. 完整主流程与系统设计5.1 主程序状态机设计称重系统的逻辑说起来简单但直接全部塞进while循环里写后期想加功能会痛苦不堪。我采用状态机的写法把系统分成三个状态初始化状态、正常工作状态、校准状态。typedef enum { SYS_INIT 0, SYS_NORMAL, SYS_CALIBRATION } SystemState; SystemState state SYS_INIT; while (1) { switch (state) { case SYS_INIT: // 初始化OLED、HX711显示开机画面2秒后进入正常状态 state SYS_NORMAL; break; case SYS_NORMAL: weight GetWeight(); OLED_ShowWeight(weight); HandleKeyEvents(state); // 按键切换状态或触发去皮 break; case SYS_CALIBRATION: CalibrationRoutine(state); break; default: state SYS_INIT; break; } }状态机的好处是每个状态的处理逻辑清晰独立后续加功能比如显示历史记录、设置上限报警只需要增加新的状态枚举和case分支。尤其是校准状态因为需要提示用户“放砝码”、“等待稳定”、“记录数据”这几个步骤如果用乱糟糟的if-else嵌套流程会很难维护。5.2 OLED界面设计与按键交互OLED显示区域只有128x64像素不多但也不至于不够用。我的设计是这样分配的顶部一行显示当前模式或者状态信息用16x16的汉字显示“称重”“校准”“去皮”状态状态值用8x16的ASCII字符。中间一行是主显示区用大字我提过一个取模的32x48字体也可以直接把16x24字体放大两倍显示重量值单位g。底部一行显示采样次数、滤波后的原始码之类的调试信息正常使用时不显示按特定按键组合才出现。大字显示的做法是用取模软件生成一个“0”到“9”和“.”的32x48字体每个数字占32列48行。对于4位数单位g128像素的宽度刚好放下4个数字占128列单位g用8x16字体显示在右下角。这里要注意32x48的字体比较大一个数字的取模数据是48*4192字节因为32列/84字节一行48行共192字节十个数字加上小数点总共占用1900多字节Flash对F103C8T6来说完全没压力。按键我用两个GPIO引脚直接接轻触开关一个管去皮一个管校准模式。这里有个硬件设计的细节按键引脚要配置成上拉输入按键的一端接GND另一端接GPIO。按下时电平拉低松手后恢复高电平。不要配置成下拉输入然后接VCCF103的GPIO内部下拉电阻阻值比较大约40kΩ和外部上拉电阻配合时容易受干扰抖动也厉害。软件上要做消抖检测到电平变化后延时20ms再读一次确认状态一致才算有效否则会在按键按下和松开的瞬间读到大量跳变信号。5.3 定时器与低功耗策略如果你的称重项目是电池供电那就得考虑功耗问题。HX711的功耗有多大正常工作大约1.5mA左右OLED背光大约20mA到30mASTM32F103全速跑大约20mA。这三个加在一起如果用一块500mAh的锂电池供电基本只能撑几个小时。解决办法是让系统“间歇工作”用STM32的一个定时器产生周期性中断每500ms唤醒一次读取重量、刷新屏幕其余时间进入待机模式。进入待机模式后静态电流可以降到几个微安。实际测试1小时内的平均功耗从20mA降到0.5mA左右一节18650电池撑几个月没问题。这里的关键点是进入待机前要把HX711的SCK拉低让芯片进入低功耗状态OLED在刷新完最后一帧后不需要特别处理但要确保I2C总线不被卡死。复位唤醒后如果需要重新初始化I2C外设和SSD1306因为显示器在待机期间可能丢失配置务必在唤醒函数里重新执行OLED_Init()否则你醒来后屏幕可能是一团乱的。6. 常见问题与故障排查速查6.1 OLED显示异常专项不亮、花屏、歪屏OLED不亮的原因排查顺序很有讲究按概率从高到低排漏发0x8D命令后的0x14参数电荷泵没打开。这类问题代码层面用逻辑分析仪抓I2C总线最直接看看初始化命令里有没有0x8D和紧跟的0x14。I2C地址不对。0x3C还是0x3D看模块背面丝印有的模块用跳线帽切换地址。电压不够。OLED需要3.3V供电如果你用5V直接供电部分模块的稳压电路会过热甚至烧毁。我用过的大多数模块都能承受5V输入但建议看看手册再决定。接线顺序错误。I2C四根线SCL和SDA接反是常见低级错误用万用表蜂鸣档测模块引脚和MCU引脚之间的连通性可以确认。花屏是个更让人头疼的问题它通常分两种。第一种是刚上电时花屏几秒后恢复正常。这是初始化时序问题MCU上电后OLED内部还没有稳定初始化命令发得太快导致部分命令丢失。解决办法是在OLED_Init()最开始加一个20ms到50ms的延时给OLED上电时间。第二种是显示过程中出现水平条纹、随机闪烁。这多半是I2C总线信号质量问题总线电容过大、线太长、干扰源太近。把I2C速率从400kHz降到100kHz或者缩短连线就能改善。千万不要指望软件死等delay能解决花屏问题它治标不治本。还有一种是“歪屏”——显示内容整体向右偏移了几列或者上下错位。这是OLED初始化命令里0xD3Display Offset和0xA8Multiplex Ratio这两个参数设置不对导致的。不同碎屏厂家的SSD1306参数可能存在细微差异虽然标准值是0xD3后跟0x00、0xA8后跟0x3F但个别屏幕需要微调。遇到这种问题试着改显示偏移值有时候就能完美对齐。6.2 HX711数据异常一直为0、数值乱跳、正负交替HX711读出来一直为0而且不是那种超时返回的0我代码里超时返回0就要先检查DOUT引脚是不是一直为高。如果DOUT永远是高说明芯片没在工作或者没数据准备好。依次排查供电有没有到、SCK和DOUT有没有接反、MCU的GPIO有没有配置成正确的方向。数值乱跳的排查思路先用万用表量传感器的E和E-之间的电压应该接近HX711模块的供电电压。再量A和A-之间的电压不施加压力时应该在供电电压的一半左右差分电压接近0施加压力后应该有毫伏级的变化。如果A和A-之间的电压根本不变化传感器大概率坏了或者接线不对。如果电压变化正常但数值还是乱跳那问题在信号路径上——检查连接线是不是太长了、有没有和电源线走在一起。你也可以用一个干净的电源比如两节干电池给HX711模块供电对比一下是不是电源纹波惹的祸。还有一个非常隐蔽的问题DOUT引脚在模块上下拉电阻的阻值如果太大比如100kΩ以上在强干扰环境下容易被拉飞。如果你的模块DOUT输出阻抗高可以在STM32端额外加一个10kΩ上拉电阻增强抗干扰能力。这个方法我亲测有效在电机启动瞬间读数不再乱飘。6.3 精度上不去的三个隐性杀手很多人的称重项目做完后发现精度只能到±5g怎么调都上不去。我复盘了三个最常见的隐性原因采样率设置太高。HX711支持10Hz和80Hz两种采样率通过单片机端SCK的第25个脉冲之后额外再发一个脉冲选择通道时会影响PD_SCK的速率判断。简单说SCK在读取数据期间持续为高的时间决定了采样率。如果你的代码里SCK高电平时间过长芯片会自动切换到80Hz采样模式噪声会显著变大。标准做法是读取完24位数据后第25个脉冲的高电平时间保持在一定范围内约1us到60us不要过长。我代码里的delay_us(1)就是刻意压制这个时间让它稳定在10Hz左右。传感器量程选择不当。如果你的传感器量程是5kg却用500g的砝码做标定那称1kg以内的重量精度会很好但超过2kg后线性度可能变差。传感器的线性区通常只在满量程的20%到80%之间。选传感器时量程要留至少30%的余量不要在量程边缘使用。温度补偿缺失。HX711芯片内部有温度补偿但补偿曲线在不同批次芯片间存在差异。如果你做完标定后环境温度变了10度以上零点偏移可能高达几十个码。这时候重新上电、重新去皮是成本最低的解决方案。7. 经验总结与扩展建议这个项目做完我最大的体会是称重这个看似简单的功能真正做出来的难点不在代码而在对信号链路每一环的理解。从传感器的毫伏级差分信号到HX711的放大、采样、时序输出再到软件层的滤波、标定、界面显示任何一环松懈都会在最终精度上体现出来。很多新手把精力全花在调代码上忽略了机械结构的稳定性、电源的纯净程度、连接线的抗干扰能力结果代码逻辑再对读数还是不准。方向搞对了后面全是水到渠成的事。最后分享一个扩展方向HX711模块上不止通道A一个输入通道B虽然只能选32倍增益但它可以用来接第二路传感器。比如做一个双传感器称重平台可以通过两个HX711通道分别采集四个角受力算出重心位置。用我上面的基础框架改动的只是HX711_ReadRaw里第25个脉冲数量的变化——发26个脉冲切到通道B发25个切回通道A切换后第一次读到的数据是上一次配置的通道结果需要连续读两次才算数。这些细节都是实际调试多了才摸清楚的。希望这篇文章能帮你少走些弯路。
上一篇/下一篇内容由系统自动关联
返回资讯列表 →