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

2021年9月6日 9点热度 0条评论 来源: C_ElecM

1. MAX30100传感器驱动

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

完整工程(STM32F013):

链接:https://pan.baidu.com/s/1nMlNo9XDEonXbudeDR7Q1A
提取码:8zd1

      在编写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上,可以用一片玻璃或者硬透明塑料片垫在传感器上方,然后将手压在玻璃片上,这样测得的效果相对较好。

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

有关心率解析算法可以参考我的另一篇博客:https://blog.csdn.net/C_ElecM/article/details/119302061

    原文作者:C_ElecM
    原文地址: https://blog.csdn.net/C_ElecM/article/details/108932322
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系管理员进行删除。