MAX30100 心率血氧的算法和硬件設(shè)計
GPIO連接圖:
LED和蜂鳴器.png (144.01 KB, 下載次數(shù): 79)
下載附件
2021-5-11 17:03 上傳
傳感器.png (26.65 KB, 下載次數(shù): 72)
下載附件
2021-5-11 17:03 上傳
屏幕.png (235.09 KB, 下載次數(shù): 53)
下載附件
2021-5-11 17:03 上傳
單片機源程序如下:
- /**
- ******************************************************************************
- * @file blood.c
- * @version V1.0.0
- * @brief 血氧圖示儀主程序部分
- ******************************************************************************
- */
- /*--Include-start-------------------------------------------------------------*/
- #include "blood.h"
- #include "MAX30100.h"
- #include "algorithm.h"
- #include "math.h"
- #include "gui.h"
- #include "stdio.h"
- #include "ST7735.h"
- #include "beep.h"
- /*Global data space ----------------------------------------------------------*/
- uint16_t g_fft_index = 0; //fft輸入輸出下標
- struct compx s1[FFT_N+16]; //FFT輸入和輸出:從S[1]開始存放,根據(jù)大小自己定義
- struct compx s2[FFT_N+16]; //FFT輸入和輸出:從S[1]開始存放,根據(jù)大小自己定義
- struct
- {
- float Hp ; //血紅蛋白
- float HpO2; //氧合血紅蛋白
-
- }g_BloodWave;//血液波形數(shù)據(jù)
- BloodData g_blooddata = {0}; //血液數(shù)據(jù)存儲
- #define CORRECTED_VALUE 47 //標定血液氧氣含量
- /*funcation start ------------------------------------------------------------*/
- /*Sensor func -----------------------------------------------------------------*/
- //血液檢測信息更新
- void blood_data_update(void)
- {
- uint16_t temp_num=0;
- uint16_t fifo_word_buff[1][2];
-
- temp_num = max30100_Bus_Read(INTERRUPT_REG);
-
- //標志位被使能時 讀取FIFO
- if (INTERRUPT_REG_A_FULL&temp_num)
- {
- HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,1);
- //讀取FIFO
- max30100_FIFO_Read(0x05,fifo_word_buff,1); //read the hr and spo2 data form fifo in reg=0x05
-
- //將數(shù)據(jù)寫入fft輸入并清除輸出
- for(int i = 0;i < 1;i++)
- {
- if(g_fft_index < FFT_N)
- {
- s1[g_fft_index].real = fifo_word_buff[i][0];
- s1[g_fft_index].imag= 0;
- s2[g_fft_index].real = fifo_word_buff[i][1];
- s2[g_fft_index].imag= 0;
- g_fft_index++;
- }
- }
- //信息更新標志位
- g_blooddata.update++;
- }
- else
- {
- HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,0);
- }
- }
- //血液信息轉(zhuǎn)換
- void blood_data_translate(void)
- {
- //緩沖區(qū)寫入結(jié)束
- if(g_fft_index>=FFT_N)
- {
- //開始變換顯示
- Gui_DrawFont_GBK16(102,2,BLACK,GREEN,"FFT");
-
- g_fft_index = 0;
-
- //數(shù)據(jù)更新標志位
- g_blooddata.display = 1;
-
- //快速傅里葉變換
- FFT(s1);
- FFT(s2);
-
- //解平方
- for(int i = 0;i < FFT_N;i++)
- {
- s1[i].real=sqrtf(s1[i].real*s1[i].real+s1[i].imag*s1[i].imag);
- s2[i].real=sqrtf(s2[i].real*s2[i].real+s2[i].imag*s2[i].imag);
- }
-
- //讀取峰值點 結(jié)果的物理意義為
- uint16_t s1_max_index = find_max_num_index(s1, 60);
- uint16_t s2_max_index = find_max_num_index(s2, 60);
-
- //檢查HbO2和Hb的變化頻率是否一致
- if(s1_max_index == s2_max_index)
- {
- //心率計算
- uint16_t Heart_Rate = 60 * SAMPLES_PER_SECOND *
- s2_max_index / FFT_N;
-
- g_blooddata.heart = Heart_Rate - 10;
-
- //血氧含量計算
- float sp02_num = (s2[s1_max_index].real * s1[0].real)
- /(s1[s1_max_index].real * s2[0].real);
-
- sp02_num = (1 - sp02_num) * SAMPLES_PER_SECOND + CORRECTED_VALUE;
-
- g_blooddata.SpO2 = sp02_num;
-
- //狀態(tài)正常
- g_blooddata.state = BLD_NORMAL;
- }
- else //數(shù)據(jù)發(fā)生異常
- {
- g_blooddata.heart = 0;
- g_blooddata.SpO2 = 0;
- g_blooddata.state = BLD_ERROR;
- }
- //結(jié)束變換顯示
- Gui_DrawFont_GBK16(102,2,GREEN,BLACK,"FFT");
- }
- }
- //血液數(shù)據(jù)更新
- void blood_wave_update(void)
- {
- static DC_FilterData hbdc = {.w = 0,.init = 0,.a = 0};
- static DC_FilterData hbodc = {.w = 0,.init = 0,.a = 0};
-
- float hbag = 0;
- float hboag = 0;
-
- float hbag_d = 0;
- float hboag_d = 0;
-
- //前8次求平均值
- for(int i = 0;i < 8;i++)
- {
- hbag += s1[g_fft_index - 8 + i].real;
- hboag += s2[g_fft_index - 8 + i].real;
- }
-
- //直流濾波
- hbag_d = dc_filter(hbag,&hbdc) / 8;
- hboag_d = dc_filter(hboag,&hbodc) / 8;
-
- //高度數(shù)據(jù)
- float hbhight = 0;
- float hbohight = 0;
-
- //比例與偏置
- hbhight = (-hbag_d / 40.0) + 5;
- hbohight = (-hboag_d / 40.0) + 5;
-
- //高度數(shù)據(jù)幅度限制
- hbhight = (hbhight > 27) ? 27 : hbhight;
- hbhight = (hbhight < 0) ? 0 : hbhight;
-
- hbohight = (hbohight > 27) ? 27 : hbohight;
- hbohight = (hbohight < 0) ? 0 : hbohight;
-
- //將數(shù)據(jù)發(fā)布到全局
- g_BloodWave.Hp = hbhight;
- g_BloodWave.HpO2 = hbohight;
-
- //血液狀態(tài)檢查
- if(hbag < 5000 ||hbag < 5000)
- {
- //清空數(shù)據(jù)
- g_blooddata.heart = 0;
- g_blooddata.SpO2 = 0;
-
- //刪除數(shù)據(jù)
- g_fft_index -= 7;
-
- if(g_blooddata.state != BLD_ERROR)
- {
- g_blooddata.state = BLD_ERROR;
- g_blooddata.display = 1;
- }
- }
- else
- {
- if(g_blooddata.state == BLD_ERROR)
- {
- g_blooddata.state = BLD_NORMAL;
- g_blooddata.display = 1;
- }
- }
- }
- /*tft display ---------------------------------------------------------------*/
- //測試顯示血液信息
- void tft_test_display(void)
- {
- uint8_t str[50];
- if (g_blooddata.display == 1)
- {
- g_blooddata.display = 0;
-
- //顯示血氧信息
- sprintf((char *)str,"heart = %3d",g_blooddata.heart);
- Gui_DrawFont_GBK16(8,8,0x00FF,BLACK,str);
-
- //顯示心率信息
- sprintf((char *)str,"SpO2 = %3.1f",g_blooddata.SpO2);
- Gui_DrawFont_GBK16(8,26,0x00FF,BLACK,str);
-
- //顯示狀態(tài)信息
- if(g_blooddata.state)
- {
- sprintf((char *)str,"ERROR ");
- Gui_DrawFont_GBK16(8,44,0xF000,BLACK,str);
- }
- else
- {
- sprintf((char *)str,"NORMAL ");
- Gui_DrawFont_GBK16(8,44,0x07E0,BLACK,str);
- }
- }
- }
- //繪制邊框
- void tft_draw_frame(uint16_t color)
- {
- gui_draw_square(0,1,127,127,color);
- gui_draw_square(0,1,127,18,color);
- gui_draw_square(0,1,100,18,color);
- gui_draw_square(0,57,127,127,color);
- }
- //狀態(tài)信息顯示
- void tft_draw_State(uint8_t st)
- {
- switch(st)
- {
- case BLD_NORMAL:
- {
- tft_draw_frame(GRAY1);
- Gui_DrawFont_GBK16(3,2,0x001F,GRAY3,"Normal ");
- break;
- }
- case BLD_ERROR:
- {
- tft_draw_frame(RED);
- Gui_DrawFont_GBK16(3,2,0xF000,GRAY3,"Caution ");
- break;
- }
- }
- }
- //繪制波形線段
- void tft_draw_wave_line(uint8_t p,uint8_t n,uint16_t h0,uint16_t h1,uint16_t color)
- {
- uint16_t x = 4; // x 起始坐標
- uint16_t y = (p == 0) ? 122 : 89; // y 起始坐標
-
- //擦除線
- if((n + 1) < 82)
- {
- Gui_DrawLine((x + n + 2),y - 28,(x + n + 2),y + 1,GRAY2);
- Gui_DrawLine((x + n + 1),y - 28,(x + n + 1),y + 1,BLACK);
- }
- //繪制線
- Gui_DrawLine((x + n),(y - h0),(x + n + 1),(y - h1),color);
-
- }
- //繪制波形圖表
- void tft_draw_wave(void)
- {
- static uint16_t hist_n = 0; //同步歷史序號
-
- static uint16_t hbhist_h = 0;//血紅蛋白歷史數(shù)值
- static uint16_t hbohist_h = 0;//氧合血紅蛋白歷史數(shù)值
- //繪制線
- tft_draw_wave_line(1, hist_n, hbhist_h , g_BloodWave.Hp, 0x8FF0);
- tft_draw_wave_line(0, hist_n, hbohist_h, g_BloodWave.HpO2, 0xFC1F);
-
- //移動下標
- hist_n = (hist_n < 81) ? (hist_n + 1) : 0;
-
- //保存歷史
- hbhist_h = g_BloodWave.Hp;
- hbohist_h = g_BloodWave.HpO2;
- }
- //心率血氧數(shù)據(jù)刷新
- void tft_draw_hrsp(void)
- {
- uint8_t str[50];
- //心率信息顯示
- sprintf((char *)str,"%3d ",g_blooddata.heart);
- Gui_DrawFont_GBK16(40,20,0xffe0,GRAY3,str);
- //血氧飽和度顯示
- g_blooddata.SpO2 = (g_blooddata.SpO2 > 99.99) ? 99.99:g_blooddata.SpO2;
- sprintf((char *)str," %2.2f%% ",g_blooddata.SpO2);
- Gui_DrawFont_GBK16(40,38,0x07ff,GRAY3,str);
- }
- //顯示窗口繪制
- void tft_draw_windows(void)
- {
- Lcd_Clear(GRAY3);
-
- //繪制窗口邊線
- tft_draw_frame(GRAY1);
-
- //繪制區(qū)塊 h31 w84
- gui_draw_Block(3,60,87,91,GRAY2,BLACK);
- gui_draw_Block(3,93,87,124,GRAY2,BLACK);
-
- gui_draw_Block(89,60,125,91,GRAY2,BLACK);
- gui_draw_Block(89,93,125,124,GRAY2,BLACK);
-
- //繪制字符串
- Gui_DrawFont_GBK16(3,20,0xffe0,GRAY3,"HR :");
- Gui_DrawFont_GBK16(3,38,0x07ff,GRAY3,"SpO2:");
-
- Gui_DrawFont_GBK16(91,67,0x8FF0,BLACK," Hb");
- Gui_DrawFont_GBK16(91,100,0xFC1F,BLACK,"HbO2");
-
- Gui_DrawFont_GBK16(102,2,GREEN,BLACK,"FFT");
-
- Gui_DrawFont_GBK16(3,2,0x001F,GRAY3,"Normal ");
- }
- //tft顯示刷新
- void tft_display_update(void)
- {
- //圖表更新
- if(g_blooddata.update >= 8)
- {
- //清除圖標更新標志位
- g_blooddata.update = 0;
- //血液波形數(shù)據(jù)更新
- blood_wave_update();
- //繪制波形
- tft_draw_wave();
- }
-
- //轉(zhuǎn)換后的數(shù)據(jù)更新
- if(g_blooddata.display >= 1)
- {
- //清除更新標志位
- g_blooddata.display = 0;
- //顯示血液狀態(tài)信息
- tft_draw_State(g_blooddata.state);
- //心率血氧數(shù)據(jù)刷新
- tft_draw_hrsp();
- }
- }
- /*Beep and LED func -------------------------------------------------------*/
- //LED和蜂鳴器狀態(tài)更新
- void Led_beep_update(void)
- {
- static uint32_t errorStartTick = 0; //錯誤發(fā)生點
- static BloodState blhist = BLD_NORMAL; //歷史狀態(tài)
- static uint8_t beepstate = 0; //蜂鳴器狀態(tài)
-
-
- switch(g_blooddata.state)
- {
- case BLD_NORMAL:
- {
- //LED指示燈
- Led_Control((g_BloodWave.Hp > 10) * 20);
- //蜂鳴器
- Beep_Control((g_BloodWave.HpO2 > 10) * 50);
- break;
- }
- case BLD_ERROR:
- {
- if(blhist == BLD_NORMAL)
- {
- errorStartTick = HAL_GetTick();
- }
-
- //錯誤前0-3秒 快速閃爍
- if((HAL_GetTick() - errorStartTick) < 3000)
- {
- if(( (HAL_GetTick() / 100) %
- (4-(HAL_GetTick() - errorStartTick) / 1000)
- ) == 0)
- {
- Led_Control(20);
- Beep_Control(50);
- }
- else
- {
- Led_Control(0);
- Beep_Control(0);
- }
-
- }
- //錯誤前3-6秒 常亮
- else if(3000 < (HAL_GetTick() - errorStartTick) &&
- 6000 > (HAL_GetTick() - errorStartTick))
- {
- Led_Control(20);
- Beep_Control(50);
- }
- //錯誤到6秒結(jié)束
- else if((HAL_GetTick() - errorStartTick) > 6000)
- {
- Led_Control(0);
- Beep_Control(0);
- }
- break;
- }
- default:break;
- }
-
- blhist = g_blooddata.state;
- }
- /*Setup and loop func -----------------------------------------------------*/
- void blood_Setup(void)
- {
- //初始化血液信息
- g_blooddata.heart = 0;
- g_blooddata.SpO2 = 0;
- g_blooddata.display = 1;
-
- //繪制窗口
- tft_draw_windows();
-
- //初始化指示燈和蜂鳴器
- settone(14);
- }
- void blood_Loop(void)
- {
- //血液信息獲取
- blood_data_update();
- //血液信息轉(zhuǎn)換
- blood_data_translate();
- //tft顯示刷新
-
- tft_display_update();
- //LED 蜂鳴器信息更新
- Led_beep_update();
-
- }
- void blood_Interrupt(void)
- {
- static int16_t div = 0;
- static int16_t div2 = 0;
- div++;
- if(div > 10)
- {
- div = 0;
- //更新血液信息
- //blood_data_update();
- }
-
- div2++;
- if(div > 50)
- {
- div2 = 0;
- //tft顯示刷新
- //tft_display_update();
- }
- }
復(fù)制代碼
所有代碼51hei提供下載:
MAX30100.7z
(5.56 MB, 下載次數(shù): 125)
2021-5-11 17:05 上傳
點擊文件名下載附件
|