从电子秤到智能设备:用STM32F4和HX711做个“压力感应”小装置(附完整源码)
从电子秤到智能设备:用STM32F4和HX711打造压力感应互动装置
在创客和电子爱好者的世界里,将简单的传感器转化为智能交互设备总是充满乐趣。今天我们要探索的,是如何把常见的HX711压力传感器模块从传统的电子秤应用,升级为一个能感知压力变化并触发多种动作的智能装置。这个项目不仅适合想要学习嵌入式开发的初学者,也能给有经验的开发者提供一些创意灵感。
想象一下:当你在桌面上放置不同重量的物体时,装置能自动改变LED颜色、播放特定音效,甚至通过串口发送消息控制其他智能设备。这种压力感应的互动方式,可以应用在智能家居、艺术装置、教育工具等多个场景中。
1. 硬件选型与原理剖析
1.1 核心组件介绍
我们的项目需要以下关键硬件组件:
- STM32F4 Discovery开发板:基于ARM Cortex-M4内核,主频可达168MHz,具备丰富的GPIO和外设资源
- HX711模块:24位高精度ADC芯片,专为称重传感器设计
- 压力传感器(称重传感器):通常采用应变片原理,常见规格有5kg、10kg等
- 辅助元件:LED、蜂鸣器、电阻等
HX711芯片的优势在于其高度集成化设计:
| 特性 | 优势 |
|---|---|
| 24位ADC | 高分辨率测量 |
| 内置可编程增益放大器 | 支持128或64倍增益 |
| 片内稳压电源 | 减少外部电路需求 |
| 简单数字接口 | 只需2个GPIO即可控制 |
1.2 压力传感物理原理
压力传感器的工作原理基于金属应变片的电阻变化。当外力作用时,应变片会产生形变,导致其电阻值发生变化。HX711通过测量这种微小变化,将其转换为数字信号。
典型的称重传感器采用惠斯通电桥结构,由四个应变片组成:
[应变片1]----[应变片2]
| |
[电源+] [输出+]
| |
[应变片3]----[应变片4]
当施加压力时,对角位置的应变片一个受拉(电阻增加),一个受压(电阻减小),产生差分电压输出。
2. 硬件连接与CubeMX配置
2.1 电路连接指南
将各组件按以下方式连接:
-
HX711与STM32F4连接:
- VCC → 3.3V
- GND → GND
- DT → PA0 (数据输入)
- SCK → PA1 (时钟输出)
-
称重传感器与HX711连接:
- 红线 → E+
- 黑线 → E-
- 白线 → A-
- 绿线 → A+
-
附加设备连接:
- LED → PB0 (通过220Ω电阻)
- 蜂鸣器 → PB1 (通过晶体管驱动)
2.2 STM32CubeMX关键配置
在CubeMX中需要进行以下设置:
// GPIO配置
// PA0 - 输入模式,上拉
// PA1 - 输出模式,推挽,无上拉下拉
// PB0 - 输出模式,推挽
// PB1 - 输出模式,推挽
// 时钟配置
// 使用外部晶振,主频设置为168MHz
// APB1分频系数为4 (42MHz)
// APB2分频系数为2 (84MHz)
// 串口配置 (用于调试输出)
// USART1, 波特率115200, 8数据位, 无校验, 1停止位
注意:HX711的时钟信号需要精确控制时序,建议使用定时器产生精确的微秒级延时。
3. 软件设计与核心代码实现
3.1 HX711驱动开发
首先创建HX711的驱动程序,包含以下关键功能:
// hx711.h
#ifndef __HX711_H
#define __HX711_H
#include "stm32f4xx_hal.h"
#define HX711_DT_PIN GPIO_PIN_0
#define HX711_DT_PORT GPIOA
#define HX711_SCK_PIN GPIO_PIN_1
#define HX711_SCK_PORT GPIOA
void HX711_Init(void);
int32_t HX711_ReadData(void);
float HX711_GetWeight(void);
#endif
驱动实现部分:
// hx711.c
#include "hx711.h"
#include "delay.h"
static uint32_t tare_value = 0;
static float calibration_factor = 106.5f; // 需要根据实际传感器校准
void HX711_Init(void) {
GPIO_InitTypeDef GPIO_InitStruct = {0};
// SCK引脚初始化
GPIO_InitStruct.Pin = HX711_SCK_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(HX711_SCK_PORT, &GPIO_InitStruct);
// DT引脚初始化
GPIO_InitStruct.Pin = HX711_DT_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(HX711_DT_PORT, &GPIO_InitStruct);
HAL_GPIO_WritePin(HX711_SCK_PORT, HX711_SCK_PIN, GPIO_PIN_RESET);
}
int32_t HX711_ReadData(void) {
uint32_t data = 0;
while(HAL_GPIO_ReadPin(HX711_DT_PORT, HX711_DT_PIN) == GPIO_PIN_SET);
for(uint8_t i=0; i<24; i++) {
HAL_GPIO_WritePin(HX711_SCK_PORT, HX711_SCK_PIN, GPIO_PIN_SET);
delay_us(1);
data <<= 1;
HAL_GPIO_WritePin(HX711_SCK_PORT, HX711_SCK_PIN, GPIO_PIN_RESET);
delay_us(1);
if(HAL_GPIO_ReadPin(HX711_DT_PORT, HX711_DT_PIN) == GPIO_PIN_SET) {
data++;
}
}
// 第25个脉冲选择通道和增益
HAL_GPIO_WritePin(HX711_SCK_PORT, HX711_SCK_PIN, GPIO_PIN_SET);
delay_us(1);
HAL_GPIO_WritePin(HX711_SCK_PORT, HX711_SCK_PIN, GPIO_PIN_RESET);
delay_us(1);
// 补码转换
if(data & 0x800000) {
data |= 0xFF000000;
}
return (int32_t)data;
}
float HX711_GetWeight(void) {
int32_t raw_data = HX711_ReadData();
float weight = (raw_data - tare_value) / calibration_factor;
return weight;
}
void HX711_Tare(void) {
tare_value = HX711_ReadData();
}
3.2 压力事件状态机设计
为了实现不同压力级别触发不同动作,我们采用状态机设计:
typedef enum {
STATE_NO_PRESSURE,
STATE_LIGHT_PRESSURE,
STATE_MEDIUM_PRESSURE,
STATE_HEAVY_PRESSURE
} PressureState;
PressureState current_state = STATE_NO_PRESSURE;
void PressureFSM_Update(float weight) {
static PressureState previous_state = STATE_NO_PRESSURE;
// 状态转换逻辑
if(weight < 10.0f) {
current_state = STATE_NO_PRESSURE;
}
else if(weight < 100.0f) {
current_state = STATE_LIGHT_PRESSURE;
}
else if(weight < 500.0f) {
current_state = STATE_MEDIUM_PRESSURE;
}
else {
current_state = STATE_HEAVY_PRESSURE;
}
// 状态进入动作
if(current_state != previous_state) {
switch(current_state) {
case STATE_NO_PRESSURE:
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET); // LED off
break;
case STATE_LIGHT_PRESSURE:
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET); // LED on
break;
case STATE_MEDIUM_PRESSURE:
// 闪烁LED
break;
case STATE_HEAVY_PRESSURE:
// 触发蜂鸣器
break;
}
previous_state = current_state;
}
}
3.3 主程序逻辑
主循环中整合所有功能:
int main(void) {
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_USART1_UART_Init();
HX711_Init();
HX711_Tare(); // 去皮
while(1) {
float weight = HX711_GetWeight();
PressureFSM_Update(weight);
printf("当前重量: %.2f g\r\n", weight);
HAL_Delay(200);
}
}
4. 高级功能扩展与优化
4.1 数据滤波与校准
原始传感器数据往往存在噪声,我们需要实现滤波算法:
#define FILTER_SAMPLES 5
float HX711_GetFilteredWeight(void) {
static float samples[FILTER_SAMPLES] = {0};
static uint8_t index = 0;
float sum = 0;
samples[index] = HX711_GetWeight();
index = (index + 1) % FILTER_SAMPLES;
for(uint8_t i=0; i<FILTER_SAMPLES; i++) {
sum += samples[i];
}
return sum / FILTER_SAMPLES;
}
校准过程建议:
- 无负载时调用HX711_Tare()
- 放置已知重量物体,记录原始读数
- 计算校准系数:calibration_factor = (raw_reading - tare_value) / known_weight
- 重复多次取平均值
4.2 多设备联动控制
通过串口或无线模块实现与其他设备通信:
void SendPressureEvent(PressureState state) {
switch(state) {
case STATE_LIGHT_PRESSURE:
printf("EVENT:LIGHT\r\n");
break;
case STATE_MEDIUM_PRESSURE:
printf("EVENT:MEDIUM\r\n");
break;
case STATE_HEAVY_PRESSURE:
printf("EVENT:HEAVY\r\n");
break;
default:
printf("EVENT:NONE\r\n");
}
// 或者通过UART发送特定协议数据
uint8_t data[] = {0xAA, (uint8_t)state, 0x55};
HAL_UART_Transmit(&huart1, data, sizeof(data), HAL_MAX_DELAY);
}
4.3 创意应用场景
这个基础框架可以扩展为多种有趣的应用:
- 智能互动桌:不同区域的压力触发不同多媒体内容
- 电子乐器:压力大小控制音高或音量
- 健身设备:监测训练时的力量分布
- 工业检测:简单重量分拣系统
对于更复杂的交互,可以添加以下功能:
- 压力变化速率检测:
float prev_weight = 0;
uint32_t prev_time = 0;
float GetPressureRate(void) {
uint32_t current_time = HAL_GetTick();
float current_weight = HX711_GetFilteredWeight();
float rate = (current_weight - prev_weight) / (current_time - prev_time);
prev_weight = current_weight;
prev_time = current_time;
return rate;
}
-
多传感器融合:结合其他传感器(如温度、湿度)实现更智能的判断
-
模式切换:通过按钮切换不同的响应模式(如称重模式、交互模式)
在项目开发过程中,我发现最关键的挑战是传感器的稳定性和抗干扰能力。通过多次实验,采用以下措施可以显著改善性能:
- 为传感器提供稳定的电源
- 使用屏蔽线连接传感器
- 在机械结构上确保受力均匀
- 软件上采用合适的滤波算法
更多推荐
所有评论(0)