基于STM32指甲式脈搏血氧儀七大核心功能解析與實現
基于STM32的SpO2-EVM

血氧儀開發套件 血氧儀開發套件
1. 血氧飽和度測量(40%~100%,精度 ±2%@70%~100%)
技術指標解析
測量范圍:覆蓋臨床緊急低氧(40%)到正常(100%)場景,滿足 ICU 重癥監護與家庭健康監測需求;
精度分級:70%~100% 時 ±2%(臨床診斷級),40%~70% 時 ±4%(低氧預警級),符合 YY/T 0784-2010 行業標準。
STM32 方案實現
硬件基礎:12 位 ADC(ADS1258)采集光信號,分辨率達 0.08mV(3.3V 參考電壓);雙波長 LED(660nm 紅光 / 940nm 近紅外)分時驅動,減少環境光干擾。
軟件算法:采用 Beer-Lambert 定律結合校準系數查表法,如前文中SPO2_Calculate函數通過 AC/DC 比值計算 SpO2;動態溫度補償:內置溫度傳感器修正光吸收系數隨溫度的漂移。
2. 脈率測量(30~250bpm,精度 ±1bpm)
技術指標解析
范圍覆蓋:從新生兒靜息心率(30bpm)到運動極限心率(250bpm);
精度控制:±1bpm 滿足臨床心率變異性(HRV)分析需求。
STM32 方案實現
信號處理:帶通濾波提取脈搏波頻率(0.5~5Hz),抑制運動偽影;峰值檢測算法(如前文中PulseRate_Detect函數)結合滑動平均濾波(8 點緩沖),消除異常脈沖。
抗干擾設計:采用自適應閾值跟蹤脈搏波幅度變化,避免弱信號漏檢;周期校驗機制:若相鄰脈率差 > 20%,則丟棄該次測量結果。
3. 弱信號處理(弱灌注強度≤0.3%)
技術指標解析
灌注指數(PI):≤0.3% 表示極弱血流(如失血性休克、低溫癥),傳統設備易測量失敗。
STM32 方案實現
硬件增強:高增益儀表運放(ISL28470)放大微弱光電流信號,增益可達 1000 倍;低噪聲電源(ISL9001)抑制紋波對前端信號的干擾。
軟件優化:自適應增益控制:當 PI<1% 時,自動提高 LED 驅動電流(如前文中NLAS4053模擬開關調節);小波變換去噪:分解信號至不同頻段,保留脈搏波特征分量。
4. 彩色 OLED 同屏顯示
功能架構
顯示內容:數值區:SpO2(大字體突出)、脈率、PI 值;波形區:實時脈搏波(128 點趨勢圖);狀態區:電池電量、信號質量圖標。
STM32 驅動方案:SPI 接口控制 OLED 驅動 IC(如 SSD1351),刷新率≥25fps;動態刷新率調節:弱信號時提高刷新率至 50fps,增強波形穩定性。
5. 無信號自動關機
功耗管理邏輯
檢測機制:連續 20 秒檢測到 PI<0.2% 且 SpO2 值固定不變(如 40%),判定為無有效信號;硬件 watchdog(X5043)監控 MCU 運行狀態,防止程序卡死導致功耗異常。
低功耗實現:STM32 進入 STANDBY 模式,功耗 < 10μA;切斷 LED、OLED 等外設電源,僅保留 RTC(ISL12022M)計時。

6. 歷史數據存儲與管理
存儲架構
硬件配置:串行 Flash(M25P16,16Mbit)可保存≥2000 條記錄(每條含 SpO2、脈率、時間戳);I2C 接口 EEPROM(M24C64)存儲校準參數與用戶配置。
軟件功能:周期性存儲:每 5 分鐘自動保存一次數據,支持手動觸發保存;數據管理:支持按日期查詢、刪除指定記錄,FIFO 循環覆蓋舊數據。
7. 屏幕旋轉功能
實現方案
硬件:三軸加速度傳感器(LIS33DE)檢測設備姿態,分辨率達 ±2g;
程序示例:基于 STM32F103 的血氧儀數據處理
以下是核心功能的 C 語言實現(使用 HAL 庫):
/* 頭文件與宏定義 */#include "stm32f10x_hal.h"#include "math.h"#define SAMPLE_RATE 1000 // 采樣率1kHz#define BUFFER_DEPTH 256 // 數據緩沖區深度#define RED_LED_PORT GPIOA // 紅光LED端口#define RED_LED_PIN GPIO_PIN_0 #define IR_LED_PORT GPIOA // 近紅外LED端口#define IR_LED_PIN GPIO_PIN_1#define ADC_CHANNEL_RED 0 // 紅光ADC通道#define ADC_CHANNEL_IR 1 // 近紅外ADC通道/* 全局變量 */ADC_HandleTypeDef hadc;
DMA_HandleTypeDef hdma_adc;uint16_t adcBuffer[2][BUFFER_DEPTH]; // ADC數據緩沖區 {紅光, 近紅外}float redSignal[BUFFER_DEPTH]; // 紅光信號float irSignal[BUFFER_DEPTH]; // 近紅外信號uint8_t spo2Value = 97; // 血氧飽和度uint16_t pulseRate = 72; // 脈率(bpm)uint8_t perfusionIndex = 0; // 灌注指數/* 函數聲明 */void SystemInit(void);void MX_GPIO_Init(void);void MX_ADC_Init(void);void MX_DMA_Init(void);void SPO2_Calculate(void);void PulseRate_Detect(void);void OLED_Display(void);void PowerManagement_Check(void);/* 主函數 */int main(void){ /* 系統初始化 */
HAL_Init();
SystemInit();
/* 外設初始化 */
MX_GPIO_Init();
MX_ADC_Init();
MX_DMA_Init();
/* 啟動ADC_DMA傳輸 */
HAL_ADC_Start_DMA(&hadc, (uint32_t*)adcBuffer, 2 * BUFFER_DEPTH);
/* 主循環 */
while (1)
{ /* 血氧與脈率計算 */
SPO2_Calculate();
PulseRate_Detect();
/* 顯示數據 */
OLED_Display();
/* 低功耗檢查 */
PowerManagement_Check();
/* 延時處理 */
HAL_Delay(10);
}
}/* ADC初始化 */void MX_ADC_Init(void){
ADC_ChannelConfTypeDef sConfig = {0};
hadc.Instance = ADC1;
hadc.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;
hadc.Init.Resolution = ADC_RESOLUTION_12B;
hadc.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc.Init.ScanConvMode = ADC_SCAN_DIRECTION_FORWARD;
hadc.Init.EOCSelection = ADC_EOC_SEQ_CONV;
hadc.Init.ContinuousConvMode = ENABLE;
hadc.Init.NbrOfConversion = 2; // 紅光+近紅外雙通道
HAL_ADC_Init(&hadc); /* 配置紅光通道 */
sConfig.Channel = ADC_CHANNEL_0;
sConfig.Rank = ADC_RANK_CHANNEL_NUMBER;
sConfig.SamplingTime = ADC_SAMPLETIME_71CYCLES_5;
HAL_ADC_ConfigChannel(&hadc, &sConfig);
/* 配置近紅外通道 */
sConfig.Channel = ADC_CHANNEL_1;
HAL_ADC_ConfigChannel(&hadc, &sConfig);
}/* 血氧飽和度計算 */void SPO2_Calculate(void){ static float redAC[BUFFER_DEPTH], redDC[BUFFER_DEPTH]; static float irAC[BUFFER_DEPTH], irDC[BUFFER_DEPTH]; float ratio, spo2;
/* 1. 轉換ADC值為電壓信號(假設參考電壓3.3V) */
for (uint16_t i = 0; i < BUFFER_DEPTH; i++) {
redSignal[i] = (float)adcBuffer[0][i] * 3.3f / 4096.0f;
irSignal[i] = (float)adcBuffer[1][i] * 3.3f / 4096.0f;
}
/* 2. 分離AC和DC分量(簡化版,實際需濾波) */
for (uint16_t i = 0; i < BUFFER_DEPTH; i++) {
redDC[i] = redSignal[i] * 0.95f + redDC[i] * 0.05f; // 一階低通濾波求DC
redAC[i] = redSignal[i] - redDC[i]; // AC分量
irDC[i] = irSignal[i] * 0.95f + irDC[i] * 0.05f;
irAC[i] = irSignal[i] - irDC[i];
}
/* 3. 計算AC/DC比值 */
float sumRedAC = 0, sumRedDC = 0; float sumIrAC = 0, sumIrDC = 0; for (uint16_t i = 0; i < BUFFER_DEPTH; i++) {
sumRedAC += fabs(redAC[i]);
sumRedDC += fabs(redDC[i]);
sumIrAC += fabs(irAC[i]);
sumIrDC += fabs(irDC[i]);
}
float ratioRed = sumRedAC / sumRedDC; float ratioIr = sumIrAC / sumIrDC;
/* 4. 計算灌注指數(PI) */
perfusionIndex = (uint8_t)((ratioRed + ratioIr) * 10);
/* 5. 根據Beer-Lambert定律計算SpO2(簡化公式) */
ratio = ratioRed / ratioIr; if (ratio > 1.0f) ratio = 1.0f / ratio; // 范圍限定
spo2 = 94.0f - 25.0f * ratio; // 經驗公式,實際需校準
/* 6. 結果校準與限幅 */
if (spo2 > 100) spo2 = 100; if (spo2 < 40) spo2 = 40;
spo2Value = (uint8_t)spo2;
}/* 脈率檢測 */void PulseRate_Detect(void){ static uint32_t lastPeakTime = 0; static float peakBuffer[8] = {0}; static uint8_t peakIndex = 0; uint32_t currentTime = HAL_GetTick(); float maxAmplitude = 0; uint16_t peakPosition = 0;
/* 1. 尋找脈搏波峰值(簡化版,實際需帶通濾波) */
for (uint16_t i = 0; i < BUFFER_DEPTH; i++) { if (redSignal[i] > maxAmplitude) {
maxAmplitude = redSignal[i];
peakPosition = i;
}
}
/* 2. 計算脈搏周期(需排除異常峰值) */
if (maxAmplitude > 0.1f && currentTime - lastPeakTime > 300 && currentTime - lastPeakTime < 2000) { uint32_t periodMs = currentTime - lastPeakTime;
pulseRate = (uint16_t)(60000.0f / periodMs); // ms轉bpm
lastPeakTime = currentTime;
/* 3. 滑動平均濾波,平滑脈率顯示 */
peakBuffer[peakIndex] = pulseRate;
peakIndex = (peakIndex + 1) % 8;
float avgPulse = 0; for (uint8_t i = 0; i < 8; i++) {
avgPulse += peakBuffer[i];
}
pulseRate = (uint16_t)(avgPulse / 8);
}
}/* OLED顯示函數 */void OLED_Display(void){ /* 實際項目中需調用OLED驅動庫 */
char displayBuf[32];
/* 顯示血氧飽和度 */
sprintf(displayBuf, "SpO2: %d%%", spo2Value);
OLED_DrawString(0, 0, displayBuf);
/* 顯示脈率 */
sprintf(displayBuf, "PR: %dbpm", pulseRate);
OLED_DrawString(0, 20, displayBuf);
/* 顯示灌注指數 */
sprintf(displayBuf, "PI: %d", perfusionIndex);
OLED_DrawString(0, 40, displayBuf);
/* 繪制簡化脈搏波形 */
for (uint16_t i = 0; i < 128; i++) { uint16_t point = i * BUFFER_DEPTH / 128; uint8_t y = (uint8_t)(redSignal[point] * 30 + 50);
OLED_DrawPoint(i, y);
}
}/* 低功耗管理 */void PowerManagement_Check(void){ static uint32_t noSignalTime = 0; static uint8_t signalStatus = 0;
/* 1. 檢測信號有效性(通過灌注指數判斷) */
if (perfusionIndex > 1) {
signalStatus = 1;
noSignalTime = HAL_GetTick();
} else { if (signalStatus) {
signalStatus = 0;
noSignalTime = HAL_GetTick();
}
}
/* 2. 無信號超過20秒則關機 */
if (!signalStatus && HAL_GetTick() - noSignalTime > 20000) { /* 關閉外設,進入待機模式 */
HAL_ADC_Stop_DMA(&hadc);
HAL_GPIO_WritePin(RED_LED_PORT, RED_LED_PIN, GPIO_PIN_RESET);
HAL_GPIO_WritePin(IR_LED_PORT, IR_LED_PIN, GPIO_PIN_RESET);
OLED_PowerOff();
/* 進入低功耗模式 */
HAL_PWR_EnterSTANDBYMode();
}
}
應用場景:支持手持、桌面等多場景使用,自動適配顯示方向。
功能協同與臨床價值示意圖

典型應用案例
秦皇島康泰醫學:采用 STM32F101C6T6 方案,結合 LIS33DE 與 M25P80 Flash,實現 24 小時連續監測,數據可通過 USB 導出至醫院 HIS 系統;
北京超思電子:在遠程血氧監護儀中加入 4G 通信模塊,當 SpO2<90% 且持續 5 分鐘時,自動向家屬手機推送報警信息,結合歷史數據存儲功能,支持醫生遠程調閱趨勢圖。
技術對比表(STM32 方案 vs 傳統方案)
功能點 | STM32 方案優勢 | 傳統方案局限 |
弱信號處理 | 自適應增益 + 小波去噪,PI≤0.3% 可用 | PI<1% 時測量失效 |
數據存儲 | 16Mbit Flash 支持 7 天連續記錄 | 僅保存最近 200 條記錄 |
屏幕旋轉 | 實時姿態檢測,用戶體驗更友好 | 固定方向顯示,視角受限 |
功耗控制 | STANDBY 模式 < 10μA,續航≥15 小時 | 待機功耗 > 50μA,續航 < 8 小時 |
評論