HB-T03 心率血氧传感器模块集成 MAX30102 芯片,通过光电容积脉搏波描记技术实现非侵入式心率与血氧测量
HB-T03 模块的核心传感芯片为 MAX30102,这是一颗集成了红光 LED(660nm)、红外光 LED(880nm)、光电检测器、光学元件和低噪声电子电路的脉搏血氧和心率监测传感器。其测量原理基于光电容积脉搏波描记法(PPG, Photoplethysmography)。
血氧饱和度(SpO2)测量原理:血液中的血红蛋白对红光和红外光的吸收特性不同。氧合血红蛋白(HbO2)对红外光吸收更多,而脱氧血红蛋白(Hb)对红光吸收更多。MAX30102 交替发射红光和红外光,通过光电检测器测量透射/反射光的强度比值(R值),再经过标定曲线计算出血氧饱和度百分比。
心率测量原理:心脏每次跳动时,动脉血液容积会周期性变化,导致光电检测器接收到的光信号产生周期性脉动。通过对这个脉动信号进行滤波和峰值检测,即可计算出心率(BPM, Beats Per Minute)。
HB-T03 模块的优势:模块内置 HC32F003 单片机,自动读取 MAX30102 原始 ADC 数据,经过 FIR 滤波和心率/血氧算法处理后输出量化的心率与血氧值。用户只需通过简单的 I2C 或 UART 接口读取数据,无需关心复杂的信号处理和算法实现。
实时检测心脏跳动频率,输出心率值(BPM),测量范围覆盖静息至运动状态
通过红光/红外光双波长比值法计算血氧饱和度(SpO2),反映血液携氧能力
模块集成 HC32F003 MCU,自动完成 FIR 滤波与数据计算,输出即用型量化数值
支持 I2C(默认,地址 0x55)和 UART(9600bps)两种通信模式,灵活适配不同主控
| 引脚号 | 名称 | 功能定义 | STM32 连接 |
|---|---|---|---|
| JP1-1 | +5V | +5V 电源供电 | 5V 电源 |
| JP1-2 | GND | 电源地 | GND |
| JP1-3 | SCL | I2C 时钟信号 / UART TXD | PB0 |
| JP1-4 | SDA | I2C 数据信号 / UART RXD | PB1 |
⚠ 模块无防反接保护,上电前务必核对极性。手指放置时间需≥2秒,保持手部温暖以获得准确读数。
HB-T03 模块采用黑色 PCB 设计,尺寸仅 23×15mm,正面集成 MAX30102 传感芯片,背面标注接口定义
模块原理图与 PCB 封装设计参考
基于 STM32F103C8T6 + HAL 库开发,使用软件模拟 I2C 通信读取 HB-T03 传感器数据并在 OLED 显示
配置 HSE 外部高速晶振(8MHz),通过 PLL 倍频至 72MHz 作为系统时钟。APB1 总线 36MHz,APB2 总线 72MHz。
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(); }
使用 GPIO 模拟 I2C 通信。SCL 配置为推挽输出(PB0),SDA 配置为开漏输出(PB1),通信速率为 100kHz(10μs 延时)。HB-T03 模块的器件地址为 0x55。
/* 引脚定义: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(); }
实现 I2C 起始信号、停止信号、字节发送/接收、ACK/NACK 应答等底层时序函数。每个位操作间隔 10μs,对应约 100kHz 的 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; }
通过 I2C 从 HB-T03 模块(地址 0x55)的寄存器 0x00 连续读取 5 字节数据。前 4 字节为血氧值(float 类型),第 5 字节为心率值(unsigned char 类型)。数据使用 union 结构实现字节与浮点数的自动转换。
/* 从指定器件地址读取多字节数据 */ 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); }
使用 union 联合体实现原始字节数组与解析后数据的内存共享。5 字节数据布局:字节 [0-3] 为血氧浮点数,字节 [4] 为心率整型值。
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 - 0x03 | 4 字节 | float | 血氧饱和度(%) | max30102.par.sop2 |
| 0x04 | 1 字节 | unsigned char | 心率值(BPM) | max30102.par.hr |
主函数初始化 OLED 和 MAX30102 后进入 while(1) 主循环。使用 HAL_GetTick() 实现非阻塞式 1 秒定时,每秒读取一次传感器数据并刷新 OLED 显示。
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(); // 更新时间戳 } } }
程序从初始化到主循环的完整执行流程,虚线框内为 while(1) 循环体
模拟手指放置在传感器上后,MAX30102 红光/红外光交替工作,实时输出心率与血氧数据