HB-T03 心率血氧传感器

MAX30102 光电容积脉搏波描记(PPG)技术
STM32F103C8T6 · HAL 库开发 · 软件模拟 I2C
工作电压
5V
通信方式
I2C
器件地址
0x55
模块尺寸
23×15mm
更新周期
1s
向下滚动查看详情 ↓

01. 传感器原理与功能

HB-T03 心率血氧传感器模块集成 MAX30102 芯片,通过光电容积脉搏波描记技术实现非侵入式心率与血氧测量

1工作原理

HB-T03 模块的核心传感芯片为 MAX30102,这是一颗集成了红光 LED(660nm)、红外光 LED(880nm)、光电检测器、光学元件和低噪声电子电路的脉搏血氧和心率监测传感器。其测量原理基于光电容积脉搏波描记法(PPG, Photoplethysmography)。

血氧饱和度(SpO2)测量原理:血液中的血红蛋白对红光和红外光的吸收特性不同。氧合血红蛋白(HbO2)对红外光吸收更多,而脱氧血红蛋白(Hb)对红光吸收更多。MAX30102 交替发射红光和红外光,通过光电检测器测量透射/反射光的强度比值(R值),再经过标定曲线计算出血氧饱和度百分比。

心率测量原理:心脏每次跳动时,动脉血液容积会周期性变化,导致光电检测器接收到的光信号产生周期性脉动。通过对这个脉动信号进行滤波和峰值检测,即可计算出心率(BPM, Beats Per Minute)。

手指 红光 660nm 红外 880nm 光电检测器 PPG信号 →

HB-T03 模块的优势:模块内置 HC32F003 单片机,自动读取 MAX30102 原始 ADC 数据,经过 FIR 滤波和心率/血氧算法处理后输出量化的心率与血氧值。用户只需通过简单的 I2C 或 UART 接口读取数据,无需关心复杂的信号处理和算法实现。

2核心功能

❤️

心率监测

实时检测心脏跳动频率,输出心率值(BPM),测量范围覆盖静息至运动状态

🩸

血氧测量

通过红光/红外光双波长比值法计算血氧饱和度(SpO2),反映血液携氧能力

⚙️

内置算法处理

模块集成 HC32F003 MCU,自动完成 FIR 滤波与数据计算,输出即用型量化数值

📡

双通信接口

支持 I2C(默认,地址 0x55)和 UART(9600bps)两种通信模式,灵活适配不同主控

3引脚定义

引脚定义
引脚号名称功能定义STM32 连接
JP1-1+5V+5V 电源供电5V 电源
JP1-2GND电源地GND
JP1-3SCLI2C 时钟信号 / UART TXDPB0
JP1-4SDAI2C 数据信号 / UART RXDPB1

⚠ 模块无防反接保护,上电前务必核对极性。手指放置时间需≥2秒,保持手部温暖以获得准确读数。

02. 模块外观展示

HB-T03 模块采用黑色 PCB 设计,尺寸仅 23×15mm,正面集成 MAX30102 传感芯片,背面标注接口定义

模块外观
模块正反面对比
模块尺寸
结构尺寸图(23×15mm)

03. 原理图与 PCB 封装

模块原理图与 PCB 封装设计参考

原理图
模块原理图 — MAX30102 传感器 + HC32F003 MCU + 电源转换电路
PCB封装
PCB 封装图 — 4Pin 接口(+5V / GND / SCL / SDA)

04. 程序分析

基于 STM32F103C8T6 + HAL 库开发,使用软件模拟 I2C 通信读取 HB-T03 传感器数据并在 OLED 显示

4.1 系统时钟配置

配置 HSE 外部高速晶振(8MHz),通过 PLL 倍频至 72MHz 作为系统时钟。APB1 总线 36MHz,APB2 总线 72MHz。

Core/Src/main.c — SystemClock_Config()
void SystemClock_Config(void)
{
    RCC_OscInitTypeDef RCC_OscInitStruct = {0};
    RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

    /* HSE 外部晶振 8MHz,PLL 倍频 ×9 = 72MHz */
    RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
    RCC_OscInitStruct.HSEState = RCC_HSE_ON;
    RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
    RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
    RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
    RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;          // 8MHz × 9 = 72MHz

    if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
        Error_Handler();

    /* AHB=72MHz, APB1=36MHz, APB2=72MHz */
    RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK
                                | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2;
    RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
    RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;     // HCLK = 72MHz
    RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;      // APB1 = 36MHz
    RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;      // APB2 = 72MHz

    if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
        Error_Handler();
}

4.2 软件模拟 I2C 引脚初始化

使用 GPIO 模拟 I2C 通信。SCL 配置为推挽输出(PB0),SDA 配置为开漏输出(PB1),通信速率为 100kHz(10μs 延时)。HB-T03 模块的器件地址为 0x55。

HAL/MAX30102/max30102.c — I2C_Init()
/* 引脚定义:SCL=PB0, SDA=PB1 */
#define SCL_PORT     GPIOB
#define SCL_PIN      GPIO_PIN_0
#define SDA_PORT     GPIOB
#define SDA_PIN      GPIO_PIN_1

#define SCL_H()       HAL_GPIO_WritePin(SCL_PORT, SCL_PIN, GPIO_PIN_SET)
#define SCL_L()       HAL_GPIO_WritePin(SCL_PORT, SCL_PIN, GPIO_PIN_RESET)
#define SDA_H()       HAL_GPIO_WritePin(SDA_PORT, SDA_PIN, GPIO_PIN_SET)
#define SDA_L()       HAL_GPIO_WritePin(SDA_PORT, SDA_PIN, GPIO_PIN_RESET)
#define READ_SDA()   HAL_GPIO_ReadPin(SDA_PORT, SDA_PIN)

#define MAX30102_DEVADDR  0x55    // HB-T03 模块器件地址

void I2C_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStruct;

    /* 使能 GPIOB 时钟 */
    __HAL_RCC_GPIOB_CLK_ENABLE();

    /* SCL — 推挽输出 */
    GPIO_InitStruct.Pin   = SCL_PIN;
    GPIO_InitStruct.Mode  = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pull  = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(SCL_PORT, &GPIO_InitStruct);

    /* SDA — 开漏输出 */
    GPIO_InitStruct.Pin   = SDA_PIN;
    GPIO_InitStruct.Mode  = GPIO_MODE_OUTPUT_OD;
    GPIO_InitStruct.Pull  = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(SDA_PORT, &GPIO_InitStruct);

    SCL_H();
    SDA_H();
}

4.3 I2C 通信协议实现

实现 I2C 起始信号、停止信号、字节发送/接收、ACK/NACK 应答等底层时序函数。每个位操作间隔 10μs,对应约 100kHz 的 I2C 速率。

HAL/MAX30102/max30102.c — I2C 时序函数
/* I2C 起始信号:SCL 高电平时 SDA 由高变低 */
static void I2C_Start(void)
{
    SDA_H();
    SCL_H();
    delay10us();
    SDA_L();        // START: CLK 高时 DATA 由高变低
    delay10us();
    SCL_L();        // 钳住总线,准备传输
}

/* I2C 停止信号:SCL 高电平时 SDA 由低变高 */
static void I2C_Stop(void)
{
    SCL_L();
    SDA_L();        // STOP: CLK 高时 DATA 由低变高
    delay10us();
    SCL_H();
    delay10us();
    SDA_H();
    delay10us();
}

/* 发送一个字节(MSB 先发)*/
static void I2C_Send_Byte(uint8_t txd)
{
    uint8_t t;
    SCL_L();
    for(t = 0; t < 8; t++)
    {
        if((txd & 0x80) >> 7)
            SDA_H();
        else
            SDA_L();
        txd <<= 1;
        delay10us();
        SCL_H();
        delay10us();
        SCL_L();
        delay10us();
    }
}

/* 接收一个字节(MSB 先收)*/
static uint8_t I2C_Read_Byte(uint8_t ack)
{
    uint8_t i, receive = 0;
    for(i = 0; i < 8; i++)
    {
        SCL_L();
        delay10us();
        SCL_H();
        receive <<= 1;
        if(READ_SDA())
            receive |= 1;
        delay10us();
    }
    if(!ack) I2C_NAck();  // 最后一个字节发 NACK
    else     I2C_Ack();   // 中间字节发 ACK
    return receive;
}

4.4 传感器数据读取

通过 I2C 从 HB-T03 模块(地址 0x55)的寄存器 0x00 连续读取 5 字节数据。前 4 字节为血氧值(float 类型),第 5 字节为心率值(unsigned char 类型)。数据使用 union 结构实现字节与浮点数的自动转换。

HAL/MAX30102/max30102.c — I2C_Read() & max30102_readData()
/* 从指定器件地址读取多字节数据 */
static uint8_t I2C_Read(uint8_t slaveID, uint8_t address, uint8_t *buf, uint16_t len)
{
    I2C_Start();
    I2C_Send_Byte(slaveID << 1);       // 发送写命令(地址 + W)
    if(I2C_Wait_Ack() == 0)
    {
        I2C_Stop();
        return 0;
    }
    I2C_Send_Byte(address);            // 发送寄存器地址 0x00
    I2C_Wait_Ack();

    I2C_Start();                        // 重复起始信号
    I2C_Send_Byte((slaveID << 1) | 0x01); // 发送读命令(地址 + R)
    I2C_Wait_Ack();
    while(len)
    {
        if(len == 1)
            *buf = I2C_Read_Byte(0); // 最后一个字节 NACK
        else
            *buf = I2C_Read_Byte(1); // 中间字节 ACK
        buf++;
        len--;
    }
    I2C_Stop();
    return 1;
}

/* 读取 HB-T03 传感器数据(5字节)*/
void max30102_readData(void)
{
    I2C_Read(MAX30102_DEVADDR, 0, max30102.tab, 5);
}

4.5 数据结构与寄存器映射

使用 union 联合体实现原始字节数组与解析后数据的内存共享。5 字节数据布局:字节 [0-3] 为血氧浮点数,字节 [4] 为心率整型值。

HAL/MAX30102/max30102.h — 数据结构定义
typedef union {
    uint8_t tab[5];           // 原始字节数组
    struct {
        float sop2;            // 血氧饱和度(4字节,对应寄存器 0x00-0x03)
        unsigned char hr;     // 心率值(1字节,对应寄存器 0x04)
    } par;
} max30102_t;

extern max30102_t max30102;
寄存器地址数据长度数据类型含义访问字段
0x00 - 0x034 字节float血氧饱和度(%)max30102.par.sop2
0x041 字节unsigned char心率值(BPM)max30102.par.hr

4.6 主循环逻辑

主函数初始化 OLED 和 MAX30102 后进入 while(1) 主循环。使用 HAL_GetTick() 实现非阻塞式 1 秒定时,每秒读取一次传感器数据并刷新 OLED 显示。

Core/Src/main.c — main()
uint32_t Time_Now;   // 系统时间戳

int main(void)
{
    HAL_Init();                  // HAL 库初始化
    SystemClock_Config();       // 系统时钟 72MHz
    MX_GPIO_Init();             // GPIO 初始化
    OLED_Init();                // OLED 显示屏初始化(PB14/SDA, PB15/SCL)
    max30102_Init();            // MAX30102 初始化(PB0/SCL, PB1/SDA)

    while (1)
    {
        if(HAL_GetTick() - Time_Now >= 1000)    // 每 1 秒更新一次
        {
            max30102_readData();                       // 读取心率血氧数据

            OLED_Clear();                              // 清屏
            OLED_CenterPrintf(12, "心率:%dbpm", max30102.par.hr);
            OLED_CenterPrintf(36, "血氧:%.1f%%", max30102.par.sop2);
            OLED_Update();                             // 刷新显示

            Time_Now = HAL_GetTick();                    // 更新时间戳
        }
    }
}

05. 程序流程图

程序从初始化到主循环的完整执行流程,虚线框内为 while(1) 循环体

开始 HAL_Init() SystemClock_Config() 72MHz MX_GPIO_Init() OLED_Init() max30102_Init() while(1) HAL_GetTick() - Time_Now ≥ 1000? 否 是 max30102_readData() OLED 显示心率和血氧 Time_Now = HAL_GetTick()

06. 交互式动画演示

模拟手指放置在传感器上后,MAX30102 红光/红外光交替工作,实时输出心率与血氧数据

传感器检测板 — MAX30102
👆
红光 660nm
红外 880nm
点击手指区域放置手指
等待测量...
心率
--
BPM
血氧饱和度
--
%
PPG 脉搏波形
操作说明:点击"开始测量"按钮启动模拟,然后点击传感器区域的手指图标模拟放置手指。模块内置的 HC32F003 会自动采集 MAX30102 原始数据,经过 FIR 滤波和算法处理后,通过 I2C 接口(地址 0x55)输出心率与血氧值。OLED 屏幕每 1 秒刷新一次显示。测量时请保持手指稳定,接触时间不少于 2 秒。