最近遇到针对家用设备数据测算的样品设计中,个人使用了一下家里买现有的血氧检测仪,除了简单的界面规划设计外和数值反馈,只能给人一个数值上面的参考,然而反馈回来的信息还是需要自己去网上比对自己的血氧数据是否处在一个正常且稳定的一个标准范围上,刚好自己之前做过相关类型的设计,同时自己的储物柜上还有一块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)
|