RT-Thread在正点原子Apollo上使用MAX30102读取心率、血氧参数

1. 买来MAX30102模块,某宝卖家只提供了两个小小的例程,一个是基于mBed的STM32F103C8T6的Keil例程,另外一个是arduion程序,其余啥都没有了,失望,网上好多人抱怨移植好麻烦,有营养的帖子不多,推荐一个这个老哥有点东西,强,可惜没有分享工程,有移植经验分享。 

2. 在Keil例程中,文件如下,最需要注意的是MAX30102.cpp、MAX30102.h,以及algorithm.cpp、algorithm.h,其中algorithm两个文件是对读取的MAX30102数据的处理,主要有两个均值滤波器,还经过了一个汉明窗,最后返回了结果,还好我大三数字信号处理学的还不错,能看懂,具体算法有兴趣的可以查一下,能看懂就行,我也不想着自己编了,将algorithm.cpp重命名为algorithm.c。还有MAX30102两个文件,是MAX30102的驱动程序,总结起来就是初始化一个IIC,然后传输数据完成配置再使用IIC进行读取,接下来是具体的分析

 3. 首先在工程中加入I2C bus device,具体什么环境按照相应方法配置,env或者RT-Thread Studio。在rtconfig.h加入

#define BSP_USING_I2C2

/* Notice: PH4 --> 116; PH5 --> 117 */

#define BSP_I2C2_SCL_PIN 116
#define BSP_I2C2_SDA_PIN 117

打开I2C,并且配置了SCL和SDA的引脚,编译工程下载,使用串口助手连接板子的串口1,在msh中输入list_device查看设备,有i2c2证明i2c2配置成功

 

 4.  接下来就是MAX30102驱动重写

      MAX30102.h直接搬来使用,重命名为max30102.h,但是其中的MAX30102的IIC从机地址需要更改,原先是给出写地址和读地址,但在RT-Thread中会自动移位,读操作会加一,这个文章有介绍,所以0xAE和0xAF右移一位就是0x57

/*
#define I2C_WRITE_ADDR 	0xAE//	0B 1010 1110 >>1 0B 0101 0111
#define I2C_READ_ADDR 	0xAF// 	0B 1010 1111
*/
#define MAX30102_ADDR 0x57

      MAX30102.cpp重命名为max30102.c,将里边的IIC的发送接受函数更改为RT-Thread的IIC API,结果如下

/*
 * Copyright (c) 2006-2020, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       		Notes
 * 2020-03-30     NUAAQIANJIN       the first version
 */
#include "max30102.h"



rt_err_t maxim_max30102_write_reg(struct rt_i2c_bus_device *bus, rt_uint8_t i2c_addr,
                                        rt_uint8_t i2c_data)
{
    struct rt_i2c_msg msgs;
    rt_uint8_t send_data[2];
    send_data[0] = i2c_addr;
    send_data[1] = i2c_data;

    msgs.addr = MAX30102_ADDR;
    msgs.flags= RT_I2C_WR;
    msgs.buf  = send_data;
    msgs.len  = 2;
    if(rt_i2c_transfer(bus, &msgs, 1) == 1)
    {
        return RT_EOK;
    }else {
        return RT_ERROR;
    }

}

rt_err_t maxim_max30102_read_reg(struct rt_i2c_bus_device *bus, rt_uint8_t i2c_addr,
                              rt_uint8_t* i2c_data)

{
    struct rt_i2c_msg msgs[2];
    rt_uint8_t send_data;
    send_data = i2c_addr;

    msgs[0].addr = MAX30102_ADDR;
    msgs[0].flags= RT_I2C_WR;
    msgs[0].buf  = &send_data;
    msgs[0].len  = 1;


    msgs[1].addr = MAX30102_ADDR;
    msgs[1].flags= RT_I2C_RD;
    msgs[1].buf  = i2c_data;
    msgs[1].len  = 1;

    if(rt_i2c_transfer(bus, msgs, 2) == 2)
    {
        return RT_EOK;
    }else {
        return RT_ERROR;
    }

}

//读取信息,并转化为最终的数值
rt_err_t maxim_max30102_read_fifo(struct rt_i2c_bus_device *bus,
                            rt_uint32_t *pun_red_led, rt_uint32_t *pun_ir_led)

{
    rt_uint32_t   un_temp;
    rt_uint8_t    uch_temp;
    rt_uint8_t ach_i2c_data[6];
    struct rt_i2c_msg msgs[2];

    *pun_red_led = 0;
    *pun_ir_led  = 0;


    //read and clear status register
    maxim_max30102_read_reg(bus, REG_INTR_STATUS_1, &uch_temp);
    maxim_max30102_read_reg(bus, REG_INTR_STATUS_2, &uch_temp);

    uch_temp = REG_FIFO_DATA;
    msgs[0].addr = MAX30102_ADDR;
    msgs[0].flags = RT_I2C_WR;
    msgs[0].buf   = &uch_temp;
    msgs[0].len   = 1;

    msgs[1].addr = MAX30102_ADDR;
    msgs[1].flags= RT_I2C_RD;
    msgs[1].buf  = ach_i2c_data;
    msgs[1].len  = 6;

    if(rt_i2c_transfer(bus, msgs, 2) != 2)
    {
        return RT_ERROR;
    }

	rt_kprintf("%s\n", (char *)ach_i2c_data);
    un_temp = (rt_uint32_t) ach_i2c_data[0];
    un_temp <<= 16;
    *pun_red_led += un_temp;
    un_temp = (rt_uint32_t) ach_i2c_data[1];
    un_temp <<= 8;
    *pun_red_led += un_temp;
    un_temp = (rt_uint32_t) ach_i2c_data[2];
    *pun_red_led += un_temp;

    un_temp = (rt_uint32_t) ach_i2c_data[3];
    un_temp <<= 16;
    *pun_ir_led += un_temp;
    un_temp = (rt_uint32_t) ach_i2c_data[4];
    un_temp <<= 8;
    *pun_ir_led += un_temp;
    un_temp = (rt_uint32_t) 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]

    return RT_EOK;
}




rt_err_t maxim_max30102_reset(struct rt_i2c_bus_device *bus)
{
    if(maxim_max30102_write_reg(bus, REG_MODE_CONFIG,0x40) == 1)
        return RT_EOK;
    else
        return RT_ERROR;
}



rt_err_t maxim_max30102_init(struct rt_i2c_bus_device *bus)
{
    rt_uint8_t temp;
//	rt_thread_mdelay(2);
	
	// INTR setting
    if(maxim_max30102_write_reg(bus, REG_INTR_ENABLE_1,0xc0) == RT_ERROR) 
        return RT_ERROR;
	maxim_max30102_read_reg(bus, REG_INTR_ENABLE_1, &temp);
	rt_kprintf("REG_INTR_ENABLE_1: %x\n", temp);
	
//	rt_thread_mdelay(2);
    if(maxim_max30102_write_reg(bus, REG_INTR_ENABLE_2,0x00) == RT_ERROR)
        return RT_ERROR;
	maxim_max30102_read_reg(bus, REG_INTR_ENABLE_2, &temp);
	rt_kprintf("REG_INTR_ENABLE_2: %x\n", temp);

//	rt_thread_mdelay(2);	
	//FIFO_WR_PTR[4:0]
    if(maxim_max30102_write_reg(bus, REG_FIFO_WR_PTR,0x00) == RT_ERROR)  
        return RT_ERROR;
	maxim_max30102_read_reg(bus, REG_FIFO_WR_PTR, &temp);
	rt_kprintf("REG_FIFO_WR_PTR: %x\n", temp);
	
//	rt_thread_mdelay(2);
	//OVF_COUNTER[4:0]
    if(maxim_max30102_write_reg(bus, REG_OVF_COUNTER,0x00) == RT_ERROR)  
        return RT_ERROR;
	maxim_max30102_read_reg(bus, REG_OVF_COUNTER, &temp);
	rt_kprintf("REG_OVF_COUNTER: %x\n", temp);

//	rt_thread_mdelay(2);
	//FIFO_RD_PTR[4:0
    if(maxim_max30102_write_reg(bus, REG_FIFO_RD_PTR,0x00) == RT_ERROR)
        return RT_ERROR;
	maxim_max30102_read_reg(bus, REG_FIFO_RD_PTR, &temp);
	rt_kprintf("REG_FIFO_RD_PTR: %x\n", temp);
	
//	rt_thread_mdelay(2);
	//sample avg = 1, fifo rollover=false, fifo almost full = 17
    if(maxim_max30102_write_reg(bus, REG_FIFO_CONFIG,0x0f) == RT_ERROR)
        return RT_ERROR;
	maxim_max30102_read_reg(bus, REG_FIFO_CONFIG, &temp);
	rt_kprintf("REG_FIFO_CONFIG: %x\n", temp);
	
//	rt_thread_mdelay(2);
	//0x02 for Red only, 0x03 for SpO2 mode 0x07 multimode LED
    if(maxim_max30102_write_reg(bus, REG_MODE_CONFIG,0x03) == RT_ERROR)   
        return RT_ERROR;
	maxim_max30102_read_reg(bus, REG_MODE_CONFIG, &temp);
	rt_kprintf("REG_MODE_CONFIG: %x\n", temp);

//	rt_thread_mdelay(2);
	// SPO2_ADC range = 4096nA, SPO2 sample rate (100 Hz), LED pulseWidth (400uS)
    if(maxim_max30102_write_reg(bus, REG_SPO2_CONFIG,0x27) == RT_ERROR)  
        return RT_ERROR;
	maxim_max30102_read_reg(bus, REG_SPO2_CONFIG, &temp);
	rt_kprintf("REG_SPO2_CONFIG: %x\n", temp);
	
//	rt_thread_mdelay(2);
	//Choose value for ~ 7mA for LED1
    if(maxim_max30102_write_reg(bus, REG_LED1_PA,0x24) == RT_ERROR)   
        return RT_ERROR;
	maxim_max30102_read_reg(bus, REG_LED1_PA, &temp);
	rt_kprintf("REG_LED1_PA: %x\n", temp);
	
//	rt_thread_mdelay(2);
	// Choose value for ~ 7mA for LED2
    if(maxim_max30102_write_reg(bus, REG_LED2_PA,0x24) == RT_ERROR)   
        return RT_ERROR;
	maxim_max30102_read_reg(bus, REG_LED2_PA, &temp);
	rt_kprintf("REG_LED2_PA: %x\n", temp);
	
//	rt_thread_mdelay(2);
	// Choose value for ~ 25mA for Pilot LED
    if(maxim_max30102_write_reg(bus, REG_PILOT_PA,0x7f) == RT_ERROR)   
        return RT_ERROR;
	maxim_max30102_read_reg(bus, REG_PILOT_PA, &temp);
	rt_kprintf("REG_PILOT_PA: %x\n", temp);

//	rt_thread_mdelay(2);
    maxim_max30102_read_reg(bus, 0, &temp);
	rt_kprintf("%x\n", temp);
    return RT_EOK;
}



     在rt_err_t maxim_max30102_init(struct rt_i2c_bus_device *bus)函数中,将写入寄存器的配置值重新读出并打印,以验证是否写入正常且正确,调试使用,完成调试完毕可以直接注释掉。接下来修改max30102.h的函数声明就可以了,修改后为

/*
 * Copyright (c) 2006-2020, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2020-03-30     NUAAQIANJIN       the first version
 */
#ifndef APPLICATIONS_MAX30102_H_
#define APPLICATIONS_MAX30102_H_

#include <rtthread.h>
#include <rtdevice.h>
/*
#define I2C_WRITE_ADDR 	0xAE//	0B 1010 1110 >>1 0B 0101 0111
#define I2C_READ_ADDR 	0xAF// 	0B 1010 1111
*/
#define MAX30102_ADDR 0x57

//register addresses
#define REG_INTR_STATUS_1 0x00
#define REG_INTR_STATUS_2 0x01
#define REG_INTR_ENABLE_1 0x02
#define REG_INTR_ENABLE_2 0x03
#define REG_FIFO_WR_PTR 0x04
#define REG_OVF_COUNTER 0x05
#define REG_FIFO_RD_PTR 0x06
#define REG_FIFO_DATA 0x07
#define REG_FIFO_CONFIG 0x08
#define REG_MODE_CONFIG 0x09
#define REG_SPO2_CONFIG 0x0A
#define REG_LED1_PA 0x0C
#define REG_LED2_PA 0x0D
#define REG_PILOT_PA 0x10
#define REG_MULTI_LED_CTRL1 0x11
#define REG_MULTI_LED_CTRL2 0x12
#define REG_TEMP_INTR 0x1F
#define REG_TEMP_FRAC 0x20
#define REG_TEMP_CONFIG 0x21
#define REG_PROX_INT_THRESH 0x30
#define REG_REV_ID 0xFE
#define REG_PART_ID 0xFF

rt_err_t maxim_max30102_init(struct rt_i2c_bus_device *bus);
rt_err_t maxim_max30102_read_reg(struct rt_i2c_bus_device *bus, rt_uint8_t i2c_addr,
                                        rt_uint8_t* i2c_data);
rt_err_t maxim_max30102_write_reg(struct rt_i2c_bus_device *bus, rt_uint8_t i2c_addr,
                                        rt_uint8_t i2c_data);
rt_err_t maxim_max30102_read_fifo(struct rt_i2c_bus_device *bus,
                            rt_uint32_t *pun_red_led, rt_uint32_t *pun_ir_led);
rt_err_t maxim_max30102_reset(struct rt_i2c_bus_device *bus);

//bool maxim_max30102_init();
//bool maxim_max30102_read_fifo(uint32_t *pun_red_led, uint32_t *pun_ir_led);
//bool maxim_max30102_write_reg(uint8_t uch_addr, uint8_t uch_data);
//bool maxim_max30102_read_reg(uint8_t uch_addr, uint8_t *puch_data);
//bool maxim_max30102_reset(void);

#endif /* APPLICATIONS_MAX30102_H_ */

 5. 接下来写一个简单的测试应用就可以了,

#include <rtthread.h>
#include <rtdevice.h>
#include "max30102.h"

#define MAX30102_I2C_NAME "i2c2"
#define THREAD_PRIORITY         25
#define THREAD_STACK_SIZE       512
#define THREAD_TIMESLICE        5

static rt_thread_t max30102_test_thread = RT_NULL;
static struct rt_i2c_bus_device *i2c_bus = RT_NULL;

/* 线程 1 的入口函数 */
static void max30102_entry(void *parameter)
{
    //rt_uint32_t count = 0;
    rt_uint32_t pun_red_led,
                pun_ir_led;
	rt_uint8_t temp = 0;
//	maxim_max30102_reset(i2c_bus);
    while (1)
    {
        

        while (1)
        {
            maxim_max30102_read_fifo(i2c_bus, &pun_red_led, &pun_ir_led);
			rt_kprintf("red = %i,", pun_red_led);
			rt_kprintf("\tir = %i,", pun_ir_led);
			maxim_max30102_read_reg(i2c_bus, 0, &temp);
			rt_kprintf("\ttemp = %x\n", temp);
            rt_thread_mdelay(10);
        }
    }
}

int max30102_sample(void)
{
    rt_uint8_t result;

    /* set LED0 pin mode to output */
    i2c_bus = (struct rt_i2c_bus_device *)rt_device_find(MAX30102_I2C_NAME);
    if (i2c_bus == RT_NULL)
    {
        rt_kprintf("can't find max30102 i2c device!\n");
        return RT_ERROR;
    }

	
    result = maxim_max30102_init(i2c_bus);
    if (result == RT_ERROR)
    {
        rt_kprintf("init max30102 failed \n");
        return RT_ERROR;
    }
    max30102_test_thread = rt_thread_create("max30102",
                            max30102_entry, RT_NULL,
                            THREAD_STACK_SIZE,
                            THREAD_PRIORITY, THREAD_TIMESLICE);
    if (max30102_test_thread != RT_NULL)
    {
        rt_thread_startup(max30102_test_thread);
    }
    else {
        return RT_ERROR;
    }
    return RT_EOK;
}
MSH_CMD_EXPORT(max30102_sample, max30102 sample);

     编译烧写看结果,很明显,写入寄存器的配置值正确,读取也有结果,这些是空状态下的测量值,并且也没有进行算法上的处理,在空状态下也有数值的,但是这些数都比较小,正常测量下这两个red和ir的值在一万多。

    接下来,创建个缓冲却,调用algorithm.c的函数处理一下也就可以了,想要更高效一些可以注册一个GPIO,设置中断,连到MAX30102的INT引脚,在中断中读取数据存到缓存区,满了后就调用algorithm.c中的函数处理,这里就不再赘述了

发布了7 篇原创文章 · 获赞 13 · 访问量 4085

猜你喜欢

转载自blog.csdn.net/qq_38784061/article/details/105372394