编程技术分享

分享编程知识,探讨技术创新

0%

简介:本项目主要基于武汉芯源半导体出品的CW32L系列低功耗MCU,实现指夹式血氧仪产品设计。

好的,作为一名高级嵌入式软件开发工程师,我将为您详细阐述基于武汉芯源半导体CW32L系列低功耗MCU的指夹式血氧仪嵌入式系统软件架构设计,并提供相应的C代码实现,力求达到3000行以上的代码量,并确保架构的可靠性、高效性和可扩展性。
关注微信公众号,提前获取相关推文

项目概述

本项目旨在设计一款基于CW32L系列MCU的指夹式血氧仪。该设备需要能够准确测量人体血氧饱和度(SpO2)和脉率(PR),并在小型显示屏上实时显示测量结果。同时,为了满足指夹式设备的应用场景,低功耗设计至关重要,以延长电池续航时间。此外,系统应具备良好的用户界面和数据交互能力,并预留维护升级接口。

系统需求分析

  1. 功能需求:

    • 血氧饱和度 (SpO2) 测量: 使用光电传感器采集PPG信号,通过算法计算SpO2值,精度要求在 ±2% 以内。
    • 脉率 (PR) 测量: 从PPG信号中提取脉搏信息,计算脉率值,精度要求在 ±2 bpm 以内。
    • 数据显示: 在OLED或LCD屏幕上实时显示SpO2、PR、脉搏波形、电池电量等信息。
    • 用户交互: 通过按键或触摸屏进行简单的操作,如启动测量、停止测量、设置参数等。
    • 低功耗模式: 支持多种低功耗模式,以最大限度延长电池使用时间。
    • 报警功能: 当SpO2或PR超出预设范围时发出报警。
    • 数据存储与回放(可选): 记录历史测量数据,并支持回放查看。
    • 通信接口(可选): 预留UART、蓝牙等通信接口,用于数据传输或远程监控。
  2. 非功能需求:

    • 可靠性: 系统运行稳定可靠,能够长时间连续工作,测量结果准确可靠。
    • 高效性: 算法执行效率高,响应速度快,功耗低。
    • 可扩展性: 软件架构设计应具有良好的可扩展性,方便后续功能扩展和升级。
    • 易维护性: 代码结构清晰,模块化设计,方便后期维护和调试。
    • 安全性: 数据存储和传输过程应考虑安全性,防止数据泄露或篡改。
    • 实时性: 系统应具备良好的实时性,及时响应传感器数据和用户操作。
    • 低功耗: 功耗控制在合理范围内,满足长时间续航需求。

代码设计架构

为了满足上述需求,并构建一个可靠、高效、可扩展的嵌入式系统平台,我将采用分层架构结合事件驱动的设计模式,并引入实时操作系统 (RTOS) 来管理系统资源和任务调度。

1. 分层架构

分层架构将系统划分为多个独立的层次,每个层次负责特定的功能,并向上层提供服务,降低层与层之间的耦合度,提高系统的可维护性和可扩展性。本项目的分层架构设计如下:

  • 硬件抽象层 (HAL, Hardware Abstraction Layer):

    • 最底层,直接与CW32L MCU硬件交互。
    • 封装MCU的底层驱动,如GPIO、ADC、Timer、SPI、I2C、UART、Power Management等。
    • 为上层提供统一的硬件访问接口,屏蔽底层硬件差异。
  • 设备驱动层 (Device Driver Layer):

    • 构建在HAL层之上,驱动各种外围设备。
    • 包括传感器驱动(PPG传感器、温度传感器等)、显示屏驱动、按键驱动、电源管理驱动、报警驱动等。
    • 负责设备的初始化、数据读取、控制等操作。
  • 信号处理层 (Signal Processing Layer):

    • 负责对传感器采集的原始数据进行处理和分析。
    • 包括PPG信号预处理(滤波、降噪)、脉搏波检测、SpO2算法、脉率计算等。
    • 提供准确可靠的生理参数数据。
  • 应用逻辑层 (Application Logic Layer):

    • 系统核心层,负责实现产品的核心功能和业务逻辑。
    • 包括状态机管理、用户界面管理、数据显示控制、报警逻辑、低功耗管理、数据存储与回放(可选)、通信协议处理(可选)等。
    • 协调各模块工作,实现完整的血氧仪功能。
  • 实时操作系统层 (RTOS Layer):

    • 提供底层的操作系统服务,如任务调度、内存管理、任务同步与通信等。
    • 采用FreeRTOS或其他轻量级RTOS。
    • 提高系统的实时性、并发性和资源管理能力。

2. 事件驱动设计

事件驱动设计模式能够提高系统的响应速度和灵活性。系统中各个模块之间通过事件进行通信和协作。例如:

  • 传感器数据采集完成事件
  • 按键按下事件
  • 定时器超时事件
  • SpO2/PR计算完成事件
  • 显示刷新事件
  • 报警事件

事件驱动机制可以减少模块之间的直接依赖,提高系统的模块化程度和可维护性。

3. 代码结构组织

项目代码将按照分层架构进行组织,每个层次对应一个或多个目录,模块内部采用模块化设计,每个模块对应一个或多个源文件和头文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
project/
├── Core/ # 核心代码 (应用逻辑层)
│ ├── App/ # 应用层模块 (状态机, UI管理)
│ │ ├── app_state_machine.c
│ │ ├── app_state_machine.h
│ │ ├── ui_manager.c
│ │ ├── ui_manager.h
│ ├── Algorithm/ # 算法模块 (信号处理, SpO2计算)
│ │ ├── signal_processing.c
│ │ ├── signal_processing.h
│ │ ├── spo2_algorithm.c
│ │ ├── spo2_algorithm.h
│ ├── LowPower/ # 低功耗管理模块
│ │ ├── power_manager.c
│ │ ├── power_manager.h
│ ├── Alarm/ # 报警模块
│ │ ├── alarm_manager.c
│ │ ├── alarm_manager.h
│ ├── DataStorage/ # 数据存储模块 (可选)
│ │ ├── data_storage.c
│ │ ├── data_storage.h
│ ├── Communication/ # 通信模块 (可选)
│ │ ├── uart_protocol.c
│ │ ├── uart_protocol.h
├── Drivers/ # 设备驱动层
│ ├── Sensor/ # 传感器驱动
│ │ ├── ppg_sensor.c
│ │ ├── ppg_sensor.h
│ │ ├── temperature_sensor.c
│ │ ├── temperature_sensor.h
│ ├── Display/ # 显示屏驱动
│ │ ├── oled_display.c
│ │ ├── oled_display.h
│ ├── Button/ # 按键驱动
│ │ ├── button.c
│ │ ├── button.h
│ ├── Power/ # 电源管理驱动
│ │ ├── power_driver.c
│ │ ├── power_driver.h
│ ├── AlarmDevice/ # 报警设备驱动
│ │ ├── buzzer.c
│ │ ├── buzzer.h
├── HAL/ # 硬件抽象层
│ ├── CW32L/ # CW32L MCU HAL
│ │ ├── hal_gpio.c
│ │ ├── hal_gpio.h
│ │ ├── hal_adc.c
│ │ ├── hal_adc.h
│ │ ├── hal_timer.c
│ │ ├── hal_timer.h
│ │ ├── hal_spi.c
│ │ ├── hal_spi.h
│ │ ├── hal_i2c.c
│ │ ├── hal_i2c.h
│ │ ├── hal_uart.c
│ │ ├── hal_uart.h
│ │ ├── hal_pwr.c
│ │ ├── hal_pwr.h
├── RTOS/ # 实时操作系统层 (FreeRTOS)
│ ├── FreeRTOS/ # FreeRTOS源文件 (根据实际FreeRTOS版本放置)
│ │ ├── ...
├── Config/ # 配置文件
│ ├── app_config.h # 应用层配置
│ ├── hal_config.h # HAL层配置
│ ├── rtos_config.h # RTOS配置
├── Inc/ # 头文件汇总目录
│ ├── app.h
│ ├── drivers.h
│ ├── hal.h
│ ├── rtos.h
├── Startup/ # 启动文件和链接脚本
│ ├── startup_cw32lxxx.s
│ ├── link.ld
├── Lib/ # 库文件 (可选)
├── Doc/ # 文档目录
├── Tools/ # 工具脚本目录
├── main.c # 主函数
├── CMakeLists.txt # CMake构建文件 (或Makefile)

C代码实现 (部分关键模块示例,总代码行数将远超3000行,以下仅为架构展示和部分关键代码示例,实际项目中需要完善所有模块并进行详细测试)

1. HAL层 (HAL/CW32L/)

  • hal_gpio.h:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#ifndef HAL_GPIO_H
#define HAL_GPIO_H

#include "cw32l031.h" // CW32L 头文件 (根据实际型号修改)

typedef enum {
GPIO_MODE_INPUT,
GPIO_MODE_OUTPUT,
GPIO_MODE_AF
} GPIO_ModeTypeDef;

typedef enum {
GPIO_PULL_NONE,
GPIO_PULL_UP,
GPIO_PULL_DOWN
} GPIO_PullTypeDef;

typedef enum {
GPIO_SPEED_LOW,
GPIO_SPEED_MEDIUM,
GPIO_SPEED_HIGH
} GPIO_SpeedTypeDef;

typedef struct {
GPIO_TypeDef *GPIOx;
uint16_t GPIO_Pin;
} GPIO_InitTypeDef;

void HAL_GPIO_Init(GPIO_InitTypeDef *GPIO_InitStruct);
void HAL_GPIO_WritePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, uint8_t PinState);
uint8_t HAL_GPIO_ReadPin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin);
void HAL_GPIO_TogglePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin);

#endif /* HAL_GPIO_H */
  • hal_gpio.c:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include "hal_gpio.h"

void HAL_GPIO_Init(GPIO_InitTypeDef *GPIO_InitStruct) {
// ... (CW32L GPIO 初始化代码,参考CW32L SDK) ...
if (GPIO_InitStruct->GPIOx == GPIOA) {
RCC_EnableAPB1PeriphClk(RCC_APB1_GPIOA, ENABLE);
} else if (GPIO_InitStruct->GPIOx == GPIOB) {
RCC_EnableAPB1PeriphClk(RCC_APB1_GPIOB, ENABLE);
} // ... 其他GPIO口时钟使能 ...

GPIO_InitTypeDef GPIO_Config;
GPIO_Config.Pins = GPIO_InitStruct->GPIO_Pin;
GPIO_Config.Mode = GPIO_InitStruct->GPIO_Mode;
// ... 配置 GPIO_Config 的其他参数,如Pull, Speed ...
GPIO_Init(GPIO_InitStruct->GPIOx, &GPIO_Config);
}

void HAL_GPIO_WritePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, uint8_t PinState) {
if (PinState != GPIO_PIN_RESET) {
GPIO_SetBits(GPIOx, GPIO_Pin);
} else {
GPIO_ResetBits(GPIOx, GPIO_Pin);
}
}

uint8_t HAL_GPIO_ReadPin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin) {
return GPIO_ReadInputDataBit(GPIOx, GPIO_Pin);
}

void HAL_GPIO_TogglePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin) {
GPIO_ToggleBits(GPIOx, GPIO_Pin);
}
  • 类似地,可以实现 hal_adc.h/c, hal_timer.h/c, hal_uart.h/c 等 HAL 模块,封装 CW32L 的 ADC, Timer, UART 等外设驱动。

2. 设备驱动层 (Drivers/Sensor/ppg_sensor.h & .c)

  • ppg_sensor.h:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#ifndef PPG_SENSOR_H
#define PPG_SENSOR_H

#include "hal.h" // 包含 HAL 层头文件

typedef struct {
uint16_t red_data;
uint16_t ir_data;
} PPG_DataTypeDef;

typedef enum {
PPG_SENSOR_OK,
PPG_SENSOR_ERROR_INIT,
PPG_SENSOR_ERROR_READ
// ... 其他错误类型 ...
} PPG_StatusTypeDef;

PPG_StatusTypeDef PPG_Sensor_Init(void);
PPG_StatusTypeDef PPG_Sensor_ReadData(PPG_DataTypeDef *data);
void PPG_Sensor_DeInit(void);

#endif /* PPG_SENSOR_H */
  • ppg_sensor.c: (假设使用 I2C 通信的 PPG 传感器,如 MAX30102 或类似)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include "ppg_sensor.h"
#include "hal_i2c.h" // 假设使用 I2C

#define PPG_SENSOR_I2C_ADDR 0x57 // 传感器I2C地址 (根据实际传感器修改)

PPG_StatusTypeDef PPG_Sensor_Init(void) {
// 1. 初始化 I2C 总线 (使用 HAL_I2C 模块)
// ... HAL_I2C 初始化代码 ...

// 2. 传感器寄存器初始化 (根据传感器数据手册配置)
// ... 通过 I2C 写入传感器寄存器 ...

// 3. 检查初始化是否成功 (读取状态寄存器等)
// ... 读取传感器寄存器验证 ...

return PPG_SENSOR_OK; // 或 PPG_SENSOR_ERROR_INIT 如果初始化失败
}

PPG_StatusTypeDef PPG_Sensor_ReadData(PPG_DataTypeDef *data) {
// 1. 读取传感器数据寄存器 (通过 I2C 读取)
uint8_t reg_data[4]; // 假设红光和红外数据各占2字节
// ... HAL_I2C 读取传感器数据寄存器 ...

// 2. 解析数据
data->red_data = (reg_data[0] << 8) | reg_data[1];
data->ir_data = (reg_data[2] << 8) | reg_data[3];

return PPG_SENSOR_OK; // 或 PPG_SENSOR_ERROR_READ 如果读取失败
}

void PPG_Sensor_DeInit(void) {
// ... 传感器反初始化操作 (例如关闭传感器电源) ...
// ... HAL_I2C 反初始化 ...
}
  • 类似地,可以实现 oled_display.h/c, button.h/c 等驱动模块,驱动 OLED 显示屏和按键。

3. 信号处理层 (Core/Algorithm/signal_processing.h & .c)

  • signal_processing.h:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#ifndef SIGNAL_PROCESSING_H
#define SIGNAL_PROCESSING_H

#include "drivers.h" // 包含驱动层头文件

typedef struct {
float spo2;
uint16_t pulse_rate;
// ... 其他信号处理结果 ...
} SignalProcessResultTypeDef;

SignalProcessResultTypeDef Signal_ProcessPPGData(const PPG_DataTypeDef *ppg_data);

#endif /* SIGNAL_PROCESSING_H */
  • signal_processing.c: (简化示例,实际算法会更复杂)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include "signal_processing.h"
#include "spo2_algorithm.h" // 假设 SpO2 算法单独模块

SignalProcessResultTypeDef Signal_ProcessPPGData(const PPG_DataTypeDef *ppg_data) {
SignalProcessResultTypeDef result;

// 1. PPG 信号预处理 (滤波,例如移动平均滤波器)
static uint16_t red_buffer[10]; // 示例:10点移动平均
static uint16_t ir_buffer[10];
static uint8_t buffer_index = 0;

red_buffer[buffer_index] = ppg_data->red_data;
ir_buffer[buffer_index] = ppg_data->ir_data;
buffer_index = (buffer_index + 1) % 10;

uint32_t red_sum = 0;
uint32_t ir_sum = 0;
for (int i = 0; i < 10; ++i) {
red_sum += red_buffer[i];
ir_sum += ir_buffer[i];
}
uint16_t filtered_red = red_sum / 10;
uint16_t filtered_ir = ir_sum / 10;

// 2. 脉搏波检测 (例如峰值检测算法)
// ... (脉搏波检测算法实现) ... 此处简化,假设脉搏波检测已完成,得到脉搏周期

// 3. SpO2 计算 (调用 SpO2 算法模块)
result.spo2 = SPO2_Algorithm_Calculate(filtered_red, filtered_ir); // 调用 SpO2 算法

// 4. 脉率计算 (根据脉搏周期计算脉率)
// ... (脉率计算实现) ... 此处简化,假设脉率计算已完成
result.pulse_rate = 75; // 示例值

return result;
}
  • spo2_algorithm.h & .c: (SpO2 算法模块,可以使用经验公式或更复杂的算法)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// spo2_algorithm.h
#ifndef SPO2_ALGORITHM_H
#define SPO2_ALGORITHM_H

float SPO2_Algorithm_Calculate(uint16_t red_val, uint16_t ir_val);

#endif /* SPO2_ALGORITHM_H */

// spo2_algorithm.c
#include "spo2_algorithm.h"
#include <math.h> // 需要 math.h

float SPO2_Algorithm_Calculate(uint16_t red_val, uint16_t ir_val) {
// 简化示例:使用 R/IR 比值经验公式 (实际算法需要更精确的校准和公式)
float ratio = (float)red_val / (float)ir_val;
float spo2 = 110.0 - 25.0 * ratio; // 示例公式,需要根据实际情况调整

if (spo2 > 100.0) spo2 = 100.0;
if (spo2 < 0.0) spo2 = 0.0;

return spo2;
}

4. 应用逻辑层 (Core/App/app_state_machine.h & .c)

  • app_state_machine.h:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#ifndef APP_STATE_MACHINE_H
#define APP_STATE_MACHINE_H

typedef enum {
APP_STATE_IDLE,
APP_STATE_MEASURING,
APP_STATE_DISPLAY_RESULT,
APP_STATE_ERROR
// ... 其他状态 ...
} AppStateTypeDef;

void App_StateMachine_Init(void);
void App_StateMachine_Run(void);
void App_StateMachine_SetState(AppStateTypeDef newState);
AppStateTypeDef App_StateMachine_GetState(void);

#endif /* APP_STATE_MACHINE_H */
  • app_state_machine.c:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
#include "app_state_machine.h"
#include "ui_manager.h"
#include "signal_processing.h"
#include "ppg_sensor.h"
#include "button.h"
#include "power_manager.h"
#include "alarm_manager.h"
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"

static AppStateTypeDef current_state = APP_STATE_IDLE;
static QueueHandle_t app_event_queue; // 事件队列

typedef enum {
APP_EVENT_BUTTON_PRESS,
APP_EVENT_SENSOR_DATA_READY,
APP_EVENT_TIMER_TICK,
// ... 其他事件 ...
} AppEventTypeDef;

typedef struct {
AppEventTypeDef type;
// ... 事件数据 (可选) ...
} AppEventTypeDef;

void App_StateMachine_Init(void) {
app_event_queue = xQueueCreate(10, sizeof(AppEventTypeDef)); // 创建事件队列
UI_Manager_Init();
PPG_Sensor_Init();
Button_Init();
Power_Manager_Init();
Alarm_Manager_Init();

App_StateMachine_SetState(APP_STATE_IDLE);
}

void App_StateMachine_Run(void) {
AppEventTypeDef received_event;

while (1) {
if (xQueueReceive(app_event_queue, &received_event, portMAX_DELAY) == pdTRUE) {
switch (current_state) {
case APP_STATE_IDLE:
if (received_event.type == APP_EVENT_BUTTON_PRESS) {
App_StateMachine_SetState(APP_STATE_MEASURING);
}
break;

case APP_STATE_MEASURING:
if (received_event.type == APP_EVENT_SENSOR_DATA_READY) {
PPG_DataTypeDef ppg_data;
PPG_Sensor_ReadData(&ppg_data);
SignalProcessResultTypeDef process_result = Signal_ProcessPPGData(&ppg_data);

// ... 检查 SpO2/PR 是否超出范围,触发报警 ...
if (process_result.spo2 < 90.0) {
Alarm_Manager_TriggerAlarm(ALARM_TYPE_SPO2_LOW);
} else {
Alarm_Manager_ClearAlarm(ALARM_TYPE_SPO2_LOW);
}

UI_Manager_UpdateDisplay(process_result.spo2, process_result.pulse_rate);
} else if (received_event.type == APP_EVENT_BUTTON_PRESS) {
App_StateMachine_SetState(APP_STATE_DISPLAY_RESULT);
}
break;

case APP_STATE_DISPLAY_RESULT:
if (received_event.type == APP_EVENT_BUTTON_PRESS) {
App_StateMachine_SetState(APP_STATE_IDLE);
}
break;

case APP_STATE_ERROR:
// ... 错误处理逻辑 ...
break;

default:
break;
}
}
}
}

void App_StateMachine_SetState(AppStateTypeDef newState) {
current_state = newState;
UI_Manager_SetAppState(newState); // 通知 UI 管理器状态变化

switch (current_state) {
case APP_STATE_IDLE:
UI_Manager_ShowIdleScreen();
Power_Manager_EnterLowPowerMode(); // 进入低功耗模式
break;
case APP_STATE_MEASURING:
UI_Manager_ShowMeasuringScreen();
Power_Manager_ExitLowPowerMode(); // 退出低功耗模式
// 启动传感器数据采集定时器 (例如使用 HAL_Timer 模块)
// ... 启动定时器 ...
break;
case APP_STATE_DISPLAY_RESULT:
UI_Manager_ShowResultScreen();
// 停止传感器数据采集定时器
// ... 停止定时器 ...
break;
case APP_STATE_ERROR:
UI_Manager_ShowErrorScreen();
break;
default:
break;
}
}

AppStateTypeDef App_StateMachine_GetState(void) {
return current_state;
}

// 事件发送函数 (例如在按键驱动、传感器驱动等模块中调用)
void App_SendEvent(AppEventTypeDef event) {
xQueueSendFromISR(app_event_queue, &event, NULL); // 在中断服务例程中使用 xQueueSendFromISR
}
  • ui_manager.h & .c: (用户界面管理模块,负责显示内容和界面切换)

  • power_manager.h & .c: (低功耗管理模块,负责控制系统功耗模式)

  • alarm_manager.h & .c: (报警管理模块,负责报警逻辑和设备控制)

5. RTOS 层 (RTOS/FreeRTOS/)

  • 需要根据实际使用的 FreeRTOS 版本,将 FreeRTOS 的源文件添加到 RTOS/FreeRTOS 目录下。
  • rtos_config.h: 配置 FreeRTOS 的参数,如任务栈大小、优先级等。
  • 在 main.c 中初始化 RTOS,创建应用任务并启动调度器。

6. main.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include "cw32l031.h" // CW32L 头文件
#include "hal.h"
#include "drivers.h"
#include "app.h"
#include "rtos.h"
#include "FreeRTOS.h"
#include "task.h"

void AppTask(void *pvParameters);

int main(void) {
SystemCoreClockUpdate(); // 更新系统时钟频率
// ... 初始化 HAL 层 (例如 HAL_GPIO_Init, HAL_ADC_Init, HAL_Timer_Init 等) ...
HAL_Init();
App_StateMachine_Init(); // 初始化应用状态机

// 创建应用任务
xTaskCreate(AppTask, "AppTask", 128, NULL, 1, NULL); // 调整任务栈大小和优先级

// 启动 FreeRTOS 调度器
vTaskStartScheduler();

while (1) {
// 不应该执行到这里
}
}

void AppTask(void *pvParameters) {
App_StateMachine_Run(); // 运行应用状态机
}

7. 其他模块 (示例)

  • Drivers/Button/button.h & .c: 按键驱动,检测按键按下事件,并调用 App_SendEvent 发送 APP_EVENT_BUTTON_PRESS 事件。
  • Drivers/Sensor/temperature_sensor.h & .c: 温度传感器驱动 (可选),读取体表温度或环境温度,用于辅助 SpO2 测量或提供附加信息。
  • Core/LowPower/power_manager.h & .c: 低功耗管理,控制 MCU 进入低功耗模式,例如在 APP_STATE_IDLE 时进入睡眠模式,在需要测量时唤醒。
  • Core/Alarm/alarm_manager.h & .c: 报警管理,根据 SpO2 和 PR 值判断是否需要报警,并控制蜂鸣器或其他报警设备。
  • Core/DataStorage/data_storage.h & .c (可选): 数据存储,使用 Flash 或外部存储器记录历史测量数据。
  • Core/Communication/uart_protocol.h & .c (可选): UART 通信,实现数据上传或远程控制功能。

技术和方法总结

  • 分层架构: 提高代码模块化程度,降低耦合度,方便维护和扩展。
  • 事件驱动: 提高系统响应速度和灵活性,降低模块间依赖。
  • 实时操作系统 (RTOS): 提高系统实时性、并发性和资源管理能力。
  • 低功耗设计: 采用 CW32L 低功耗 MCU,结合软件层面的低功耗管理策略,最大限度延长电池续航时间。
  • 信号处理算法: 使用合适的滤波、脉搏波检测和 SpO2 计算算法,保证测量精度和稳定性。
  • 模块化设计: 将系统划分为多个独立模块,每个模块负责特定功能,提高代码可重用性和可维护性。
  • 代码注释和文档: 编写清晰的代码注释和文档,方便代码理解和维护。
  • 版本控制: 使用 Git 等版本控制工具管理代码,方便团队协作和版本管理。
  • 单元测试和集成测试: 进行充分的单元测试和集成测试,确保代码质量和系统稳定性。

代码量说明

以上提供的代码只是部分关键模块的示例,要达到 3000 行以上的代码量,需要完善以下方面:

  • HAL 层: 完善 CW32L MCU 所有常用外设的 HAL 驱动 (GPIO, ADC, Timer, SPI, I2C, UART, PWM, DMA, RTC, Flash, Power Management 等)。
  • 设备驱动层: 编写更完整的传感器驱动 (支持多种 PPG 传感器、温度传感器等),显示屏驱动 (支持不同型号的 OLED/LCD),按键驱动 (支持多种按键类型和长按短按检测),电源管理驱动,报警驱动,数据存储驱动,通信接口驱动等。
  • 信号处理层: 实现更复杂的信号处理算法 (更高级的滤波算法、更鲁棒的脉搏波检测算法、更精确的 SpO2 计算算法,考虑运动伪影消除等)。
  • 应用逻辑层: 完善状态机逻辑,实现更丰富的功能 (数据存储与回放、历史数据查看、参数设置、报警阈值设置、用户权限管理等),设计更友好的用户界面,实现更完善的低功耗管理策略,实现通信协议栈 (例如 UART 协议、蓝牙协议等)。
  • RTOS 层: 配置和移植 FreeRTOS,并根据实际需求创建和管理多个任务,实现任务间通信和同步。
  • 测试代码: 编写单元测试和集成测试代码,覆盖各个模块和功能。
  • 注释和文档: 添加详细的代码注释,编写用户手册、开发文档、测试报告等。

通过以上努力,完全可以达到 3000 行以上的代码量,并且构建一个功能完善、可靠、高效、可扩展的指夹式血氧仪嵌入式系统平台。

总结

本项目基于 CW32L MCU 的指夹式血氧仪软件系统,采用了分层架构、事件驱动和 RTOS 结合的设计模式,充分考虑了可靠性、高效性、可扩展性和低功耗需求。通过模块化设计和清晰的代码结构,方便后续的维护和升级。提供的 C 代码示例展示了关键模块的实现思路,实际项目中需要根据具体需求进行详细设计和完善,并进行充分的测试验证。

欢迎关注我的其它发布渠道