这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » DIY与开源设计 » 电子DIY » 第二期-智能手环DIY活动-成果贴(基础任务)

共1条 1/1 1 跳转至

第二期-智能手环DIY活动-成果贴(基础任务)

工程师
2025-10-05 17:19:38     打赏

本次Let's Do的项目使用的是ADI公司的MAX78000作为主控,使用max30102进行数据采集并在OLED上显示相关信息。

过程贴通过一个硬件I2C,一个模拟I2C进行两个设备的显示,成果贴将修改成共用I2C模式。

1759650831142896.png

点灯任务比较简单,就不在这边再进行复述,具体可以看第二期-智能手环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提供的学习机会,官方老师的教程也很详细,再次感谢老师的付出。


项目代码:

xinlv.zip


共1条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]