1. 项目概述:HX711传感器数据读取实战
在嵌入式开发中,传感器数据采集是最基础也最考验基本功的环节。最近我在用STM32调试一款电子秤项目时,选择了HX711这款24位ADC芯片作为压力传感器的信号转换器。虽然现在各种代码生成工具能一键输出驱动代码,但真正要解决实际问题时,还是得自己吃透时序逻辑。下面我就把从零解读HX711时序图到最终实现数据采集的全过程做个详细记录,特别适合刚接触传感器驱动的朋友参考。
HX711作为专为电子秤设计的ADC芯片,其核心优势在于内置可编程增益放大器(PGA)和24位高精度模数转换器。与常见的I2C/SPI接口传感器不同,它采用简单的两线制串行通信(SCK时钟线和DT数据线),这种设计既降低了硬件复杂度,又避免了协议栈的资源消耗。但代价就是开发者需要手动控制时序,这也是本文要重点解析的部分。
2. 时序图深度解析
2.1 引脚功能与基本时序
HX711仅有两个通信引脚:
- SCK(PD_SCK): 时钟输入,由MCU控制,用于同步数据传输
- DT(DOUT): 数据输出,传感器通过该引脚向MCU发送转换结果
观察图1的初始状态时序可以发现三个关键特征:
- 空闲时DT保持高电平(上拉电阻典型值50kΩ)
- 当ADC转换完成且数据就绪时,DT会自动拉低作为中断信号
- SCK的每个上升沿触发一位数据的输出
实际调试中发现:如果DT长时间保持低电平,可能是供电不足(建议3.3V-5V)或传感器过载,此时读取的数据无效。
2.2 数据读取时序详解
图2展示了完整的24位数据读取过程,我们需要关注几个重要时间参数:
| 参数 | 描述 | 典型值 | 设置建议 |
|---|---|---|---|
| T1 | SCK首次拉低到首次拉高的间隔 | ≥1μs | 设为2μs更稳妥 |
| T2 | DT响应时间 | 0.1μs | 无需主动设置 |
| T3 | SCK高电平持续时间 | ≥0.2μs | 建议1μs |
| T4 | SCK低电平持续时间 | ≥0.2μs | 建议1μs |
数据读取的核心逻辑是:
- 检测DT变低后,先拉低SCK至少1μs(满足T1)
- 循环24次以下操作:
- 拉高SCK并保持1μs(T3)
- 读取DT当前电平(MSB优先)
- 拉低SCK并保持1μs(T4)
- 第25-27个脉冲用于设置下次转换的通道和增益
2.3 增益与通道选择
HX711支持两种差分输入通道和三种增益组合:
c复制// 增益选择脉冲数对照表
#define GAIN_128 25 // 通道A,增益128
#define GAIN_64 27 // 通道A,增益64
#define GAIN_32 26 // 通道B,增益32
实际应用中选择建议:
- 电子秤压力检测:通道A + 增益128(最佳信噪比)
- 电池电压检测:通道B + 增益32(输入范围更宽)
3. STM32驱动实现
3.1 硬件连接方案
以STM32F103C8T6最小系统板为例:
| HX711引脚 | STM32连接 | 备注 |
|---|---|---|
| VCC | 3.3V | 也可接5V |
| GND | GND | 需共地 |
| DT | PA0 | 配置为上拉输入 |
| SCK | PA1 | 推挽输出 |
注意:传感器与MCU间导线长度建议不超过20cm,过长可能引入干扰
3.2 关键代码实现
引脚初始化
c复制void HX711_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
// 使能GPIO时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
// SCK引脚配置
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_1;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStruct);
// DT引脚配置
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU; // 上拉输入
GPIO_Init(GPIOA, &GPIO_InitStruct);
HX711_SCK_L; // 初始拉低SCK
}
数据读取函数优化版
c复制uint32_t HX711_ReadData(void)
{
uint32_t data = 0;
uint8_t i;
// 等待数据就绪
while(HX711_DT_READ());
// 禁用中断保证时序精确
__disable_irq();
// 读取24位数据
for(i=0; i<24; i++) {
HX711_SCK_H;
Delay_us(1);
data <<= 1;
HX711_SCK_L;
Delay_us(1);
if(HX711_DT_READ()) data++;
}
// 设置下次为通道A增益128
HX711_SCK_H;
Delay_us(1);
HX711_SCK_L;
Delay_us(1);
__enable_irq();
// 符号位处理
return data ^ 0x800000;
}
4. 数据处理与校准
4.1 原始数据处理
HX711输出的24位数据需要经过以下处理:
- 去符号处理:最高位为符号位,通过异或0x800000转换为纯数值
- 数值转换:
c复制float weight = (raw_data - offset) / scale;- offset: 空载时的基准值
- scale: 灵敏度系数(需校准)
4.2 校准方法实践
推荐采用两点校准法:
-
空载校准:
- 不放任何重物,连续采样100次取平均值作为offset
c复制for(int i=0; i<100; i++) { offset += HX711_ReadData(); Delay_ms(10); } offset /= 100; -
满量程校准:
- 放置已知重物(如500g砝码),记录raw_data
- 计算scale = (raw_data - offset)/实际重量
实测技巧:校准前先预热传感器10分钟,读数更稳定
5. 常见问题排查
5.1 数据跳动严重
可能原因及解决方案:
-
电源干扰:
- 在HX711的VCC与GND间加100nF+10μF电容
- 避免与电机等大电流设备共用电源
-
机械振动:
- 使用橡胶垫隔离传感器与振动源
- 软件上采用滑动平均滤波:
c复制#define SAMPLE_SIZE 5 int32_t buffer[SAMPLE_SIZE]; int32_t GetFilteredData() { static uint8_t index = 0; buffer[index++] = HX711_ReadData(); if(index >= SAMPLE_SIZE) index = 0; int64_t sum = 0; for(uint8_t i=0; i<SAMPLE_SIZE; i++) { sum += buffer[i]; } return sum / SAMPLE_SIZE; }
5.2 读数始终为零
检查步骤:
- 用万用表测量DT引脚电压,正常应在0-3.3V间跳变
- 检查SCK信号是否正常输出(示波器观察1μs脉冲)
- 确认HX711的E引脚(使能端)已接高电平
5.3 长期漂移问题
解决方法:
- 定期自动零点校准(每小时间隔)
- 采用温度补偿算法(需额外DS18B20传感器)
- 选择更高精度的基准电压源
6. 性能优化建议
-
低功耗设计:
c复制void HX711_PowerDown(void) { HX711_SCK_H; Delay_us(60); // 保持高电平>60μs进入休眠 } void HX711_WakeUp(void) { HX711_SCK_L; Delay_ms(1); // 唤醒延迟 } -
多传感器轮询:
- 通过模拟开关(如CD4051)扩展多个HX711
- 分时复用SCK线,每个DT接独立IO
-
高速采样模式:
- 将SCK时钟周期缩短至0.4μs(T3+T4)
- 需确保STM32主频≥72MHz
经过实际项目验证,这套驱动方案在电子秤应用中可实现±0.1g的测量精度。最关键的是通过手动解析时序图,再遇到类似传感器时就能举一反三。下次我将分享如何通过这个基础驱动,实现蓝牙传输和手机APP显示的高级功能。