|
最近遇到針對(duì)家用設(shè)備數(shù)據(jù)測(cè)算的樣品設(shè)計(jì)中,個(gè)人使用了一下家里買現(xiàn)有的血氧檢測(cè)儀,除了簡(jiǎn)單的界面規(guī)劃設(shè)計(jì)外和數(shù)值反饋,只能給人一個(gè)數(shù)值上面的參考,然而反饋回來的信息還是需要自己去網(wǎng)上比對(duì)自己的血氧數(shù)據(jù)是否處在一個(gè)正常且穩(wěn)定的一個(gè)標(biāo)準(zhǔn)范圍上,剛好自己之前做過相關(guān)類型的設(shè)計(jì),同時(shí)自己的儲(chǔ)物柜上還有一塊MAX30100系列的血氧檢測(cè)傳感器,為了更適合家用檢測(cè)設(shè)計(jì)和對(duì)血氧數(shù)據(jù)分析更加便捷,于是對(duì)之前項(xiàng)目設(shè)計(jì)進(jìn)行了升級(jí)。
單片機(jī)源程序如下:
main.c
int main(void)
{
int i=0;
u8 timeout;
delay_init();
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);// 設(shè)置中斷優(yōu)先級(jí)分組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,"開始測(cè)量血氧\r\n");
for(i = 0;i <128;i++)
{
while(MAX30102_INTPin_Read()==0)
{
//讀取FIFO
max30102_read_fifo();
}
}
UsartPrintf(USART1,"測(cè)量結(jié)束\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,"進(jìn)入主循環(huán)\r\n");
Delay_us(300);
if(++timeout >=50)
{
OLED_Printf_EN(6,0,"Dataing...");
blood_Loop();
UsartPrintf(USART1,"心率血氧測(cè)量完畢\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;//血液波形數(shù)據(jù)
BloodData g_blooddata = {0}; //血液數(shù)據(jù)存儲(chǔ)
#define CORRECTED_VALUE 47 //標(biāo)定血液氧氣含量
/*funcation start ------------------------------------------------------------*/
//血液檢測(cè)信息更新
void blood_data_update(void)
{
//標(biāo)志位被使能時(shí) 讀取FIFO
g_fft_index=0;
while(g_fft_index < FFT_N)
{
while(MAX30102_INTPin_Read()==0)
{
//讀取FIFO
max30102_read_fifo(); //read from MAX30102 FIFO2
//將數(shù)據(jù)寫入fft輸入并清除輸出
if(g_fft_index < FFT_N)
{
//將數(shù)據(jù)寫入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++;
}
}
}
}
//血液信息轉(zhuǎn)換
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 ;
}
//移動(dòng)平均濾波
//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;
}
//八點(diǎn)平均濾波
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");
//代碼實(shí)現(xià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);
}
//計(jì)算交流分量
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,"結(jié)束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, "開始血液信息轉(zhuǎn)換\r\n");
//血液信息轉(zhuǎn)換
blood_data_translate();
UsartPrintf(USART_DEBUG, "血液信息轉(zhuǎn)換完畢\r\n");
//顯示血液狀態(tài)信息
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, 下載次數(shù): 23)
2023-7-9 04:02 上傳
點(diǎn)擊文件名下載附件
手冊(cè):
MAX30102數(shù)據(jù)手冊(cè).7z
(1.97 MB, 下載次數(shù): 10)
2023-7-9 04:02 上傳
點(diǎn)擊文件名下載附件
|
評(píng)分
-
查看全部評(píng)分
|