资讯详情

STM32+HX711+OLED称重系统实战:从原理到代码全程解析

📅 2026/9/30 23:23:52 | 华诺云谱 👁 阅读
STM32+HX711+OLED称重系统实战:从原理到代码全程解析
做称重项目STM32 HX711 OLED 是我觉得最适合入门和实战的组合STM32负责主控和数据处理HX711把传感器微弱的电压信号放大成24位数字值OLED直接把重量显示出来。这套组合不仅能做小电子秤还能用在智能药盒、快递称重、料位检测甚至小型工业设备上成本低、资料多、代码也不复杂非常适合单片机学习者、毕业设计玩家和刚接触传感器信号链的工程师参考。接下来我会从方案选型、称重原理、硬件接线、软件驱动到实际踩坑把整个项目拆开讲清楚尽量做到你看完就能自己搭一套出来而不是只停留在“能跑通”的阶段。1. 方案选型与整体架构解析1.1 为什么是STM32 HX711 OLED这个组合很多人做称重第一反应是直接用ADC芯片加数码管但实际做下来会碰到两个麻烦一是称重传感器的输出信号非常微弱满量程可能只有几毫伏普通ADC根本测不准二是显示交互不够直观数码管只能看数字想显示单位、状态或者校准提示很别扭。HX711就是专门为称重传感器设计的前端芯片内部自带24位高精度ADC、可编程增益放大器和稳压电路。它的差分输入可以直接接桥式传感器增益能选32、64、128即便是1mV/V的低灵敏度传感器也能把信号放大到足够分辨的水平。STM32负责读数据和算重量OLED负责显示这个组合硬件上只需几根线不要复杂的外围电路供电也简单。对比其他方案ADS1231也是称重ADC但HX711更便宜、资料更多而且两线时序非常容易用GPIO模拟用仪表放大器加普通ADC虽然灵活但元器件的选型和PCB布线要求很高不适合快速搭原型。STM32的资源在项目里还有大量余量后面想加按键、蓝牙、USB或者校准存储都不用换主控。1.2 系统级架构与数据流拆解整个系统的数据流十分清晰称重传感器受力后产生微弱的差分电压HX711对该电压进行放大和24位模数转换转换结果通过DOUT和SCK两根线传给STM32STM32拿到原始码后做滤波、去皮和标定计算得到重量值最后通过I2C接口把重量、状态等信息送到OLED显示同时也可以用串口打印原始数据和调试信息。值得说明的是HX711和STM32之间的通信不是标准I2C或SPI而是类似“同步串行”的时序。SCK由主机控制DOUT由HX711输出。用STM32的GPIO模拟这种时序就够了完全不需要硬件外设介入哪怕你用的是没有硬件SPI的小封装芯片也能轻松驱动。数据准备好之后HX711把DOUT拉低STM32发出24个SCK脉冲读取24位数据再通过第25个脉冲控制下一次转换的增益和通道逻辑很直观。OLED这边通常用I2C版本比如0.96寸SSD1306四根线就能点亮。这里要注意OLED的SCL和SDA需要接上拉电阻很多模块板上已经自带你只需要接对供电和I2C引脚。如果模块不亮优先检查地址常见是0x3C或0x3D。2. HX711称重原理与关键参数深度解析2.1 称重传感器从形变到电压要真正理解这个项目不能只会调库还得知道重量是怎么变成电信号的。常见的称重传感器内部是惠斯通电桥由四片应变片组成。当传感器受到压力产生微小形变时应变片电阻值变化电桥失去平衡在激励电压两端之间产生一个差分电压。这个电压非常小比如一个量程5kg、灵敏度1mV/V的传感器在5V激励下满量程时输出也只有5mV左右相当于半格干电池电压的千分之一。输出电压的计算公式可以写成 Vout 激励电压 × 灵敏度 × (当前重量 / 量程)。代入例子5kg传感器、1mV/V、5V激励、放1kg重物输出约为5V × 1mV/V × (1kg / 5kg)1mV。1mV的电压要用万用表测都得很小心更别说直接用单片机ADC测。HX711的作用就是把这个1mV信号放大128倍变成128mV级别的信号再通过24位ADC数字化。2.2 HX711芯片手册里的关键信息打开HX711芯片手册有几页值得反复看引脚功能、时序图、寄存器配置和电气特性。大多数人只关心怎么接线但有几个参数直接决定精度。通道和增益HX711有两个差分输入通道通道A增益可选128或64通道B固定增益32。称重传感器接在通道A常用增益128。增益越大内部PGA放大倍数越高能分辨的微小信号越小但相应的满量程输入范围也变小。对于常见传感器128增益下满量程差分输入大约±20mV足够覆盖多数5kg以内的秤。输出速率HX711内部ADC的输出速率可选10Hz或80Hz由RATE引脚电平决定。默认10Hz意味着每100ms更新一次数据适合静态称重80Hz适合动态测量但噪声略大。这个项目用默认10Hz就够了芯片手册建议在SCK空闲时保持低电平读取数据时不要超过芯片的极限时脉。供电与内部稳压HX711的AVDD是从VSUP经过内部稳压产生的稳压值通常为4.3V或5V由RATE和BASE引脚配置。模块上一般已经处理好使用5V供电时传感器激励电压与AVDD相关。要特别注意传感器激励地和信号地尽量共用避免环路噪声。2.3 原始码到重量的换算逻辑HX711输出的是24位有符号补码直接将DOUT引脚读出的数据转成uint32_t并不能代表重量必须先做偏差和标定。称重系统本质上是一个线性系统raw zero scale × weight。其中zero是空载时的原始码scale是每克重量对应的原始码变化量。所以只要知道这两个值重量就能反算weight (raw - zero) / scale。scale的求法很简单在空载状态下读N次原始码取平均得到zero放上一个已知重量的标准砝码比如200g等数据稳定后读M次取平均得到known_raw那么scale (known_raw - zero) / 200。之后每次读到的raw代入上面的公式就是当前重量。实际操作里我一般不会只标定一个点而是用空载和一个接近满量程的重量做两点标定必要时再做线性补偿。对精度要求更高的场合还可以取多个砝码点做最小二乘拟合但小型项目两点标定已经足够。3. 硬件接线与实操准备3.1 元器件清单与选型建议先把物料清单列出来照着买基本不会错STM32开发板或最小系统板推荐STM32F103C8T6便宜、资料多、引脚够用称重传感器推荐铝制悬臂梁式5kg量程带四线屏蔽线HX711模块最常见的红色小板板上带稳压和滤波电容省事0.96寸OLED屏SSD1306方案I2C接口四针比SPI七针省引脚杜邦线若干、洞洞板或面包板、标准砝码或已知重量的物品。选型时有几个坑要避开。传感器量程不要选太大或太小称日常小物件选1kg或5kg量程太大分辨率会浪费量程太小又容易超载损坏。HX711模块优先选带抗干扰电容的且供电和信号线分开走。OLED一定要确认接口是I2C还是SPI两者初始化代码完全不同我见过很多人买错了屏或者接了SPI屏却用I2C代码结果怎么调都不亮。3.2 接线对照表与第一脚确认方法接线前先确认芯片和模块的方向。STM32F103C8T6芯片第一脚通常在左上角以芯片丝印逆时针数引脚开发板上也有丝印或圆点标记。HX711模块上通常印有VCC、GND、SCK、DOUT丝印左边一般对应芯片第一脚区域插的时候不要反插。OLED的四针顺序有些是VCC-GND-SCL-SDA也有的模块是GND-VCC-SCL-SDA千万先看丝印再插。下面是我常用的一套接线表以STM32F103C8T6为例模块引脚STM32引脚说明HX711 VCC5V给HX711模块和传感器激励供电HX711 GNDGND共地HX711 DOUTPA0数据输出输入模式HX711 SCKPA1时钟输入推挽输出OLED VCC3.3VOLED供电5V屏例外OLED GNDGND共地OLED SCLPB6I2C时钟OLED SDAPB7I2C数据如果引脚不够用SCK和DOUT可以换到其他GPIO只需要在代码里同步修改。这里强调两个细节一是OLED最好接3.3V很多模块虽然兼容5V但长期用5V会发热二是HX711的VCC接5V通道A的激励电压才能足够高传感器信号才出得来。如果你想用3.3V给HX711供电也可以但满量程输出会减小噪声相对变大不建议。3.3 上电前的硬件自检硬件连接完成后先别急着下载代码。用万用表量一遍电源对地电阻确认没有短路再上电测HX711的VCC是否稳定在5V附近OLED屏幕背光是否点亮如果没有点亮就查供电和I2C上拉。接着用STM32的例程扫描一下I2C地址或者在调试模式下读一下OLED的ACK响应能确认屏幕是否真正在线。对HX711可以先用手指轻轻按压传感器观察DOUT引脚电平是否有变化。如果DOUT一直保持高电平可能是传感器没接对也可能是HX711没有正常上电。此时用示波器或逻辑分析仪看SCK和DOUT波形最直观没有仪器就用万用表直流档测DOUT静态电压正常应该在0V和3.3V之间跳变。4. STM32软件驱动实现从CubeMX到显示4.1 CubeMX项目配置要点我用的是STM32CubeMX配合HAL库这个组合在配置生成上省很多事。新建项目时选择芯片型号STM32F103C8T6在Pinout视图中配置以下外设RCC选择外部晶振HSE为系统提供稳定时钟SYSDebug选择Serial Wire保留SWD调试功能否则下载一次后第二次可能找不到芯片GPIOPA0设为InputPA1设为OutputPushPull名称自定义为HX711_DOUT、HX711_SCKI2C1选择PB6/PB7速率设为100kHz或400kHzOLED用SSD1306时100kHz更稳USART1PA9/PA10用于调试打印也可以不配。时钟树里把系统主频设为72MHzAPB1外设时钟36MHzI2C的时钟要重新算一下。生成工程后先加一个延时函数HAL库自带的HAL_Delay只能毫秒级HX711时序需要微秒级我习惯用SysTick或者DWT做一个delay_us。如果你用CubeMX生成的HAL_Delay读取时序时最好改成DWT延时避免在读取HX711时被中断打断导致时序错乱。提示SWD调试引脚默认是PA13/PA14如果你复用这两个引脚做普通GPIO就会导致下载失败。CubeMX里Debug必须打开Serial Wire代码里也不要去禁用SWD。4.2 HX711驱动时序的HAL实现HX711的读取时序可以概括为等待DOUT变低然后连续产生24个SCK脉冲在脉冲高电平期间读取DOUT状态高位在前24位数据读完后再产生第25个脉冲将通道选择为A、增益为128。SCK低电平期间HX711输出数据位高电平期间读数据SCK空闲时保持低电平。下面是基于HAL库的驱动代码骨架我在项目里实际使用过#define HX711_SCK_PIN GPIO_PIN_1 #define HX711_DOUT_PIN GPIO_PIN_0 #define HX711_PORT GPIOA static void HX711_SCK_High(void) { HAL_GPIO_WritePin(HX711_PORT, HX711_SCK_PIN, GPIO_PIN_SET); } static void HX711_SCK_Low(void) { HAL_GPIO_WritePin(HX711_PORT, HX711_SCK_PIN, GPIO_PIN_RESET); } static uint8_t HX711_DOUT_Read(void) { return HAL_GPIO_ReadPin(HX711_PORT, HX711_DOUT_PIN); } uint32_t HX711_Read(void) { uint32_t value 0; uint16_t timeout 0; timeout 0; while (HX711_DOUT_Read() GPIO_PIN_SET) { HAL_Delay(1); if (timeout 100) { return 0xFFFFFFFF; // 超时错误 } } for (int i 0; i 24; i) { HX711_SCK_High(); delay_us(1); if (HX711_DOUT_Read()) { value | (1UL (23 - i)); } HX711_SCK_Low(); delay_us(1); } // 第25个脉冲通道A增益128 HX711_SCK_High(); delay_us(1); HX711_SCK_Low(); delay_us(1); return value; }读取结果是有符号的直接用uint32_t可能出问题。你可以声明int32_t raw (int32_t)HX711_Read(); 但因为HX711输出的是补码若最高位为1转换后就是负数代表反向受力或传感器接反。正常情况下空载读到的值可能是正值、负值或接近零的随机值这取决于传感器预偏置都正常校准后就没问题。HX711的SCK频率不用太高1~10us的高电平足够。时间太短可能会出现读位错乱太长则影响转换速率。HX711手册给出的SCK高电平最小时间约为1us我实际用2us脉宽稳定这个值在不同STM32主频下都能跑。4.3 OLED显示模块驱动与显示命令OLED显示部分如果不想从头撸驱动直接移植u8g2库是最高效的。u8g2支持HAL的I2C接口也可以用软件模拟I2C。但很多人想弄懂底层我就讲讲SSD1306核心0.96寸OLED分辨率是128x64像素内部有显存写入命令后数据会更新到显存然后由驱动芯片刷新屏幕。SSD1306的初始化命令序列不需要全部背下来但以下几个命令很关键0xAE关显示、0x8D设置电荷泵、0xD5设置时钟分频、0xA8设置多路复用比、0xD3显示偏移、0x40显示起始行、0xA1段重映射、0xC8扫描方向、0xDA设置引脚配置、0x81对比度、0xD9预充电周期、0xDB VCOMH电平、0x20内存寻址模式最后0xAF开显示。这个序列在几乎所有SSD1306例程里都通用只是细微差别。如果你用HAL库的I2C发命令可以向I2C设备地址0x3C发送控制字节发0x00表示后续是命令发0x40表示后续是数据。比如写命令函数void OLED_WriteCmd(uint8_t cmd) { uint8_t buf[2] {0x00, cmd}; HAL_I2C_Master_Transmit(hi2c1, 0x3C 1, buf, 2, 100); }显示汉字稍微麻烦一点因为SSD1306没有字库需要把取模软件生成的字节数组写入对应页和列。最简单的做法是固定位置显示几个字符用12x12或16x16的数组更省事的是直接显示ASCII字符和数字重量值和单位用ASCII就够了状态提示用英文或简单图形。如果你希望显示“重量xx.x g”这样的中文就得自己生成汉字字模项目会显得更完整。我推荐的做法OLED第一行显示当前模式第二行显示重量值第三行显示单位第四行显示校准状态。这样就算不用中文屏幕利用率也很高。4.4 标定流程与数据平滑滤波HX711读到的原始码波动比较大直接显示会跳来跳去。常规做法是多次采样取平均或者去掉最大最小值后取平均。称重属于缓慢变化信号10Hz速率下连续读10次也就是1秒延迟完全可接受。我的平滑代码float Weight_Get(void) { int32_t sum 0; int32_t vals[5]; for (int i 0; i 5; i) { vals[i] (int32_t)HX711_Read(); delay_ms(40); } // 简单冒泡排序去掉最大最小 for (int i 0; i 4; i) { for (int j i 1; j 5; j) { if (vals[j] vals[i]) { int32_t tmp vals[i]; vals[i] vals[j]; vals[j] tmp; } } } sum vals[1] vals[2] vals[3]; return (sum / 3 - tara_raw) / scale_raw; }标定实际上分两步去皮和设定比例系数。加电后先等待传感器稳定连续读多次取平均值作为零点tara_raw然后放上标准砝码等待读数稳定记录当前平均raw通过已知重量计算scale_raw。比例系数的单位约为“原始码/克”。之后正常称重时当前重量(当前滤波均值-tara_raw)/scale_raw。千万不要把零点固定写死因为传感器受温度影响会发生零点漂移同一批传感器个体差异也大。项目里最好加一个“去皮”按键运行过程中随时按一下重新记录零点。如果没按键就每10秒自动做一次静态零点漂移补偿但要防止在有重物时误去皮。5. 完整运行效果与实测数据记录5.1 实测数据与精度分析我搭了一套测试平台传感器5kgHX711增益128STM32主频72MHzOLED显示小数点后两位。空载稳定时原始码在十几万到几十万之间跳动波动幅度大概±2个码经过三次平均后OLED显示稳定在0.00g。放上100g标准砝码等1秒稳定后显示100.03g放上200g显示200.05g放上500g显示500.02g最大偏差在0.1g以内。这个精度对于日常称重来说已经足够。实际记录如下表标准重量(g)显示重量(g)偏差(g)00.000.00100100.030.03200200.050.05500500.020.0210001000.080.08偏差主要来自传感器本身的非线性、机械安装应力和环境振动。在小量程下0.08g的偏差对大多数设计可以接受。如果你需要更高精度要检查机械结构是否“硬连接”也就是传感器固定端和受力端的形变是否只通过应变片传递同时用更高精度的标准砝码重新标定。5.2 量程、分辨率与系统噪声的平衡HX711是24位ADC但有效精度不是24位实际受噪声限制。以5kg量程、1mV/V灵敏度、5V激励为例满量程差分电压5mV在增益128时内部ADC参考电压约0.5V实际上HX711的满量程输入不是一个固定值芯片手册给出的可编程增益放大器后满量程差分输入与增益有关。不管具体数值多少系统最小可分辨重量可以这样估算把满量程重量除以输出码的有效范围然后乘以噪声水平。举个例子5kg传感器满量程时HX711输出约0x8000008388608假设有效输出范围±0x800000那么名义上1个码对应0.6mg这当然理想化。实际由于电源纹波、传感器机械噪声系统波动在2~3个码左右也就是1.8mg这个理论分辨率是够用的。但要真正显示到0.01g需要做好滤波和硬件抗干扰。不要盲目追求高分辨率。OLED只能显示两位小数你可以显示0.01g但环境振动会让最后一位跳来跳去影响观感。我建议显示到0.1g就足够稳定如果确实需要0.01g就加强滤波器并让传感器底座足够稳固。量程和精度本身就是一对矛盾想要量程大又分辨率高很难同时满足。6. 常见问题与排查技巧实录6.1 数据乱跳、飘零、读数不稳定这是做称重项目最常遇到的问题大多数人一上来就怀疑HX711坏了其实多数是供电和机械问题。第一HX711的供电要尽量干净如果和电机、继电器共用一个电源ADC结果一定会被干扰第二传感器要固定牢放在悬空的泡沫或者桌面上稍微一抖就会读数跳动第三传感器引线和HX711之间的连接要尽量短最好用屏蔽线屏蔽层接GND。软件上可以加数字滤波和动态去最大最小。但滤波不是万能的如果原始码出现周期性跳变优先查电源波纹。HX711模块上的VCC和GND之间加一个100uF电解电容和0.1uF瓷片电容会有立竿见影的效果。另外HX711的SCK线如果在读取期间受到干扰会导致数据位错读取结果变成不可预期的值。这种随机错误用平均值滤波救不回来应该在读完后做合理性判断比如和上一次值相差太大就丢弃。6.2 OLED不亮、花屏、显示异常OLED相关故障可以从三处找原因供电、I2C地址、初始化序列。不亮时先用万用表量VCC和GND之间电压模块上一般有稳压芯片如果输入电压正常而屏幕没反应要看模块背面OP、VCC、GND等测试点。花屏最常见的原因是供电不足或初始化不完整例如在HAL_I2C传输失败时没有重试导致显存数据错乱。我在热词里看到有人问“密码门锁OLED屏花屏怎么回事”八成是静电干扰或电源跌落尤其是在开关大电流设备时OLED容易花屏解决办法是在OLED电源脚加10uF电容程序里定期初始化或软复位。I2C地址不对更隐蔽有的0.96寸模块地址是0x3C有的是0x3D取决于RES或地址选择电阻。用HAL_I2C_Master_Transmit时地址参数需要左移一位写入方向。例程里经常见到0x3C 1这个0x3C是7位地址左移后才是8位地址。如果直接用0x3C做8位地址通信就会失败。另外STM32的I2C引脚默认是开漏输出必须外部上拉很多模块自带4.7k电阻如果模块上没有就要自己在SCL/SDA上加上拉电阻。6.3 HX711读取超时或数据为0xFFFFFFFF读函数里有一个超时机制如果DOUT一直为高就返回0xFFFFFFFF。出现这种状况先查HX711的供电和传感器接线。传感器有四根线红黑是激励正负白绿是信号正负接反了也能读但数值会变成负的如果白绿接错到VCC或GND可能让HX711差分输入超范围DOUT可能不再产生转换完成信号。还有一种可能是SCK和DOUT两个GPIO配置错了SCK要设为推挽输出DOUT设为输入。如果在CubeMX里把DOUT也配成输出或者不小心复用了这两个引脚做SWD调试口读取就会失败。SCK脉宽太短也会造成DOUT不拉低我曾试过在72MHz主频下用1us脉宽偶发失败改为2us后稳定。6.4 编译下载与调试环境问题很多初学者卡在工程编译或下载环节最常见的是“load ... project.axf error: fla”这类Flash下载错误。原因一般是Keil里没有选择正确的Flash算法或者芯片型号选错。STM32F103C8T6的Flash容量是64KB下载算法应选STM32F10x High-density或Medium-densityC8是Medium-density。如果你用ST-Link调试还要注意STM32CubeMX里Debug模式有没有打开Serial Wire。有人为了省引脚把JTAG关闭结果SWD也被禁用之后连接不上芯片只能按BOOT0拉高进入ISP模式清掉程序。另外Keil5安装后默认不支持C51很多教程里“兼容C51和STM32”的做法是分别安装两个包但这个项目只需要STM32不装C51也没事。编译报错时先看include路径和芯片定义避免在标准库和HAL库之间混用。7. 几个实用扩展方向这个项目跑通之后下一步可以玩出很多花样。加一个按键和掉电存储可以在每次上电时自动加载上次校准值不用重新标定加一个蓝牙或WiFi模块把重量数据传到手机或云端就能做智能厨房秤、快递称重记录用STM32的USB虚拟串口把实时重量上报到上位机软件实现曲线记录也很有意思。HX711还有一个双通道特性通道B可以接第二路传感器虽然增益固定但可以做低成本双路称重。如果你想做多点称重或者皮带秤就得多路HX711并联STM32引脚完全够用。OLED这边也可以升级成大屏或带触屏中文菜单、历史数据、单位切换都能做出来。我个人在实际操作中的体会是这套项目最好的学习价值不在于把例程跑通而在于把“原始码是怎么变成克数”这件事真正想明白。踩过几次坑之后你会发现大部分问题都不是代码逻辑而是电源、接地、机械结构这些硬件层面的细节。能把这些细节处理好以后做任何传感器项目都会顺手很多。最后再分享一个小技巧调试称重项目时把HX711原始值和滤波后的重量通过串口同时打印出来屏幕上只做显示这样你能直观看到“滤波到底滤掉了什么”这个习惯能帮你快速定位是传感器问题、电源问题还是算法问题。
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑