本次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提供的学习机会,官方老师的教程也很详细,再次感谢老师的付出。
项目代码: