Abstract: MAX30102 测试代码,仅使用温度和血氧测量功能。
目录:
代码部分
main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "MAX30102.h"
#include "Serial.h"
#include "MAX30102_Reg.h"
uint8_t pf=0;
uint8_t mode=1;
void Config_Check(void);
int main(void)
{
MAX30102_Init();
Serial_Init();
Config_Check();
while (1)
{
float Spo2;
MAX30102_Read_FIFO_Data(MAX30102_PPG_Data);
if (MAX30102_PPG_Data[0]<=0x186a0) //未接近
{
if (pf==0)
{
pf=1;
Serial_Printf("Waiting...\r\n");
}
//Delay_ms(20);
continue;
}
pf=0;
if (mode==0) //输出原始数据
{
Serial_Printf("RED=%d,IR=%d\r\n",MAX30102_PPG_Data[0],MAX30102_PPG_Data[1]);
MAX30102_Read_Temp_Data(MAX30102_Temp_Data);
Serial_Printf("Temp=%.2f\r\n",MAX30102_Temp_Cal(MAX30102_Temp_Data));
}
else if (mode==1)//输出计算后的血氧值
{
MAX30102_Spo2_Cal_Median_Filter(&Spo2);
// Serial_Printf("RED=%d,IR=%d\r\n",MAX30102_PPG_Data[0],MAX30102_PPG_Data[1]);
Serial_Printf("Spo2=%.3f%\r\n",Spo2);
}
Delay_ms(200);
}
}
void Config_Check(void)
{
Serial_Printf("rv_id=%hX\r\n",MAX30102_ReadReg(MAX30102_Revision_ID));
Serial_Printf("part_id=%hX\r\n",MAX30102_ReadReg(MAX30102_Part_ID));
Serial_Printf("mode=%hX\r\n",MAX30102_ReadReg(MAX30102_Mode_Configuration));
Serial_Printf("status1=%hX\r\n",MAX30102_ReadReg(MAX30102_Interrupt_Status_1));
Serial_Printf("status2=%hX\r\n",MAX30102_ReadReg(MAX30102_Interrupt_Status_2));
Serial_Printf("enable1=%hX\r\n",MAX30102_ReadReg(MAX30102_Interrupt_Enable_1));
Serial_Printf("enable2=%hX\r\n",MAX30102_ReadReg(MAX30102_Interrupt_Enable_2));
Serial_Printf("rd_ptr=%hX\r\n",MAX30102_ReadReg(MAX30102_FIFO_Read_Pointer));
Serial_Printf("wr_ptr=%hX\r\n",MAX30102_ReadReg(MAX30102_FIFO_Writer_Pointer));
Serial_Printf("overflow=%hX\r\n",MAX30102_ReadReg(MAX30102_Overflow_Counter));
Serial_Printf("fifo_conf=%hX\r\n",MAX30102_ReadReg(MAX30102_FIFO_Configuration));
Serial_Printf("sp02_conf=%hX\r\n",MAX30102_ReadReg(MAX30102_SpO2_Configuration));
Serial_Printf("temp_conf=%hX\r\n",MAX30102_ReadReg(MAX30102_Die_Temperature_Config));
Serial_Printf("led1_amp=%hX\r\n",MAX30102_ReadReg(MAX30102_LED1_Pulse_Amplitude_Configuration));
Serial_Printf("led2_amp=%hX\r\n",MAX30102_ReadReg(MAX30102_LED2_Pulse_Amplitude_Configuration));
}
MAX30102.c
#include "stm32f10x.h" // Device header
#include "MAX30102_Reg.h"
#include "Delay.h"
#define MAX30102_ADDR 0xAE //地址
uint32_t MAX30102_PPG_Data[2];
uint8_t MAX30102_Temp_Data[2];
/**
* @brief 求最值函数
* @param 无
* @retval 数据缓存区大小及指针
*/
void MAX30102_Max_Min(uint32_t *Arr,uint8_t Size,uint32_t *Buf)
{
uint32_t Max=0;
uint32_t Min=Arr[0];
uint8_t i;
for (i=0;i<Size;i++)
{
if (Arr[i]>Max)
{
Max=Arr[i];
}
if (Arr[i]<Min)
{
Min=Arr[i];
}
}
if (Max==Min)
{
Min-=1;
}
Buf[0]=Max;
Buf[1]=Min;
}
/**
* @brief 变量交换函数
* @param 无
* @retval 待交换数据的指针
*/
void MAX30102_Swap(uint32_t *a, uint32_t *b)
{
uint32_t temp = *a;
*a = *b;
*b = temp;
}
//中位数计算,参考自https://blog.csdn.net/ONEDAY_789/article/details/76681764
uint32_t MAX30102_PartSort(uint32_t *arr, uint8_t start, uint8_t end)
{
uint8_t left = start;
uint8_t right = end;
uint32_t key = arr[end]; //选取关键字
while (left < right)
{
while (left < right && arr[left] <= key) //左边找比key大的值
{
++left;
}
while (left < right && arr[right] >= key) //右边找比key小的值
{
--right;
}
if (left < right)
{
MAX30102_Swap(&arr[left], &arr[right]); //找到之后交换左右的值
}
}
MAX30102_Swap(&arr[right], &arr[end]);
return left;
}
uint32_t MAX30102_GetMidNumNoSort(uint32_t *arr,uint32_t size)
{
uint8_t start = 0;
uint8_t end = size - 1;
uint8_t mid = (size - 1) / 2;
uint8_t div = MAX30102_PartSort(arr,start,end);
while (div != mid)
{
if (mid < div) //左半区间找
div = MAX30102_PartSort(arr, start, div - 1);
else //左半区间找
div = MAX30102_PartSort(arr, div + 1, end);
}
return arr[mid];
}
//中位数计算结束
/**
* @brief 等待事件发生
* @param 无
* @retval 略
*/
void MAX30102_WaitEvent(I2C_TypeDef* I2Cx, uint32_t I2C_EVENT)
{
uint32_t TimeOut;
TimeOut=1000;
while(I2C_CheckEvent(I2Cx,I2C_EVENT)!=SUCCESS)
{
TimeOut--;
if (TimeOut==0)
{
break;
}
}
}
/**
* @brief I2C指定地址写入数据
* @param 无
* @retval 指定写地址,数据
*/
void MAX30102_WriteReg(uint8_t RegAddress,uint8_t Data)
{
I2C_GenerateSTART(I2C2,ENABLE); //生成起始条件
MAX30102_WaitEvent(I2C2,I2C_EVENT_MASTER_MODE_SELECT); //检测EV5
I2C_Send7bitAddress(I2C2,MAX30102_ADDR,I2C_Direction_Transmitter); //发送地址
MAX30102_WaitEvent(I2C2,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED); //检测EV6
I2C_SendData(I2C2,RegAddress); //发送写入地址
MAX30102_WaitEvent(I2C2,I2C_EVENT_MASTER_BYTE_TRANSMITTING); //检测EV8
I2C_SendData(I2C2,Data); //发送写入数据
MAX30102_WaitEvent(I2C2,I2C_EVENT_MASTER_BYTE_TRANSMITTED); //检测EV8_2
I2C_GenerateSTOP(I2C2, ENABLE); //终止
}
/**
* @brief I2C指定地址读取数据
* @param 无
* @retval 指定读地址
*/
uint8_t MAX30102_ReadReg(uint8_t RegAddress)
{
uint8_t Data;
I2C_GenerateSTART(I2C2,ENABLE); //生成起始条件
MAX30102_WaitEvent(I2C2,I2C_EVENT_MASTER_MODE_SELECT); //检测EV5
I2C_Send7bitAddress(I2C2,MAX30102_ADDR,I2C_Direction_Transmitter); //发送地址
MAX30102_WaitEvent(I2C2,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED); //检测EV6
I2C_SendData(I2C2,RegAddress); //发送读取地址
MAX30102_WaitEvent(I2C2,I2C_EVENT_MASTER_BYTE_TRANSMITTED); //检测EV8
I2C_GenerateSTART(I2C2,ENABLE); //生成起始条件
MAX30102_WaitEvent(I2C2,I2C_EVENT_MASTER_MODE_SELECT); //检测EV5
I2C_Send7bitAddress(I2C2,MAX30102_ADDR,I2C_Direction_Receiver); //发送地址
MAX30102_WaitEvent(I2C2,I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED); //检测EV6
//只接受一字节
I2C_AcknowledgeConfig(I2C2,DISABLE); // 应答失效
I2C_GenerateSTOP(I2C2,ENABLE); //申请终止
MAX30102_WaitEvent(I2C2,I2C_EVENT_MASTER_BYTE_RECEIVED); //检测EV7
Data=I2C_ReceiveData(I2C2);
I2C_AcknowledgeConfig(I2C2,ENABLE); // 应答有效
return Data;
}
/**
* @brief 中断初始化函数
* @param 无
* @retval 无
*/
//void MAX30102_Exit_Init(void)
//{
// RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
// RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
//
// GPIO_InitTypeDef GPIO_Initstructure;
// GPIO_Initstructure.GPIO_Mode=GPIO_Mode_IN_FLOATING; //浮空输入
// GPIO_Initstructure.GPIO_Pin=GPIO_Pin_0; //PA0
// GPIO_Initstructure.GPIO_Speed=GPIO_Speed_50MHz;
// GPIO_Init(GPIOA,&GPIO_Initstructure);
//
// GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource0);
//
// EXTI_InitTypeDef EXIT_InitStructure;
// EXIT_InitStructure.EXTI_Line=EXTI_Line0; //指定中断线
// EXIT_InitStructure.EXTI_LineCmd=ENABLE; //开启或关闭
// EXIT_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt; //中断模式
// EXIT_InitStructure.EXTI_Trigger=EXTI_Trigger_Falling; //下降沿触发
// EXTI_Init(&EXIT_InitStructure);
//
// NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //整个工程只用执行一次
// NVIC_InitTypeDef NVIC_InitStructure;
// NVIC_InitStructure.NVIC_IRQChannel=EXTI0_IRQn;
// NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
// NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1;
// NVIC_InitStructure.NVIC_IRQChannelSubPriority=1;
// NVIC_Init(&NVIC_InitStructure);
//}
/**
* @brief 初始化函数
* @param 无
* @retval 无
*/
void MAX30102_Init(void)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C2,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
I2C_InitTypeDef I2C_InitStructure;
I2C_InitStructure.I2C_Ack=I2C_Ack_Enable; //应答开启
I2C_InitStructure.I2C_AcknowledgedAddress=I2C_AcknowledgedAddress_7bit; //七位地址
I2C_InitStructure.I2C_ClockSpeed=200000; //200Khz
I2C_InitStructure.I2C_DutyCycle=I2C_DutyCycle_2; //低:高=2:1,高速有效
I2C_InitStructure.I2C_Mode=I2C_Mode_I2C;
I2C_InitStructure.I2C_OwnAddress1=0X00; //自身地址,从机有效
I2C_Init(I2C2,&I2C_InitStructure);
I2C_Cmd(I2C2,ENABLE);
MAX30102_WriteReg(MAX30102_Mode_Configuration,0X40); //重置模式
Delay_ms(20);
MAX30102_WriteReg(MAX30102_Interrupt_Enable_1,0X40); //中断开启 PPG_RDY 0x40 中断信号为低电平
MAX30102_WriteReg(MAX30102_Interrupt_Enable_2,0X02); //中断开启 DIE_TEMP_RDY 0x02
MAX30102_WriteReg(MAX30102_FIFO_Read_Pointer,0X00); //读指针清零
MAX30102_WriteReg(MAX30102_FIFO_Writer_Pointer,0X00); //写指针清零
MAX30102_WriteReg(MAX30102_Overflow_Counter,0X00); //溢出计数清零
MAX30102_WriteReg(MAX30102_FIFO_Configuration,0X2F); //FIFO配置,AVE=2,ROL=1,FULL=17
MAX30102_WriteReg(MAX30102_SpO2_Configuration,0X2B); //血氧测量配置,ADC=4096,SR=200/s,LED_PW=411,18
MAX30102_WriteReg(MAX30102_LED1_Pulse_Amplitude_Configuration,0X24);
MAX30102_WriteReg(MAX30102_LED2_Pulse_Amplitude_Configuration,0X24);
MAX30102_ReadReg(MAX30102_Interrupt_Status_1); //中断清零
MAX30102_ReadReg(MAX30102_Interrupt_Status_2); //中断清零
MAX30102_WriteReg(MAX30102_Die_Temperature_Config,0X01); //温度测量开
MAX30102_WriteReg(MAX30102_Mode_Configuration,0X03); //血氧模式
// MAX30102_Exit_Init();
}
/**
* @brief 读取FIFO采样数据
* @param 无
* @retval 数据缓存区指针
*/
void MAX30102_Read_FIFO_Data(uint32_t *Buf)
{
uint8_t Data[6];
I2C_AcknowledgeConfig(I2C2,ENABLE); // 应答失效
I2C_GenerateSTART(I2C2,ENABLE); //生成起始条件
MAX30102_WaitEvent(I2C2,I2C_EVENT_MASTER_MODE_SELECT); //检测EV5
I2C_Send7bitAddress(I2C2,MAX30102_ADDR,I2C_Direction_Transmitter); //发送地址
MAX30102_WaitEvent(I2C2,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED); //检测EV6
I2C_SendData(I2C2,MAX30102_FIFO_Data_Register); //发送读取地址
MAX30102_WaitEvent(I2C2,I2C_EVENT_MASTER_BYTE_TRANSMITTED); //检测EV8
I2C_GenerateSTART(I2C2,ENABLE); //生成起始条件
MAX30102_WaitEvent(I2C2,I2C_EVENT_MASTER_MODE_SELECT); //检测EV5
I2C_Send7bitAddress(I2C2,MAX30102_ADDR,I2C_Direction_Receiver); //发送地址
MAX30102_WaitEvent(I2C2,I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED); //检测EV6
uint8_t i;
uint8_t Tmp;
for (i=0;i<6;i++)
{
if (i==5)
{
//接受最后一字节前设置ACK=0,S=1
I2C_AcknowledgeConfig(I2C2,DISABLE); // 应答失效
I2C_GenerateSTOP(I2C2,ENABLE); //申请终止
}
Tmp=0;
MAX30102_WaitEvent(I2C2,I2C_EVENT_MASTER_BYTE_RECEIVED); //检测EV7
Tmp=I2C_ReceiveData(I2C2);
Data[i]=Tmp;
}
I2C_AcknowledgeConfig(I2C2,ENABLE); // 应答有效
Buf[0] = ((Data[0]<<16 | Data[1]<<8 | Data[2]) & 0x03ffff); //3fffff
Buf[1] = ((Data[3]<<16 | Data[4]<<8 | Data[5]) & 0x03ffff);
}
/**
* @brief 读取温度数据
* @param 无
* @retval 数据缓存区指针
*/
void MAX30102_Read_Temp_Data(uint8_t *Buf)
{
Buf[0]=MAX30102_ReadReg(MAX30102_Die_Temp_Integer); //整数部分
Buf[1]=MAX30102_ReadReg(MAX30102_Die_Temp_Fraction); //小数部分(只取后四位)
}
/**
* @brief 读取版本ID
* @param 无
* @retval 无
*/
uint8_t MAX30102_Get_Revision_ID(void)
{
return MAX30102_ReadReg(MAX30102_Revision_ID);
}
/**
* @brief 读取部件ID
* @param 无
* @retval 无
*/
uint8_t MAX30102_Get_Part_ID(void)
{
return MAX30102_ReadReg(MAX30102_Part_ID);
}
/**
* @brief 温度计算函数
* @param 无
* @retval 数据缓存区指针
*/
float MAX30102_Temp_Cal(uint8_t *Buf)
{
return (float)Buf[0]+(float)Buf[1]*0.625;
}
/**
* @brief 血氧计算函数
* @param 无
* @retval 略
*/
float MAX30102_Spo2_Cal(uint32_t *Red_Data,uint32_t *Ir_Data,uint8_t Size)
{
float Red_Max,Red_Min;
float Ir_Max,Ir_Min;
float R;
uint32_t Temp[2];
//求出最大最小值
MAX30102_Max_Min(Red_Data,Size,Temp);
Red_Max=Temp[0];
Red_Min=Temp[1];
MAX30102_Max_Min(Ir_Data,Size,Temp);
Ir_Max=Temp[0];
Ir_Min=Temp[1];
R=((Red_Max + Red_Min) / (Red_Max - Red_Min)) / ((Ir_Max + Ir_Min) / (Ir_Max - Ir_Min));
float result=-45.060 * R * R + 30.354 * R + 94.845;
if (result<0)
{
result=0;
}
return result;
}
/**
* @brief 血氧原始数据中位数滤波函数
* @param 无
* @retval 略
*/
void MAX30102_Spo2_Cal_Median_Filter(float *Buf)
{
const uint8_t each_1=8; //each_1 个数据为一组计算血氧
uint32_t Red_m[each_1];
uint32_t Ir_m[each_1];
uint8_t i;
for (i=0;i<each_1;i++)
{
const uint8_t each_2=5; //each_2(奇数) 个数据为一组计算中位数
uint32_t Red_Data[each_2];
uint32_t Ir_Data[each_2];
uint8_t j=0;
for (j=0;j<each_2;j++)
{
MAX30102_Read_FIFO_Data(MAX30102_PPG_Data); // 读取FIFO数据
Red_Data[j]=MAX30102_PPG_Data[0];
Ir_Data[j]=MAX30102_PPG_Data[1];
}
Red_m[i]=MAX30102_GetMidNumNoSort(Red_Data,each_2); //分别计算中位数
Ir_m[i]=MAX30102_GetMidNumNoSort(Ir_Data,each_2);
}
*Buf=MAX30102_Spo2_Cal(Red_m,Ir_m,each_1);
}
/**
* @brief 中断函数
* @param 无
* @retval 无
*/
//void EXTI0_IRQHandler(void)
//{
// if (EXTI_GetITStatus(EXTI_Line0)==SET)
// {
// uint8_t ppg_flag = MAX30102_ReadReg(MAX30102_Interrupt_Status_1) & 0x40;
// uint8_t temp_flag = MAX30102_ReadReg(MAX30102_Interrupt_Status_1) & 0x02;
// if (ppg_flag==0x40)
// {
// MAX30102_Read_FIFO_Data(MAX30102_PPG_Data); //读取采样数据
// }
// if (temp_flag==0x02)
// {
// MAX30102_Read_Temp_Data(MAX30102_Temp_Data); //读取温度数据
// }
// EXTI_ClearITPendingBit(EXTI_Line0);
// }
//}
MAX30102.h
#ifndef __MAX30102_H
#define __MAX30102_H
#include "stm32f10x.h" // Device header
#include "MAX30102_Reg.h"
extern uint32_t MAX30102_PPG_Data[2];
extern uint8_t MAX30102_Temp_Data[2];
void MAX30102_Init(void);
void MAX30102_Read_FIFO_Data(uint32_t *Buf);
void MAX30102_Read_Temp_Data(uint8_t *Buf);
uint8_t MAX30102_Get_Revision_ID(void);
uint8_t MAX30102_Get_Part_ID(void);
uint8_t MAX30102_ReadReg(uint8_t RegAddress);
float MAX30102_Temp_Cal(uint8_t *Buf);
void MAX30102_Spo2_Cal_Median_Filter(float *Buf);
#endif
MAX30102_Reg.h
#ifndef __MAX30102_Reg_H
#define __MAX30102_Reg_H
//STATUS
#define MAX30102_Interrupt_Status_1 0X00 //中断状态1
#define MAX30102_Interrupt_Status_2 0X01 //中断状态2
#define MAX30102_Interrupt_Enable_1 0x02 //中断使能1
#define MAX30102_Interrupt_Enable_2 0x03 //中断使能2
#define MAX30102_FIFO_Writer_Pointer 0x04 //FIFO写指针
#define MAX30102_Overflow_Counter 0x05 //FIFO满后遗失的采样数
#define MAX30102_FIFO_Read_Pointer 0x06 //FIFO读指针
#define MAX30102_FIFO_Data_Register 0x07 //FIFO数据寄存器
//Configuration
#define MAX30102_FIFO_Configuration 0x08 //FIFO配置
#define MAX30102_Mode_Configuration 0x09 //模式配置
#define MAX30102_SpO2_Configuration 0x0A //血氧测量配置
#define MAX30102_LED1_Pulse_Amplitude_Configuration 0x0C //LED1脉冲波幅配置(RED)
#define MAX30102_LED2_Pulse_Amplitude_Configuration 0x0D //LED2脉冲波幅配置(IR)
#define MAX30102_Proximity_Mode_LED_Pulse_Amplitude 0X10 //接近检测模式LED波幅配置
#define MAX30102_MultiLED_Mode_Control_Registers_SLOT2_SLOT1 0x11 //混合LED模式配置(slot2、slot1)
#define MAX30102_MultiLED_Mode_Control_Registers_SLOT4_SLOT3 0x12 //混合LED模式配置(slot4、slot3)
//DIE TEMPERATURE
#define MAX30102_Die_Temp_Integer 0x1F //温度测量值(整数部分)
#define MAX30102_Die_Temp_Fraction 0x20 //温度测量值(小数部分)
#define MAX30102_Die_Temperature_Config 0x21 //温度测量配置
//PROXIMITY FUNCTION
#define MAX30102_Proximity_Interrupt_Threshold 0x30 //接近检测中断阈值
//PART ID
#define MAX30102_Revision_ID 0XFE //版本ID,从寄存器读取
#define MAX30102_Part_ID 0xFF //器件ID,0x15
#endif
串口数据样例
mode=0,输出原始数据
RED=150445,IR=134586
Temp=28.25
RED=150581,IR=134643
Temp=28.25
RED=150676,IR=134684
Temp=28.25
RED=150872,IR=134769
Temp=28.25
RED=151041,IR=134828
Temp=28.25
RED=151136,IR=134869
Temp=28.25
RED=151271,IR=134928
Temp=28.25
RED=151357,IR=134979
Temp=28.25
RED=151447,IR=135035
Temp=28.25
RED=151501,IR=135065
Temp=28.25
RED=151573,IR=135072
Temp=28.25
RED=151270,IR=134908
Temp=28.25
RED=150767,IR=134666
Temp=28.25
RED=150037,IR=134310
Temp=28.25
RED=149637,IR=134133
Temp=28.25
Waiting…
mode=1,输出血氧值
Spo2=98.968
Spo2=97.083
Spo2=99.789
Spo2=99.755
Spo2=99.537
Spo2=97.666
Spo2=99.740
Spo2=99.955
Spo2=96.622
Spo2=97.839
Spo2=98.715
Spo2=95.240
Spo2=96.473
Spo2=99.735
Spo2=95.161
Spo2=87.345
Spo2=59.356
Spo2=89.408
Spo2=95.111
Spo2=90.659
Spo2=99.778
Spo2=97.260
Spo2=99.744
Spo2=99.481
Spo2=98.376
Spo2=97.231
Spo2=99.917
Spo2=97.851
Spo2=98.049
Spo2=99.458
Spo2=99.815
Spo2=80.416
Waiting…
其他
- 有时会出现I2C应答正常但是读写数据异常的情况,此时断电重启就可以解决,具体原因未知
- 代码注释部分为中断功能,使用困难固注释
- 原始数据使用了中位数滤波,但是结果仍不稳定