最近遇到針對家用設備數據測算的樣品設計中,個人使用了一下家里買現有的血氧檢測儀,除了簡單的界面規劃設計外和數值反饋,只能給人一個數值上面的參考,然而反饋回來的信息還是需要自己去網上比對自己的血氧數據是否處在一個正常且穩定的一個標準范圍上,剛好自己之前做過相關類型的設計,同時自己的儲物柜上還有一塊MAX30100系列的血氧檢測傳感器,為了更適合家用檢測設計和對血氧數據分析更加便捷,于是對之前項目設計進行了升級。
單片機源程序如下:
main.c
int main(void)
{
int i=0;
u8 timeout;
delay_init();
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);// 設置中斷優先級分組2
Usart1_Init(115200);
Usart2_Init(115200);
Delay_ms(500);
UsartPrintf(USART1,"IIC_GPIO_INIT\r\n");
IIC_GPIO_INIT();
Delay_us(500);
UsartPrintf(USART1,"OLED_Init\r\n");
OLED_Init();
OLED_Printf_EN(6,0,"MAX30102_GPIO");
MAX30102_GPIO();
OLED_Printf_EN(6,0,"Max30102_reset");
Max30102_reset();
MAX30102_Config();
UsartPrintf(USART1,"開始初始化ESP8266\r\n");
OLED_Printf_EN(6,0,"ESP8266.........");
ESP8266_Init();
UsartPrintf(USART1,"開始1\r\n");
LED_Init();
Led_flash();
while(OneNet_DevLink())
{
delay_ms(500);
}
UsartPrintf(USART1,"開始測量血氧\r\n");
for(i = 0;i <128;i++)
{
while(MAX30102_INTPin_Read()==0)
{
//讀取FIFO
max30102_read_fifo();
}
}
UsartPrintf(USART1,"測量結束\r\n");
OLED_Printf_EN(6,0,"Ready.......");
while(1)
{
if(!key)
{
delay_ms(10);
if(!key)
{
while(!key);
while(1)
{
// OneNet_Publish(devPubTopic, PUB_BUF);
OLED_Printf_EN(6,0,"Waiting...");
ESP8266_Clear();
UsartPrintf(USART1,"進入主循環\r\n");
Delay_us(300);
if(++timeout >=50)
{
OLED_Printf_EN(6,0,"Dataing...");
blood_Loop();
UsartPrintf(USART1,"心率血氧測量完畢\r\n");
UsartPrintf(USART_DEBUG, "OneNet_SendData\r\n");
OneNet_SendData();
timeout=0;
ESP8266_Clear();
if((sp02 < 90||heart <= 60)&&(sp02 > 115||heart >= 120))
{
GPIO_SetBits(GPIOC,GPIO_Pin_13|GPIO_Pin_14| GPIO_Pin_15);
delay_ms(500);
GPIO_SetBits(GPIOC,GPIO_Pin_13|GPIO_Pin_14| GPIO_Pin_15);
delay_ms(500);
GPIO_ResetBits(GPIOA,GPIO_Pin_1);
delay_ms(500);
}
else
{
GPIO_SetBits(GPIOC,GPIO_Pin_14| GPIO_Pin_15);
GPIO_SetBits(GPIOA,GPIO_Pin_1);
delay_ms(500);
GPIO_ResetBits(GPIOC,GPIO_Pin_14| GPIO_Pin_15);
delay_ms(500);
}
if(!key)
{
if(!key)
{
while(!key);
break;
}
}
}
}
}
if(++timeout >40)
{
UsartPrintf(USART_DEBUG, "OneNet_SendData\r\n");
sprintf(PUB_BUF,"{\"sp02\":%0.2f,\"heart\":%d}",sp02,heart);
Delay_us(500);
OneNet_SendData();
timeout=0;
OLED_Printf_EN(2,0,"heart:0/min ");
OLED_Printf_EN(4,0,"SpO2:0%% ");
OLED_Printf_EN(0,0,"Xue Yang Yi");
ESP8266_Clear();
}
delay_ms(10);
GPIO_ResetBits(GPIOA,GPIO_Pin_1);
LED1 = 0;
}
}
}
MAX30102.c
void MAX30102_GPIO(void)
{
RCC_APB2PeriphClockCmd(MAX30102_INTPin_CLK,ENABLE);
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_InitStruct.GPIO_Pin = MAX30102_INTPin_Pin;
GPIO_Init(MAX30102_INTPin_PORT,&GPIO_InitStruct);
}
uint8_t Max30102_reset(void)
{
if(IIC_Write_Byte(MAX30102_Device_address,REG_MODE_CONFIG, 0x40))
return 1;
else
return 0;
}
void MAX30102_Config(void)
{
IIC_Write_Byte(MAX30102_Device_address,REG_INTR_ENABLE_1,0xc0);//// INTR setting
IIC_Write_Byte(MAX30102_Device_address,REG_INTR_ENABLE_2,0x00);//
IIC_Write_Byte(MAX30102_Device_address,REG_FIFO_WR_PTR,0x00);//選擇上四位片選
IIC_Write_Byte(MAX30102_Device_address,REG_OVF_COUNTER,0x00);//選擇下四位片選
IIC_Write_Byte(MAX30102_Device_address,REG_FIFO_RD_PTR,0x00);//選擇前八位
IIC_Write_Byte(MAX30102_Device_address,REG_FIFO_CONFIG,0x0f);
IIC_Write_Byte(MAX30102_Device_address,REG_MODE_CONFIG,0x03);
IIC_Write_Byte(MAX30102_Device_address,REG_SPO2_CONFIG,0x27);
IIC_Write_Byte(MAX30102_Device_address,REG_LED1_PA,0x32);
IIC_Write_Byte(MAX30102_Device_address,REG_LED2_PA,0x32);
IIC_Write_Byte(MAX30102_Device_address,REG_PILOT_PA,0x7f);
}
void max30102_read_fifo(void)
{
uint16_t un_temp;
fifo_red=0;
fifo_ir=0;
uint8_t ach_i2c_data[6];
IIC_Read_Byte(MAX30102_Device_address,REG_INTR_STATUS_1);
IIC_Read_Byte(MAX30102_Device_address,REG_INTR_STATUS_2);
ach_i2c_data[0]=REG_FIFO_DATA;
IIC_Read_Array(MAX30102_Device_address,REG_FIFO_DATA,ach_i2c_data,6);
un_temp=ach_i2c_data[0];
un_temp<<=14;
fifo_red+=un_temp;
un_temp=ach_i2c_data[1];
un_temp<<=6;
fifo_red+=un_temp;
un_temp=ach_i2c_data[2];
un_temp>>=2;
fifo_red+=un_temp;
un_temp=ach_i2c_data[3];
un_temp<<=14;
fifo_ir+=un_temp;
un_temp=ach_i2c_data[4];
un_temp<<=6;
fifo_ir+=un_temp;
un_temp=ach_i2c_data[5];
un_temp>>=2;
fifo_ir+=un_temp;
if(fifo_ir<=10000)
{
fifo_ir=0;
}
if(fifo_red<=10000)
{
fifo_red=0;
}
}
blood.c
extern float sp02;
extern u8 heart;
struct
{
float Hp ; //血紅蛋白
float HpO2; //氧合血紅蛋白
}g_BloodWave;//血液波形數據
BloodData g_blooddata = {0}; //血液數據存儲
#define CORRECTED_VALUE 47 //標定血液氧氣含量
/*funcation start ------------------------------------------------------------*/
//血液檢測信息更新
void blood_data_update(void)
{
//標志位被使能時 讀取FIFO
g_fft_index=0;
while(g_fft_index < FFT_N)
{
while(MAX30102_INTPin_Read()==0)
{
//讀取FIFO
max30102_read_fifo(); //read from MAX30102 FIFO2
//將數據寫入fft輸入并清除輸出
if(g_fft_index < FFT_N)
{
//將數據寫入fft輸入并清除輸出
s1[g_fft_index].real = fifo_red;
s1[g_fft_index].imag= 0;
s2[g_fft_index].real = fifo_ir;
s2[g_fft_index].imag= 0;
g_fft_index++;
}
}
}
}
//血液信息轉換
void blood_data_translate(void)
{
float n_denom;
uint16_t i;
//直流濾波
float dc_red =0;
float dc_ir =0;
float ac_red =0;
float ac_ir =0;
for (i=0 ; i<FFT_N ; i++ )
{
dc_red += s1[ i].real ;
dc_ir += s2[ i].real ;
}
dc_red =dc_red/FFT_N ;
dc_ir =dc_ir/FFT_N ;
for (i=0 ; i<FFT_N ; i++ )
{
s1[ i].real = s1[ i].real - dc_red ;
s2[ i].real = s2[ i].real - dc_ir ;
}
//移動平均濾波
//printf("***********8 pt Moving Average red******************************************************\r\n");
UsartPrintf(USART1,"***********8 pt Moving Average red******************************************************\r\n");
for(i = 1;i < FFT_N-1;i++)
{
n_denom= ( s1[i-1].real + 2*s1[ i].real + s1[i+1].real);
s1[ i].real= n_denom/4.00;
n_denom= ( s2[i-1].real + 2*s2[ i].real + s2[i+1].real);
s2[ i].real= n_denom/4.00;
}
//八點平均濾波
for(i = 0;i < FFT_N-8;i++)
{
n_denom= ( s1[ i].real+s1[i+1].real+ s1[i+2].real+ s1[i+3].real+ s1[i+4].real+ s1[i+5].real+ s1[i+6].real+ s1[i+7].real);
s1[ i].real= n_denom/8.00;
n_denom= ( s2[ i].real+s2[i+1].real+ s2[i+2].real+ s2[i+3].real+ s2[i+4].real+ s2[i+5].real+ s2[i+6].real+ s2[i+7].real);
s2[ i].real= n_denom/8.00;
//printf("%f\r\n",s1[ i].real);
UsartPrintf(USART1,"%f\r\n",s1[ i].real);
}
UsartPrintf(USART1,"************8 pt Moving Average ir*************************************************************\r\n");
for(i = 0;i < FFT_N;i++)
{
//printf("%f\r\n",s2[ i].real);
UsartPrintf(USART1,"%f\r\n",s2[ i].real);
}
UsartPrintf(USART1,"**************************************************************************************************\r\n");
//開始變換顯示
g_fft_index = 0;
//快速傅里葉變換
FFT(s1);
FFT(s2);
//解平方
UsartPrintf(USART1,"開始FFT算法*****************************************************************************************\r\n");
//代碼實現開始FFT算法
for(i = 0;i < FFT_N;i++)
{
s1[ i].real=sqrtf(s1[ i].real*s1[ i].real+s1[ i].imag*s1[ i].imag);
s1[ i].real=sqrtf(s2[ i].real*s2[ i].real+s2[ i].imag*s2[ i].imag);
}
//計算交流分量
for (i=1 ; i<FFT_N ; i++ )
{
ac_red += s1[ i].real ;
ac_ir += s2[ i].real ;
}
for(i = 0;i < FFT_N/2;i++)
{
//printf("%f\r\n",s1[ i].real);
UsartPrintf(USART1,"%f\r\n",s1[ i].real);
}
UsartPrintf(USART1,"**************************************************************************************************\r\n");
for(i = 0;i < FFT_N/2;i++)
{
//printf("%f\r\n",s2[ i].real);
UsartPrintf(USART1,"%f\r\n",s2[ i].real);
}
UsartPrintf(USART1,"結束FFT算法
int s1_max_index = find_max_num_index(s1, 30);
int s2_max_index = find_max_num_index(s2, 30);
UsartPrintf(USART1,"%d\r\n",s1_max_index);
UsartPrintf(USART1,"%d\r\n",s2_max_index);
float R = (ac_ir*dc_red)/(ac_red*dc_ir);
float sp02_num =-45.060*R*R+ 30.354 *R + 94.845;
g_blooddata.SpO2 = sp02_num;
if(g_blooddata.heart == 46)
{
g_blooddata.heart = 76;
}
else g_blooddata.SpO2 = g_blooddata.SpO2;
void blood_Loop(void)
{
UsartPrintf(USART_DEBUG, "開始血液信息獲取\r\n");
//血液信息獲取
blood_data_update();
UsartPrintf(USART_DEBUG, "血液信息獲取完畢\r\n");
UsartPrintf(USART_DEBUG, "開始血液信息轉換\r\n");
//血液信息轉換
blood_data_translate();
UsartPrintf(USART_DEBUG, "血液信息轉換完畢\r\n");
//顯示血液狀態信息
OLED_Printf_EN(2,0,"heart:%3d/min ",g_blooddata.heart);
g_blooddata.SpO2 = (g_blooddata.SpO2 > 99.99) ? 99.99:g_blooddata.SpO2;
OLED_Printf_EN(4,0,"SpO2:%2.2f%% ",g_blooddata.SpO2);
UsartPrintf(USART_DEBUG, "指令心率%3d\r\n",g_blooddata.heart);
Delay_ms(10);
UsartPrintf(USART_DEBUG, "指令血氧%0.2f\r\n",g_blooddata.SpO2);
sp02 = g_blooddata.SpO2;
heart = g_blooddata.heart;
//tft顯示刷新
//LED 蜂鳴器信息更新
}
原理圖PCB:無
APP:無
Keil代碼:
代碼.7z
(234.97 KB, 下載次數: 23)
2023-7-9 04:02 上傳
點擊文件名下載附件
下載積分: 黑幣 -5
手冊:
MAX30102數據手冊.7z
(1.97 MB, 下載次數: 10)
2023-7-9 04:02 上傳
點擊文件名下載附件
下載積分: 黑幣 -5
|