HX711电子秤DIY全攻略:从芯片选型到OLED显示(附STM32源码)
你是否曾想过,厨房里那个精准显示克数的电子秤,或者超市里快速结算的计价秤,其核心原理其实并不神秘?作为一名嵌入式爱好者,亲手打造一台属于自己的高精度电子秤,不仅能让你深刻理解传感器、模数转换和嵌入式系统的协同工作,更能收获从无到有的巨大成就感。这篇文章,就是为你准备的实战手册。我们将抛开复杂的理论堆砌,直接切入动手环节,从最关键的HX711模数转换芯片的选型与电路设计开始,一步步连接应变片传感器,编写STM32的驱动代码,最终在精致的OLED屏幕上实时显示重量。无论你是刚接触STM32的新手,还是想找一个完整项目练手的DIY玩家,跟随这个攻略,你都能在周末的时光里,收获一个既实用又有趣的作品。
1. 项目核心:深入理解HX711模数转换器
在电子秤系统中,称重传感器输出的信号极其微弱,通常是毫伏甚至微伏级别。普通的单片机ADC(比如STM32内置的12位ADC)分辨率有限,难以直接精确测量这种微小变化。这时,就需要一个专用的前端芯片来放大和转换信号,HX711正是为此而生的明星芯片。
它不仅仅是一个24位高精度ADC,更是一个高度集成的解决方案。其内部集成了可编程增益放大器(PGA)、稳压电源和时钟振荡器。这意味着,你只需要极少的外围元件(几个电阻电容),就能搭建一个稳定可靠的前端采集电路,极大地简化了硬件设计难度。
1.1 HX711关键特性与通道选择
我们先来拆解几个你必须掌握的核心参数:
- 高分辨率:24位模数转换,理论分辨率高达2^24,这意味着它能将模拟信号划分为超过1600万个等级,为高精度测量奠定了基础。
- 双差分输入通道:芯片提供A和B两个通道,但同一时刻只能使用一个。通道A更常用,因为它支持两种增益(64或128),适合连接桥式应变传感器;通道B增益固定为32,可用于其他信号源或温度传感器。
- 可编程增益放大器(PGA):这是放大微弱信号的关键。增益越高,对小信号的放大倍数越大,测量越灵敏。具体关系如下表所示:
| 通道选择 | 可编程增益 | 输出数据周期(时钟脉冲数) | 典型应用场景 |
|---|---|---|---|
| 通道 A | 128 | 25 | 高精度称重,放大倍数最大 |
| 通道 A | 64 | 27 | 平衡精度与量程的称重 |
| 通道 B | 32 | 26 | 辅助信号测量,如温度补偿 |
- 输出速率:通过RATE引脚可选择10Hz或80Hz的数据输出率。10Hz时噪声更低,精度更高;80Hz则刷新更快,适合动态称重。对于大部分静态称重DIY项目,10Hz完全足够。
提示:在实际选购HX711模块时,你会发现市面上常见的小模块通常已将通道A和增益设置为128(或通过焊盘选择),并引出VCC、GND、DOUT、SCK、以及为传感器供电的E+和E-。这为我们省去了许多配置麻烦。
1.2 内部稳压电路与传感器供电
HX711的一个设计精妙之处在于其内部稳压电路。它不仅能为自己提供稳定的模拟电源(AVDD),还能通过简单的三极管(如S8550)扩流,为外部的称重传感器提供一个非常稳定的激励电压(通常为5V或3.3V)。传感器供电的稳定性直接决定了输出的稳定性,是保证测量精度的重要一环。
其输出电压V_AVDD由外部电阻R1和R2决定,公式为:V_AVDD = V_BG * (R1 + R2) / R2,其中V_BG是芯片内部基准电压,典型值为1.25V。通过选择合适的R1和R2,我们可以得到所需的AVDD电压。
// 理解公式的简单计算示例(假设V_BG=1.25V)
// 若R1=20kΩ, R2=12kΩ,则:
// V_AVDD = 1.25 * (20k + 12k) / 12k ≈ 1.25 * 2.667 ≈ 3.33V
这种设计确保了即使单片机系统的电源有轻微波动,传感器和HX711模拟部分的供电也能保持恒定,从而减少测量误差。
2. 硬件系统搭建:从原理图到实物连接
有了对HX711的深入理解,现在我们可以开始动手搭建硬件了。一个完整的DIY电子秤系统通常包含以下几个部分:主控MCU、HX711模块、称重传感器、OLED显示屏以及必要的按键。
2.1 元器件选型清单
在开始焊接或连接杜邦线之前,请确保你手头有以下材料:
- 主控制器:STM32F103C8T6(蓝色小板)。这款芯片资源丰富,价格亲民,社区支持极好,是DIY项目的首选。
- 称重传感器:推荐使用常见的“悬臂梁式”应变传感器,量程有1kg、5kg、10kg等可选。初次尝试建议用1kg或5kg,精度感受更明显。
- HX711模块:直接购买集成好的模块,通常售价仅几元。注意检查模块是否引出传感器接口(E+/E-/A+/A-)和单片机接口(VCC/GND/DOUT/SCK)。
- 显示模块:0.96寸或1.3寸的I2C接口OLED屏。I2C接线简单,仅需两根信号线。
- 其他:轻触按键若干、面包板、杜邦线(公对公、母对母)、微调电阻(用于校准时的“砝码”模拟)、以及一个稳定的5V/2A电源适配器(为整个系统供电)。
2.2 电路连接详解
连接是项目成功的第一步,务必仔细。下图展示了核心的连接关系:
[系统连接示意图(文字描述)]
STM32F103C8T6 <--> 外围设备
-----------------------------------------------------
PB14 (GPIO Output) <--> HX711.SCK (时钟)
PB15 (GPIO Input) <--> HX711.DOUT (数据)
3.3V <--> HX711.VCC, OLED.VCC
GND <--> 所有GND引脚
PB10 (I2C2_SDA) <--> OLED.SDA
PB11 (I2C2_SCL) <--> OLED.SCL
PB1 (GPIO Input) <--> 按键(另一端接GND)
|
|
HX711模块 <--> 称重传感器
E+ <--> 传感器红线 (激励+)
E- <--> 传感器黑线 (激励-)
A- <--> 传感器白线 (信号-)
A+ <--> 传感器绿线 (信号+)
注意:传感器线色可能因厂家而异,最常见的是红(E+)、黑(E-)、白(A-)、绿(A+)。如果不确定,最好查阅卖家提供的资料。接反可能导致读数反向或异常。
给传感器供电的要点:务必使用HX711模块产生的稳压电源(E+和E-)为传感器供电,而不是直接使用单片机系统的5V或3.3V。这是保证测量基线稳定的关键。
3. 软件驱动:STM32与HX711的通信实战
硬件连接妥当后,我们进入核心的软件部分。与HX711的通信是一种简单的同步串行协议,需要我们用GPIO来模拟时序。
3.1 HX711通信时序与数据读取函数
HX711的通信协议可以概括为:当DOUT为低电平时,表示芯片数据就绪。随后,主控在SCK引脚上产生25~27个时钟脉冲,同时在每个脉冲的上升沿读取DOUT上的数据位。前24位是转换结果,后续的1-3位用于设置下次转换的通道和增益。
下面是一个针对通道A,增益128的经典读取函数实现。我更喜欢用寄存器操作来获得更精确的时序控制。
/**
* @brief 读取HX711的24位AD值(通道A,增益128)
* @param 无
* @retval 返回原始的32位无符号AD值
*/
uint32_t HX711_Read(void) {
uint32_t count = 0;
uint8_t i;
HX711_SCK_LOW(); // 时钟线拉低,准备开始
delay_us(1);
// 等待HX711数据就绪(DOUT变低)
while(HX711_DOUT_READ() == GPIO_PIN_SET) {
// 可以加入超时退出机制,防止死循环
}
// 连续读取24位数据
for(i = 0; i < 24; i++) {
HX711_SCK_HIGH(); // 产生上升沿
delay_us(1); // 保持高电平,HX711在上升沿后稳定数据
count <<= 1; // 左移,为下一位腾出空间
HX711_SCK_LOW(); // 拉低时钟
delay_us(1);
if(HX711_DOUT_READ() == GPIO_PIN_SET) {
count++;
}
}
// 第25个脉冲,用于设置下一次为通道A增益128
HX711_SCK_HIGH();
delay_us(1);
HX711_SCK_LOW();
// 转换补码到直接二进制形式(可选,取决于你的数据处理逻辑)
// count = count ^ 0x800000;
return count;
}
这段代码中,HX711_SCK_HIGH/LOW和HX711_DOUT_READ是宏定义,指向具体的GPIO操作。delay_us函数需要用一个定时器(如TIM3)精确实现微秒级延时,这是通信稳定的保证。
3.2 重量计算与校准:从AD值到克数
读取到的count是一个与传感器所受压力成正比的原始AD值。要把它转换成有意义的重量(克),需要两步:去皮和标定。
首先,在传感器空载时读取一个AD值,作为“皮重”(Tare Weight)。之后的所有测量值都减去这个皮重,得到“净重”AD值。
uint32_t tare_weight_ad = 0;
void get_tare_weight(void) {
uint32_t sum = 0;
for(int i=0; i<10; i++) { // 采样10次取平均,减少噪声
sum += HX711_Read();
delay_ms(10);
}
tare_weight_ad = sum / 10;
}
接下来是最关键的标定。你需要一个已知重量的标准砝码(比如100g或500g)。将砝码放在秤上,读取此时的净重AD值。
uint32_t cal_weight_ad = 0;
float scale_factor = 0.0; // 比例系数,单位:AD值/克
void calibration(uint32_t known_weight_grams) {
uint32_t sum = 0;
printf("请放置 %lug 砝码后按键...\n", known_weight_grams);
wait_for_key();
for(int i=0; i<20; i++) {
sum += HX711_Read();
delay_ms(10);
}
cal_weight_ad = (sum / 20) - tare_weight_ad; // 得到砝码对应的净AD值
scale_factor = (float)cal_weight_ad / known_weight_grams;
printf("标定完成!比例系数: %.2f AD/g\n", scale_factor);
// 将scale_factor存入Flash,下次上电直接使用
}
得到比例系数scale_factor后,任何净重AD值都可以转换为克数:weight_grams = (raw_ad - tare_weight_ad) / scale_factor。
在实际代码中,我通常会将tare_weight_ad和scale_factor保存到STM32的内部Flash中,这样设备断电重启后无需重新标定。
4. 用户界面与系统集成:OLED显示与按键交互
硬件驱动和核心算法完成后,一个友好的用户界面能让你的作品瞬间提升档次。我们使用I2C OLED来显示重量,并用按键实现去皮、切换单位等功能。
4.1 OLED显示驱动与界面设计
使用STM32的硬件I2C或软件模拟I2C驱动OLED都很方便。这里以硬件I2C2(PB10, PB11)为例。在STM32CubeMX中配置好I2C后,可以借助成熟的OLED驱动库(如ssd1306或u8g2)来简化开发。
一个清晰的称重界面可以包含以下信息:
- 当前重量(大字体显示)
- 单位(g/kg)
- 电池电量图标(如果使用电池供电)
- 状态提示(如“稳定”、“去皮中”)
在主循环中,你可以这样组织显示逻辑:
void update_display(void) {
char weight_str[20];
float current_weight = get_current_weight(); // 调用你的重量计算函数
OLED_Clear();
OLED_SetFont(&Font16x24); // 使用大字体
sprintf(weight_str, "%.1f", current_weight);
OLED_PrintStringCentered(40, weight_str); // 居中显示重量
OLED_SetFont(&Font8x16);
OLED_PrintString(90, 45, "g"); // 显示单位
if(is_weight_stable()) { // 判断重量是否稳定
OLED_DrawRect(10, 10, 108, 20); // 绘制稳定框
}
OLED_Update(); // 刷新屏幕
}
4.2 按键功能设计与状态机
通常我们需要两个按键:一个用于去皮/归零,一个用于切换单位(如克与盎司)。为了消抖和实现长按等功能,建议使用状态机来处理按键。
typedef enum {
KEY_IDLE,
KEY_DEBOUNCE,
KEY_PRESSED,
KEY_LONG_PRESS
} key_state_t;
void key_scan_task(void) {
static key_state_t state = KEY_IDLE;
static uint32_t press_tick = 0;
switch(state) {
case KEY_IDLE:
if(is_key_tare_pressed()) { // 检测按键按下
state = KEY_DEBOUNCE;
press_tick = HAL_GetTick();
}
break;
case KEY_DEBOUNCE:
if(HAL_GetTick() - press_tick > 20) { // 消抖20ms
if(is_key_tare_pressed()) {
state = KEY_PRESSED;
tare(); // 执行去皮操作
} else {
state = KEY_IDLE;
}
}
break;
case KEY_PRESSED:
if(!is_key_tare_pressed()) { // 按键释放
state = KEY_IDLE;
} else if(HAL_GetTick() - press_tick > 1000) { // 长按1秒
state = KEY_LONG_PRESS;
enter_calibration_mode(); // 进入标定模式
}
break;
case KEY_LONG_PRESS:
if(!is_key_tare_pressed()) {
state = KEY_IDLE;
}
break;
}
}
将按键扫描任务放在定时器中断或主循环中定期执行,整个系统的交互就变得非常流畅。
5. 精度优化与进阶玩法
一个基础能工作的电子秤只是开始,如何让它更准、更稳、更专业,才是DIY的乐趣所在。
5.1 软件滤波:让读数更稳定
称重传感器的输出信号难免含有高频噪声。除了在硬件上做好电源滤波,软件滤波算法成本低、效果显著。移动平均滤波和一阶低通滤波是两种最常用的方法。
#define FILTER_SAMPLE_NUM 10
uint32_t adc_filter_buffer[FILTER_SAMPLE_NUM];
uint8_t filter_index = 0;
/**
* @brief 滑动平均滤波器
* @param new_ad_value: 新的AD采样值
* @retval 滤波后的AD值
*/
uint32_t moving_average_filter(uint32_t new_ad_value) {
uint32_t sum = 0;
adc_filter_buffer[filter_index] = new_ad_value;
filter_index = (filter_index + 1) % FILTER_SAMPLE_NUM;
for(int i=0; i<FILTER_SAMPLE_NUM; i++) {
sum += adc_filter_buffer[i];
}
return sum / FILTER_SAMPLE_NUM;
}
/**
* @brief 一阶低通数字滤波器 (IIR)
* @param new_ad_value: 新的AD采样值
* @param prev_filtered: 上一次滤波输出
* @param alpha: 滤波系数 (0.0~1.0),越小越平滑,响应越慢
* @retval 滤波后的AD值
*/
float low_pass_filter(float new_ad_value, float prev_filtered, float alpha) {
return alpha * new_ad_value + (1.0 - alpha) * prev_filtered;
}
在实际项目中,我常常先使用移动平均滤除明显毛刺,再用一个较轻的低通滤波让最终显示值缓慢变化,这样既能快速响应重量变化,又能避免最后一位数字不停跳动,用户体验非常好。
5.2 进阶扩展思路
当你的基础版本运行良好后,可以尝试以下扩展,让项目更具挑战性:
- 多传感器求和:将四个传感器放在桌角,用四个HX711模块,STM32读取四路数据相加,制作一个“体重秤”或“行李秤”。
- 蓝牙/Wi-Fi数据传输:接入ESP-01S或HC-05蓝牙模块,将重量数据发送到手机APP,实现记录和统计。你可以使用串口JSON格式传输数据,例如
{"weight": 250.5, "unit": "g"}。 - 自动休眠与低功耗:利用HX711的掉电模式(将SCK引脚置高并保持超过60us),当长时间无重量变化时,让STM32和HX711进入休眠,仅由按键中断唤醒,非常适合电池供电的场景。
- 外壳与结构设计:使用3D打印或亚克力板为你的电子秤制作一个漂亮、坚固的外壳。结构设计要保证传感器只承受垂直方向的力,避免侧向力影响精度。
调试这类项目,一台逻辑分析仪会是你的得力助手。它可以清晰地捕捉SCK和DOUT之间的时序,帮助你确认通信是否正常,延时是否准确。当读数不准时,不要急于调整代码,先检查硬件连接是否牢固,电源是否干净,传感器安装是否受力均匀。这些细节往往决定了项目的最终精度。
472



被折叠的 条评论
为什么被折叠?



