代码挺漂亮
发一个之前做的血氧心率检测的小东西,使用的是美信的MAX30100和30102
芯片框图:
携带氧气的红血球能吸收较多红外光(850-1000nm),未携带氧气的红血球则是吸收较多的红光(600-750nm),通过这个特性就可以计算血液的血氧饱和度。
大概效果如下图:
数据处理方式:
首先配置传感器工作在FIFO模式下然后周期性读取FIFO,通过1024点的FFT变换得到频域数据,然后选择频带内的最高幅值为心率,通过对比两个幅值的幅度计算出血氧饱和度。通过平均其他频点的差值来标定两个波长数据。
数据波形图:
部分算法代码:
struct compx FFTBUF1[FFT_N+16];
struct compx FFTBUF2[FFT_N+16];
uint16_t g_fft_index = 0;
BloodData g_blooddata = {0};
void test(float data1,float data2)
{
static uint8_t str[50];
sprintf((char *)str,"%f,%f\r\n",data1,data2);
HAL_UART_Transmit_DMA(&huart1,str,sizeof(str));
}
//血液检测信息更新
void blood_data_update(void)
{
static DC_FilterData dc1 = {.w = 0,.init = 0,.a = 0.8};
static DC_FilterData dc2 = {.w = 0,.init = 0,.a = 0.8};
static float data1buf[20];
static uint8_t data1cur = 0;
static float data2buf[20];
static uint8_t data2cur = 0;
uint16_t temp_num=0;
uint16_t fifo_word_buff[1][2];
temp_num = max30100_Bus_Read(INTERRUPT_REG);
if (INTERRUPT_REG_A_FULL&temp_num)
{
max30100_FIFO_Read(0x05,fifo_word_buff,1); //read the hr and spo2 data form fifo in reg=0x05
float data1 = dc_filter(fifo_word_buff[0][0],&dc1)+100.0;
float data2 = dc_filter(fifo_word_buff[0][1],&dc2)+100.0;
data1buf[data1cur] = data1;
data2buf[data2cur] = data2;
data1 = 0;
data2 = 0;
for(int i = 0;i < 20;i++)
{
data1 += data1buf[i];
data2 += data2buf[i];
}
data1 /= 20;
data2 /= 20;
data1cur = (data1cur < 19) ? data1cur + 1 : 0;
data2cur = (data2cur < 19) ? data2cur + 1 : 0;
// fifo_word_buff[0][0] = data1;
// fifo_word_buff[0][1] = data2;
// test(data1,data2);
g_blooddata.hb = data1 + 50;
g_blooddata.hbo2 = data2 + 50;
//将数据写入fft输入并清除输出
for(int i = 0;i < 1;i++)
{
if(g_fft_index < FFT_N)
{
FFTBUF1[g_fft_index].real = fifo_word_buff[i][0];
FFTBUF1[g_fft_index].imag= 0;
FFTBUF2[g_fft_index].real = fifo_word_buff[i][1];
FFTBUF2[g_fft_index].imag= 0;
g_fft_index++;
}
}
//信息更新标志位
g_blooddata.update++;
}
}
//血液信息转换
void blood_data_translate(void)
{
//缓冲区写入结束
if(g_fft_index>=FFT_N)
{
//快速傅里叶变换
FFT(FFTBUF1);
FFT(FFTBUF2);
//解平方
for(int i = 0;i < FFT_N;i++)
{
FFTBUF1[i].real = sqrtf(FFTBUF1[i].real * FFTBUF1[i].real + FFTBUF1[i].imag * FFTBUF1[i].imag);
FFTBUF2[i].real = sqrtf(FFTBUF2[i].real * FFTBUF2[i].real + FFTBUF2[i].imag * FFTBUF2[i].imag);
}
//读取峰值点 10-100带通 频率范围30-292次/分钟
uint16_t s1_max_index = find_max_num_index(FFTBUF1, 100);
uint16_t s2_max_index = find_max_num_index(FFTBUF2, 100);
//检查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;
//血氧含量计算
float sp02_num = (FFTBUF1[s1_max_index].real * FFTBUF1[0].real)
/(FFTBUF2[s1_max_index].real * FFTBUF2[0].real);
sp02_num = sp02_num * SAMPLES_PER_SECOND + CORRECTED_VALUE;
g_blooddata.SpO2 = sp02_num;
//状态正常
g_blooddata.state = BLD_NORMAL;
// for(int i = 0;i < FFT_N;i++)
// {
// static uint8_t str[50];
// sprintf((char *)str,"%f,%f\r\n\0",FFTBUF1[i].real,FFTBUF2[i].real);
// HAL_UART_Transmit(&huart1,str,sizeof(str),20);
// }
// static uint8_t str[50];
// sprintf((char *)str,"H:%d,S:%f\r\n",g_blooddata.heart,g_blooddata.SpO2 );
// HAL_UART_Transmit(&huart1,str,sizeof(str),20);
}
else //数据发生异常
{
g_blooddata.heart = 0;
g_blooddata.SpO2 = 0;
g_blooddata.state = BLD_ERROR;
}
g_fft_index = 0;
}
}
200字以内,仅用于支线交流,主线讨论请采用回复功能。