本次Let's Do的项目使用的是ADI公司的MAX78000作为主控,使用max30102进行数据采集并在OLED上显示相关信息。
过程贴通过一个硬件I2C,一个模拟I2C进行两个设备的显示,成果贴将修改成共用I2C模式。

点灯任务比较简单,就不在这边再进行复述,具体可以看第二期-智能手环DIY开发环境搭建
OLED的驱动和心率血氧的读取主要是学习I2C的使用,特别是一个I2C通过不同地址选取不同设备。
mxc_i2c_req_t req; req.i2c = MXC_I2C1; // 根据你的硬件选择 req.addr = SSD1306_ADDR;或者MAX30102_ADDR
下面是MAX30102相关读取命令代码
// ---------- 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
#define MA4_SIZE 4
#define HAMMING_SIZE 5
uint32_t red_buffer[BUFFER_SIZE];
uint32_t ir_buffer[BUFFER_SIZE];
int buffer_index = 0;
// 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;
}最后再处理一下显示
if (hr_valid && heart_rate > 40 && heart_rate < 180)
                {
                    printf("Heart Rate = %d BPM, ", heart_rate);
                    SSD1306_Print(0, 0, "Let's Do EEPW");
                    SSD1306_Print(0, 2, "HR:");
                    SSD1306_Print(30, 2, "     ");
                    SSD1306_PrintInt(30, 2, heart_rate);
                    SSD1306_Print(80, 2, "BPM");
                }
                else
                {
                    printf("Heart Rate = --- , ");
                    SSD1306_Print(0, 0, "Let's Do EEPW");
                    SSD1306_Print(0, 2, "HR:  ---");
                    SSD1306_Print(80, 2, "BPM");
                }
                // 打印血氧
                if (spo2_valid && spo2 > 70 && spo2 <= 100)
                {
                    printf("SpO2 = %d%%\n", spo2);
                    SSD1306_Print(0, 4, "SpO2:");
                    SSD1306_Print(40,4, "     ");
                    SSD1306_PrintInt(40, 4, spo2);
                    SSD1306_Print(80, 4, "%");  
                }
                else
                {
                    printf("SpO2 = ---\n");
                    SSD1306_Print(0, 4, "SpO2:  ---");
                    SSD1306_Print(80, 4, "%");  
                }演示视频:
总结:
通过本次项目,学习了ADI的单片机,包括环境搭建,外设使用,也学习了I2C复用,很感谢EEPW提供的学习机会,官方老师的教程也很详细,再次感谢老师的付出。
项目代码:

 
					
				
 
			
			
			
						
			 
					
				 我要赚赏金
 我要赚赏金 STM32
STM32 MCU
MCU 通讯及无线技术
通讯及无线技术 物联网技术
物联网技术 电子DIY
电子DIY 板卡试用
板卡试用 基础知识
基础知识 软件与操作系统
软件与操作系统 我爱生活
我爱生活 小e食堂
小e食堂

