从电子秤到智能设备:用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 电路连接指南

将各组件按以下方式连接:

  1. HX711与STM32F4连接

    • VCC → 3.3V
    • GND → GND
    • DT → PA0 (数据输入)
    • SCK → PA1 (时钟输出)
  2. 称重传感器与HX711连接

    • 红线 → E+
    • 黑线 → E-
    • 白线 → A-
    • 绿线 → A+
  3. 附加设备连接

    • 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;
}

校准过程建议:

  1. 无负载时调用HX711_Tare()
  2. 放置已知重量物体,记录原始读数
  3. 计算校准系数:calibration_factor = (raw_reading - tare_value) / known_weight
  4. 重复多次取平均值

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 创意应用场景

这个基础框架可以扩展为多种有趣的应用:

  • 智能互动桌:不同区域的压力触发不同多媒体内容
  • 电子乐器:压力大小控制音高或音量
  • 健身设备:监测训练时的力量分布
  • 工业检测:简单重量分拣系统

对于更复杂的交互,可以添加以下功能:

  1. 压力变化速率检测
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;
}
  1. 多传感器融合:结合其他传感器(如温度、湿度)实现更智能的判断

  2. 模式切换:通过按钮切换不同的响应模式(如称重模式、交互模式)

在项目开发过程中,我发现最关键的挑战是传感器的稳定性和抗干扰能力。通过多次实验,采用以下措施可以显著改善性能:

  • 为传感器提供稳定的电源
  • 使用屏蔽线连接传感器
  • 在机械结构上确保受力均匀
  • 软件上采用合适的滤波算法
Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐