本篇我们来点亮iic协议的心率血氧模块(主控MAX30102)。

接线跟OLED类似,模块的iic SCL、SDA线分别对接开发板的SCL(P0.16)、SDA(P0.17),也是开发板的i2c1模块。
涉及到MAX30102的函数接口,都已经定义在MAX30102.c和MAX30102.h中
我们先了解下MAX30102.h中提供了哪些函数
#include <stdint.h> #ifndef MAX30102_H_ #define MAX30102_H_ #endif /* MAX30102_H_ */ // ---------- MAX30102 寄存器 ---------- #define MAX30102_ADDR 0x57 // 7-bit I2C 地址 #define REG_INT_STATUS_1 0x00 #define REG_INT_ENABLE_1 0x02 #define REG_FIFO_WR_PTR 0x04 #define REG_OVF_COUNTER 0x05 #define REG_FIFO_RD_PTR 0x06 #define REG_FIFO_DATA 0x07 #define REG_MODE_CONFIG 0x09 #define REG_SPO2_CONFIG 0x0A #define REG_LED1_PA 0x0C // RED LED #define REG_LED2_PA 0x0D // IR LED #define REG_TEMP_INTEGER 0x1F #define REG_TEMP_FRACTION 0x20 #define REG_PART_ID 0xFF int max30102_write_reg(uint8_t reg, uint8_t value); //写寄存器 int max30102_read_reg(uint8_t reg, uint8_t *value); //读寄存器 int max30102_read_multi(uint8_t reg, uint8_t *buf, int len); //读多个字节 int max30102_init(void); //max30102初始化 int max30102_read_fifo(uint32_t *red, uint32_t *ir); //读max30102的fifo
具体的实现都在MAX30102.c中
其实可以看到写寄存器与读寄存器是同一个函数,
只不过写寄存器的函数
req.rx_buf = NULL;
req.rx_len = 0;
读寄存器的函数,先发送再接收
req.tx_buf = NULL;
req.tx_len = 0;
#include "MAX30102.h"
#include "mxc_device.h"
#include "mxc_delay.h"
#include "i2c.h"
#include <stdio.h>
#include <string.h>
#include "algorithm.h"
#include "ssd1306.h"
#define MA4_SIZE 4
#define HAMMING_SIZE 5
// I2C 使用 I2C1
#define I2C_INST MXC_I2C1
// ---------------- I2C 基础读写函数 ----------------
int max30102_write_reg(uint8_t reg, uint8_t value)
{
uint8_t data[2] = {reg, value};
mxc_i2c_req_t req;
req.i2c = I2C_INST;
req.addr = MAX30102_ADDR;
req.tx_buf = data;
req.tx_len = 2;
req.rx_buf = NULL;
req.rx_len = 0;
req.restart = 0;
req.callback = NULL;
return MXC_I2C_MasterTransaction(&req);
}
int max30102_read_reg(uint8_t reg, uint8_t *value)
{
mxc_i2c_req_t req;
int err;
req.i2c = I2C_INST;
req.addr = MAX30102_ADDR;
req.tx_buf = ®
req.tx_len = 1;
req.rx_buf = NULL;
req.rx_len = 0;
req.restart = 1; // Repeated START
req.callback = NULL;
err = MXC_I2C_MasterTransaction(&req);
if (err != E_NO_ERROR)
return err;
req.tx_buf = NULL;
req.tx_len = 0;
req.rx_buf = value;
req.rx_len = 1;
req.restart = 0;
req.callback = NULL;
return MXC_I2C_MasterTransaction(&req);
}
int max30102_read_multi(uint8_t reg, uint8_t *buf, int len)
{
mxc_i2c_req_t req;
int err;
req.i2c = I2C_INST;
req.addr = MAX30102_ADDR;
req.tx_buf = ®
req.tx_len = 1;
req.rx_buf = NULL;
req.rx_len = 0;
req.restart = 1;
req.callback = NULL;
err = MXC_I2C_MasterTransaction(&req);
if (err != E_NO_ERROR)
return err;
req.tx_buf = NULL;
req.tx_len = 0;
req.rx_buf = buf;
req.rx_len = len;
req.restart = 0;
req.callback = NULL;
return MXC_I2C_MasterTransaction(&req);
}
// ---------------- MAX30102 初始化 ----------------
int max30102_init(void)
{
int ret;
uint8_t part_id;
MXC_I2C_Shutdown(I2C_INST);
MXC_I2C_Init(I2C_INST, 1, 0); // master mode
MXC_I2C_SetFrequency(I2C_INST, 400000); // 400kHz
ret = max30102_read_reg(REG_PART_ID, &part_id);
if (ret != E_NO_ERROR)
{
printf("I2C read failed\n");
return ret;
}
if (part_id != 0x15)
{
printf("MAX30102 not found! Part ID: 0x%02X\n", part_id);
return -1;
}
printf("MAX30102 detected, Part ID: 0x%02X\n", part_id);
max30102_write_reg(REG_MODE_CONFIG, 0x40);
MXC_Delay(MXC_DELAY_MSEC(10));
max30102_write_reg(REG_MODE_CONFIG, 0x03); // SpO2 模式
max30102_write_reg(REG_SPO2_CONFIG, 0x27); // 18-bit, 100Hz
max30102_write_reg(REG_LED1_PA, 0x24); // 红光 ~7mA
max30102_write_reg(REG_LED2_PA, 0x24); // IR ~7mA
return 0;
}
// ---------------- 读取 FIFO 数据 ----------------
int max30102_read_fifo(uint32_t *red, uint32_t *ir)
{
uint8_t buf[6];
int ret = max30102_read_multi(REG_FIFO_DATA, buf, 6);
if (ret != E_NO_ERROR)
return ret;
*red = ((uint32_t)buf[0] << 16) | ((uint32_t)buf[1] << 8) | buf[2];
*ir = ((uint32_t)buf[3] << 16) | ((uint32_t)buf[4] << 8) | buf[5];
*red &= 0x3FFFF; // 18-bit
*ir &= 0x3FFFF;
return 0;
}在main.c函数中,我们看下
int main(void)
{
uint32_t red, ir;
if (max30102_init() != 0) //初始化
{
printf("MAX30102 init failed!\n");
return 0;
}
while (1)
{
if (max30102_read_fifo(&red, &ir) == 0) //读fifo
{
red_buffer[buffer_index] = red;
ir_buffer[buffer_index] = ir;
buffer_index++;
if (buffer_index >= BUFFER_SIZE)
{
int32_t spo2, heart_rate;
int8_t spo2_valid, hr_valid;
// 调用算法
maxim_heart_rate_and_oxygen_saturation(
ir_buffer, BUFFER_SIZE,
red_buffer,
&spo2, &spo2_valid,
&heart_rate, &hr_valid);
// 打印心率
if (hr_valid && heart_rate > 40 && heart_rate < 180) //40~180
{
printf("心率 = %d BPM, ", heart_rate);
}
else
{
printf("心率 = --- , ");
}
// 打印血氧
if (spo2_valid && spo2 > 70 && spo2 <= 100)
{
printf("血氧 = %d%%\n", spo2);
}
else
{
printf("血氧 = ---\n");
}
// 重置 buffer
buffer_index = 0;
}
}
// 100Hz 采样
MXC_Delay(MXC_DELAY_MSEC(5));
}
}其实就是
初始化MAX30102
读取MAX30102的fifo,赋值red值及ir值
一直读取到Buffer size(800个)
调用算法,将red值及ir值通过运算转化为心率和血氧,具体算法参考官方示例,这里就不作具体展示了
5. 通过串口打印出心率和血氧
运行结果如下:

模块亮红灯。串口输出心率和血氧数据。
ua
我要赚赏金
