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