MAX30100心率血氧浓度传感器的使用(一)

    科技2022-08-27  127

    1. MAX30100传感器驱动

           MAX30100传感器是以IIC为接口的心率和血氧浓度传感器,这个传感器可以存储16个样本,每个样本包含1个RED和1个IR数据,每个RED和IR又由2个字节组成,一般为了使用方便,不使用内部的64bytes的FIFO,而只使用4bytes的FIFO,即每次RED和IR数据采集完成后,就直接读取其值,去读完后将读写指针归零,这样就相当于只用了4字节的FIFO。根据官方给出的程序,可以看出也是用的这种方式。

          在编写IIC驱动时,应该注意IIC的时钟速度不得大于400KHz,同时为了提高代码的移植性,这里使用IIC软件模拟的方法:

    MAX30100.c源文件:

    /*============================================================================ * 文件编码: Encoding in UTF-8 without signature * * 文件描述: 该文件是MAX30100的源文件,主要实现用户API * * * * 基本功能: 实现用户API * * 作者 : ElecM * * 版权 : 遵守BSD开源协议 * * 时间 : 2020.10.5 * * 责任声明: 使用该代码所造成的一切后果均由使用者承担,作者不负任何法律责任。* ============================================================================*/ #include "MAX30100.h" static void DelayUs(unsigned char us) { unsigned int i; while(us--) { for(i=0;i<100;i++) ; } } /*--------------------------------------------------------------------------- * 函数:MAX30100_GPIO_Init(void) * 描述:MAX30100引脚初始化 * 参数:无 * 返回:无 *---------------------------------------------------------------------------*/ static void MAX30100_GPIO_Init(void) { RCC->APB2ENR |= (1<<2); //开启GPIOA时钟 GPIOA->CRL &= 0XFFFFF000; //设置GPIOA2为上拉输入,设置GPIOA0,1为推挽输出 GPIOA->CRL |= 0X00000833; GPIOA->ODR |= ((1<<2)|(1<<1)|(1<<0));//挂起IIC总线 } /*--------------------------------------------------------------------------- * 函数:MAX30100_IIC_Start(void) * 描述:IIC主机发出开始条件 * 参数:无 * 返回:无 *---------------------------------------------------------------------------*/ static void MAX30100_IIC_Start(void) { MAX30100_SDA_SET_OUT MAX30100_SCL_SET_H //IIC开始条件 MAX30100_SDA_SET_H DelayUs(5); MAX30100_SDA_SET_L DelayUs(5); MAX30100_SCL_SET_L } /*--------------------------------------------------------------------------- * 函数:MAX30100_IIC_SendByte(void) * 描述:IIC主机往从机发送数据 * 参数:无 * 返回:0:发送成功; 1:发送失败 *---------------------------------------------------------------------------*/ static void MAX30100_IIC_SendByte(MAX30100_U8 byte) { MAX30100_U8 i; MAX30100_SDA_SET_OUT MAX30100_SCL_SET_L for(i=0;i<8;i++) //传输单个Byte { if(byte &(0x80)) MAX30100_SDA_SET_H else MAX30100_SDA_SET_L byte <<= 1; DelayUs(5); MAX30100_SCL_SET_H DelayUs(5); MAX30100_SCL_SET_L DelayUs(5); } MAX30100_SDA_SET_H } /*--------------------------------------------------------------------------- * 函数:MAX30100_IIC_ReceiveByte(void) * 描述:IIC主机从从机接收数据 * 参数:无 * 返回:byte:接收到的数据 *---------------------------------------------------------------------------*/ static MAX30100_U8 MAX30100_IIC_ReceiveByte(void) { MAX30100_U8 i,byte=0; MAX30100_SDA_SET_IN MAX30100_SDA_SET_H for(i=0;i<8;i++) //传输单个Byte { byte <<= 1; MAX30100_SCL_SET_L DelayUs(5); MAX30100_SCL_SET_H DelayUs(5); if(MAX30100_SDA_GET) byte |= 0x01; DelayUs(5); } MAX30100_SCL_SET_L MAX30100_SDA_SET_H DelayUs(5); return byte; } /*--------------------------------------------------------------------------- * 函数:MAX30100_IIC_Stop(void) * 描述:IIC主机发出停止条件 * 参数:无 * 返回:无 *---------------------------------------------------------------------------*/ static void MAX30100_IIC_Stop(void) { MAX30100_SDA_SET_OUT MAX30100_SCL_SET_L MAX30100_SDA_SET_L DelayUs(5); MAX30100_SCL_SET_H DelayUs(5); MAX30100_SDA_SET_H DelayUs(5); } /*--------------------------------------------------------------------------- * 函数:MAX30100_IIC_WaitACK(void) * 描述:IIC主机等待从机应答 * 参数:无 * 返回:1:从机无应答; 0:从机应答 *---------------------------------------------------------------------------*/ static MAX30100_U8 MAX30100_IIC_WaitACK(void) { MAX30100_U16 time_out=0; MAX30100_SCL_SET_L MAX30100_SDA_SET_IN DelayUs(5); MAX30100_SCL_SET_H DelayUs(5); while(MAX30100_SDA_GET) { time_out ++ ; if(time_out>2000) { MAX30100_IIC_Stop(); return 1; } } MAX30100_SCL_SET_L DelayUs(5); return 0; } /*--------------------------------------------------------------------------- * 函数:MAX30100_IIC_SendACK(void) * 描述:IIC主机发出应答 * 参数:无 * 返回:无 *---------------------------------------------------------------------------*/ static void MAX30100_IIC_SendACK(void) { MAX30100_SDA_SET_OUT MAX30100_SCL_SET_L DelayUs(5); MAX30100_SDA_SET_L DelayUs(5); MAX30100_SCL_SET_H DelayUs(5); MAX30100_SDA_SET_L DelayUs(5); MAX30100_SCL_SET_L DelayUs(5); } /*--------------------------------------------------------------------------- * 函数:MAX30100_IIC_SendNACK(void) * 描述:IIC主机发出非应答 * 参数:无 * 返回:无 *---------------------------------------------------------------------------*/ static void MAX30100_IIC_SendNACK(void) { MAX30100_SDA_SET_OUT MAX30100_SCL_SET_L DelayUs(5); MAX30100_SDA_SET_H DelayUs(5); MAX30100_SCL_SET_H DelayUs(5); MAX30100_SCL_SET_L } /*--------------------------------------------------------------------------- * 函数:MAX30100_WriteDatas(MAX30100_U8 Reg, MAX30100_U8 byte) * 描述:把数据写入到相应的寄存器中 * 参数:无 * 返回:0:写入成功; 1:写入失败 *---------------------------------------------------------------------------*/ static MAX30100_U8 MAX30100_WriteByte(MAX30100_U8 Reg, MAX30100_U8 Byte) { MAX30100_IIC_Start(); MAX30100_IIC_SendByte(0XAE); MAX30100_IIC_WaitACK(); MAX30100_IIC_SendByte(Reg); MAX30100_IIC_WaitACK(); MAX30100_IIC_SendByte(Byte); MAX30100_IIC_WaitACK(); MAX30100_IIC_Stop(); return 0; } /*--------------------------------------------------------------------------- * 函数:MAX30100_ReadOneByte(MAX30100_U8 Reg, MAX30100_U8 *Byte) * 描述:把数据从寄存器中读出一个字节 * 参数:Reg:要读的寄存器; *Byte:读出数据 * 返回:0:读出成功; 1:读出失败 *---------------------------------------------------------------------------*/ static MAX30100_U8 MAX30100_ReadOneByte(MAX30100_U8 Reg, MAX30100_U8 *Byte) { MAX30100_IIC_Start(); MAX30100_IIC_SendByte(0XAE); if(MAX30100_IIC_WaitACK()) return 1; MAX30100_IIC_SendByte(Reg); if(MAX30100_IIC_WaitACK()) return 1; MAX30100_IIC_Start(); MAX30100_IIC_SendByte(0XAF); if(MAX30100_IIC_WaitACK()) return 1; (*Byte) = MAX30100_IIC_ReceiveByte(); MAX30100_IIC_SendNACK(); MAX30100_IIC_Stop(); return 0; } /*--------------------------------------------------------------------------- * 函数:MAX30100_ReadBytes(MAX30100_U8 Reg, MAX30100_U8 *Bytes,MAX30100_U8 Len) * 描述:把数据从寄存器中读出 * 参数:Reg:要读的寄存器; *Bytes:读出数据流; Len:读取的数据长度 * 返回:0:读出成功; 1:读出失败 *---------------------------------------------------------------------------*/ static MAX30100_U8 MAX30100_ReadBytes(MAX30100_U8 Reg, MAX30100_U8 *Bytes,MAX30100_U8 Len) { MAX30100_U8 i,data; MAX30100_IIC_Start(); MAX30100_IIC_SendByte(0XAE); if(MAX30100_IIC_WaitACK()) return 1; MAX30100_IIC_SendByte(Reg); if(MAX30100_IIC_WaitACK()) return 1; MAX30100_IIC_Start(); MAX30100_IIC_SendByte(0XAF); if(MAX30100_IIC_WaitACK()) return 1; for(i=0;i<Len-1;i++) { *Bytes = MAX30100_IIC_ReceiveByte(); MAX30100_IIC_SendACK(); Bytes++; } *Bytes = MAX30100_IIC_ReceiveByte(); MAX30100_IIC_SendNACK(); MAX30100_IIC_Stop(); return 0; } /*--------------------------------------------------------------------------- * 函数:MAX30100_Init(void) * 描述:MAX30100初始化 * 参数:无 * 返回:无 *---------------------------------------------------------------------------*/ void MAX30100_Init(void) { MAX30100_GPIO_Init(); MAX30100_WriteByte(MAX30100_CONFIG_MODE,0X40); //复位 MAX30100_WriteByte(MAX30100_INT_ENB,0X50); //打开MAX30100的温度和SPO2数据采集完成中断 MAX30100_WriteByte(MAX30100_FIFO_WR_PTR,0X00); //清零FIFO写寄存器 MAX30100_WriteByte(MAX30100_FIFO_OVF_COUNTER,0X00);//清零FIFO溢出计数器 MAX30100_WriteByte(MAX30100_FIFO_RD_PTR,0X00); //清零FIFO读寄存器 MAX30100_WriteByte(MAX30100_CONFIG_MODE,0X0b); //开启SPO2和温度功能和SPO2功能 MAX30100_WriteByte(MAX30100_CONFIG_SPO2,0X43); //开启高精度ADC,设置脉冲宽度为800uS MAX30100_WriteByte(MAX30100_CONFIG_LED,0X88); //设置RED和IR的LED电流均为27.1mA } /*--------------------------------------------------------------------------- * 函数:MAX30100_ReadIrAndRedData(MAX30100_U16 *Ir,MAX30100_U16 *Red) * 描述:读取MAX30100中的RED和IR数据 * 参数:*Ir:读出的红外数据指针; *Red:读出的红光数据指针 * 返回:1:读出失败; 0:读取成功 *---------------------------------------------------------------------------*/ MAX30100_U8 MAX30100_ReadIrAndRedData(MAX30100_U16 *Ir,MAX30100_U16 *Red) { MAX30100_U8 LED[4]; if(!MAX30100_ReadBytes(0X05,LED,4)) { *Red = ((LED[0]<<8)|(LED[1])); *Ir = ((LED[2]<<8)|(LED[3])); } else return 1; MAX30100_WriteByte(MAX30100_FIFO_WR_PTR,0X00); //读写指针清零 MAX30100_WriteByte(MAX30100_FIFO_OVF_COUNTER,0X00); MAX30100_WriteByte(MAX30100_FIFO_RD_PTR,0X00); return 0; } /*--------------------------------------------------------------------------- * 函数:MAX30100_CheckIrAndRedDataRdy(void) * 描述:检查RED和IR数据是否就位 * 参数:无 * 返回:1:未就位; 0:已就位 *---------------------------------------------------------------------------*/ MAX30100_U8 MAX30100_CheckIrAndRedDataRdy(void) { MAX30100_U8 status; if(!MAX30100_INT_GET) { MAX30100_ReadOneByte(0x00,&status); if(status &0x10) return 0; else return 1; } else return 1; }

    根据MAX30100的数据手册,可以编写如下头文件:

    MAX30100.h头文件:

    /*============================================================================ * 文件编码: Encoding in UTF-8 without signature * * 文件描述: 该文件是MAX30100的头文件,主要提供用户API * * * * 基本功能: 提供MAX30100读取FIFO的API * * 作者 : ElecM * * 版权 : 遵守BSD开源协议 * * 时间 : 2020.10.5 * * 责任声明: 使用该代码所造成的一切后果均由使用者承担,作者不负任何法律责任。* ============================================================================*/ #ifndef __MAX30100_H__ #define __MAX30100_H__ #include "stm32f10x.h" /*--------------------------------------------------------------------------- * MAX30100模块引脚定义 * 参考MAX30100模块手册 ---------------------------------------------------------------------------*/ #define MAX30100_INT_GET (GPIOA->IDR&(1<<2)) #define MAX30100_SDA_SET_IN GPIOA->CRL&=0XFFFFFFF0;GPIOA->CRL|=0X00000008;GPIOA->ODR|=(1<<0);//设置为上拉输入 #define MAX30100_SDA_SET_OUT GPIOA->CRL&=0XFFFFFFF0;GPIOA->CRL|=0X00000003;//设置为推挽输出 #define MAX30100_SDA_SET_L GPIOA->ODR&=(~(1<<0)); #define MAX30100_SDA_SET_H GPIOA->ODR|=((1<<0)); #define MAX30100_SDA_GET (GPIOA->IDR&(1<<0)) #define MAX30100_SCL_SET_L GPIOA->ODR&=(~(1<<1)); #define MAX30100_SCL_SET_H GPIOA->ODR|=((1<<1)); /*--------------------------------------------------------------------------- * MAX30100使用变量类型定义 * 参考编译器中使用的类型 ---------------------------------------------------------------------------*/ typedef vu8 MAX30100_U8; typedef vu16 MAX30100_U16; typedef vu32 MAX30100_U32; typedef vs8 MAX30100_S8; typedef vs16 MAX30100_S16; typedef vs32 MAX30100_S32; /*--------------------------------------------------------------------------- * MAX30100寄存器声明 * 参考MAX30100数据手册 ---------------------------------------------------------------------------*/ //状态寄存器 #define MAX30100_INT_STATUS 0X00 #define MAX30100_INT_ENB 0X01 //FIFO寄存器 #define MAX30100_FIFO_WR_PTR 0X02 #define MAX30100_FIFO_OVF_COUNTER 0X03 #define MAX30100_FIFO_RD_PTR 0X04 #define MAX30100_FIFO_DATA 0X05 //配置寄存器 #define MAX30100_CONFIG_MODE 0X06 #define MAX30100_CONFIG_SPO2 0X07 #define MAX30100_CONFIG_LED 0X09 //温度相关寄存器 #define MAX30100_TEMP_INT 0X16 #define MAX30100_TEMP_FRAC 0X17 #define MAX30100_TEMP_EN 0X21 //芯片ID寄存器 #define MAX30100_REV_ID 0XFE #define MAX30100_PART_ID 0XFF /*########################################################################### # MAX30100用户API # #---------------------------------------------------------------------------- # 使用方法(查询):首先初始化MAX30100模块,再检测模块是否准备就绪, # # 随后就可以读取MAX30100内部FIFO数据。 # #---------------------------------------------------------------------------- # 使用方法(中断):在中断服务函数中设置标志位,根据标志位响应读取数据 # # # # by ElecM 2020.10.05 # ###########################################################################*/ void MAX30100_Init(void); MAX30100_U8 MAX30100_CheckIrAndRedDataRdy(void); MAX30100_U8 MAX30100_ReadIrAndRedData(MAX30100_U16 *Ir,MAX30100_U16 *Red); #endif

    在主函数中不停地检测数据就绪状态,然后把采集的数据送入缓冲区,最先显示在串口中。

    2.MAX30100的PPG数据分析

         上面得到的是RED和IR数据是PPG数据,这些数据不能直接使用,由于MAX30100内部集成了滤波器,所以滤波这部分就不需要考虑了,我们重点关注PPG数据的脉冲解析。将串口打印的PPG数据输入到MATLAB中,如下图所示:

            图片组的最后一张由于位置变化造成的,在使用MAX30100时,手指最好不要直接按压在RED上,可以用一片玻璃或者硬透明塑料片垫在传感器上方,然后将手压在玻璃片上,这样测得的效果相对较好。

           从图中可以发现,波形的峰值是有一些规律的,我们现在需要做的工作就是识别出波形中出现脉冲的个数,然后根据这个数值来计算心率。官方虽然给出了计算心率的代码,但是经过测试发现效果并不理想,在下一篇中将介绍一些效果相对较好的算法来实现心率的计算。

     

    Processed: 0.013, SQL: 9