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

Izellah ·
更新时间:2024-11-10
· 610 次阅读

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 #include /* #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 #include #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中的函数处理,这里就不再赘述了


作者:nuaa_qianjin



心率 max rt-thread 参数 apollo

需要 登录 后方可回复, 如果你还没有账号请 注册新账号