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

资讯详情

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

从传感器到算法:基于单片机的电子秤设计全流程解析

从传感器到算法:基于单片机的电子秤设计全流程解析 1. 项目概述从零打造一台自己的电子秤做硬件开发的朋友估计都绕不开“电子秤”这个经典项目。它看起来简单不就是放个东西屏幕上显示个重量嘛。但真上手做你会发现里面门道不少怎么把微小的形变转换成电信号信号那么弱怎么放大到单片机可以识别的范围怎么让显示的数字又准又稳我前前后后做过不下五六个版本的电子秤从最简陋的51单片机配电阻应变片到用上24位高精度ADC的STM32方案踩过的坑、烧过的芯片都能写本小册子了。今天我就以“基于单片机的电子秤设计”这个项目为例把整个从传感器原理到软件滤波的全流程掰开揉碎了讲清楚。无论你是正在做课程设计的学生还是想深入了解测力系统的工程师这篇文章都能给你一套可以直接“抄作业”的完整方案。咱们不搞虚的就从最核心的称重传感器和ADC选型开始聊。2. 核心硬件选型与电路设计解析电子秤的硬件核心就三样称重传感器、信号调理电路、单片机。选型决定了你这款秤的性能天花板是精度1克还是0.1克是晃两下就稳定还是飘忽不定硬件设计阶段就基本定调了。2.1 称重传感器电子秤的“感知神经”市面上最常见的称重传感器是电阻应变式。它的核心是一根弹性梁通常是合金钢上面贴着像蜘蛛网一样的电阻应变片。当物体放在秤盘上弹性梁发生弯曲形变贴在它表面的应变片也随之被拉伸或压缩。应变片的奥秘在于它的电阻值会随着自身的形变应变而线性变化这就是“应变效应”。通常我们会把四个完全相同的应变片组成一个惠斯通电桥。两个应变片受拉电阻增加另外两个受压电阻减小。这样微小的形变就被转换成了电桥输出的差分电压信号。这个信号非常微弱满量程输出通常在1-2mV/V左右。也就是说如果你给电桥提供5V激励电压V_excitation那么满载时的输出电压变化只有5-10mV。要把这个信号准确测量出来后续的放大和采集至关重要。注意选购传感器时别只看量程。关键参数还有灵敏度mV/V、非线性度、蠕变和温度系数。对于DIY项目一款常见的5Kg量程、2mV/V灵敏度、综合精度0.05%的悬臂梁式传感器就非常合适价格也亲民。2.2 信号调理电路放大与稳定的艺术传感器输出的毫伏级信号直接送给单片机的ADC通常是0-3.3V量程无异于“蚊子与牛对话”根本分辨不出来。所以我们必须用仪表放大器将这个信号放大数百倍。为什么一定是仪表放大器而不是普通的运放因为传感器输出的是差分信号且源阻抗较高。仪表放大器具有极高的输入阻抗几乎不从前级汲取电流、极高的共模抑制比CMRR和精准的差分放大能力。它能有效抑制由长导线引入的共模干扰比如工频噪声只放大我们需要的差分信号。我常用的是ADI的AD620或TI的INA125。这里以INA125为例它的放大倍数由一颗外接电阻R_g决定Gain 4 (60kΩ / R_g)。假设我们需要500倍的增益可以反算出R_g ≈ 120Ω。除了放大电路设计上还有几个关键点激励电压源必须稳定传感器的输出与激励电压成正比。如果激励电压波动1%输出信号就会波动1%。因此必须使用一颗基准电压源芯片如REF5050或LDO如AMS1117-5.0来为电桥供电绝不能直接用单片机的VCC。滤波电路必不可少在仪表放大器的输出端需要加入一个低通滤波电路滤除高频噪声。一个简单的RC无源滤波器如1kΩ电阻和0.1uF电容组成截止频率约1.6kHz的滤波器就很有用。如果想效果更好可以用运放构成有源滤波器。布局与走线要考究模拟信号部分要远离数字电路特别是单片机的晶振和数字IO线。采用“星型接地”或单点接地避免地线噪声干扰。传感器信号线最好使用双绞线或屏蔽线。2.3 单片机与ADC选型精度与速度的权衡放大后的信号是模拟电压需要ADC转换成数字量。很多单片机自带12位ADC但对于电子秤够用吗我们来算笔账。假设系统设计5Kg量程目标分辨率1克。传感器满量程输出5V激励 * 2mV/V 10mV。放大500倍后满量程电压为10mV * 500 5V。1克对应的电压变化为 (5V / 5000g) 1mV。如果使用3.3V供电、12位ADC分辨率 3.3V / 4096 ≈ 0.8mV那么1g的变化刚刚超过1个LSB勉强可以分辨但毫无余量噪声很容易导致显示值跳动。因此追求一定精度的电子秤外置或内置高分辨率Σ-Δ型ADC是更佳选择。比如24位的ADS1232或HX711。以HX711为例它专为电子秤设计内部集成了可编程增益放大器PGA和24位ADC分辨率高达2^24动态范围极大。即使存在噪声通过软件滤波也能得到非常稳定的读数。STM32等高端单片机的部分型号也内置了16位甚至更高精度的ADC但通常需要精心设计模拟前端和滤波算法才能发挥最佳性能。实操心得对于初学者或快速原型HX711模块是首选。它把最难搞的模拟前端和ADC集成好了你只需要用单片机的两个IO口时钟和数据以串行方式读取数据即可极大降低了硬件设计和调试门槛。我第一个稳定的电子秤就是用STM32F103C8T6HX711做的。3. 软件架构与核心算法实现硬件是骨架软件是灵魂。电子秤的软件核心就两件事稳定地读取重量数据和友好地与人交互。下面我们分步拆解。3.1 驱动程序编写与ADC的“对话”首先你得让单片机从ADC芯片里把数据读出来。我们以最常用的HX711为例它的通信时序是特定的。HX711有两个通道增益可选128或64。驱动流程如下初始化将数据线DOUT和时钟线PD_SCK配置为GPIODOUT设为输入PD_SCK设为输出并置低。等待就绪HX711在数据未准备好时DOUT为高电平准备好后DOUT拉低。因此主程序需要不断查询DOUT引脚状态一旦变低即可读取。读取数据连续产生25个时钟脉冲。前24个脉冲的上升沿从DOUT引脚依次移出24位数据高位在先。第25个脉冲用于设置下一个转换的通道和增益。数据转换读出的24位数据是二进制补码形式需要转换成有符号整数。下面是一段典型的HX711读取函数C语言示例// 假设 PD_SCK 接 PB0, DOUT 接 PB1 #define HX711_SCK_GPIO_Port GPIOB #define HX711_SCK_Pin GPIO_PIN_0 #define HX711_DOUT_GPIO_Port GPIOB #define HX711_DOUT_Pin GPIO_PIN_1 int32_t HX711_Read(void) { uint32_t count 0; uint8_t i; // 等待HX711准备好 while (HAL_GPIO_ReadPin(HX711_DOUT_GPIO_Port, HX711_DOUT_Pin) GPIO_PIN_SET); // 读取24位数据 for (i 0; i 24; i) { HAL_GPIO_WritePin(HX711_SCK_GPIO_Port, HX711_SCK_Pin, GPIO_PIN_SET); delay_us(1); // 短暂延时满足时序要求 count count 1; // 左移为下一位腾出空间 HAL_GPIO_WritePin(HX711_SCK_GPIO_Port, HX711_SCK_Pin, GPIO_PIN_RESET); delay_us(1); if (HAL_GPIO_ReadPin(HX711_DOUT_GPIO_Port, HX711_DOUT_Pin) GPIO_PIN_SET) { count; } } // 第25个脉冲设置下一次为通道A增益128 HAL_GPIO_WritePin(HX711_SCK_GPIO_Port, HX711_SCK_Pin, GPIO_PIN_SET); delay_us(1); HAL_GPIO_WritePin(HX711_SCK_GPIO_Port, HX711_SCK_Pin, GPIO_PIN_RESET); delay_us(1); // 将24位二进制补码转换为有符号32位整数 if (count 0x800000) { // 检查最高位第24位是否为1即是否为负数 count | 0xFF000000; // 进行符号扩展 } return (int32_t)count; }3.2 标定算法将AD值转换为重量读出来的原始AD值RawData只是一个数字我们需要通过标定建立它与实际重量的线性关系。标定需要两个已知重量的砝码通常用零点空载和满量程附近的一个点如5Kg砝码。标定过程清空秤盘读取并记录此时的AD值RawData_Zero。放置已知重量的标准砝码如Weight_Cal 5000.0g读取并记录稳定后的AD值RawData_Cal。计算比例系数系数KK Weight_Cal / (RawData_Cal - RawData_Zero)这个K值就是每个AD值增量所代表的重量单位g/AD。实时重量计算Current_Weight (RawData_Current - RawData_Zero) * K在程序中我们需要保存RawData_Zero和K这两个参数。为了防止断电丢失务必将其存入单片机的EEPROM或Flash中。每次上电初始化时从存储器中读取这些参数。3.3 数字滤波算法让显示值“稳如泰山”直接从ADC读取的数据是包含噪声的会导致显示值不停跳动。数字滤波的目的就是平滑这些噪声。电子秤常用的滤波算法有滑动平均滤波最简单有效。维护一个固定长度的数组每次存入新数据并计算数组内所有数据的平均值作为输出。#define FILTER_LEN 10 int32_t filter_buf[FILTER_LEN]; uint8_t filter_index 0; int32_t Moving_Average_Filter(int32_t new_val) { filter_buf[filter_index] new_val; filter_index (filter_index 1) % FILTER_LEN; int64_t sum 0; for(int i0; iFILTER_LEN; i) { sum filter_buf[i]; } return (int32_t)(sum / FILTER_LEN); }优点简单对周期性干扰有良好抑制。缺点会产生滞后响应速度随窗口长度增加而变慢。一阶滞后滤波低通滤波Y(n) α * X(n) (1-α) * Y(n-1)。其中α是滤波系数0α1X(n)是新采样值Y(n-1)是上次滤波输出。α越接近1响应越快但滤波效果差α越接近0越平滑但滞后严重。这个算法计算量小非常适合单片机。中位值平均滤波结合了中值滤波和平均滤波。连续采样N个数据去掉一个最大值和一个最小值然后计算剩余N-2个数据的平均值。这种方法能有效抑制脉冲性干扰。我的经验在实际项目中我常采用“滑动平均 一阶滞后” 两级滤波。先用一个长度为5的滑动平均快速平滑毛刺再将结果送入一阶滞后滤波器α0.2~0.5这样既能保证一定的实时性又能让最终显示值非常稳定不会出现尾数频繁跳变的情况。4. 人机交互与系统功能实现重量数据稳定了接下来就是如何展示给用户以及增加一些实用功能。4.1 显示模块驱动最常用的是LCD1602液晶屏或OLED屏。LCD1602价格低廉驱动简单OLED显示效果更佳但价格稍高。驱动本质都是按照时序向屏幕发送命令和数据。以LCD1602为例你需要实现初始化序列设置显示模式、光标、清屏等。写命令/写数据函数根据数据手册的时序图操作RS、RW、E和D0-D7引脚。显示字符串/数字函数将计算得到的重量浮点数转换为字符数组并定位到屏幕特定位置显示。现在有很多现成的驱动库如Arduino的LiquidCrystal库但在实际产品中为了优化代码体积和可控性我更喜欢根据数据手册自己编写驱动这样能更深入了解时序和屏幕的“脾气”。4.2 按键功能与菜单设计一个基本的电子秤至少需要以下几个按键“去皮/归零”键将当前重量设为零点用于扣除容器重量。“单位切换”键在克(g)、千克(kg)、盎司(oz)等单位间切换。“标定”键进入标定模式通常需要组合键或长按进入防止误触发。按键处理推荐使用状态机模型并加入消抖处理。下面是一个简单的按键状态机示例typedef enum { KEY_IDLE, KEY_DEBOUNCE, KEY_PRESSED, KEY_REPEAT, KEY_RELEASED } KeyState; KeyState key_scan(GPIO_PinState pin_state) { static KeyState state KEY_IDLE; static uint32_t press_tick 0; switch(state) { case KEY_IDLE: if(pin_state GPIO_PIN_RESET) { // 按键按下假设低电平有效 state KEY_DEBOUNCE; press_tick HAL_GetTick(); } break; case KEY_DEBOUNCE: if(HAL_GetTick() - press_tick 20) { // 消抖20ms if(pin_state GPIO_PIN_RESET) { state KEY_PRESSED; // 确认为有效按下 } else { state KEY_IDLE; } } break; case KEY_PRESSED: // 执行一次按键动作 // ... state KEY_REPEAT; // 进入长按/连按判断 break; // ... 其他状态处理 } return state; }菜单设计上为了不占用过多RAM可以使用“菜单项表”的方式用结构体数组定义所有菜单项和对应的处理函数通过上下键切换焦点确认键执行。4.3 低功耗与稳定性考量对于电池供电的便携式电子秤低功耗设计是必须的。单片机休眠在无操作一段时间后让单片机进入STOP或SLEEP模式。HX711本身功耗很低也可以控制其掉电引脚PDWN使其进入休眠。间歇采样不需要实时快速更新时可以降低ADC的采样频率比如从10Hz降到1Hz。外设管理关闭不用的外设时钟如显示屏背光、蜂鸣器等。稳定性方面除了硬件上的滤波软件上还要做“零位跟踪”和“动态检测”。零位跟踪长时间运行传感器的零点可能会缓慢漂移。可以设定一个很小的阈值比如±2g当重量持续一段时间处于此阈值内时自动缓慢地修正零点值避免长期使用后空秤不归零。动态检测通过判断连续多次采样的差值是否超过某个阈值来判断秤盘上的物体是否已稳定放置。只有稳定后才更新最终显示重量避免在放东西的过程中数字乱跳。5. 系统集成调试与性能优化当硬件焊接好软件也写得差不多了就到了最考验耐心和经验的环节——调试与优化。5.1 调试流程与工具电源检查万用表测量传感器激励电压、运放供电电压、基准电压是否准确稳定。这是所有问题的根源。信号通路探测用示波器是最直观的。第一步测量传感器电桥输出端差分信号空载和加载时是否有毫伏级的电压变化这能验证传感器和机械结构是否正常。第二步测量仪表放大器输出端。空载时理论上应为0V但实际可能有几毫伏到几十毫伏的偏移由运放失调电压引起。加载后电压是否按预期比例增益*输入变化第三步测量单片机ADC输入引脚或HX711模拟输入端的波形。观察信号是否干净有无明显的毛刺或周期性噪声如50Hz工频干扰。软件数据观察通过串口将单片机读取的原始AD值、滤波后的值、计算出的重量实时打印出来。这是分析问题的关键。5.2 常见问题与解决方案实录下面是我在调试中遇到的一些典型问题及解决方法整理成了表格问题现象可能原因排查步骤与解决方案显示值始终为0或接近01. 传感器未正确受力或损坏。2. 电桥激励电压为0。3. 仪表放大器无输出或增益设置错误。4. ADC未正确读取数据。1. 万用表测量传感器输入激励电压是否正常如5.0V。2. 测量电桥输出差分电压用手轻压传感器看是否有变化。3. 测量仪表放大器输出检查增益电阻是否焊接正确。4. 检查单片机与ADC的通信时序用逻辑分析仪抓取波形。显示值跳动剧烈跳变几十上百克1. 电源噪声大。2. 模拟地线设计不合理引入数字噪声。3. 机械结构不稳定或传感器安装不当。4. 软件滤波参数过弱或未启用。1. 示波器查看电源纹波在电源入口和芯片电源脚加退耦电容如10uF钽电容0.1uF陶瓷电容。2. 检查PCB布局模拟部分和数字部分地线单点连接。3. 确保传感器受力均匀安装牢固无晃动。4. 加大滑动平均窗口或减小一阶滞后滤波的α值。空秤时显示值缓慢漂移蠕变1. 传感器质量差自身蠕变大。2. 温度变化引起电桥和运放参数漂移。3. 激励电压源温漂大。1. 选用更高品质的传感器。2. 在软件中加入自动零位跟踪功能。3. 使用低温漂的基准电压源为电桥供电。标定后线性度差小重量准大重量偏1. 传感器非线性度过大。2. 仪表放大器输出接近电源轨进入非线性区。3. 机械结构在重载下形变不理想。1. 检查放大后的满量程电压是否在ADC量程的70%-90%内为运放留出余量。2. 采用多点标定如0g, 2Kg, 5Kg在软件中采用分段线性补偿或查表法。按键响应不灵或误触发1. 按键硬件消抖不足。2. 软件消抖逻辑或状态机有bug。3. IO口配置错误如上拉电阻未启用。1. 确保硬件上有上拉电阻和滤波电容。2. 用逻辑分析仪查看按键按下时的波形调整软件消抖时间通常10-50ms。3. 检查单片机IO口配置模式是否正确。5.3 精度提升的进阶技巧如果基础功能实现后还想进一步提升精度和稳定性可以尝试以下方法软件校准与补偿多点标定不止校准零点和满点在中间量程再取1-2个点进行标定用最小二乘法拟合出更精确的曲线补偿传感器的非线性。温度补偿如果应用环境温度变化大可以增加一个温度传感器如DS18B20。通过实验测量出传感器零点、灵敏度随温度变化的系数在软件中进行实时补偿。高级滤波算法卡尔曼滤波这是一种最优估计算法特别适合存在随机噪声的系统。它可以根据系统的模型比如重量的变化通常比较缓慢和观测值ADC读数预测出最可能的重重量值。虽然计算比滑动平均复杂但在STM32这类MCU上完全可以运行能极大提升动态称重比如放上物品瞬间的稳定性和响应速度。抗干扰设计屏蔽与隔离将传感器信号线用屏蔽线包裹屏蔽层单点接地。在信号进入ADC前可以使用一个由运放构成的“驱动屏蔽层”电路有效消除电缆电容的影响。软件陷波器如果发现示波器上有明显的50Hz/60Hz工频干扰可以在数字滤波算法中增加一个陷波器专门滤除这个特定频率的噪声。做完所有这些一台由你亲手打造的、显示稳定、反应灵敏、精度可靠的电子秤就真正诞生了。从理解应变片的微小形变到编写滤波算法平滑数据每一个环节的打通都是对“从物理世界到数字世界”这一过程更深一层的理解。这个项目麻雀虽小五脏俱全涵盖了模拟电路、数字电路、单片机编程、传感器应用和算法是一个绝佳的嵌入式系统综合实践。我建议你在基本功能实现后不妨挑战一下自己试试增加蓝牙模块连接手机APP记录重量曲线或者加入电池管理系统做成便携式乐趣和收获会更多。
返回列表