多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

从零构建高精度电子秤:基于STM32与HX711的嵌入式系统实践

从零构建高精度电子秤:基于STM32与HX711的嵌入式系统实践 1. 项目缘起从“差不多”到“精准称重”的转变几年前我在一个DIY工坊里帮忙经常需要称量一些电子元件和化学试剂。那时候用的是一台老旧的机械台秤精度差不说读数还得靠估摸每次称个几克的东西都提心吊胆。后来接触到一个开源硬件项目需要精确控制物料配比机械秤的误差直接导致了实验结果的不可重复。这事儿让我下定决心自己动手做一个高精度、数字化的电子秤。市面上成品电子秤当然很多但要么功能过剩价格昂贵要么核心参数不透明对于想了解其工作原理、或者有特殊定制需求比如超大量程、超低功耗、数据无线传输的爱好者来说一个从零搭建的“基于单片机的电子秤”项目其价值远不止于得到一个称重工具更在于彻底掌握一套将物理量压力转化为数字信号并精确处理的完整技术链路。这个项目麻雀虽小五脏俱全。它涉及传感器选型、模拟信号调理、高精度模数转换、单片机数据处理、人机交互设计等多个嵌入式开发的核心环节。无论你是电子专业的学生想做一个综合性的课程设计还是创客爱好者想给自己的咖啡机、喂食器加个精准配料功能亦或是工厂里需要为一个非标设备定制称重模块这个项目都能提供一个清晰、可复现的蓝本。接下来我将以一个实际可用的50kg量程、精度可达1g的电子秤为例拆解其从硬件选型到软件调试的全过程并分享那些只有亲手做过才会知道的“坑”和经验。2. 核心硬件选型传感器、ADC与MCU的黄金三角电子秤的精度和稳定性七分靠硬件。硬件设计的核心是处理好“传感器-ADC-MCU”这个信号链。选型不当后续软件再怎么优化也是事倍功半。2.1 称重传感器HX711模块为何是首选称重传感器的本质是一个电阻应变片当它受力形变时其电阻值会发生微小变化。通常四个应变片会连接成惠斯通电桥将微小的电阻变化转换为差分电压信号。对于爱好者和小批量项目直接使用成熟的“称重传感器 HX711模块”组合是最务实的选择。为什么是HX711因为它是一个专为电子秤设计的24位模数转换器ADC芯片。它内部集成了可编程增益放大器PGA和稳压电路直接与应变片电桥对接。其核心优势在于高分辨率24位ADC在5V供电、增益128时理论上最小可检测的电压变化极低为高精度称重提供了基础。集成度高外围电路极其简单通常只需要几个电容和电阻大大降低了硬件设计难度和噪声干扰。成本低廉模块价格通常在十元以内性价比无敌。传感器选型要点量程选择比最大称重量略大的传感器例如最大称重40kg可选50kg传感器留出余量以保护传感器并提高在常用量程内的线性度。灵敏度通常为2.0mV/V。这意味着在额定电压如5V激励下达到满量程时输出差分电压为10mV。这个信号非常微弱正好需要HX711内部的高增益放大器来放大。接线方式常见的有4线制和6线制。6线制多了两条 Sense 和 Sense- 线用于补偿长导线带来的压降使实际加载在电桥上的电压更稳定。对于精度要求高或导线较长的场景建议选用6线制传感器HX711模块也支持。注意购买传感器时一定要确认其配套的“接线颜色定义”。红E、黑E-为激励电压正负绿O、白O-为信号输出正负。不同厂家颜色可能不同接错会损坏传感器或模块。2.2 单片机STM32与Arduino的权衡MCU负责读取HX711的数据、进行滤波和标定计算、驱动显示和响应按键。选择哪款MCU取决于项目复杂度和对实时性的要求。Arduino (ATmega328P)适合快速原型验证和初学者。其优势在于生态丰富有现成的HX711库几行代码就能读出重量值。对于只需要基本称重和显示功能的项目它是完美的起点。缺点是处理能力和外设相对有限如果要实现复杂的菜单、数据存储、无线通信如蓝牙/Wi-Fi或多任务会显得力不从心。STM32 (如STM32F103C8T6)推荐用于任何严肃的、或有扩展需求的项目。它基于ARM Cortex-M内核主频更高72MHz资源更丰富更多的GPIO、定时器、通信接口。虽然需要配置开发环境如Keil、STM32CubeIDE但一旦掌握其灵活性和性能是Arduino无法比拟的。例如你可以轻松地用定时器中断实现精确的定时采样用DMA搬运数据而不占用CPU用硬件I2C/SPI驱动更复杂的显示屏。我的选择与理由在本项目中我选择STM32F103C8T6即常见的“蓝色药丸”开发板。原因有三第一我希望电子秤具有“自动零点跟踪”和“动态滤波”等进阶功能需要较强的算力第二我计划未来增加蓝牙模块将称重数据发送到手机AppSTM32的USART接口更稳定第三学习STM32的过程本身对嵌入式技能的提升大有裨益。2.3 人机交互显示屏与按键显示屏0.96寸或1.3寸的OLEDI2C接口是绝佳选择。它无需背光显示对比度高功耗低且驱动简单。对于电子秤主要显示当前重量、单位、电池电量等信息OLED的尺寸和效果完全足够。如果成本极其敏感也可以使用LCD1602但视觉效果和功耗不如OLED。按键通常需要3-4个按键实现“开机/关机”、“单位切换g/kg/oz”、“去皮/清零”、“校准”等功能。按键电路采用简单的上拉电阻即可软件中需加入消抖处理。电源管理如果设计便携式电子秤电源管理至关重要。建议采用一节3.7V锂电池供电通过一个高效的DC-DC升压芯片如MT3608升压至5V为HX711和传感器供电。STM32可以直接用3.3V LDO供电。同时需要用STM32的ADC监测电池电压并在OLED上显示电量图标。3. 硬件电路设计与布局细节决定精度当主要器件选定后电路设计就是将它们可靠地连接起来。对于电子秤模拟部分的布局布线是影响最终精度的关键。3.1 原理图设计要点HX711与传感器连接将传感器的E、E-接HX711的E、E-或AVDD、AVSSO、O-接A、A-。如果传感器是6线制将Sense、Sense-分别接到HX711的E、E-上。HX711与STM32连接只需要两根线——数据线DT和时钟线SCK。连接至STM32任意两个GPIO即可。注意HX711的数据输出是单向的时钟输入也是单向的配置为推挽输出模式。电源去耦在HX711的AVDD和DVDD引脚附近必须放置一个10uF的钽电容和一个0.1uF的陶瓷电容到地以滤除电源噪声。STM32的每个电源引脚也应放置0.1uF电容。参考电压HX711的基准电压源自其内部的稳压器。确保电源5V干净稳定是保证基准电压准确的前提。3.2 PCB布局的“军规”即使原理图正确糟糕的PCB布局也会引入噪声导致读数跳动。以下是几条必须遵守的规则模拟与数字区域隔离将PCB划分为模拟区HX711、传感器接口、模拟电源和数字区STM32、OLED、按键、数字电源。两地之间用磁珠或0欧电阻进行单点连接。传感器信号线走线从传感器接口到HX711的A、A-走线必须等长、平行、贴近走线最好在PCB内层走线并用GND包围即“包地”以抑制共模干扰。电源走线要粗给传感器和HX711供电的5V走线应尽可能宽而短减少压降和电感。晶振远离模拟部分STM32的晶振及其走线是强噪声源务必远离HX711的模拟输入走线。实操心得第一次打样时我忽略了区域隔离将HX711放在了STM32旁边。结果空载时读数就有几十个字的随机跳动。第二次改版严格划分区域并优化走线后空载跳动范围缩小到了±3个字以内效果立竿见影。4. 嵌入式软件设计从原始数据到稳定重量硬件是骨架软件是灵魂。电子秤的软件核心在于稳定地读取ADC值并通过滤波和标定算法将其转换为准确的重量。4.1 HX711驱动程序编写HX711的通信协议是一种特殊的同步串行协议。驱动它需要精准的时序控制。// 示例STM32 HAL库驱动HX711 #define HX711_DT_PIN GPIO_PIN_0 #define HX711_DT_PORT GPIOA #define HX711_SCK_PIN GPIO_PIN_1 #define HX711_SCK_PORT GPIOA // 初始化GPIO void HX711_Init(void) { GPIO_InitTypeDef GPIO_InitStruct {0}; // SCK 配置为输出 GPIO_InitStruct.Pin HX711_SCK_PIN; GPIO_InitStruct.Mode GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull GPIO_NOPULL; GPIO_InitStruct.Speed GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(HX711_SCK_PORT, GPIO_InitStruct); // DT 配置为输入 GPIO_InitStruct.Pin HX711_DT_PIN; GPIO_InitStruct.Mode GPIO_MODE_INPUT; GPIO_InitStruct.Pull GPIO_PULLUP; // HX711 DT引脚内部为开漏需要上拉 HAL_GPIO_Init(HX711_DT_PORT, GPIO_InitStruct); HAL_GPIO_WritePin(HX711_SCK_PORT, HX711_SCK_PIN, GPIO_PIN_RESET); } // 等待HX711数据就绪 static void HX711_Delay(void) { // 简单延时实际应用中建议用微秒级延时函数 for(int i0; i10; i); } // 读取一个原始数据24位 增益位 int32_t HX711_Read(void) { int32_t count 0; uint8_t i; // 等待DT变低表示数据就绪 while (HAL_GPIO_ReadPin(HX711_DT_PORT, HX711_DT_PIN) GPIO_PIN_SET); // 读取24位数据 for (i 0; i 24; i) { HAL_GPIO_WritePin(HX711_SCK_PORT, HX711_SCK_PIN, GPIO_PIN_SET); HX711_Delay(); count count 1; // 左移一位 HAL_GPIO_WritePin(HX711_SCK_PORT, HX711_SCK_PIN, GPIO_PIN_RESET); HX711_Delay(); if (HAL_GPIO_ReadPin(HX711_DT_PORT, HX711_DT_PIN) GPIO_PIN_SET) { count; } } // 再产生一个时钟脉冲设置下一次读取的增益128 HAL_GPIO_WritePin(HX711_SCK_PORT, HX711_SCK_PIN, GPIO_PIN_SET); HX711_Delay(); HAL_GPIO_WritePin(HX711_SCK_PORT, HX711_SCK_PIN, GPIO_PIN_RESET); HX711_Delay(); // 将24位有符号数扩展为32位 if (count 0x800000) { // 检查第24位符号位 count | 0xFF000000; // 如果是负数高8位补1 } return count; }关键点读取数据后需要根据增益设置通常是128来理解数据的含义。上述代码实现了增益128的时序。4.2 数字滤波算法对抗噪声的利器直接从HX711读出的数据是包含高频噪声的。必须进行滤波才能得到稳定的值。常用的方法有均值滤波连续读取N次取平均值。简单有效但响应速度慢。#define SAMPLE_TIMES 10 int32_t Get_Average_Value(void) { int64_t sum 0; for(int i0; iSAMPLE_TIMES; i) { sum HX711_Read(); } return (int32_t)(sum / SAMPLE_TIMES); }滑动平均滤波维护一个固定长度的队列每次新数据入队最旧数据出队计算队列均值。兼顾了平滑度和实时性。一阶低通滤波指数加权平均filtered_val alpha * raw_val (1 - alpha) * last_filtered_val。alpha是滤波系数0alpha1越小越平滑但滞后越严重。这种方法计算量小非常适合在MCU上实现。我的方案我采用了一种复合滤波策略。首先在定时器中断里以约80Hz的频率快速读取HX711原始值并做一个长度为5的滑动平均得到一个“中速滤波值”。然后在主循环中对这个“中速滤波值”再进行一次一阶低通滤波alpha0.2得到最终用于显示的“慢速滤波值”。这样既能快速响应重量的变化比如放上物品的瞬间又能让最终显示值非常稳定无闪烁。4.3 标定与线性拟合建立数字与重量的桥梁这是电子秤准不准的核心步骤。标定的目的是找到ADC原始值adc_value与实际重量weight之间的换算关系。通常认为这个关系是线性的weight k * adc_value b。两步标定法零点标定去皮秤盘空载时记录此时的ADC值adc_zero。理论上此时的重量应为0所以b -k * adc_zero。软件中每次读取的adc_value应先减去adc_zero得到adc_diff。量程标定在秤盘上放置一个已知准确重量的砝码如500g标准砝码记录此时的ADC值adc_weight。计算系数k。已知weight k * (adc_weight - adc_zero)所以k weight / (adc_weight - adc_zero)软件实现在系统中设置一个“校准模式”。进入该模式后首先提示“空载按键确认”此时记录adc_zero。然后提示“加载砝码按键确认”用户放上标准砝码并按键后记录adc_weight计算并保存k和adc_zero到STM32的Flash中。以后每次上电都从Flash读取这些参数进行计算。避坑指南标定砝码的重量最好接近电子秤日常使用量程的中间值或最大值。例如50kg的秤用20kg或50kg的砝码标定会比用1kg砝码标定在全量程内线性度更好。因为传感器本身可能存在微小的非线性在标定点附近的误差最小。5. 进阶功能与优化让电子秤更“智能”基础功能实现后可以添加一些提升用户体验和实用性的功能。5.1 自动零点跟踪与去皮自动零点跟踪长时间使用后传感器的零点可能会因温度漂移而缓慢变化。可以在软件中设定一个阈值比如±10个ADC字当检测到重量值在阈值范围内且稳定一段时间如5秒后自动将当前值设为新的零点。这能有效抑制温漂带来的误差。去皮功能记录当前重量值作为“皮重”后续显示重量为当前测量值 - 皮重。实现时只需在计算最终重量前减去存储的皮重变量即可。注意提供“清除皮重”的功能。5.2 低功耗设计对于电池供电的便携秤功耗至关重要。间歇工作大部分时间STM32和HX711处于睡眠或关机模式。只有按下按键或压力传感器检测到有物体放入可通过监测DT引脚电平变化实现时才唤醒。外设电源管理用STM32的GPIO控制一个MOSFET来开关HX711和传感器的5V供电。在睡眠时彻底切断模拟部分电源。动态滤波系数在重量稳定时使用更强的滤波更小的alpha让显示更稳在重量快速变化时自动增大alpha让显示更跟手。5.3 数据通信与存储蓝牙传输集成HC-05或BLE模块将称重数据实时发送到手机App实现记录、统计甚至远程监控。本地存储使用STM32内部的Flash或外接EEPROM芯片存储多组标定参数用于不同量程单位切换、历史称重记录等。6. 系统集成、测试与故障排查将所有硬件焊接好程序烧录进去只是第一步。真正的挑战在于调试和测试。6.1 上电调试流程电源检查首先用万用表测量各点电压5V 3.3V是否正常、稳定。HX711通信测试编写一个简单的测试程序循环打印HX711的原始ADC值。空载时这个值应该在一个较小的范围内波动例如±5以内。如果读数全为0或0xFFFFFF检查接线和时序。传感器测试用手轻轻按压秤盘观察ADC值是否随压力增大而单调变化。如果变化反向或不变检查传感器接线特别是A和A-是否接反。6.2 标定与精度测试执行标定流程使用标准砝码严格按照前述两步法进行标定。线性度测试用多个不同重量的标准砝码如100g, 200g, 500g, 1kg, 2kg...依次加载记录电子秤显示值。计算每个点的误差误差 (显示值 - 标准值) / 满量程 * 100%。一个好的设计在全量程内的线性误差应小于0.1%。重复性测试对同一重量如500g反复加载、卸载10次记录每次的显示值。其最大值与最小值之差应小于电子秤的分度值例如1g。6.3 常见故障与排查问题读数跳动大不稳定。排查首先检查机械结构确保传感器受力均匀秤盘安装稳固无晃动。然后检查电源用示波器观察5V电源上是否有明显的纹波。最后检查PCB布局重点排查传感器信号线是否受到数字噪声干扰。解决加强电源滤波增加电容、优化PCB布局、在软件中增加更有效的数字滤波。问题空载时不为零且有缓慢漂移。排查这是典型的零点漂移。可能是传感器本身的热漂移也可能是HX711基准电压的温漂。解决实施“自动零点跟踪”功能。确保电子秤在使用前有足够的热机时间通电预热几分钟。在硬件上尽量让HX711远离热源如MCU、LDO。问题标定后小重量准确大重量偏差越来越大。排查这可能是传感器的非线性误差也可能是机械结构导致的“力传递非线性”例如秤盘在重压下发生形变导致力没有完全垂直作用于传感器。解决尝试用更接近满量程的砝码重新标定。检查机械结构确保传感器只承受垂直方向的力无侧向力或弯矩。对于极高精度要求可以考虑在软件中采用分段线性补偿或二次曲线拟合。经过以上六个部分的拆解一个高精度、高稳定性的基于单片机的电子秤就从想法变成了现实。这个项目最吸引人的地方在于它完美地诠释了嵌入式系统如何作为桥梁连接物理世界与数字世界。每一个跳动的数字背后都是传感器、模拟电路、数字采样和软件算法精密协作的结果。当你亲手做出第一个能稳定显示到克级别的电子秤时那种对底层原理的掌控感和成就感是使用任何现成产品都无法替代的。它不仅仅是一个工具更是一个深入理解信号处理、控制系统和嵌入式开发的绝佳实践平台。
返回列表