基于STM32的高精度电子秤设计与实现

高精度电子秤系统,包含硬件设计、软件算法、滤波校准和用户界面。

一、系统总体设计

1.1 系统架构

复制代码
称重传感器 → 信号调理 → 高精度ADC → STM32 → 显示/通信
  (应变片)  (仪表放大)  (24位Σ-Δ)    (数据处理) (LCD/蓝牙)

1.2 技术指标

参数 规格 说明
量程 0-5kg 可扩展
精度 0.01g 分辨率
线性度 ±0.02%FS 满量程
采样率 10-80Hz 可调
显示 0.96寸OLED 显示重量/单位
接口 蓝牙/USB 数据传输
供电 锂电池/Type-C 低功耗设计

二、硬件电路设计

2.1 核心元件选型

模块 推荐型号 关键参数 接口
主控MCU STM32F103C8T6 72MHz, 64KB Flash, 20KB RAM -
称重传感器 BLR-1KG/5KG 电阻应变式, 灵敏度2mV/V 惠斯通电桥
ADC芯片 HX711 24位Σ-Δ, 可编程增益 GPIO
备选ADC ADS1232 24位, 内置PGA, SPI接口 SPI
运放 INA128 仪表放大器, 高共模抑制比 -
显示屏 SSD1306 0.96" 128×64 OLED, I2C接口 I2C
蓝牙模块 HC-05/HM-10 蓝牙4.0/5.0, 透传模式 UART
电池管理 TP4056 锂电池充电管理 -
LDO AMS1117-3.3 3.3V稳压, 1A输出 -

2.2 电路设计

2.2.1 信号调理电路
c 复制代码
/* 应变片信号调理电路设计 */
// 惠斯通电桥配置:
// R1,R2,R3,R4 = 350Ω (应变片电阻)
// 激励电压: 5V
// 输出电压: Vout = (ΔR/R) * Vex * GF

// 仪表放大器(INA128)配置:
// 增益公式: G = 1 + (50kΩ / Rg)
// 选择Rg = 200Ω → G ≈ 251倍
// 输出电压范围: 0-3.3V

// 低通滤波设计:
// 截止频率: 10Hz (抑制高频噪声)
// RC滤波器: R=10kΩ, C=1.6μF
2.2.2 HX711连接电路
c 复制代码
/* HX711与STM32连接 */
// HX711引脚定义:
#define HX711_DOUT    PA0  // 数据输出
#define HX711_SCK     PA1  // 时钟输入
#define HX711_AVDD    5V   // 模拟电源
#define HX711_DVDD    3.3V // 数字电源

// 外部参考电压:2.5V基准
#define REF_VOLTAGE   2.5

// 增益选择:
// 通道A: 增益128 (用于差分输入)
// 通道B: 增益32

2.3 PCB设计要点

复制代码
布局原则:
1. 模拟/数字分区:传感器信号在模拟区
2. 地线分离:模拟地和数字地单点连接
3. 电源滤波:每个IC的电源引脚加0.1μF退耦电容
4. 信号屏蔽:模拟信号线用地线包围

走线规范:
- 应变片信号线:差分走线,等长等距
- 电源线宽:≥0.5mm (1A电流)
- 晶振:靠近MCU,包地处理

三、软件设计与算法

3.1 主程序框架

c 复制代码
#include "stm32f1xx_hal.h"
#include <stdio.h>
#include <math.h>
#include <string.h>

// 系统定义
#define SAMPLE_RATE         10      // 采样率10Hz
#define FILTER_WINDOW       20      // 滤波窗口大小
#define CALIBRATION_POINTS  3       // 校准点数量

// 全局变量
typedef struct {
    float weight_g;         // 当前重量(g)
    float offset;          // 零点偏移
    float scale;           // 比例系数
    float tare_value;      // 去皮值
    uint8_t unit;          // 单位: 0=g, 1=kg, 2=lb, 3=oz
    uint8_t mode;          // 模式: 0=称重, 1=计数, 2=百分比
    uint8_t stability;     // 稳定标志
    float temperature;     // 温度补偿
} ScaleData;

ScaleData scale_data = {0};
float raw_data_buffer[FILTER_WINDOW] = {0};
uint8_t buffer_index = 0;

// 按键定义
#define KEY_TARE    PA4
#define KEY_UNIT    PA5
#define KEY_MODE    PA6
#define KEY_CAL     PA7

// 主函数
int main(void) {
    // HAL库初始化
    HAL_Init();
    
    // 系统时钟配置
    SystemClock_Config();
    
    // 外设初始化
    MX_GPIO_Init();
    MX_I2C1_Init();
    MX_USART1_UART_Init();
    MX_ADC1_Init();
    
    // 模块初始化
    hx711_init();
    oled_init();
    key_init();
    buzzer_init();
    
    // 加载校准参数
    load_calibration();
    
    // 开机自检
    self_test();
    
    // 主循环
    while (1) {
        // 1. 读取原始数据
        float raw_data = hx711_read_avg(5);
        
        // 2. 数字滤波
        float filtered = digital_filter(raw_data);
        
        // 3. 计算重量
        scale_data.weight_g = calculate_weight(filtered);
        
        // 4. 稳定性检测
        scale_data.stability = check_stability();
        
        // 5. 温度补偿
        if (need_temperature_compensation()) {
            temperature_compensation();
        }
        
        // 6. 更新显示
        update_display();
        
        // 7. 处理按键
        process_keys();
        
        // 8. 数据上传(如需要)
        upload_data();
        
        HAL_Delay(1000 / SAMPLE_RATE);
    }
}

3.2 HX711驱动程序

c 复制代码
/* HX711驱动程序 */
#include "hx711.h"

// HX711初始化
void hx711_init(void) {
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    
    // 使能GPIO时钟
    __HAL_RCC_GPIOA_CLK_ENABLE();
    
    // 配置SCK为输出
    GPIO_InitStruct.Pin = GPIO_PIN_1;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
    
    // 配置DOUT为输入
    GPIO_InitStruct.Pin = GPIO_PIN_0;
    GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
    GPIO_InitStruct.Pull = GPIO_PULLUP;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
    
    // 初始状态
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_RESET);
    
    // 等待HX711就绪
    while (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_SET);
    
    // 设置增益和通道
    hx711_set_gain(HX711_GAIN_A_128);
}

// 设置增益
void hx711_set_gain(uint8_t gain) {
    hx711_gain = gain;
    
    // 通过发送脉冲设置增益
    // 增益128: 1个脉冲
    // 增益64:  3个脉冲
    // 增益32:  2个脉冲
    
    for (uint8_t i = 0; i < gain; i++) {
        HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_SET);
        HAL_Delay(1);
        HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_RESET);
    }
}

// 读取单个原始值
int32_t hx711_read(void) {
    uint32_t data = 0;
    
    // 等待数据就绪
    while (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_SET);
    
    // 读取24位数据
    for (uint8_t i = 0; i < 24; i++) {
        HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_SET);
        data <<= 1;
        HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_RESET);
        
        if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_SET) {
            data++;
        }
    }
    
    // 设置通道和增益
    for (uint8_t i = 0; i < hx711_gain; i++) {
        HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_SET);
        HAL_Delay(1);
        HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_RESET);
    }
    
    // 符号扩展
    if (data & 0x800000) {
        data |= 0xFF000000;
    }
    
    return (int32_t)data;
}

// 读取多次平均值
float hx711_read_avg(uint8_t times) {
    int64_t sum = 0;
    
    for (uint8_t i = 0; i < times; i++) {
        sum += hx711_read();
        HAL_Delay(1);
    }
    
    return (float)sum / times;
}

// 去皮(清零)
void hx711_tare(uint8_t times) {
    float avg = hx711_read_avg(times);
    scale_data.offset = avg;
    scale_data.tare_value = 0;
    
    // 保存零点
    save_offset(avg);
}

// 校准
void hx711_calibrate(float known_weight) {
    // 1. 清零
    hx711_tare(10);
    
    // 2. 放置已知重量
    HAL_Delay(2000);
    
    // 3. 读取重量
    float raw_data = hx711_read_avg(20);
    
    // 4. 计算比例系数
    scale_data.scale = (raw_data - scale_data.offset) / known_weight;
    
    // 5. 保存校准系数
    save_calibration(scale_data.scale);
}

3.3 数字滤波算法

c 复制代码
/* 数字滤波算法 */
#include "filter.h"

// 移动平均滤波
float moving_average_filter(float new_value) {
    static float buffer[FILTER_WINDOW] = {0};
    static uint8_t index = 0;
    static float sum = 0;
    
    // 减去最旧的值
    sum -= buffer[index];
    
    // 加入新值
    buffer[index] = new_value;
    sum += new_value;
    
    // 更新索引
    index = (index + 1) % FILTER_WINDOW;
    
    return sum / FILTER_WINDOW;
}

// 中值滤波
float median_filter(float new_value) {
    static float buffer[5] = {0};
    static uint8_t index = 0;
    
    // 更新缓冲区
    buffer[index] = new_value;
    index = (index + 1) % 5;
    
    // 复制数组并排序
    float temp[5];
    memcpy(temp, buffer, sizeof(temp));
    
    // 冒泡排序
    for (uint8_t i = 0; i < 4; i++) {
        for (uint8_t j = 0; j < 4 - i; j++) {
            if (temp[j] > temp[j + 1]) {
                float tmp = temp[j];
                temp[j] = temp[j + 1];
                temp[j + 1] = tmp;
            }
        }
    }
    
    // 返回中值
    return temp[2];
}

// 卡尔曼滤波
typedef struct {
    float q;      // 过程噪声协方差
    float r;      // 测量噪声协方差
    float x;      // 状态估计值
    float p;      // 估计误差协方差
    float k;      // 卡尔曼增益
} KalmanFilter;

float kalman_filter(KalmanFilter *kf, float measurement) {
    // 预测
    kf->p = kf->p + kf->q;
    
    // 更新
    kf->k = kf->p / (kf->p + kf->r);
    kf->x = kf->x + kf->k * (measurement - kf->x);
    kf->p = (1 - kf->k) * kf->p;
    
    return kf->x;
}

// 复合滤波(移动平均 + 中值)
float composite_filter(float new_value) {
    // 中值滤波去除脉冲干扰
    float median = median_filter(new_value);
    
    // 移动平均平滑
    return moving_average_filter(median);
}

3.4 重量计算与校准

c 复制代码
/* 重量计算与校准算法 */
#include "calibration.h"

// 计算重量
float calculate_weight(float raw_data) {
    // 减去零点偏移
    float weight = raw_data - scale_data.offset;
    
    // 减去去皮值
    weight -= scale_data.tare_value;
    
    // 应用比例系数
    weight = weight / scale_data.scale;
    
    // 非线性补偿
    weight = nonlinear_compensation(weight);
    
    // 温度补偿
    weight = temperature_compensation(weight);
    
    return weight;
}

// 非线性补偿(二次曲线拟合)
float nonlinear_compensation(float weight) {
    // 补偿系数,通过多点校准得到
    static const float a = 1.0f;     // 一次项系数
    static const float b = 0.0001f;  // 二次项系数
    static const float c = 0.0f;     // 常数项
    
    return a * weight + b * weight * weight + c;
}

// 温度补偿
float temperature_compensation(float weight) {
    // 读取温度传感器
    float temp = read_temperature();
    
    // 温度补偿系数
    float tc_scale = 1.0f + 0.0001f * (temp - 25.0f);  // 25℃为参考温度
    float tc_offset = 0.1f * (temp - 25.0f);
    
    return weight * tc_scale + tc_offset;
}

// 稳定性检测
uint8_t check_stability(void) {
    static float last_values[5] = {0};
    static uint8_t index = 0;
    
    // 更新历史数据
    last_values[index] = scale_data.weight_g;
    index = (index + 1) % 5;
    
    // 计算标准差
    float sum = 0, mean = 0, variance = 0;
    
    for (uint8_t i = 0; i < 5; i++) {
        sum += last_values[i];
    }
    mean = sum / 5;
    
    for (uint8_t i = 0; i < 5; i++) {
        float diff = last_values[i] - mean;
        variance += diff * diff;
    }
    variance /= 5;
    
    float std_dev = sqrt(variance);
    
    // 判断是否稳定
    if (std_dev < 0.01f) {  // 标准差小于0.01g
        return 1;  // 稳定
    } else {
        return 0;  // 不稳定
    }
}

// 多点校准
void multi_point_calibration(float weights[], uint8_t num_points) {
    float raw_data[num_points];
    
    // 读取各校准点的原始数据
    for (uint8_t i = 0; i < num_points; i++) {
        // 显示提示
        display_message("Place %dg", (int)weights[i]);
        HAL_Delay(3000);
        
        // 读取数据
        raw_data[i] = hx711_read_avg(20);
    }
    
    // 最小二乘法拟合线性方程
    float sum_x = 0, sum_y = 0, sum_xy = 0, sum_xx = 0;
    
    for (uint8_t i = 0; i < num_points; i++) {
        sum_x += weights[i];
        sum_y += raw_data[i];
        sum_xy += weights[i] * raw_data[i];
        sum_xx += weights[i] * weights[i];
    }
    
    // 计算斜率和截距
    float n = (float)num_points;
    float denominator = n * sum_xx - sum_x * sum_x;
    
    if (fabs(denominator) > 0.0001f) {
        scale_data.scale = (n * sum_xy - sum_x * sum_y) / denominator;
        scale_data.offset = (sum_y - scale_data.scale * sum_x) / n;
    }
    
    // 保存校准参数
    save_calibration_params(scale_data.offset, scale_data.scale);
}

3.5 OLED显示驱动

c 复制代码
/* OLED显示驱动程序 */
#include "ssd1306.h"
#include "fonts.h"

// 显示初始化
void oled_init(void) {
    // I2C初始化
    uint8_t init_cmds[] = {
        0xAE, 0x20, 0x10, 0xB0, 0xC8, 0x00, 0x10, 0x40, 0x81, 0xFF,
        0xA1, 0xA6, 0xA8, 0x3F, 0xA4, 0xD3, 0x00, 0xD5, 0xF0, 0xD9,
        0x22, 0xDA, 0x12, 0xDB, 0x20, 0x8D, 0x14, 0xAF
    };
    
    for (uint8_t i = 0; i < sizeof(init_cmds); i++) {
        i2c_write_cmd(init_cmds[i]);
    }
    
    oled_clear();
}

// 更新显示
void update_display(void) {
    char buffer[20];
    
    // 清屏
    oled_clear();
    
    // 显示重量
    float display_weight = convert_unit(scale_data.weight_g, scale_data.unit);
    
    if (scale_data.mode == 0) {  // 称重模式
        // 大字体显示重量
        snprintf(buffer, sizeof(buffer), "%.2f", display_weight);
        oled_put_string(20, 0, buffer, Font_16x26);
        
        // 显示单位
        const char *unit_str[] = {"g", "kg", "lb", "oz"};
        oled_put_string(90, 0, unit_str[scale_data.unit], Font_11x18);
        
        // 显示稳定性
        if (scale_data.stability) {
            oled_put_string(0, 4, "Stable", Font_6x8);
        } else {
            oled_put_string(0, 4, "Moving", Font_6x8);
        }
        
        // 显示去皮状态
        if (fabs(scale_data.tare_value) > 0.001f) {
            oled_put_string(70, 4, "TARE", Font_6x8);
        }
        
    } else if (scale_data.mode == 1) {  // 计数模式
        // 计算数量
        float unit_weight = get_unit_weight();
        uint32_t count = 0;
        
        if (unit_weight > 0.001f) {
            count = (uint32_t)(scale_data.weight_g / unit_weight + 0.5f);
        }
        
        // 显示数量
        snprintf(buffer, sizeof(buffer), "Count: %lu", count);
        oled_put_string(0, 0, buffer, Font_11x18);
        
        snprintf(buffer, sizeof(buffer), "%.2f g/each", unit_weight);
        oled_put_string(0, 2, buffer, Font_6x8);
    }
    
    // 显示电池电量
    display_battery_level();
    
    // 显示单位指示
    display_unit_indicator();
}

// 单位转换
float convert_unit(float weight_g, uint8_t unit) {
    switch (unit) {
        case 0:  // g
            return weight_g;
        case 1:  // kg
            return weight_g / 1000.0f;
        case 2:  // lb
            return weight_g * 0.00220462f;
        case 3:  // oz
            return weight_g * 0.035274f;
        default:
            return weight_g;
    }
}

四、校准程序

4.1 自动校准程序

c 复制代码
/* 电子秤校准程序 */
#include "calibration.h"

// 校准状态
typedef enum {
    CAL_STATE_IDLE,      // 空闲
    CAL_STATE_ZERO,      // 零点校准
    CAL_STATE_WEIGHT,    // 重量校准
    CAL_STATE_DONE       // 完成
} CalibrationState;

CalibrationState cal_state = CAL_STATE_IDLE;
float cal_target_weight = 0;

// 校准处理
void calibration_process(void) {
    static uint32_t cal_timer = 0;
    static uint8_t cal_step = 0;
    
    switch (cal_state) {
        case CAL_STATE_IDLE:
            // 等待开始
            break;
            
        case CAL_STATE_ZERO:
            // 显示提示
            oled_put_string(0, 0, "Remove all weight", Font_6x8);
            oled_put_string(0, 2, "Press TARE to zero", Font_6x8);
            
            // 检查按键
            if (key_pressed(KEY_TARE)) {
                hx711_tare(20);
                cal_state = CAL_STATE_WEIGHT;
                cal_step = 0;
                cal_timer = HAL_GetTick();
            }
            break;
            
        case CAL_STATE_WEIGHT:
            if (cal_step == 0) {
                // 提示放置100g砝码
                oled_put_string(0, 0, "Place 100g weight", Font_6x8);
                oled_put_string(0, 2, "Then press TARE", Font_6x8);
                
                if (key_pressed(KEY_TARE)) {
                    cal_target_weight = 100.0f;
                    cal_step = 1;
                    cal_timer = HAL_GetTick();
                }
            } else if (cal_step == 1) {
                // 等待稳定
                if (HAL_GetTick() - cal_timer > 3000) {
                    float raw_data = hx711_read_avg(20);
                    scale_data.scale = (raw_data - scale_data.offset) / cal_target_weight;
                    cal_step = 2;
                }
            } else if (cal_step == 2) {
                // 提示放置500g砝码
                oled_put_string(0, 0, "Place 500g weight", Font_6x8);
                oled_put_string(0, 2, "Then press TARE", Font_6x8);
                
                if (key_pressed(KEY_TARE)) {
                    cal_target_weight = 500.0f;
                    cal_step = 3;
                    cal_timer = HAL_GetTick();
                }
            } else if (cal_step == 3) {
                // 等待稳定
                if (HAL_GetTick() - cal_timer > 3000) {
                    float raw_data = hx711_read_avg(20);
                    float new_scale = (raw_data - scale_data.offset) / cal_target_weight;
                    
                    // 取平均值
                    scale_data.scale = (scale_data.scale + new_scale) / 2.0f;
                    cal_step = 4;
                }
            } else if (cal_step == 4) {
                // 保存校准参数
                save_calibration_params(scale_data.offset, scale_data.scale);
                cal_state = CAL_STATE_DONE;
            }
            break;
            
        case CAL_STATE_DONE:
            oled_put_string(0, 0, "Calibration Done!", Font_6x8);
            oled_put_string(0, 2, "Scale: %.6f", Font_6x8, scale_data.scale);
            HAL_Delay(2000);
            cal_state = CAL_STATE_IDLE;
            break;
    }
}

// 进入校准模式
void enter_calibration_mode(void) {
    cal_state = CAL_STATE_ZERO;
    cal_step = 0;
}

参考代码 基于STM32的高精度电子秤 www.youwenfan.com/contentcst/133703.html

五、高级功能

5.1 计数功能

c 复制代码
/* 计数功能实现 */
#include "counting.h"

// 计数模式数据结构
typedef struct {
    float unit_weight;      // 单件重量
    uint32_t quantity;      // 数量
    uint8_t sample_count;   // 取样数量
    float samples[10];      // 取样重量
} CountingData;

CountingData count_data = {0};

// 进入计数模式
void enter_counting_mode(void) {
    count_data.unit_weight = 0;
    count_data.quantity = 0;
    count_data.sample_count = 0;
    scale_data.mode = 1;  // 计数模式
}

// 取样
void take_sample(void) {
    if (count_data.sample_count < 10 && scale_data.stability) {
        count_data.samples[count_data.sample_count] = scale_data.weight_g;
        count_data.sample_count++;
        
        // 更新单件重量
        if (count_data.sample_count > 0) {
            float sum = 0;
            for (uint8_t i = 0; i < count_data.sample_count; i++) {
                sum += count_data.samples[i];
            }
            count_data.unit_weight = sum / count_data.sample_count;
        }
    }
}

// 计算数量
uint32_t calculate_quantity(void) {
    if (count_data.unit_weight > 0.001f) {
        return (uint32_t)(scale_data.weight_g / count_data.unit_weight + 0.5f);
    }
    return 0;
}

// 清除取样
void clear_samples(void) {
    count_data.sample_count = 0;
    count_data.unit_weight = 0;
}

5.2 数据记录与统计

c 复制代码
/* 数据记录功能 */
#include "data_log.h"

#define MAX_RECORDS  100

// 称重记录
typedef struct {
    uint32_t timestamp;    // 时间戳
    float weight;         // 重量
    uint8_t unit;         // 单位
    uint8_t stable;       // 是否稳定
} WeightRecord;

WeightRecord records[MAX_RECORDS];
uint8_t record_index = 0;
uint32_t total_count = 0;
float total_weight = 0;
float max_weight = 0;
float min_weight = 999999;

// 添加记录
void add_record(float weight, uint8_t stable) {
    if (record_index >= MAX_RECORDS) {
        record_index = 0;
    }
    
    records[record_index].timestamp = HAL_GetTick();
    records[record_index].weight = weight;
    records[record_index].unit = scale_data.unit;
    records[record_index].stable = stable;
    
    // 更新统计
    total_count++;
    total_weight += weight;
    
    if (weight > max_weight) max_weight = weight;
    if (weight < min_weight) min_weight = weight;
    
    record_index++;
}

// 获取统计信息
void get_statistics(float *avg, float *max, float *min) {
    if (total_count > 0) {
        *avg = total_weight / total_count;
    } else {
        *avg = 0;
    }
    *max = max_weight;
    *min = min_weight;
}

// 保存到EEPROM
void save_records(void) {
    // 使用STM32内部EEPROM
    uint32_t address = 0x08080000;  // EEPROM起始地址
    
    for (uint8_t i = 0; i < MAX_RECORDS; i++) {
        HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, address, records[i].timestamp);
        address += 4;
        
        uint32_t weight_int = *(uint32_t*)&records[i].weight;
        HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, address, weight_int);
        address += 4;
    }
}

六、蓝牙通信

6.1 蓝牙数据传输

c 复制代码
/* 蓝牙通信协议 */
#include "bluetooth.h"

// 蓝牙命令定义
typedef enum {
    CMD_GET_WEIGHT = 0x01,     // 获取重量
    CMD_SET_TARE = 0x02,       // 去皮
    CMD_SET_UNIT = 0x03,       // 设置单位
    CMD_CALIBRATE = 0x04,      // 校准
    CMD_GET_STAT = 0x05,       // 获取统计
    CMD_START_LOG = 0x06,      // 开始记录
    CMD_STOP_LOG = 0x07,       // 停止记录
} BluetoothCommand;

// 蓝牙数据处理
void bluetooth_process(uint8_t *data, uint16_t length) {
    if (length < 1) return;
    
    uint8_t cmd = data[0];
    
    switch (cmd) {
        case CMD_GET_WEIGHT:
            send_weight_data();
            break;
            
        case CMD_SET_TARE:
            hx711_tare(10);
            send_ack(CMD_SET_TARE, 1);
            break;
            
        case CMD_SET_UNIT:
            if (length >= 2) {
                scale_data.unit = data[1] & 0x03;
                send_ack(CMD_SET_UNIT, 1);
            }
            break;
            
        case CMD_CALIBRATE:
            if (length >= 5) {
                float weight = *(float*)&data[1];
                calibrate_with_weight(weight);
                send_ack(CMD_CALIBRATE, 1);
            }
            break;
            
        case CMD_GET_STAT:
            send_statistics();
            break;
    }
}

// 发送重量数据
void send_weight_data(void) {
    uint8_t buffer[20];
    uint8_t index = 0;
    
    buffer[index++] = 0xAA;  // 帧头
    buffer[index++] = 0x55;
    buffer[index++] = 0x01;  // 数据类型:重量
    
    // 重量值
    float weight = convert_unit(scale_data.weight_g, scale_data.unit);
    uint8_t *weight_ptr = (uint8_t*)&weight;
    for (uint8_t i = 0; i < 4; i++) {
        buffer[index++] = weight_ptr[i];
    }
    
    // 单位
    buffer[index++] = scale_data.unit;
    
    // 稳定性
    buffer[index++] = scale_data.stability;
    
    // 校验和
    uint8_t checksum = 0;
    for (uint8_t i = 0; i < index; i++) {
        checksum ^= buffer[i];
    }
    buffer[index++] = checksum;
    
    // 发送
    HAL_UART_Transmit(&huart1, buffer, index, 100);
}

七、电源管理

7.1 低功耗设计

c 复制代码
/* 电源管理系统 */
#include "power.h"

// 电源状态
typedef enum {
    PWR_STATE_ACTIVE,      // 活跃状态
    PWR_STATE_IDLE,        // 空闲状态
    PWR_STATE_SLEEP,       // 睡眠状态
    PWR_STATE_STANDBY      // 待机状态
} PowerState;

PowerState pwr_state = PWR_STATE_ACTIVE;
uint32_t last_activity = 0;
float battery_voltage = 0;
uint8_t battery_level = 100;

// 电源管理任务
void power_management_task(void) {
    uint32_t current_time = HAL_GetTick();
    
    // 检测无操作时间
    if (current_time - last_activity > 30000) {  // 30秒无操作
        if (pwr_state == PWR_STATE_ACTIVE) {
            enter_idle_state();
        }
    }
    
    if (current_time - last_activity > 60000) {  // 60秒无操作
        if (pwr_state == PWR_STATE_IDLE) {
            enter_sleep_state();
        }
    }
    
    // 电池检测
    static uint32_t last_battery_check = 0;
    if (current_time - last_battery_check > 10000) {  // 10秒检测一次
        battery_voltage = read_battery_voltage();
        battery_level = calculate_battery_level(battery_voltage);
        last_battery_check = current_time;
        
        // 低电量警告
        if (battery_level < 10) {
            low_battery_warning();
        }
    }
}

// 进入空闲状态
void enter_idle_state(void) {
    pwr_state = PWR_STATE_IDLE;
    
    // 降低显示屏亮度
    oled_set_contrast(1);
    
    // 降低采样率
    SAMPLE_RATE = 2;  // 2Hz
}

// 进入睡眠状态
void enter_sleep_state(void) {
    pwr_state = PWR_STATE_SLEEP;
    
    // 关闭显示屏
    oled_display_off();
    
    // 关闭蓝牙
    bluetooth_sleep();
    
    // 进入STOP模式
    HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);
}

// 唤醒处理
void wakeup_handler(void) {
    if (pwr_state == PWR_STATE_SLEEP) {
        // 恢复时钟
        SystemClock_Config();
        
        // 唤醒外设
        oled_display_on();
        oled_set_contrast(255);
        bluetooth_wakeup();
        
        // 恢复采样率
        SAMPLE_RATE = 10;
        
        pwr_state = PWR_STATE_ACTIVE;
    }
    
    last_activity = HAL_GetTick();
}

八、测试与校准程序

8.1 系统自检

c 复制代码
/* 系统自检程序 */
void system_self_test(void) {
    printf("=== Electronic Scale Self Test ===\n");
    
    // 1. 内存测试
    printf("1. Memory Test...\n");
    if (memory_test()) {
        printf("  ✓ RAM Test Passed\n");
    } else {
        printf("  ✗ RAM Test Failed\n");
    }
    
    // 2. 外设测试
    printf("2. Peripheral Test...\n");
    
    // 测试HX711
    int32_t raw = hx711_read();
    printf("  HX711 Raw: %ld\n", raw);
    
    if (raw != 0) {
        printf("  ✓ HX711 Test Passed\n");
    } else {
        printf("  ✗ HX711 Test Failed\n");
    }
    
    // 测试OLED
    oled_test_pattern();
    printf("  OLED Test Pattern Displayed\n");
    
    // 3. 传感器测试
    printf("3. Sensor Test...\n");
    
    // 读取10次
    float readings[10];
    for (uint8_t i = 0; i < 10; i++) {
        readings[i] = hx711_read_avg(3);
        printf("  Reading %d: %.0f\n", i, readings[i]);
        HAL_Delay(100);
    }
    
    // 计算噪声
    float avg = 0, std = 0;
    for (uint8_t i = 0; i < 10; i++) {
        avg += readings[i];
    }
    avg /= 10;
    
    for (uint8_t i = 0; i < 10; i++) {
        float diff = readings[i] - avg;
        std += diff * diff;
    }
    std = sqrt(std / 9);
    
    printf("  Average: %.2f, Std Dev: %.2f\n", avg, std);
    
    if (std < 100) {  // 噪声小于100个计数
        printf("  ✓ Sensor Noise Test Passed\n");
    } else {
        printf("  ✗ Sensor Noise Too High\n");
    }
    
    // 4. 按键测试
    printf("4. Button Test...\n");
    printf("  Please press all buttons...\n");
    
    uint8_t buttons_pressed = 0;
    uint32_t start_time = HAL_GetTick();
    
    while (HAL_GetTick() - start_time < 10000) {  // 10秒测试时间
        for (uint8_t i = 0; i < 4; i++) {
            if (key_pressed(i)) {
                buttons_pressed |= (1 << i);
                printf("  Button %d pressed\n", i);
                HAL_Delay(200);
            }
        }
        
        if (buttons_pressed == 0x0F) {  // 所有按键都按下过
            printf("  ✓ All Buttons Tested\n");
            break;
        }
    }
    
    if (buttons_pressed != 0x0F) {
        printf("  ✗ Button Test Incomplete\n");
    }
    
    printf("=== Self Test Complete ===\n");
}

九、完整工程结构

复制代码
HighPrecisionScale/
├── Core/
│   ├── Src/
│   │   ├── main.c              # 主程序
│   │   ├── stm32f1xx_it.c      # 中断服务
│   │   └── system_stm32f1xx.c
│   └── Inc/
│       └── main.h
├── Drivers/
│   ├── CMSIS/
│   ├── STM32F1xx_HAL_Driver/
│   └── BSP/
│       ├── hx711.c/.h          # HX711驱动
│       ├── ssd1306.c/.h        # OLED驱动
│       ├── key.c/.h            # 按键驱动
│       └── buzzer.c/.h         # 蜂鸣器驱动
├── Middlewares/
│   ├── Filter/
│   │   ├── moving_average.c/.h
│   │   ├── kalman_filter.c/.h
│   │   └── median_filter.c/.h
│   └── Algorithm/
│       ├── calibration.c/.h    # 校准算法
│       ├── counting.c/.h       # 计数算法
│       └── compensation.c/.h   # 补偿算法
├── Application/
│   ├── display.c/.h           # 显示管理
│   ├── bluetooth.c/.h         # 蓝牙通信
│   ├── power.c/.h             # 电源管理
│   └── data_log.c/.h          # 数据记录
└── README.md
相关推荐
Hello_Embed3 小时前
嵌入式上位机开发入门(二十九):JsonRPC TCP Server
网络·单片机·网络协议·tcp/ip·json·嵌入式
笨笨饿3 小时前
# 67_MCU的几大分区
数据结构·单片机·嵌入式硬件·算法·机器人·线性回归·个人开发
SDAU20053 小时前
CH552的时钟应用
stm32·单片机·嵌入式硬件
实在太懒于是不想取名3 小时前
STM32N6的开发日记(6):用ISP中间件点亮IMX335相机的专业画质
stm32·嵌入式硬件·接口隔离原则
天狼IoT3 小时前
STM32开发速查笔记
stm32·单片机·嵌入式硬件
济6174 小时前
FreeRTOS 控制任务设计 (1)--- 双模式闭环控制:IDLE/RUN 状态机与任务通知机制
stm32·单片机·嵌入式·freertos
7yewh4 小时前
针对灵巧手机械结构的探究
网络·人工智能·单片机·深度学习·嵌入式
balance_rui13 小时前
FreeRTOS
笔记·stm32
LCG元13 小时前
STM32实战案例:基于HC-SR04的超声波测距与倒车雷达系统
stm32·单片机·嵌入式硬件