基于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
相关推荐
崇山峻岭之间37 分钟前
单片机USB虚拟串口实验
单片机·嵌入式硬件
崇山峻岭之间1 小时前
单片机USB U盘实验
单片机·嵌入式硬件
点灯小铭1 小时前
基于单片机的锅炉压力与温度监测报警系统设计
数据库·单片机·嵌入式硬件·毕业设计·课程设计·期末大作业
环境倒逼我学习1 小时前
无人机地面站之第13章 Mission Planner 入门与界面总览
单片机·嵌入式硬件·无人机
大阳1231 小时前
ARM.8(ADC,SPI)
单片机·嵌入式硬件·adc·spi
hoiii1872 小时前
基于 STM32 的标准遥控器架构与源码
stm32·嵌入式硬件·架构
少年、潜行2 小时前
STM32 ISP 升级体验
stm32·嵌入式硬件·isp升级·系统编程区域
杨连江2 小时前
一种三模式可调气隙式双侧定子滑移可变磁通轴向永磁电机
单片机·嵌入式硬件
Aaron158811 小时前
无人机反制中AOA+TDOA联合定位技术与雷达探测定位技术的应用对比分析
arm开发·嵌入式硬件·fpga开发·硬件工程·无人机·信息与通信·信号处理
foundbug99912 小时前
STM32 睡眠模式测试程序
stm32·单片机·嵌入式硬件