首先来张硬件全家福,运行结果和软件项目构成图。
硬件上主要用了以下几个模块:
MAX78000开发板 2.MAXREFDES117开发板 3. OLED模块 4.转接底板5.USB转mini usb数据线(和stlink相连)
软件上,使用VSCODE作为开发环境,依托于ADI提供的maximsdk 的固件进行开发。
电路中使用一个软件模拟IIC用来驱动OLED,使用硬件IIC来驱动max30102,使用一个IO控制RGB中的R灯。
max78000开发板资源分配如下:
IO分配 | 信号 | IO方向 | IO功能 |
P2.3 | OLED_SDA | IO | OLED模拟IIC数据 |
P2.4 | OLED_SCL | O | oled模拟IIC时钟 |
P0.16 | IIC1_SCL1 | 0 | max30102 IIC时钟 |
P0.17 | IIC1_SDA1 | IO | max30102 IIC数据 |
P0.19 | max30102 INT | I | 中断信号 |
P2.0 | RGB R灯 | O | R灯控制信号 |
实现的任务:
1、使用 eclipse maximsdk 的固件,学会点亮 RGB 灯
2、实现 OLED 屏幕显示信息
3、驱动 MAX30102 传感器,采集数据并通过分析,生成血氧、心率,显示 到 OLED 屏幕中
下面是成果。
第一张图是驱动RGB中的R灯。
第二张图是U8G2驱动库的移植。可详见过程贴【“Let'sdo“活动第二期-DIY智能手环】过程贴-电子产品世界论坛 (eepw.com.cn)
第三张图是驱动 MAX30102 传感器,采集数据并通过分析,生成血氧、心率,显示 到 OLED 屏幕中

下面是主要的功能代码。
在数据结构上,定义了u8g2需要的结构体,其余则是心率和血氧的采集和计算过程。这部分参考了论坛网友的一些算法,但感觉结果不太准,因此这里就简单描述一下。在过程贴中有采集的原始数据,估计还是要一些更合适的算法才能计算得到较好的结果。
/***** Includes *****/ #include <stdio.h> #include <stdint.h> #include <string.h> #include "board.h" #include "mxc_device.h" #include "mxc_delay.h" #include "nvic_table.h" #include "i2c.h" #include "max30102.h" #include "max7800_u8g2.h" #include "pb.h" #include "gpio.h" #include "u8g2.h" /***** Globals *****/ uint8_t counter = 0; char buff[20]; #define MXC_GPIO_PORT_OUT MXC_GPIO2 #define MXC_GPIO_PIN_OUT MXC_GPIO_PIN_0 uint8_t spo2Data=0; // 血氧数据 uint8_t heartData=0; // 心率数据 u8g2_t u8g2; void max30102_i2c_init(void) { int error = MXC_I2C_Init(I2C_MASTER, 1, 0); if (error != E_NO_ERROR) { printf("I2C init fail:%d\n", error); } else { MXC_I2C_SetFrequency(I2C_MASTER, I2C_FREQ); printf("I2C init success\n"); } } int main(void) { unsigned int flag = 0; mxc_gpio_cfg_t gpio_out; IIC_Init(); //Init I2C pins u8g2Init(&u8g2); draw(&u8g2); MXC_Delay(1000000); /* Setup output pin. */ gpio_out.port = MXC_GPIO_PORT_OUT; gpio_out.mask = MXC_GPIO_PIN_OUT; gpio_out.pad = MXC_GPIO_PAD_NONE; gpio_out.func = MXC_GPIO_FUNC_OUT; MXC_GPIO_Config(&gpio_out); u8g2_SetFont(&u8g2, u8g2_font_sirclivethebold_tr); //u8g2_font_smart_patrol_nbp_tr u8g2_ClearBuffer(&u8g2); u8g2_SetFontRefHeightText(&u8g2); u8g2_SetFontPosTop(&u8g2); //u8g2_DrawStr(&u8g2, 0, 30, "u8g2 Soft I2C"); // u8g2_SendBuffer(&u8g2); max30102_i2c_init(); if (max30102_init() != E_NO_ERROR) { printf("MAX30102 init failed\n"); u8g2_DrawStr(&u8g2, 0, 30, "MAX30102 init failed"); u8g2_SendBuffer(&u8g2); } else{ printf("MAX30102 init OK\n"); u8g2_DrawStr(&u8g2, 0, 30, "MAX30102 init OK"); u8g2_SendBuffer(&u8g2); } // max30102_read_fifo(&REDDATA, &IRDATA); // u8g2_ClearBuffer(&u8g2); // u8g2_DrawStr(&u8g2, 0, 30, "HR"); // sprintf(buff,"%d%%",REDDATA); // u8g2_DrawStr(&u8g2,80,30,buff); // u8g2_DrawStr(&u8g2, 0, 40, "spo2"); // sprintf(buff,"%d%%",IRDATA); // u8g2_DrawStr(&u8g2,80,40,buff); // u8g2_SendBuffer(&u8g2); while (1) { flag = ~flag; if(flag != 0) { MXC_GPIO_OutSet(gpio_out.port, gpio_out.mask); printf("LED FLAG IS %d,R is OFF\n", flag); MXC_Delay(1000000); //时间不能很短,否则会出错 } else { MXC_GPIO_OutClr(gpio_out.port, gpio_out.mask); printf("LED FLAG IS %d,R is ON\n", flag); MXC_Delay(1000000); } max30102_get(&heartData, &spo2Data); printf("Heart Rate:%d bpm, SpO2:%d%% \n", heartData,spo2Data ); u8g2_ClearBuffer(&u8g2); u8g2_DrawStr(&u8g2, 0, 30, "HR"); sprintf(buff,"%4d",heartData); u8g2_DrawStr(&u8g2,50,30,buff); u8g2_DrawStr(&u8g2, 0, 40, "spo2"); sprintf(buff,"%d%%",spo2Data); u8g2_DrawStr(&u8g2,50,40,buff); u8g2_SendBuffer(&u8g2); } } //max30102.c int max30102_get(uint8_t *HeartRate, uint8_t *spo2Data) { for (int i = 0; i < BUFFER_LENTH; i++) { while (MAX30102_INT == 1) ; // 等待中断引脚 max30102_read_fifo(&aun_red_buffer[i], &aun_ir_buffer[i]); } maxim_heart_rate_and_oxygen_saturation(aun_ir_buffer, BUFFER_LENTH, aun_red_buffer, &n_sp02, &ch_spo2_valid, &n_heart_rate, &ch_hr_valid); // 传入500个心率和血氧数据计算传感器检测结论,反馈心率和血氧测试结果 if ((1 == ch_hr_valid) && (1 == ch_spo2_valid) && (n_heart_rate < 120) && (n_sp02 < 101)) { *HeartRate = n_heart_rate; *spo2Data = n_sp02; return 1; // 有效数据 } return 0; }