这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » DIY与开源设计 » 电子DIY » 【Let'sdo第2期-DIY智能手环】成果贴-心率血氧采集并显示

共2条 1/1 1 跳转至

【Let'sdo第2期-DIY智能手环】成果贴-心率血氧采集并显示

菜鸟
2025-10-19 23:56:26     打赏

首先来张硬件全家福,运行结果和软件项目构成图。

image.png

image.png

image.png


硬件上主要用了以下几个模块:

  1. 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灯。

image.png

第二张图是U8G2驱动库的移植。可详见过程贴【“Let'sdo“活动第二期-DIY智能手环】过程贴-电子产品世界论坛 (eepw.com.cn)

image.png

第三张图是驱动 MAX30102 传感器,采集数据并通过分析,生成血氧、心率,显示 到 OLED 屏幕中

image.png

下面是主要的功能代码。

在数据结构上,定义了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;
}



高工
2025-10-20 07:27:53     打赏
2楼

字体弄得挺好的。


共2条 1/1 1 跳转至

回复

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