1. 项目概述
这个基于STM32的多功能健康监测系统,是我最近完成的一个嵌入式硬件项目。它集成了心率、血氧、血压、体温检测,以及计步、定位和无线数据传输功能,算是一个比较全面的便携式健康监测设备。整套系统以STM32F103C8T6为核心,搭配多种传感器模块,通过1.44寸TFT彩屏显示数据,同时支持蓝牙无线连接手机APP。
在实际开发过程中,我发现这种多传感器集成的系统有几个关键点需要注意:首先是传感器数据的准确性和稳定性,其次是多任务处理的优先级安排,还有就是无线通信的可靠性。下面我会详细拆解这个项目的实现过程,分享一些我在开发中积累的经验和踩过的坑。
2. 硬件设计与选型
2.1 核心控制器选择
选用STM32F103C8T6作为主控芯片主要基于以下几点考虑:
- 72MHz主频足够处理多个传感器的数据
- 64KB Flash和20KB SRAM满足程序存储和运行需求
- 丰富的外设接口(I2C、SPI、USART等)方便连接各类传感器
- 成本低廉且开发资源丰富
提示:虽然STM32F103C8T6性能足够,但如果预算允许,可以考虑STM32F4系列,其浮点运算能力在处理传感器数据时更有优势。
2.2 传感器模块选型
2.2.1 心率血氧传感器MAX30102
这个传感器通过光电法检测心率血氧,具有以下特点:
- 集成红光和红外LED及光电探测器
- I2C接口通信
- 内置环境光消除电路
- 采样率可调(最高3.2kHz)
实际使用中发现,手指的贴合程度对测量结果影响很大。我在程序中加入了接触检测逻辑,只有检测到有效接触才开始测量。
2.2.2 血压传感器XGZP6847A
这是一个基于MEMS技术的压力传感器:
- 测量范围:-100kPa~+100kPa
- 模拟电压输出(需ADC采集)
- 需要配合气泵和袖带使用
血压测量时需要注意:
- 袖带压力需要稳定后才能测量
- 测量过程中被测者应保持静止
- 需要定期校准以确保准确性
2.2.3 温度传感器DS18B20
选用这款数字温度传感器是因为:
- 单总线接口节省IO资源
- ±0.5℃的精度满足医疗需求
- 防水封装便于接触测量
2.2.4 运动传感器ADXL345
用于计步功能的三轴加速度计:
- I2C/SPI接口
- 可编程测量范围(±2g~±16g)
- 内置计步算法
2.2.5 GPS/北斗定位模块
选用的是常见的NEO-6M模块:
- 同时支持GPS和北斗系统
- UART接口通信
- 需要外接天线
注意:定位模块必须放置在户外开阔区域,天线黑色面朝上才能获得良好信号。
2.3 显示与通信模块
1.44寸TFT彩屏选用的是常见的SPI接口屏幕,分辨率128x128,足够显示各项监测数据。蓝牙模块用的是HC-05,支持SPP协议,可与手机APP通信。
3. 系统软件设计
3.1 主程序流程
系统软件采用前后台架构,主循环中轮询处理各个功能模块:
c复制void main(void)
{
hardware_init(); // 硬件初始化
load_config(); // 加载配置参数
while(1)
{
key_scan(); // 按键扫描
sensor_update(); // 传感器数据更新
display_update(); // 显示更新
ble_process(); // 蓝牙通信处理
alarm_check(); // 报警检查
}
}
3.2 多传感器数据采集策略
由于需要同时处理多个传感器,我采用了分时采集策略:
- 心率血氧:最高优先级,采样间隔100ms
- 血压:次优先级,采样间隔1s
- 温度:采样间隔5s
- 计步:由ADXL345中断触发
- 定位:采样间隔10s
这种安排确保了关键生理参数的实时性,同时避免了处理器过载。
3.3 数据处理算法
3.3.1 心率血氧计算
MAX30102采集的是原始光电容积脉搏波(PPG),需要经过以下处理:
- 直流分量去除
- 带通滤波(0.5Hz-5Hz)
- 峰值检测
- 心率计算(60/峰峰间隔)
- 血氧计算(红光/红外光吸收比)
c复制// 伪代码示例
float calculate_hr(uint32_t *ir_data, uint32_t len)
{
// 1. 去除直流分量
for(i=0; i<len; i++){
ir_data[i] -= dc_offset;
}
// 2. 带通滤波
butterworth_filter(ir_data, len, 0.5, 5, sample_rate);
// 3. 找峰值
peaks = find_peaks(ir_data, len);
// 4. 计算心率
float hr = 60.0 / (avg_peak_interval / sample_rate);
return hr;
}
3.3.2 血压计算
XGZP6847A输出的是压力电压值,需要通过振荡法计算血压:
- 记录袖带压力曲线
- 提取脉搏波振荡幅度
- 最大振荡幅度对应平均压
- 根据经验公式计算收缩压和舒张压
3.4 无线通信协议设计
蓝牙通信采用自定义的简单协议格式:
code复制*[命令类型][参数]#
例如:
*ST035# // 设置温度阈值为35
*SH120# // 设置心率阈值为120
*SS095# // 设置血氧阈值为95
*SY140# // 设置血压阈值为140
*SJ001# // 启动监测
*CLEAR# // 清空计步
在实现时,我添加了数据长度校验和超时处理,提高了通信可靠性。
4. 关键实现细节
4.1 低功耗设计
虽然本项目没有特别强调低功耗,但我还是做了一些优化:
- 传感器在不使用时进入休眠模式
- 显示屏在没有操作时降低亮度
- 蓝牙模块在没有连接时进入低功耗模式
这些措施使系统平均工作电流从120mA降到了约80mA。
4.2 数据存储设计
系统参数和阈值设置需要掉电保存,我使用了STM32的内部Flash模拟EEPROM:
c复制#define PARAM_ADDR 0x0800FC00 // 使用最后一页Flash
void save_params(void)
{
FLASH_Unlock();
FLASH_ErasePage(PARAM_ADDR);
for(int i=0; i<PARAM_SIZE; i+=4){
FLASH_ProgramWord(PARAM_ADDR+i, *(uint32_t*)(¶ms+i));
}
FLASH_Lock();
}
注意:Flash写入前必须先擦除,且擦除以页为单位。频繁写入会缩短Flash寿命,建议只在参数改变时保存。
4.3 报警逻辑实现
报警系统需要考虑多种情况:
- 监测功能是否开启
- 传感器是否就绪(如手指是否放置正确)
- 数值是否超过阈值
- 是否需要延时报警(避免瞬时波动误报)
实现代码如下:
c复制void check_alarm(void)
{
if(!monitor_enabled) return;
// 温度报警
if(temp_ready && temp_value > temp_threshold){
trigger_alarm(TEMP_ALARM);
}
// 心率报警
if(hr_ready && hr_value > hr_threshold){
trigger_alarm(HR_ALARM);
}
// 血氧报警
if(spo2_ready && spo2_value < spo2_threshold){
trigger_alarm(SPO2_ALARM);
}
// 血压报警
if(bp_ready && bp_value > bp_threshold){
trigger_alarm(BP_ALARM);
}
}
5. 移动端APP设计
5.1 功能需求
APP需要实现以下功能:
- 蓝牙连接管理
- 实时数据显示(与液晶屏一致)
- 参数设置
- 历史数据查看
5.2 通信处理
Android端处理蓝牙通信的关键代码:
java复制private final BluetoothGattCallback gattCallback = new BluetoothGattCallback() {
@Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
// 处理接收到的数据
byte[] data = characteristic.getValue();
processReceivedData(data);
}
};
private void sendCommand(String cmd) {
if(bluetoothGatt == null) return;
BluetoothGattService service = bluetoothGatt.getService(UUID.fromString(SERVICE_UUID));
if(service == null) return;
BluetoothGattCharacteristic characteristic = service.getCharacteristic(UUID.fromString(CHAR_UUID));
if(characteristic == null) return;
characteristic.setValue(cmd.getBytes());
bluetoothGatt.writeCharacteristic(characteristic);
}
5.3 数据可视化
使用MPAndroidChart库实现数据曲线绘制:
java复制LineChart chart = findViewById(R.id.chart);
LineData data = new LineData();
LineDataSet set = new LineDataSet(entries, "心率");
set.setColor(Color.RED);
set.setLineWidth(2f);
data.addDataSet(set);
chart.setData(data);
chart.invalidate();
6. 开发中的问题与解决方案
6.1 MAX30102测量不稳定问题
现象:心率血氧测量值波动大,有时无法检测到信号。
排查:
- 检查硬件连接 - 正常
- 测试不同采样率 - 影响不大
- 尝试不同手指压力 - 有明显改善
解决:
- 在传感器表面增加软性缓冲层,改善接触
- 添加接触检测算法,只有检测到稳定信号才开始测量
- 增加数字滤波,平滑输出数据
6.2 多任务处理延迟问题
现象:在进行心率血氧测量时,其他功能响应变慢。
排查:分析发现MAX30102数据处理占用了大量CPU时间。
解决:
- 优化算法,减少计算量
- 将部分计算移到空闲时段
- 采用DMA方式传输传感器数据
- 适当降低心率血氧采样率
6.3 蓝牙通信丢包问题
现象:APP与设备通信时偶尔出现命令丢失。
排查:
- 测试距离和环境影响 - 无明显关系
- 检查协议设计 - 缺少重传机制
解决:
- 添加命令应答机制
- 实现简单的超时重传
- 增加数据校验
7. 系统优化与扩展
7.1 性能优化建议
- 使用RTOS替代前后台系统,更好地管理多任务
- 添加传感器数据校准功能,提高准确性
- 实现数据本地存储,记录历史趋势
7.2 功能扩展方向
- 增加WiFi连接,支持云端数据同步
- 添加跌倒检测等安全功能
- 实现多用户模式,支持用户切换
- 开发更丰富的数据分析功能
7.3 产品化考虑
如果要进行小批量生产,还需要:
- 设计专用PCB,集成各模块
- 优化电源管理,延长续航
- 通过相关医疗认证
- 开发更专业的APP
这个项目从硬件选型到软件开发涉及嵌入式系统的多个方面,在实际开发过程中,最大的挑战是如何平衡各功能模块的资源占用和实时性要求。通过合理的任务调度和算法优化,最终实现了一个性能稳定的多功能健康监测系统。