这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » 综合技术 » 物联网技术 » 【MAX78000】移植MAX30102血氧心率传感器

共4条 1/1 1 跳转至

【MAX78000】移植MAX30102血氧心率传感器

工程师
2025-06-21 18:57:02     打赏

【前言】

本篇将使用模拟IIC来驱动MAX30102传感器。

【IO选择以及初始化】

前面我使用了硬件IIC来实现驱动OLED屏,但是在驱动MAX30102时,发现工作一段时间后,recv会报错,因此修改为模拟IIC更加稳定。

从开发板的原理图中,我选择了P2_6、P2_7来做为MAX30102的SCL、SDA来做为IO接口。

image.png

1、首先创建xiic.h/xiic.c入入工程中:

image.png

2、宏定义端口,以及SDA、SCL的高低电平:

#define MXC_GPIO_PORT_SDA MXC_GPIO2
#define MXC_GPIO_PORT_SCL MXC_GPIO2
#define MXC_GPIO_PIN_SDA MXC_GPIO_PIN_6
#define MXC_GPIO_PIN_SCL MXC_GPIO_PIN_7

#define IIC_SCL_1 			MXC_GPIO_OutSet(gpio_scl_out.port, gpio_scl_out.mask)
#define IIC_SCL_0 			MXC_GPIO_OutClr(gpio_scl_out.port, gpio_scl_out.mask)
#define IIC_SDA_1 			MXC_GPIO_OutSet(gpio_sda_out.port, gpio_sda_out.mask)
#define IIC_SDA_0 			MXC_GPIO_OutClr(gpio_sda_out.port, gpio_sda_out.mask)
#define	IIC_SDA_READ		MXC_GPIO_InGet(gpio_sda_out.port, gpio_sda_out.mask)

3、初始化IIC

mxc_gpio_cfg_t gpio_sda_out;
mxc_gpio_cfg_t gpio_scl_out;

void IIC_GPIO_INIT(void)
{
	gpio_sda_out.port = MXC_GPIO_PORT_SDA;
	gpio_sda_out.mask = MXC_GPIO_PIN_SDA;
	gpio_sda_out.pad = MXC_GPIO_PAD_NONE;
	gpio_sda_out.func = MXC_GPIO_FUNC_OUT;
	gpio_sda_out.vssel = MXC_GPIO_VSSEL_VDDIOH;
	gpio_sda_out.drvstr = MXC_GPIO_DRVSTR_0;
    MXC_GPIO_Config(&gpio_sda_out);

    gpio_scl_out.port = MXC_GPIO_PORT_SCL;
    gpio_scl_out.mask = MXC_GPIO_PIN_SCL;
    gpio_scl_out.pad = MXC_GPIO_PAD_NONE;
    gpio_scl_out.func = MXC_GPIO_FUNC_OUT;
    gpio_scl_out.vssel = MXC_GPIO_VSSEL_VDDIOH;
    gpio_scl_out.drvstr = MXC_GPIO_DRVSTR_0;
    MXC_GPIO_Config(&gpio_scl_out);

}

4、IIC的基础配置到这里就结束,基他的IIC读取详见如下的驱动源码:

/*
 * xiic.c
 *
 *  Created on: 2025年6月21日
 *      Author: liujianhua
 */

#include "xiic.h"
#include <stdint.h>
#include "mxc_device.h"
#include "mxc_delay.h"
#include "nvic_table.h"

#include "board.h"
#include "gpio.h"

uint8_t ack;


mxc_gpio_cfg_t gpio_sda_out;
mxc_gpio_cfg_t gpio_scl_out;

void IIC_GPIO_INIT(void)
{
	gpio_sda_out.port = MXC_GPIO_PORT_SDA;
	gpio_sda_out.mask = MXC_GPIO_PIN_SDA;
	gpio_sda_out.pad = MXC_GPIO_PAD_NONE;
	gpio_sda_out.func = MXC_GPIO_FUNC_OUT;
	gpio_sda_out.vssel = MXC_GPIO_VSSEL_VDDIOH;
	gpio_sda_out.drvstr = MXC_GPIO_DRVSTR_0;
    MXC_GPIO_Config(&gpio_sda_out);

    gpio_scl_out.port = MXC_GPIO_PORT_SCL;
    gpio_scl_out.mask = MXC_GPIO_PIN_SCL;
    gpio_scl_out.pad = MXC_GPIO_PAD_NONE;
    gpio_scl_out.func = MXC_GPIO_FUNC_OUT;
    gpio_scl_out.vssel = MXC_GPIO_VSSEL_VDDIOH;
    gpio_scl_out.drvstr = MXC_GPIO_DRVSTR_0;
    MXC_GPIO_Config(&gpio_scl_out);

}




void IIC_Delay(void)
{
	uint8_t i=6; //i=10延时1.5us//这里可以优化速度 ,经测试最低到5还能写入
   while(i--);
//	delay_us(1);
}


void IIC_Start(void)
{
	IIC_SCL_1;
	IIC_SDA_1;//启始信号建立时间 0.6us 400KHz
	IIC_Delay();
	IIC_SDA_0;
	IIC_Delay();//启始信号保持时间0.6us
	IIC_SCL_0;
	IIC_Delay();//时钟低电平时间1.3us
}

void IIC_Stop(void)
{
	IIC_SDA_0;
	IIC_SCL_1;
	IIC_Delay();//结束信号建立时间0.6us
	IIC_SDA_1;
	IIC_Delay();//总线空闲时间时间1.3us
}

void IIC_Send_Byte(uint8_t byte)
{
	uint8_t i;//先发送高位
	for(i=0;i<8;i++)
	{
		if(byte & 0x80)
		{
			IIC_SDA_1;
		}
		else
		{
			IIC_SDA_0;
		}
		IIC_Delay();
		IIC_SCL_1;
		IIC_Delay();
		IIC_SCL_0;
		IIC_Delay();
		byte<<=1;
	}
	IIC_SDA_1;
	IIC_Delay();
	IIC_SCL_1;
	IIC_Delay();
	if(IIC_SDA_READ)
	{
		ack=1;
	}
	else
	{
		ack=0;
	}
	IIC_SCL_0;
	IIC_Delay();
}

uint8_t IIC_Receive_Byte(void)
{
   uint8_t receive=0;
   uint8_t i;//置数据线为输入方式
   for(i=0;i<8;i++)
   {
			receive<<=1;
			IIC_SCL_1;//置时钟线为高使数据线上数据有效
			IIC_Delay();
			if(IIC_SDA_READ)
			{
				receive++;//读数据位,接收的数据位放入retc中
			}
			IIC_SCL_0;
			IIC_Delay();
   }
   return receive;
}

uint8_t IIC_Write_Byte(uint8_t device_addr,uint8_t register_addr,uint8_t data)
{
	IIC_Start();
	IIC_Send_Byte(device_addr+0);
	if (ack == 1)return 0;
	IIC_Send_Byte(register_addr);
	if (ack == 1)return 0;
	IIC_Send_Byte(data);
	if (ack == 1)return 0;
	IIC_Stop();
	return 1;
}

void I2C_Ack(uint8_t a)
{
	if(a)
	{
		IIC_SDA_1;            //非应答
		IIC_Delay();
		IIC_SCL_1;
		IIC_Delay();
		IIC_SCL_0;
		IIC_Delay();
	}
	else
	{
		IIC_SDA_0; 						//应答
		IIC_Delay();
		IIC_SCL_1;
		IIC_Delay();
		IIC_SCL_0;
		IIC_Delay();
		IIC_SDA_1;
	}
}

uint8_t IIC_Read_Byte(uint8_t device_addr,uint8_t register_addr)
{
	uint8_t read_data;
	IIC_Start();
	IIC_Send_Byte(device_addr+0);
	if (ack == 1)return 0;
	IIC_Send_Byte(register_addr);
	if (ack == 1)return 0;
	IIC_Start();
	IIC_Send_Byte(device_addr+1);
	if (ack == 1)return 0;
	read_data = IIC_Receive_Byte();
	I2C_Ack(1);
	IIC_Stop();
	return read_data;
}

uint8_t IIC_Write_Array(uint8_t device_addr,uint8_t register_addr,uint8_t *Data,uint16_t Num)
{
	uint16_t i;
	IIC_Start();
	IIC_Send_Byte(device_addr+0);
	if (ack == 1)return 0;
	IIC_Send_Byte(register_addr);
	if (ack == 1)return 0;
	for(i=0;i<Num;i++)
	{
		IIC_Send_Byte(*Data++);
		if (ack == 1)return 0;
	}
	IIC_Stop();
	return 1;
}

uint8_t IIC_Read_Array(uint8_t device_addr,uint8_t register_addr,uint8_t *Data,uint16_t Num)
{
	uint16_t i;
	IIC_Start();
	IIC_Send_Byte(device_addr+0);
	if (ack == 1)return 0;
	IIC_Send_Byte(register_addr);
	if (ack == 1)return 0;
	IIC_Start();
	IIC_Send_Byte(device_addr+1);
	if (ack == 1)return 0;
	for(i=0;i<Num;i++)
	{
		*Data++ = IIC_Receive_Byte();
		if(i==Num-1)
			I2C_Ack(1);
		else
			I2C_Ack(0);
	}
	IIC_Stop();
	return 1;
}

【MAX30102驱动适配】

1、主要需要适配写入与读取函数,代码如下:

uint8_t max30102_Bus_Write(uint8_t Register_Address, uint8_t Word_Data)
{
    uint8_t data[1];
    data[0] = Word_Data;
    return IIC_Write_Array(max30100_WR_address,Register_Address,data,1);

}



uint8_t max30102_Bus_Read(uint8_t Register_Address)
{
   return IIC_Read_Byte(max30100_WR_address,Register_Address);
}

2、初始化配置:

void max30102_init(void)
{
	hardIIC_init();
    max30102_reset();
    max30102_Bus_Write(REG_INTR_ENABLE_1, 0xc0); // INTR setting
    max30102_Bus_Write(REG_INTR_ENABLE_2, 0x00);
    max30102_Bus_Write(REG_FIFO_WR_PTR, 0x00); // FIFO_WR_PTR[4:0]
    max30102_Bus_Write(REG_OVF_COUNTER, 0x00); // OVF_COUNTER[4:0]
    max30102_Bus_Write(REG_FIFO_RD_PTR, 0x00); // FIFO_RD_PTR[4:0]
    max30102_Bus_Write(REG_FIFO_CONFIG, 0x0f); // sample avg = 1, fifo rollover=false, fifo almost full = 17
    max30102_Bus_Write(REG_MODE_CONFIG, 0x03); // 0x02 for Red only, 0x03 for SpO2 mode 0x07 multimode LED
    max30102_Bus_Write(REG_SPO2_CONFIG, 0x27); // SPO2_ADC range = 4096nA, SPO2 sample rate (100 Hz), LED pulseWidth (400uS)
    max30102_Bus_Write(REG_LED1_PA, 0x24);     // Choose value for ~ 7mA for LED1
    max30102_Bus_Write(REG_LED2_PA, 0x24);     // Choose value for ~ 7mA for LED2
    max30102_Bus_Write(REG_PILOT_PA, 0x7f);    // Choose value for ~ 25mA for Pilot LED
}

3、最后是读取一个fifo的,即读取0x09的6个bit的数据,其代码如下:

void maxim_max30102_read_fifo(uint32_t *pun_red_led, uint32_t *pun_ir_led)
{
    uint32_t un_temp;
    unsigned char uch_temp;
    unsigned char ach_i2c_data[6];
    *pun_red_led = 0;
    *pun_ir_led = 0;
    // read and clear status register
    max30102_Bus_Read(REG_INTR_STATUS_1);
    max30102_Bus_Read(REG_INTR_STATUS_2);

    IIC_Read_Array(max30100_WR_address,REG_FIFO_DATA,ach_i2c_data,6);
    un_temp = (unsigned char)ach_i2c_data[0];
    un_temp <<= 16;
    *pun_red_led += un_temp;
    un_temp = (unsigned char)ach_i2c_data[1];
    un_temp <<= 8;
    *pun_red_led += un_temp;
    un_temp = (unsigned char)ach_i2c_data[2];
    *pun_red_led += un_temp;
    un_temp = (unsigned char)ach_i2c_data[3];
    un_temp <<= 16;
    *pun_ir_led += un_temp;
    un_temp = (unsigned char)ach_i2c_data[4];
    un_temp <<= 8;
    *pun_ir_led += un_temp;
    un_temp = (unsigned char)ach_i2c_data[5];
    *pun_ir_led += un_temp;
    *pun_red_led &= 0x03FFFF; // Mask MSB [23:18]
    *pun_ir_led &= 0x03FFFF;  // Mask MSB [23:18]
		
		if(*pun_red_led<=10000)
		{
			*pun_red_led = 0;
		}
		if(*pun_ir_led<=10000)
		{
			*pun_ir_led = 0;
		}
}

其余的代码移植官方的FFT变换以及血氧分析,可以参观官方的驱动即可。

【实验效果】

image.png

【总结】

我在移植MAX30102驱动,起码是使用硬件IIC的,结果写入参数没有问题,但是读取数据时会出现错误,而且是随机的,因此修改为模拟,当然在FreeRTOS环境中,可以创建多任务调度,也不需要考虑IIC的冲突。




关键词: MAX78000     eclipse     maxiSDK         

专家
2025-06-22 16:39:25     打赏
2楼

感谢分享


专家
2025-06-22 16:41:12     打赏
3楼

感谢分享


专家
2025-06-22 16:42:38     打赏
4楼

感谢分享


共4条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]