HX711介绍

HX711是一种高精度、低成本的压力传感器信号放大器,主要用于测量重力或压力变化。它工作在 2.7V 至 5.5V 的电源范围内,支持多种传感器类型,如电桥、应变计等。HX711 提供了一种简便的数字接口来读取模拟输出信号,适用于嵌入式系统和传感器应用。
1、海芯HX711介绍手册
2、海芯HX711手册

一、HX711参数和电路

称重传感器模块有灵敏度和激励电压两个重要参数:(这两个参数是假设值,以实际设计电路为准)
灵敏度:1.0mV/V
激励电压:3~12V
满量程输出电压 = 激励电压 * 灵敏度
这样的话,假如激励电压是 3V,那么输出最大电压是 3mV。

引脚名称 描述
GND 模块地线
DT 数据输出端口
SCK 时钟输入端口
VCC 电源

HX711 Pinout

二、工作原理介绍

HX711压力传感器的工作原理是基于全臂电桥,利用压力传感器的阻值变化,转化为电压变化,从而实现重量的测量。

应变片一般由金属丝为材料制作的应变电阻,当金属丝受外力作用时,其长度和截面积都会发生变化,进而其电阻值即会发生改变。

当金属丝受外力作用而伸长时,其长度增加,而截面积减少,电阻值便会增大。
当金属丝受外力作用而压缩时,长度减小而截面增加,电阻值则会减小。
金属应变片对电阻丝材料有较高的要求,一般要求灵敏度系数大,电阻温系数小,具有优良的机械加工和焊接性能等,康铜是目前应用最广泛的应变丝材料。

应变片示意图

称重传感器模块上下表面各有一个应变片,每个应变片内有 2 个压力电阻。一共为 4 个压力电阻,组成全桥式电路。全桥电路可以提高所测的精度,而且电桥本身也能实现自补偿(温度补偿)。

全桥电路示意图

三、数据的获取与处理

HX711 芯片与单片机的通讯只需要两个引脚,时钟引脚PD_SCK及数据引脚DOUT,用来输出数据,选择输入通道和增益。
当数据输出管脚 DOUT 为高电平时,表明 A/D 转换器还未准备好输出数据,此时串口时钟输入信号 PD_SCK 应为低电平。当 DOUT 从高电平变低电平后,PD_SCK 应输入 25 至 27 个不等的时钟脉冲。
其中第一个时钟脉冲的上升沿将读出输出 24 位数据的最高位(MSB),直至第 24 个时钟脉冲完成,24 位输出数据从最高位至最低位逐位输出完成。第 25 至 27 个时钟脉冲用来决定下一次 A/D 转换的输入通道和增益。

HX711.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#ifndef __HX711_H
#define __HX711_H

#include "stm32f10x.h" // 包含STM32F10x系列的头文件

#define HX711_DT_GPIO GPIOA
#define HX711_SCK_GPIO GPIOA
#define HX711_DT_PIN GPIO_Pin_0
#define HX711_SCK_PIN GPIO_Pin_1

// --- HX711 相关宏定义 ---
// 用于控制SCK和读取DT的引脚状态
#define W_SCK(x) GPIO_WriteBit(HX711_SCK_GPIO, HX711_SCK_PIN, (BitAction)(x))
#define R_DT GPIO_ReadInputDataBit(HX711_DT_GPIO, HX711_DT_PIN)



// 校准系数 (需要根据实际校准结果调整)
// GapValue 代表单位重量变化对应的ADC差值
extern float GapValue;

// --- 全局变量声明 ---
extern unsigned long HX711_Buffer;
extern unsigned long Weight_Maopi;
extern int32_t Weight_Zhengshu;
extern int32_t Weight_xiaoshu;
extern float Weight_tmp;

extern float Weight_filtered;

// --- 函数声明 ---
void HX711_Init(void);
unsigned long HX711_Read(void);
void Get_Maopi(void); // 获取毛皮值(去皮)
void Get_Weight(void); // 获取当前重量

#endif /* __HX711_H */

HX711.C

为了实现较高精度并且稳定数值,本代码采用以下算法:
取五次连续测量值,排序后刨除两个极值,再对剩余的三个值求平均值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
#include "HX711.h"

// --- 全局变量定义 ---
unsigned long HX711_Buffer = 0;
unsigned long Weight_Maopi = 0;
int32_t Weight_Zhengshu = 0;
int32_t Weight_xiaoshu = 0;
float Weight_tmp = 0.0f;
float Weight_filtered = 0.0f;
float GapValue = 3774.2f;
/**
* @brief 初始化HX711连接的GPIO引脚
* 针对STM32F103C8T6 (如BluePill核心板) 进行了时钟使能配置
* @param None
* @retval None
*/

void HX711_Init(void)
{
GPIO_InitTypeDef hx711_gpio_struct;

// 使能GPIO时钟
// 对于PB0, PB1,需要使能GPIOB的时钟
// STM32F103C8T6通常只挂载在APB2总线上
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);

// 配置SCK引脚 (PB1) - 推挽输出
hx711_gpio_struct.GPIO_Mode = GPIO_Mode_Out_PP;
hx711_gpio_struct.GPIO_Pin = HX711_SCK_PIN;
hx711_gpio_struct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(HX711_SCK_GPIO, &hx711_gpio_struct);

// 配置DT引脚 (PB0) - 上拉输入
hx711_gpio_struct.GPIO_Mode = GPIO_Mode_IPU; // 上拉输入
hx711_gpio_struct.GPIO_Pin = HX711_DT_PIN;
// GPIO_Speed 对输入模式无意义,可不设置或保持上次设置
GPIO_Init(HX711_DT_GPIO, &hx711_gpio_struct);

// 初始化时将SCK拉高 (虽然注释说拉高,但函数内部是拉低,这里保持原逻辑)
// 根据HX711协议,SCK空闲时应为低电平,这里拉高是为了在初始化后确保状态
W_SCK(1);
}

/**
* @brief 从HX711读取一次A/D转换结果
* @param None
* @retval unsigned long - 24位ADC值 (已处理符号位)
*/
unsigned long HX711_Read(void)
{
unsigned long Count = 0;
unsigned char i;

W_SCK(0); // 拉低SCK,准备开始读取

// 等待HX711准备好 (DT拉低表示转换完成)
// 注意:如果HX711一直未准备好,这里可能会死等,实际应用中可能需要超时处理
while(R_DT);

// 读取24位数据 (对应128增益)
for (i = 0; i < 24; i++)
{
W_SCK(1); // SCK拉高,数据准备好
Count = Count << 1; // 数据左移一位
W_SCK(0); // SCK拉低,为下次读取做准备
if(R_DT) // 读取当前位数据
{
Count++; // 如果DT为高,则当前位为1
}
}

// 读取完24位后,需要额外的一个时钟脉冲来设置下一次转换的增益 (对于128增益)
// 这样总共是25个脉冲,对应A+通道128倍增益
W_SCK(1); // 第25个脉冲
Count = Count ^ 0x800000; // 对24位数据的最高位(符号位)进行处理
// HX711的24位数据是有符号的,最高位为符号位
// 当读数为负时,最高位为1,这里通过异或操作将其取反,
// 转换为无符号数,方便后续计算。注意这改变了负数的表示方式。
W_SCK(0); // 结束读取,拉低SCK

return Count;
}


/**
* @brief 获取当前毛皮值 (去皮值)
* @param None
* @retval None
*/
void Get_Maopi(void)
{
Weight_Maopi = HX711_Read(); // 读取当前空载状态下的ADC值作为毛皮值
}

/**
* @brief 获取当前重量
* @param None
* @retval None
*/
/**
* @brief 获取当前重量
* @param None
* @retval None
*/
void Get_Weight(void)
{
unsigned long temp_buffers[5] = {0};
float current_weights[5] = {0.0f};
float sorted_weights[5] = {0.0f};
float current_weight = 0.0f;

// 读取五次ADC值,中间添加延迟
for(int i = 0; i < 5; i++)
{
temp_buffers[i] = HX711_Read();
// delayus(60);
}

// 计算五次重量
for(int i = 0; i < 5; i++)
{
// 不再强制限制为0,保留负值
int32_t temp_diff = temp_buffers[i] - Weight_Maopi;
current_weights[i] = ((float)temp_diff / GapValue) * 10.0f / 1.082f;
}

// 复制到排序数组
for(int i = 0; i < 5; i++)
{
sorted_weights[i] = current_weights[i];
}

// 冒泡排序
for(int i = 0; i < 4; i++)
{
for(int j = 0; j < 4 - i; j++)
{
if(sorted_weights[j] > sorted_weights[j+1])
{
float temp = sorted_weights[j];
sorted_weights[j] = sorted_weights[j+1];
sorted_weights[j+1] = temp;
}
}
}

// 去除最大最小值,取中间3个求平均
current_weight = (sorted_weights[1] + sorted_weights[2] + sorted_weights[3]) / 3.0f;

// 应用死区阈值处理
if (Weight_filtered == 0.0f)
{
// 第一次读取,直接使用当前值
Weight_filtered = current_weight;
}
else
{
// 计算当前值与上一个滤波值的差值
float diff = (current_weight > Weight_filtered) ?
(current_weight - Weight_filtered) :
(Weight_filtered - current_weight);

// 死区阈值
const float dead_zone = 0.03f; // 死区阈值,0.03g以内认为无效

if (diff >= dead_zone)
{
// 差值大于等于死区阈值,更新滤波值
Weight_filtered = current_weight;
}
// 如果差值小于死区阈值,保持原值不变
}

Weight_tmp = Weight_filtered;

// 提取整数部分
Weight_Zhengshu = (int32_t)Weight_tmp;

// 正确计算小数部分(保留符号)
float decimal_part = Weight_tmp - (float)Weight_Zhengshu;
Weight_xiaoshu = (int32_t)(decimal_part * 100);

// 保证小数部分的绝对值不超过99
if (Weight_xiaoshu >= 100) {
Weight_xiaoshu = 99;
} else if (Weight_xiaoshu <= -100) {
Weight_xiaoshu = -99;
}
}

四、实物展示

实物图