资讯动态

基于STM32的智能体重秤设计:从传感器到蓝牙通信的完整实现

发布时间:2026/10/21 0:09:08 来源:尧图企业网站定制
1. 从一个厨房角落里的需求说起前阵子帮朋友改造他家的旧体重秤拆开一看里面就是一块廉价的8位单片机加一个称重传感器显示用的是段码屏功能单一到只能站上去看个体重数字。朋友抱怨说家里老人眼神不好那个小屏幕的数字根本看不清而且每次称完还得自己拿本子记时间一长就懒得记了。这个场景让我意识到一个带数据记录、能语音播报、还能通过蓝牙把数据传到手机的智能体重秤其实是一个非常适合用STM32来做的小项目。它涉及模拟前端信号采集、ADC采样、数字滤波、OLED显示驱动、蓝牙通信协议、低功耗管理等多个嵌入式开发的核心知识点麻雀虽小五脏俱全。这个项目我前后做了大概三周中间踩了不少坑从传感器选型到滤波算法调参从电源噪声干扰到蓝牙数据丢包每一个环节都有值得说道的地方。今天就把整个项目的设计思路、原理图关键部分、代码架构、仿真验证方法以及实际调试中遇到的问题完整地梳理一遍。不管你是刚学完STM32基础外设想找个综合项目练手的学生还是想做一个实用小工具放在家里的电子爱好者这个项目都能给你提供一条清晰的实现路径。我尽量把每个设计决策背后的原因讲清楚让你不仅知道怎么做还知道为什么这么做。2. 整体方案设计与核心器件选型2.1 为什么选STM32而不是更便宜的8位机很多人会问一个体重秤而已用STC89C52或者STM8这种便宜片子不就行了吗我一开始也这么想过但实际算下来STM32F103C8T6这颗芯片的性价比在这个项目里反而更高。原因有几个第一体重秤需要做数字滤波像滑动平均、中值滤波、甚至卡尔曼滤波这些算法在8位机上跑起来很吃力而STM32F103的72MHz主频和硬件乘法器处理起来绰绰有余第二我要驱动OLED显示屏I2C或者SPI接口的OLED刷新需要一定的带宽8位机模拟I2C速度慢刷新率上不去显示会有闪烁感第三蓝牙模块的串口通信需要及时响应STM32的DMA加空闲中断接收方式可以做到不丢包而8位机的中断响应和缓冲区管理要麻烦得多第四后续如果要加Flash存储历史数据、做OTA升级STM32的资源和生态优势就体现出来了。具体型号我选的是STM32F103C8T6也就是常说的“蓝板”或者“最小系统板”。这颗芯片有64KB Flash、20KB RAM对于这个项目来说完全够用。封装是LQFP48焊接难度适中自己打板或者用现成的最小系统板都可以。如果你手头有STM32F401或者G030系列也完全可以替换代码移植量不大主要改一下时钟配置和HAL库的型号宏定义就行。2.2 称重传感器的选型与信号调理体重秤的核心传感器是电阻应变式称重传感器常见的有两种结构一种是四个半桥应变片贴在秤体金属支架上组成全桥另一种是直接买一个成品的人体秤传感器模组。我选的是后者因为成品模组的四角已经校准好了线性度和重复性都有保证省去了自己贴片和组桥的麻烦。具体参数是量程150kg输出灵敏度1mV/V输入阻抗1kΩ左右。这里有一个关键点STM32的ADC参考电压是3.3V12位分辨率下最小分辨电压是3.3V/4096≈0.8mV。而传感器在150kg满载时的输出只有1mV/V×3.3V3.3mV也就是说满量程输出才3.3mV对应ADC只有4个LSB的变化根本没法用。所以必须加仪表放大器进行信号放大。我选的是HX711这款专用的24位ADC芯片它内部集成了低噪声可编程增益放大器增益可选128倍专门为称重传感器设计。HX711的输出是串行数字信号直接跟STM32的GPIO相连读取时序很简单比用STM32内部ADC加外部运放的方案要稳定得多。注意HX711的供电电压会影响零点漂移建议用独立的LDO给它供3.3V不要跟电机、显示屏共用一路电源否则称重读数会跳得厉害。2.3 显示与交互方案显示部分我用了0.96寸的OLED屏SSD1306驱动芯片I2C接口分辨率128×64。选它的理由很简单自发光、对比度高、可视角度大而且功耗低适合电池供电的场景。相比LCD屏OLED不需要背光在暗光环境下看数字更清晰。交互方面我加了三个轻触按键一个用于去皮归零一个用于切换单位kg/lb一个用于查看历史记录。按键电路用10kΩ上拉电阻加100nF电容做硬件消抖软件里再做一次20ms的延时确认双保险。蓝牙模块用的是HC-05虽然它有点老但胜在资料多、AT指令简单、配对稳定。如果你想要更低功耗可以换成BLE模块但那样手机端就需要自己写APP或者用微信小程序来接收数据复杂度会上升。HC-05走的是SPP协议安卓手机可以直接用蓝牙串口助手接收数据调试起来非常方便。2.4 电源管理设计整个系统可以用三节AA电池供电也可以用USB 5V供电。电池供电时需要一颗低压差稳压器把电压稳到3.3V。我选的是XC6206P332静态电流只有1μA非常适合电池场景。为了进一步省电我在固件里做了自动休眠如果连续30秒没有称重动作通过检测HX711的输出变化率来判断就关闭OLED显示把STM32切到Stop模式RTC定时唤醒检查一次传感器。实测下来三节2000mAh的AA电池可以用大概两个月左右。3. 原理图关键电路解析与设计考量3.1 HX711与传感器的连接细节先看传感器到HX711的部分。人体秤传感器模组一般有四根线E、E-、A、A-分别对应激励正、激励负、信号正、信号负。HX711的AVDD和DVDD都接3.3VVFB悬空不用。传感器E接HX711的AVDDE-接GNDA接INPAA-接INPA-。这里要注意传感器的屏蔽线要单点接地而且走线尽量短避免引入工频干扰。我在PCB上把HX711和传感器接口放在板子边缘远离STM32的晶振和数字信号线这样称重读数最稳定。HX711的DOUT和PD_SCK分别接STM32的PB0和PB1。DOUT是数据输出PD_SCK是时钟输入。读取时序是当DOUT为低电平时表示数据准备好然后PD_SCK发送25到27个时钟脉冲前24个脉冲读出24位数据后面的脉冲用来选择下一次的增益通道。具体代码后面会详细讲。3.2 OLED与按键电路OLED的I2C接口我接的是STM32的PB6SCL和PB7SDA这两个引脚是I2C1的默认复用引脚。上拉电阻用4.7kΩ接3.3V。OLED的VCC和GND之间并一个100nF的退耦电容放在靠近屏幕接口的位置。按键部分三个按键分别接PA0、PA1、PA2配置为上拉输入按下时接地。每个按键并联一个100nF电容到地用来滤除机械抖动产生的高频噪声。3.3 电源部分与退耦电源部分USB 5V输入经过一个SS34肖特基二极管防反接然后分两路一路直接给HC-05供电HC-05工作电压3.6V到6V5V没问题另一路经过XC6206稳压到3.3V给STM32、HX711和OLED供电。每个芯片的电源引脚旁边都要放一个100nF的陶瓷电容和一个10μF的钽电容位置越靠近引脚越好。HX711的AVDD和DVDD之间还要加一个1Ω的电阻加100nF电容做RC滤波进一步隔离数字噪声。实操心得我第一版PCB没加这个RC滤波结果称重读数在OLED刷新的时候会跳变十几克加了之后跳变降到2克以内。这个细节在HX711的数据手册里其实有提到但很容易被忽略。4. 固件架构与核心代码实现4.1 工程目录结构与模块划分整个工程我用Keil MDK开发基于HAL库。目录结构是这样的Core文件夹放main.c和中断处理Drivers文件夹放HAL库和STM32的启动文件Hardware文件夹放各个外设的驱动包括hx711.c、oled.c、key.c、bluetooth.cApp文件夹放应用逻辑包括weight.c称重算法、menu.c菜单状态机、storage.cFlash存储。这样分层的目的是让代码可移植比如你换一个显示屏只需要改oled.c其他模块不用动。4.2 HX711驱动与数据读取HX711的驱动核心是一个读取24位数据的函数。先等DOUT变低然后循环24次每次拉高PD_SCK延时1微秒读取DOUT的值放到最高位再拉低PD_SCK延时1微秒。24位读完后再发一个脉冲把PD_SCK拉高再拉低这样下一次转换的增益就是128。读到的24位数据是二进制补码形式需要转换成有符号的32位整数。如果最高位是1说明是负数要取反加一。int32_t HX711_Read(void) { int32_t value 0; uint8_t i; while(HAL_GPIO_ReadPin(HX711_DOUT_GPIO_Port, HX711_DOUT_Pin) GPIO_PIN_SET); for(i0; i24; i) { HAL_GPIO_WritePin(HX711_SCK_GPIO_Port, HX711_SCK_Pin, GPIO_PIN_SET); delay_us(1); value value 1; if(HAL_GPIO_ReadPin(HX711_DOUT_GPIO_Port, HX711_DOUT_Pin) GPIO_PIN_SET) value; HAL_GPIO_WritePin(HX711_SCK_GPIO_Port, HX711_SCK_Pin, GPIO_PIN_RESET); delay_us(1); } 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); if(value 0x800000) value | 0xFF000000; return value; }这个函数每调用一次大概需要几十微秒采样率可以做到10Hz以上对于体重秤来说完全够用。实际使用的时候我会连续读10次去掉最大值和最小值剩下的8个取平均这样一次称重结果大概需要1秒左右但稳定性很好。4.3 称重算法从原始值到公斤数HX711读出来的原始值是一个24位有符号整数范围大概是-8388608到8388607。空载的时候这个值在零点附近波动大概几千个LSB。放上已知重量的物体后原始值会线性变化。所以标定过程就是先记录空载时的原始值offset再放一个已知重量比如5kg的哑铃记录原始值raw然后计算比例系数scale (raw - offset) / 5.0。之后每次称重重量 (当前原始值 - offset) / scale。float Get_Weight(void) { int32_t raw HX711_Read_Average(10); float weight (raw - offset) / scale; if(weight 0.05) weight 0; return weight; }这里有个细节offset和scale需要存在Flash里掉电不丢失。我用了STM32内部的Flash最后一页来存这两个参数每次标定后写入上电时读出来。注意Flash写入前要先擦除整页而且写入次数有限制大概1万次所以不要频繁标定。4.4 OLED显示驱动与菜单状态机OLED驱动我用的是软件I2C因为硬件I2C在STM32F103上有些坑比如死锁问题。软件I2C虽然速度慢一点但稳定可控。显示内容分两行第一行显示当前重量字体用16×32的大号字方便老人看清第二行显示单位、蓝牙连接状态和电池电量。菜单状态机用switch-case实现三个按键分别对应“去皮”、“单位切换”、“历史记录”。历史记录最多存30条存在Flash的一个独立扇区里每条记录4个字节float类型的重量循环覆盖写入。4.5 蓝牙通信协议设计HC-05默认波特率是9600我改成115200以提高传输速度。数据协议我定义了一个简单的帧格式帧头0xAA然后是4字节的float重量数据最后是1字节的校验和前面5个字节的异或。手机端收到后先找帧头再校验通过后解析出重量值。发送频率是每2秒一次避免数据太快手机端处理不过来。void Bluetooth_SendWeight(float weight) { uint8_t buf[6]; buf[0] 0xAA; memcpy(buf[1], weight, 4); buf[5] buf[0] ^ buf[1] ^ buf[2] ^ buf[3] ^ buf[4]; HAL_UART_Transmit(huart1, buf, 6, 100); }注意HC-05在配对成功后如果手机端没有打开串口接收模块会缓冲数据缓冲满了之后新的数据会丢失。所以最好在手机端定时清空缓冲区或者降低发送频率。5. 仿真验证与实物调试对比5.1 用Proteus做前期验证在打板之前我用Proteus搭了一个仿真电路主要是验证HX711的读取时序和OLED的显示逻辑。Proteus里没有HX711的模型我用一个电位器模拟传感器输出然后用ADC0832代替HX711虽然不完全一样但可以验证STM32的GPIO时序和显示刷新。OLED用Proteus自带的12864液晶模拟显示效果虽然不如真实的OLED但足以验证菜单逻辑。仿真里我重点看了两个东西一是按键消抖的逻辑是否正确二是称重数据的滑动平均滤波效果。通过Proteus的虚拟示波器我可以看到滤波前后的波形对比滤波后的曲线明显平滑很多。这一步大概花了一个下午但省去了打板后反复飞线的麻烦。5.2 实物调试中的电源噪声问题第一版实物板子焊好之后发现称重读数一直在跳空载时波动范围有±50克。用示波器看HX711的AVDD引脚发现上面有大概20mV的高频纹波频率跟OLED的I2C时钟一致。原因找到了OLED刷新的时候I2C总线上的电流突变通过电源线耦合到了HX711的模拟供电上。解决办法就是在HX711的AVDD引脚前面加一个RC滤波电阻用10Ω电容用10μF加100nF并联。改完之后空载波动降到±3克以内。另一个问题是蓝牙发送数据时称重读数会突然跳一下。这是因为HC-05在发送瞬间电流增大导致3.3V电源瞬间跌落。我在HC-05的VCC引脚旁边加了一个220μF的电解电容问题解决。5.3 标定过程的实操记录标定是体重秤精度的关键。我的标定步骤是这样的第一步上电后先预热5分钟让HX711和传感器的温度稳定下来第二步空载状态下连续读取100次原始值取平均作为offset第三步放上5kg的标准砝码我用的是厨房秤校准过的哑铃读取100次取平均计算scale第四步把offset和scale写入Flash第五步用10kg和20kg的物体验证误差在±20克以内算合格。这里有个经验标定时的环境温度要和实际使用温度接近。我试过在空调房里标定然后拿到没有空调的厨房用读数会偏大概30克。后来我在固件里加了一个简单的温度补偿用STM32内部的温度传感器读温度然后根据温度偏移量微调offset效果好了很多。6. 常见问题排查与避坑指南6.1 称重读数不稳定怎么办这是最常见的问题原因通常有三个电源噪声、传感器接触不良、滤波参数不合适。排查顺序是先用万用表量HX711的AVDD电压看是否稳定在3.3V然后检查传感器四根线的焊接是否牢固特别是屏蔽线是否单点接地最后调整滤波参数把滑动平均的窗口从10增加到20或者加入中值滤波。如果还是跳可以在HX711的输入端并一个0.1μF的电容滤除高频干扰。6.2 OLED不显示或显示乱码先检查I2C地址是否正确SSD1306的地址通常是0x78或0x7A取决于SA0引脚的电平。然后检查初始化序列是否完整特别是对比度和显示模式的配置。如果显示乱码多半是I2C时序问题可以降低软件I2C的时钟频率把延时从1微秒增加到5微秒试试。另外OLED的VCC和GND一定要接对接反了屏幕会烧掉。6.3 蓝牙连接不稳定或数据丢包HC-05的默认波特率是9600如果STM32的串口配置也是9600一般没问题。但如果改成115200需要先用AT指令把HC-05的波特率也改过来否则收到的全是乱码。数据丢包的话检查STM32的串口发送是否用了DMA如果用阻塞发送在发送期间如果被中断打断可能会导致数据错位。建议用DMA发送或者发送前关中断发送完再开。6.4 Flash写入失败或数据丢失STM32F103的Flash写入需要先擦除整页擦除的最小单位是1KB。如果offset和scale存在同一页每次标定都要擦除整页会缩短Flash寿命。我的做法是把它们存在不同的页或者加一个写入计数器超过一定次数就换一页。另外写入前要确保电压稳定如果电池电量低Flash写入可能会失败所以最好在固件里加一个电压检测低于3.0V就禁止标定操作。6.5 常见问题速查表问题现象可能原因排查方法解决方案称重读数跳动大电源噪声示波器看AVDD纹波加RC滤波独立LDO供电OLED不亮I2C地址错误用逻辑分析仪抓I2C波形确认SA0电平改地址蓝牙乱码波特率不匹配检查双方波特率设置统一改为115200Flash数据丢失擦除次数过多读Flash状态寄存器分页存储加磨损均衡按键无反应上拉电阻未接万用表量按键引脚电压加10kΩ上拉软件消抖电池续航短未进休眠测休眠电流加自动休眠关外设时钟7. 项目扩展方向与个人体会这个项目做完之后我又陆续加了一些功能。比如用STM32的RTC做一个简单的日历每次称重时把日期和时间一起存到Flash里这样历史记录就能看到体重变化的趋势。还加了一个蜂鸣器称重完成后“滴”一声提示可以下来了。后来觉得蜂鸣器太吵换成了振动马达体验更好。蓝牙部分我试过换成BLE模块配合手机端的一个简单APP可以画出体重曲线图。不过APP开发不是我的强项就用了一个开源的蓝牙串口APP把数据导出成CSV再用Excel画图。如果你有安卓开发基础可以自己写一个APP用MPAndroidChart库画曲线那样体验会好很多。最后分享一个我在调试过程中发现的小技巧HX711的读取速率可以通过调整PD_SCK的时钟频率来改变但不要超过1MHz否则数据会出错。另外如果传感器是四线制的但你的HX711模块是带屏蔽罩的那种记得把屏蔽罩接地否则容易引入干扰。这个项目整体成本大概在50元左右其中STM32最小系统板15元HX711模块8元OLED屏12元HC-05模块10元传感器模组20元其他阻容和PCB大概10元。如果你手头有现成的模块可以直接用杜邦线连接不用打板也能跑起来。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑