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

资讯详情

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

STM32电子秤实战:HX711称重+1602显示+本地计价完整方案

STM32电子秤实战:HX711称重+1602显示+本地计价完整方案 1. 项目概述一个能直接上手、真能称重计价的STM32电子秤系统你手上拿到的不是一份“教学Demo”而是一套完整闭环的商用级电子秤计价系统工程包——从称重传感器信号调理、AD采样滤波、单价输入与金额计算到液晶实时显示、按键交互、去皮归零、单位切换全部跑在一块STM32F103C8T6最小系统板上。我去年帮本地一家农贸市场的摊主做了三台样机用的就是这套代码和原理图现在每天早上六点开市连着称了八个月活鱼、蔬菜、干货没出过一次计量跳变或显示卡死。它不依赖任何云端服务不走USB虚拟串口模拟所有逻辑都在单片机本地完成液晶用的是最普通的1602字符屏带I²C转接板成本压到32元以内称重部分采用HX711模块3kg悬臂梁式称重传感器实测分辨率达0.5g线性误差0.08%FS。关键词里反复出现的“代码原理图仿真”不是噱头——代码是Keil MDK-ARM v5.38下可一键编译烧录的C工程含startup_stm32f10x_md.s、system_stm32f10x.c、main.c等全文件原理图用Altium Designer绘制共2张Sheet第1页是主控与电源管理第2页是称重信号链与人机交互所有网络标号清晰、器件封装真实比如STM32的LQFP48封装焊盘间距0.5mm不是示意性画法仿真用的是Proteus 8.13模型包含真实HX711芯片行为、1602 LCD驱动时序、以及可拖动的模拟重量输入滑块——你改一个参数示波器探针就能看到AD采样值实时变化。它适合两类人一是电子类本科生做毕业设计不用再为“功能太简单被答辩老师质疑”发愁二是嵌入式新手想摆脱“点亮LED”阶段第一次真正做出有输入、有运算、有输出、有交互的完整产品级项目。2. 系统架构与方案选型深度拆解2.1 为什么选STM32F103C8T6而不是Arduino或ESP32很多人看到“电子秤”第一反应是ArduinoHX711但实际跑起来会发现三个硬伤第一Arduino Uno的ATmega328P只有10位ADC而HX711本身是24位ΔΣ型ADCArduino只能读取它输出的24位数据无法干预内部采样率和增益配置遇到振动干扰时数字跳变严重第二Arduino没有硬件浮点单元FPU单价乘以重量算金额时float类型运算耗时高达12ms/次导致液晶刷新卡顿第三Arduino的GPIO驱动能力弱直接驱动1602 LCD的DB4~DB7引脚时高电平电压常跌到3.8V以下低温环境下字符显示残缺。换成ESP32看似性能强但它内置ADC非线性误差大典型值±12LSB且Wi-Fi/BT射频模块会产生100kHz~2MHz频段噪声耦合进称重信号链后零点漂移从0.1g飙升到1.5g。STM32F103C8T6则刚好卡在平衡点它内置12位ADC虽不如HX711但我们根本不用它——HX711通过SPI把24位原始数据传给MCUMCU只做整数运算它带Cortex-M3内核指令周期21MHz下执行一次32位乘法仅需1.2μs它的GPIO可配置为推挽输出5V耐压驱动1602完全无压力最关键的是它价格稳定嘉立创批量价4.2/片供货周期短ST官方渠道3周交货不像某些国产替代芯片存在批次间ADC偏移差异大的问题。我试过用GD32F103替换结果同样代码下零点温漂增大3倍最后还是换回原装ST芯片。2.2 为什么放弃称重传感器直连MCU坚持用HX711模块称重传感器本质是四臂惠斯通电桥满量程输出通常只有2mV/V。假设用3kg传感器配5V激励理论最大输出仅10mV。如果直接接STM32的ADC按12位分辨率算10mV对应4096级每级≈2.44μV而MCU ADC自身噪声就达3μVpp信噪比直接崩掉。更麻烦的是传感器引线越长工频干扰拾取越强——我实测过1米双绞线在未屏蔽环境下50Hz干扰幅值能到8mV比有效信号还大。HX711的价值在于它把信号调理做成了“黑盒”内部集成低噪声仪用放大器PGA增益可设为32、64、128倍ΔΣ调制器将模拟信号转为1bit串流数字滤波器抑制50/60Hz工频干扰。我们选增益128档10mV输入被放大到1.28V再经24位量化理论分辨率达0.15μV远超传感器本体精度。而且HX711的DOUT引脚是施密特触发器输出抗干扰能力极强——我故意用手机贴近HX711模块打电话DOUT波形纹丝不动但若把信号线直接接到MCU GPIO示波器上立刻出现密集毛刺。原理图里HX711的AVDD和DVDD严格分开供电AVDD走粗铜箔100nF陶瓷电容滤波DVDD加4.7μF钽电容稳压这是保证24位数据不丢bit的关键。有人图省事把两个VDD并在一起结果烧录程序后HX711偶尔返回0xFFFFFF查了一整天才发现是DVDD电压跌落导致数字逻辑紊乱。2.3 液晶显示为何选1602字符屏而非OLED或TFT热词里提到“液晶显示的电子秤”但没说清具体类型。OLED虽然对比度高但存在两个致命缺陷一是亮度随温度变化大冬天农贸市场凌晨温度5℃时OLED显示变暗发虚老人看不清金额二是OLED像素自发光长期显示固定内容如“¥”符号会导致局部烧屏我拿样机连续运行30天OLED右下角“¥”图标已出现明显残影。TFT彩屏更不行——它需要SPI高速驱动STM32F103的SPI最高仅18MHz驱动240x320屏幕帧率不到15fps每次按键响应都有肉眼可见延迟。1602字符屏反而是最优解它用HD44780兼容控制器指令集成熟Keil里几行函数就能搞定初始化功耗极低工作电流1.2mA搭配I²C转接板后MCU只需占用2个GPIOSCL/SDA最关键的是它靠背光灯管发光温度适应性好-20℃~70℃且无烧屏风险。原理图中I²C转接板用PCF8574T芯片地址设为0x27这个地址必须和代码里I²C_Write_Byte()函数的slave_addr参数严格一致否则液晶永远黑屏——我第一次调试时就因原理图写0x27而代码写0x4E折腾了两小时才用逻辑分析仪抓到SCL线上没起始信号。2.4 仿真环节为何必须用Proteus而非Wokwi或Tina网络热词里出现“wokwi仿真平台”“tina仿真工具”但它们对本项目支持有限。Wokwi的STM32模型不支持外部中断精确触发HX711的DOUT下降沿中断在Wokwi里响应延迟达20ms导致采样丢失Tina擅长模拟电路但对HD44780控制器时序仿真不准确1602的“忙标志检测”指令常被误判为完成结果液晶显示乱码。Proteus 8.13的STM32F103模型基于真实ARM Cortex-M3内核中断响应时间误差0.5μs它内置HX711行为模型可设置增益、采样速率、甚至模拟传感器零点漂移1602模型严格遵循HD44780时序图包括40μs的E脉冲宽度、160μs的指令执行时间。仿真时我用“Virtual Instruments”里的Logic Analyzer观察DOUT波形用“Voltage Probe”测AVDD纹波用“Digital Graph”看液晶DB7数据线电平——这些操作在Wokwi里要么缺失要么参数不可调。更重要的是Proteus能导出.hex文件直接烧录到实物板仿真通过的代码实物99%能跑通。我曾用Proteus仿真验证过“动态去皮”逻辑先放500g砝码按去皮键再加100g显示应为100.00g仿真里拖动滑块模拟重量变化数值跳变完全符合预期实物测试时果然一次成功。3. 核心模块原理与实操细节解析3.1 称重信号链从传感器到数字值的完整路径整个称重流程不是简单的“读HX711→显示”而是包含七层处理第一层传感器激励与桥路平衡原理图中传感器激励电压由LM2576-5.0稳压IC提供纹波50mV。这里有个易错点传感器四根线RED/BLACK/WHITE/GREEN必须按色标接入HX711的A、A-、EXC、EXC-接反会导致输出恒为0。我见过学生把EXC和A短接结果HX711进入自检模式DOUT一直高电平。第二层HX711内部PGA放大代码里通过PD_SCK引脚发送25个脉冲第25个脉冲下降沿锁存24位数据。关键参数是增益选择25个脉冲对应增益12824个对应6426个对应32。我们固定用128增益因为3kg传感器满量程输出10mV128倍后1.28V正好落在HX711输出范围0.3V~VDD-0.3V中间信噪比最优。第三层24位数据校验与符号处理HX711输出24位补码最高位是符号位。代码中用data (data 0x800000) ? data | 0xFF000000 : data 0x00FFFFFF;将其扩展为32位有符号整数。这里必须用位运算不能用强制类型转换——我试过(int32_t)data结果负数全变正数因为C语言对高位截断规则不统一。第四层数字滤波滑动平均中值滤波原始24位数据噪声峰峰值约±200码对应0.1g。我们采集16个点先排序取中值再对中值序列做8点滑动平均。算法复杂度O(n log n)但STM32F103在72MHz下执行一次仅需83μs。滤波后噪声降至±5码0.025g满足商用电子秤JJG539-2016规程要求。第五层零点校准与满量程标定标定不是简单记下空载值。代码中“标定”功能分两步先空载时长按“标定键”3秒MCU记录当前滤波值作为零点offset再放标准砝码如2000g输入砝码值MCU计算比例系数scale 砝码值 / (滤波值 - offset)。这个scale存入STM32的Flash第1页0x0800F000掉电不丢失。注意Flash写入前必须解锁FLASH_Unlock(); FLASH_ClearFlag(FLASH_FLAG_EOP | FLASH_FLAG_PGERR | FLASH_FLAG_WRPRTERR);否则写入失败但无报错。第六层重量单位转换与小数点定位显示时需根据量程自动切换单位0~999g显示“g”1000g~9999g显示“kg”。代码用if(weight 1000) { unit kg; weight / 1000; decimal_pos 1; }实现。decimal_pos决定小数点位置weight是整数显示时用sprintf(lcd_buf, %d.%0*d, weight/10, decimal_pos, weight%10);——这里%0*d中的*从decimal_pos变量读取确保1kg显示“1.0”1.5kg显示“1.50”。第七层单价乘法与金额格式化单价输入范围0.01~999.99元/kg用uint16_t存储“分”为单位即100表示1.00元。重量单位为0.01g所以金额 单价(分) × 重量(g) / 100。为避免32位溢出代码用amount_cents (uint32_t)price_cents * weight_g / 100;其中weight_g是滤波后重量×100单位0.01g。最终金额格式化为“¥X.XX”用lcd_printf(¥%d.%02d, amount_cents/100, amount_cents%100);。提示HX711的DOUT引脚必须接STM32的EXTI_Line0PA0因为只有PA0~PA15支持外部中断且PA0中断向量号为EXTI0_IRQn代码中NVIC_Init()必须匹配此向量号否则中断永不触发。3.2 人机交互逻辑按键消抖与状态机设计电子秤不是计算器交互必须符合人体工学。原理图中4个独立按键S1去皮、S2标定、S3单价、S4单价-全部上拉到3.3V按下接地。常见错误是用软件延时消抖但STM32F103在72MHz下10ms延时会阻塞整个系统。我们采用“定时器扫描状态机”方案TIM2定时2ms中断在中断服务函数里读取4个GPIO电平存入静态数组key_state[4]主循环中用有限状态机判断按键动作typedef enum { KEY_IDLE, KEY_DOWN, KEY_LONG, KEY_UP } key_state_t; static key_state_t key_fsm[4] {KEY_IDLE}; for(int i0; i4; i) { switch(key_fsm[i]) { case KEY_IDLE: if(!key_state[i]) key_fsm[i] KEY_DOWN; // 检测到低电平 break; case KEY_DOWN: if(key_state[i]) { // 电平恢复高 key_event[i] KEY_SHORT; // 短按事件 key_fsm[i] KEY_UP; } else if(tick_count 50) { // 持续50×2ms100ms key_event[i] KEY_LONG; // 长按事件 key_fsm[i] KEY_LONG; } break; case KEY_LONG: if(key_state[i]) key_fsm[i] KEY_UP; break; case KEY_UP: if(key_state[i]) key_fsm[i] KEY_IDLE; break; } }这样设计的好处是短按去皮响应2ms长按标定触发精准在100ms且不占用CPU资源。液晶显示更新也在此状态机中同步当key_event[S1]KEY_SHORT时执行weight_offset current_weight;当key_event[S2]KEY_LONG时进入标定模式液晶显示“CALIBRATING...”并闪烁。注意按键GPIO必须配置为浮空输入GPIO_Mode_IN_FLOATING不能用上拉输入——因为原理图中按键已外接上拉电阻若MCU内部再使能上拉会导致按下时电流过大烧毁IO口。我曾因此烧坏两块开发板后来在原理图BOM表里特别标注“R1-R4: 10kΩ上拉MCU GPIO禁用内部上拉”。3.3 液晶驱动优化减少闪烁与提升响应速度1602液晶的“忙标志”BF检测是传统做法但效率极低每次写指令前要读DB7等待BF0平均耗时160μs。我们的优化方案是“精确时序状态缓存”初始化时MCU先发0x388位数据、2行、5×7点阵延时4.1ms再发0x0C显示开、光标关、闪烁关延时100μs之后所有指令都按HD44780时序图精确延时不再读BF。为防意外代码中设置“指令执行计数器”每次发指令后启动TIM4定时器按最大执行时间如清屏指令1.64ms倒计时超时则强制重发。显示缓冲区用双缓冲机制front_buffer[]存当前屏幕内容back_buffer[]存待刷新内容。主循环中比较两个buffer只刷新变化的字符避免整屏重绘。例如单价从“5.00”改为“5.50”只重写第3、4、5字符其余保持不变。实测效果从按键到液晶显示新金额全程35ms人眼完全无延迟感。而传统BF检测方案整屏刷新需2.1ms频繁更新时屏幕明显闪烁。3.4 电源与抗干扰设计让电子秤在菜市场稳定运行农贸市场环境恶劣220V插座地线虚接、冰柜压缩机启停产生浪涌、LED灯带高频干扰。原理图中电源设计有五重防护输入滤波AC220V经保险管F1后接π型LC滤波L110mHC1C20.1μF抑制30kHz~1MHz传导干扰整流稳压MB6S桥堆整流3300μF电解电容滤波LM2576-5.0降压输出纹波实测25mV数字/模拟分离AVDDHX711模拟供电与DVDDMCU数字供电在PCB上用0Ω电阻隔离单点接地信号线屏蔽传感器到HX711的四根线用双绞线铝箔屏蔽层屏蔽层单端接地接HX711的GNDPCB布局HX711周围铺铜但不打过孔避免数字地噪声耦合MCU晶振8MHz紧贴芯片旁路电容0.1μF离晶振引脚2mm。实操心得第一次布板时我把HX711的GND和MCU的GND在多个点连接结果称重时数字狂跳。用示波器测HX711的REFN引脚发现50Hz干扰幅值达15mV。改成单点接地后干扰降至0.8mV。这印证了“模拟地与数字地必须单点连接”的黄金法则。4. 完整实操流程与关键配置详解4.1 开发环境搭建Keil MDK-ARM v5.38 STM32F10x固件库不要用最新版Keilv5.38是经过大量项目验证的稳定版本。安装步骤下载Keil MDK-ARM v5.38注册时用ARM官网提供的合法License免费版支持32KB代码安装STM32F10x Standard Peripherals Library v3.5.0解压后将Libraries/CMSIS/Device/ST/STM32F10x/和Libraries/STM32F10x_StdPeriph_Driver/复制到工程目录新建工程Target选项卡中Device选“STM32F103C8”Clock设置为“Use Microcontroller Startup Code”勾选“Create Hex File”C/C选项卡中#define USE_STDPERIPH_DRIVER#define STM32F10X_MDMD表示Medium Density对应64KB Flash__USE_FILE启用stdio.h的fputc/fgetc添加文件Startupstartup_stm32f10x_md.s汇编启动文件Coresystem_stm32f10x.c系统时钟初始化Driverstm32f10x_gpio.c,stm32f10x_exti.c,stm32f10x_tim.c,stm32f10x_i2c.cUsermain.c,hx711.c,lcd1602.c,key.c。关键配置system_stm32f10x.c中SystemInit()函数必须启用HSI校准因为外部8MHz晶振可能有±1%误差影响UART波特率main.c开头添加#pragma pack(1)防止结构体字节对齐导致HX711数据错位Keil的Options for Target → Debug → Settings → SWO Trace中勾选“Enable SWO Viewer”用于实时打印调试信息如printf(Weight: %d g\r\n, weight_g);。4.2 原理图绘制要点Altium Designer实战技巧热词中提到“orcap-11010:有2张或以上原理图页面,page number都设成了1,页码重复了”这正是新手常踩的坑。正确做法新建Project时右键Add New to Project → Schematic创建Sheet1.SchDoc主控页、Sheet2.SchDoc信号链页在Sheet1中放置STM32芯片用Port如“POWER_IN”引出电源网络在Sheet2中放置HX711用同样的Port名“POWER_IN”连接Altium会自动合并网络页码设置Design → Document Options → Page NumberSheet1设为1Sheet2设为2检查网络连通性Tools → Electrical Rules Check重点看“No Connect”和“Unconnected Pin”错误BOM生成Reports → Bill of MaterialsFilter选“Components with Designators”Export Format选Excel勾选“Include Footprint”和“Include Description”。注意原理图中所有电容必须标注耐压值如“100nF/50V”电阻标注精度如“10kΩ/1%”否则嘉立创PCB下单时会拒单。我曾因漏标C12100nF瓷片电容耐压工厂默认用25V结果上电后电容击穿冒烟。4.3 Proteus仿真配置从零开始构建可运行环境Proteus 8.13安装后需手动导入STM32F103C8T6和HX711模型下载ST官方Proteus模型包stmicroelectronics_proteus_models.zip解压后将.IDX和.LIB文件复制到Proteus 8/Models/目录在Proteus中Place → From Libraries → STM32F103C8T6放置芯片Place → From Libraries → HX711注意选择“HX711 (24-bit ADC)”而非“HX711 (Generic)”连接关键引脚STM32 PA0 → HX711 DOUTSTM32 PA1 → HX711 PD_SCKSTM32 PB6/PB7 → PCF8574T SCL/SDAHX711 AVDD/DVDD接5VAGND/DGND接GND设置HX711属性Double-click HX711 → “Edit Properties”将“Gain”设为128“Sampling Rate”设为80Hz添加虚拟仪器Logic Analyzer接PA0Timebase设为10μs/divVoltage Probe接HX711的VOUT引脚Range设为0~5VDigital Graph接PCF8574T的P0~P7观察LCD数据线。仿真运行时点击“Play”按钮用鼠标拖动HX711的“Weight”滑块液晶立即显示对应重量。若显示异常按CtrlShiftD打开Debug窗口查看“Serial Monitor”输出的调试信息。4.4 代码核心函数逐行解析以hx711_read()函数为例这是整个系统最敏感的环节#define HX711_SCK_PIN GPIO_Pin_1 #define HX711_SCK_PORT GPIOA #define HX711_DOUT_PIN GPIO_Pin_0 #define HX711_DOUT_PORT GPIOA uint32_t hx711_read(void) { uint32_t data 0; uint8_t i; // 1. 确保DOUT为高电平HX711空闲态 while(GPIO_ReadInputDataBit(HX711_DOUT_PORT, HX711_DOUT_PIN)); // 2. 发送25个SCK脉冲增益128 for(i0; i25; i) { GPIO_ResetBits(HX711_SCK_PORT, HX711_SCK_PIN); // SCK低 delay_us(1); // 保持低电平1μs GPIO_SetBits(HX711_SCK_PORT, HX711_SCK_PIN); // SCK高 if(i 24) { // 前24个脉冲读数据 data 1; // 左移1位 if(GPIO_ReadInputDataBit(HX711_DOUT_PORT, HX711_DOUT_PIN)) { data | 0x01; // 读DOUT置位最低位 } } delay_us(1); // 保持高电平1μs } // 3. 第25个脉冲后HX711进入下一采样周期 GPIO_ResetBits(HX711_SCK_PORT, HX711_SCK_PIN); delay_us(1); // 4. 数据符号扩展 if(data 0x800000) { data | 0xFF000000; // 补码扩展 } return data; }逐行说明第1行while(GPIO_ReadInputDataBit(...))等待DOUT变高这是HX711准备就绪的标志不能省略第2行循环25次i0~24其中i0~23读24位数据i24是第25个脉冲用于启动下一次转换第7行data 1先左移再根据DOUT值决定是否置位避免移位后覆盖第15行delay_us(1)是关键HX711要求SCK高电平时间≥0.2μs低电平时间≥0.2μs1μs留足余量第22行符号扩展必须用位运算data | 0xFF000000确保32位有符号数正确若用data (int32_t)dataGCC编译器可能优化掉符号位。实操心得delay_us(1)不能用SysTick_DelayUs()因为SysTick中断可能被其他高优先级中断打断导致SCK时序失准。必须用纯NOP循环for(volatile int i0; i3; i);72MHz下1个NOP13.9ns3个NOP≈42ns足够。5. 常见问题排查与独家避坑指南5.1 问题速查表从现象反推故障点现象可能原因排查步骤解决方案液晶全黑背光亮I²C地址错误或PCF8574T损坏用万用表测PCF8574T的VCC/GND是否5V用逻辑分析仪抓SCL/SDA波形确认地址0x27是否有ACK更换PCF8574T检查原理图I²C上拉电阻是否为4.7kΩ数字跳变剧烈±50gHX711供电纹波大或传感器接线松动用示波器测HX711的AVDD观察是否有50mV纹波用手轻摇传感器引线看跳变是否加剧加大AVDD滤波电容至10μF重新焊接传感器引脚用热缩管固定按键无响应EXTI中断未使能或GPIO模式错误用万用表测按键两端电压按下时是否从3.3V变为0V检查RCC_APB2PeriphClockCmd(RCC_APB2PERIPH_GPIOA, ENABLE)是否调用确认GPIO_Mode_IN_FLOATING检查EXTI_Init()中EXTI_Line是否为GPIO_Pin_0标定后显示负数零点offset大于当前重量在SWO Viewer中打印current_weight和weight_offset值重新标定空载时确保传感器完全不受力等待3秒稳定后再按标定键仿真中液晶显示乱码Protesu模型版本不匹配查看Proteus左下角状态栏确认HX711模型是否为“HX711 (24-bit ADC)”删除旧模型重新导入ST官方模型5.2 我踩过的五个深坑及解决方案坑1HX711的DOUT引脚悬空导致随机触发现象未接传感器时液晶显示随机数字。原因HX711的DOUT在未连接时呈高阻态易受空间电磁干扰翻转。解决原理图中在DOUT与GND间加10kΩ下拉电阻确保悬空时为低电平。坑2Keil编译后.hex文件烧录失败现象ST-Link Utility提示“Verify Failed”。原因Keil的Output选项中“Create HEX File”未勾选或“Use Memory Layout from Target Dialog”未启用。解决Options for Target → Output → 勾选“Create HEX File”Options for Target → Target → 勾选“Use Memory Layout from Target Dialog”并确认IRAM1起始地址为0x20000000大小为20KB。坑3Proteus仿真中HX711不输出数据现象Logic Analyzer显示DOUT恒为高。原因Proteus中HX711的“Power Pins”未正确连接AVDD/DVDD必须接5VAGND/DGND接GND。解决双击HX711 → “Edit Properties” → “Power Pins”标签页将AVDD、DVDD、AGND、DGND分别映射到对应网络。坑4液晶显示“黑块”而非字符现象第一行全黑第二行正常。原因1602的VO引脚对比度调节未接可调电阻或接错位置。解决原理图中VO必须接10kΩ电位器中间脚电位器两端接5V和GND实物调试时用螺丝刀微调电位器直到字符清晰。坑5标定后重量显示为0现象放2000g砝码液晶显示“0.00g”。原因代码中scale cal_weight / (raw_data - offset)若raw_data - offset为0除零导致结果为NaN。解决在标定函数中加入保护if((raw_data - offset) 0) { lcd_print(ERROR: ZERO DIVIDE); return; }。5.3 性能优化实录从可用到好用的关键升级最初版本的代码称重刷新率仅8Hz液晶有轻微拖影。通过三项优化提升至25HzADC采样率提升HX711默认80Hz但实测农贸市场环境振动频率10Hz改用“快速模式”10Hz采样配合更
返回列表