当前位置: 首页 > news >正文

基于STM32F103C8T6与HX711的电子秤设计:HAL库驱动与数据校准实战

1. 项目背景与硬件选型

最近在做一个智能厨房项目,需要精确测量食材重量。市面上现成的电子秤模块要么精度不够,要么价格太高,于是决定自己用STM32F103C8T6和HX711搭建一个高精度电子秤系统。这个组合特别适合创客和嵌入式开发者,成本不到50元,精度却能轻松达到0.1%级别。

选择STM32F103C8T6这块"蓝色小药丸"有几个原因:首先是性价比超高,20块钱就能买到正品;其次它自带硬件SPI和定时器,方便与HX711通信;最重要的是HAL库支持完善,开发效率高。HX711则是专为电子秤设计的24位ADC芯片,内部集成放大器,直接连接应变片就能用,省去了复杂的外围电路。

硬件连接非常简单:

  • HX711的DT脚接PA1(任何GPIO都可)
  • SCK脚接PA0
  • VCC接3.3V
  • GND共地
  • 应变片的E+、E-接HX711的正负输入端

2. 开发环境搭建

我用的是STM32CubeIDE,这个IDE最大的好处是自带HAL库和图形化配置工具。新建工程时选择STM32F103C8T6,时钟配置为72MHz(外部8MHz晶振倍频)。关键是要开启两个外设:

  1. USART1:用于调试输出重量数据
  2. TIM2:用于实现微秒级延时

在CubeMX里配置GPIO时,PA0设为输出模式(推挽),PA1设为输入模式(上拉)。时钟树配置要注意APB1总线不要超过36MHz,否则定时器会出问题。生成代码后,记得在main.c里添加重定向printf的代码:

int fputc(int ch, FILE *f) { HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xff); return ch; }

3. HX711驱动实现

HX711的通信协议比较特殊,不是标准的SPI或I2C,需要手动控制时序。在HX711.h中定义几个宏简化操作:

#define CLK_1 HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_SET) #define CLK_0 HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_RESET) #define Read_PIN HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1)

数据读取的核心在Get_number()函数。HX711每次输出24位数据,高位在前。关键是要在时钟下降沿读取数据,每个时钟周期保持至少1us的低电平:

unsigned long Get_number() { val = 0; CLK_0; while(!Read_PIN); // 等待数据就绪 for(int i=0; i<24; i++) { HAL_Delay_us(1); CLK_1; val = val << 1; HAL_Delay_us(1); CLK_0; if(Read_PIN) val++; HAL_Delay_us(1); } // 第25个脉冲设置增益 CLK_1; val = val ^ 0x800000; // 补码转换 HAL_Delay_us(1); CLK_0; return val; }

这里有个坑要注意:很多网上的例程没有在读取后清零val变量,会导致后续数据异常。实测发现延时参数也很关键,太快会导致数据错位,太慢会影响采样率。

4. 数据校准与滤波处理

原始ADC值需要转换为实际重量。我的20kg应变片分度系数是103,在Get_Weight()函数中处理:

long Get_Weight(void) { HX711_Buffer = Get_number(); Weight = HX711_Buffer; Weight = (long)((float)Weight / 103); // 校准系数 return Weight; }

校准步骤很关键:

  1. 空载时读取10次取平均值作为零点
  2. 放置已知重物(如500g砝码)
  3. 计算系数 = 原始ADC值 / 实际重量
  4. 重复三次取平均

主循环中加入了简单的滑动滤波:

while(1) { static long weights[5] = {0}; static int index = 0; weights[index] = -Get_Weight() + First_weight; index = (index + 1) % 5; long avg = 0; for(int i=0; i<5; i++) { avg += weights[i]; } printf("%ld\n", avg / 5); HAL_Delay(120); // 约10Hz更新率 }

5. 常见问题排查

调试时遇到几个典型问题:

  1. 数据全为0:检查DT脚是否接触不良,HX711供电是否稳定
  2. 数据跳动大:尝试在VCC和GND之间加104电容,缩短传感器到HX711的导线
  3. 读数不稳定:确保应变片牢固粘贴,避免机械振动
  4. 通信超时:检查while(!Read_PIN)是否有超时退出机制

一个实用的调试技巧:先用示波器看SCK和DT波形,确认时序符合HX711规格书要求。如果没示波器,可以用LED闪烁指示通信状态:

if(Get_number() == 0) { HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin); }

6. 性能优化技巧

经过实测,这几个优化很有效:

  1. 将HAL_Delay_us()改为定时器实现,精度更高
  2. 在Get_Weight()中加入温度补偿算法
  3. 使用DMA传输USART数据,降低CPU占用
  4. 添加按键去皮功能:
if(HAL_GPIO_ReadPin(BTN_GPIO_Port, BTN_Pin) == GPIO_PIN_RESET) { First_weight = Get_Weight(); HAL_Delay(200); // 消抖 }

电源管理也很重要,HX711对电源噪声敏感,建议:

  • 单独LDO给HX711供电
  • 模拟和数字地之间加0欧电阻
  • 电源走线尽量粗短

7. 扩展应用方向

这个基础框架可以扩展很多实用功能:

  1. 蓝牙传输:加个HC-05模块实现手机APP显示
  2. 数据记录:用SPI Flash存储历史称重数据
  3. 自动识别:通过重量变化模式识别不同食材
  4. 流量计算:结合时间戳计算粉末物料流量

比如实现简单的配方功能:

typedef struct { char name[20]; long target_weight; } Ingredient; Ingredient recipe[] = { {"面粉", 500}, {"糖", 200}, {"盐", 5} }; void check_recipe() { for(int i=0; i<3; i++) { while(Get_Weight() < recipe[i].target_weight) { printf("请添加%s...\n", recipe[i].name); HAL_Delay(1000); } printf("%s添加完成\n", recipe[i].name); } }

8. 工程代码结构建议

好的代码组织能大幅提高可维护性,我的项目结构如下:

/Drivers /HX711 hx711.c hx711.h /Filters moving_avg.c /Utils delay.c /Application /Recipe recipe.c /Display lcd.c

关键设计原则:

  1. 硬件驱动与业务逻辑分离
  2. 使用面向接口编程
  3. 重要参数集中配置
  4. 版本控制提交注释规范

比如将校准系数改为配置文件:

// config.h #define CALIB_FACTOR 103.0f #define MAX_WEIGHT 20000 // 20kg

最后提醒,不同批次的应变片灵敏度可能有差异,建议每批都做单独校准。实际测试发现,温度每变化10℃,读数会漂移0.5%左右,对精度要求高的场合需要做温度补偿。

http://www.jsqmd.com/news/1085902/

相关文章:

  • Py之scikit-learn-extra:从安装到实战,解锁scikit-learn官方扩展库的进阶用法
  • d2s-editor:5个实用技巧让你成为暗黑2存档编辑大师
  • RA8T2 ADC16H进阶数据处理:比较匹配与FIFO功能实战解析
  • 【技术解析】MIPI D-PHY:从电气特性到高速传输的实战指南
  • 5分钟部署:Arknights-Mower明日方舟自动化工具终极指南
  • 终极指南:3分钟搞定游戏乱码!Locale Remulator让你的日韩游戏完美显示
  • 从STM32H7到AK4499EX:构建高解析度DSD音乐播放器的硬件架构与选型思考
  • Windows原生运行安卓应用:APK安装器如何实现3分钟快速部署?
  • Win11虚拟机频繁蓝屏?VMware与Hyper-V兼容性冲突的排查与修复
  • 从二维到三维:GIS坐标转换中的四参数与七参数实战解析
  • CoppeliaSim实战:从STL模型到可驱动机械臂的完整动力学建模流程
  • STM32F1 HAL库SD卡DMA模式下的FATFS移植与性能优化
  • 告别空白图标:用SVG Explorer Extension点亮Windows文件资源管理器
  • B站会员购抢票神器biliTickerBuy:告别手速焦虑的终极解决方案
  • Yakit+Nuclei:新手友好的图形化漏洞验证实战指南
  • AI 链上推理:去中心化模型执行与验证的可信计算架构
  • 三层安全防护 + 命令白名单:一个敢带进生产的 AI 运维排查脚本
  • 阿里巴巴 算法岗笔试真题【坏掉的键盘】
  • 从OHEM到Focal Loss:深入剖析目标检测中的难例挖掘策略演进与PyTorch实战
  • 从ORA-00257归档错误到系统恢复:Oracle DBA的实战排障与空间治理
  • 从Co-training到多视图学习:如何让AI模型“多角度看世界”以提升性能?
  • 亚马逊为何放弃 OpenAI 电影项目?数据中心员工奋起反抗,Meta 泄露员工数据
  • FinalShell密码找回:从本地存储到Java解码的完整实践
  • 如何为Windows XP/2003构建创新兼容层:突破性解决方案指南
  • AD实战指南 | 从封装选型到PCB布局:二极管、三极管与连接件的设计避坑手册
  • WindowResizer终极指南:如何强制调整任意窗口大小的3个简单步骤
  • AI诊断分析
  • Element-UI 弹窗遮罩层 z-index 管理:从 PopupManager 原理到复杂嵌套场景的实战修复
  • Confucius4-TTS:几秒克隆声音,跨语言情感迁移超自然,多语言自然配音神器 一键整合包下载
  • 5分钟构建专业可视化图表:Mermaid Live Editor的交互式设计革命