GH3020 是一款低成本、低功耗的心率 / 血氧(SpO2)传感器,核心通信方式为I2C,以下以 STM32F103(最常用的入门款)为例,提供完整的驱动代码和硬件 / 软件说明,新手可直接复用。
一、硬件连接说明
| STM32 引脚 | GH3020 引脚 | 说明 |
|---|---|---|
| PB6 | SCL | I2C 时钟线(上拉 10KΩ) |
| PB7 | SDA | I2C 数据线(上拉 10KΩ) |
| 3.3V | VDD | 电源(请勿接 5V) |
| GND | GND | 接地 |
| PA0(可选) | INT | 数据就绪中断(低电平有效) |
二、软件驱动代码(STM32 标准库)
1. 头文件定义(gh3020.h)
#ifndef __GH3020_H
#define __GH3020_H
#include "stm32f10x.h"
// GH3020 I2C地址(7位地址为0x2F,左移1位后写0x5E,读0x5F)
#define GH3020_I2C_ADDR 0x5E
// GH3020核心寄存器地址
#define GH3020_REG_ID 0x00 // 设备ID寄存器(固定值0x30)
#define GH3020_REG_CTRL1 0x01 // 控制寄存器1(模式配置)
#define GH3020_REG_CTRL2 0x02 // 控制寄存器2(采样率配置)
#define GH3020_REG_HR_DATA 0x03 // 心率数据寄存器(低字节)
#define GH3020_REG_SPO2_DATA 0x05 // 血氧数据寄存器(低字节)
#define GH3020_REG_RED_ADC 0x07 // 红光ADC值(低字节)
#define GH3020_REG_IR_ADC 0x09 // 红外光ADC值(低字节)
// 数据结构体(存储传感器数据)
typedef struct {
uint8_t device_id; // 设备ID
uint16_t heart_rate; // 心率值(bpm)
uint16_t spo2; // 血氧值(%)
uint16_t red_adc; // 红光ADC原始值
uint16_t ir_adc; // 红外光ADC原始值
} GH3020_Data_TypeDef;
// 函数声明
void GH3020_I2C_Init(void); // 初始化STM32的I2C外设
uint8_t GH3020_Check_ID(void); // 检测GH3020是否存在
void GH3020_Init(void); // 初始化GH3020传感器
uint8_t GH3020_Read_Reg(uint8_t reg_addr); // 读取单个寄存器
void GH3020_Write_Reg(uint8_t reg_addr, uint8_t data); // 写入单个寄存器
void GH3020_Read_Data(GH3020_Data_TypeDef *data); // 读取心率/血氧数据
#endif
2. 源文件实现(gh3020.c)
#include "gh3020.h"
#include "delay.h" // 需自行实现延时函数(us/ms级)
// 初始化STM32的I2C1外设(标准模式,100KHz)
void GH3020_I2C_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
I2C_InitTypeDef I2C_InitStruct;
// 使能时钟:I2C1 + GPIOB
RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
// 配置PB6(SCL)、PB7(SDA)为复用开漏输出
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_OD; // 复用开漏
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStruct);
// 初始化I2C1
I2C_InitStruct.I2C_Mode = I2C_Mode_I2C;
I2C_InitStruct.I2C_DutyCycle = I2C_DutyCycle_2; // 占空比2:1
I2C_InitStruct.I2C_OwnAddress1 = 0x00; // 自身地址(无需设置)
I2C_InitStruct.I2C_Ack = I2C_Ack_Enable; // 使能应答
I2C_InitStruct.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit; // 7位地址
I2C_InitStruct.I2C_ClockSpeed = 100000; // 100KHz
I2C_Init(I2C1, &I2C_InitStruct);
// 使能I2C1
I2C_Cmd(I2C1, ENABLE);
}
// 读取GH3020单个寄存器值
uint8_t GH3020_Read_Reg(uint8_t reg_addr)
{
uint8_t reg_data = 0;
// 等待I2C总线空闲
while(I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY));
// 发送起始信号
I2C_GenerateSTART(I2C1, ENABLE);
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT));
// 发送设备写地址
I2C_Send7bitAddress(I2C1, GH3020_I2C_ADDR, I2C_Direction_Transmitter);
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));
// 发送寄存器地址
I2C_SendData(I2C1, reg_addr);
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED));
// 发送重复起始信号
I2C_GenerateSTART(I2C1, ENABLE);
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT));
// 发送设备读地址
I2C_Send7bitAddress(I2C1, GH3020_I2C_ADDR, I2C_Direction_Receiver);
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED));
// 关闭应答,准备接收数据
I2C_AcknowledgeConfig(I2C1, DISABLE);
I2C_GenerateSTOP(I2C1, ENABLE); // 发送停止信号
// 读取寄存器数据
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_RECEIVED));
reg_data = I2C_ReceiveData(I2C1);
// 恢复应答使能
I2C_AcknowledgeConfig(I2C1, ENABLE);
return reg_data;
}
// 写入GH3020单个寄存器值
void GH3020_Write_Reg(uint8_t reg_addr, uint8_t data)
{
// 等待I2C总线空闲
while(I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY));
// 发送起始信号
I2C_GenerateSTART(I2C1, ENABLE);
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT));
// 发送设备写地址
I2C_Send7bitAddress(I2C1, GH3020_I2C_ADDR, I2C_Direction_Transmitter);
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));
// 发送寄存器地址
I2C_SendData(I2C1, reg_addr);
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED));
// 发送要写入的数据
I2C_SendData(I2C1, data);
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED));
// 发送停止信号
I2C_GenerateSTOP(I2C1, ENABLE);
delay_ms(1); // 短延时,确保写入完成
}
// 检测GH3020是否存在(读取设备ID,正确值为0x30)
uint8_t GH3020_Check_ID(void)
{
uint8_t dev_id = GH3020_Read_Reg(GH3020_REG_ID);
if(dev_id == 0x30)
{
return 1; // 检测成功
}
return 0; // 检测失败
}
// 初始化GH3020传感器(配置为心率+血氧模式,采样率50Hz)
void GH3020_Init(void)
{
// 先检测设备是否存在
if(!GH3020_Check_ID())
{
return; // 检测失败,直接返回
}
// 配置控制寄存器1:0x07 = 心率+血氧模式,使能传感器
GH3020_Write_Reg(GH3020_REG_CTRL1, 0x07);
// 配置控制寄存器2:0x01 = 采样率50Hz(可选:0x00=25Hz,0x02=100Hz)
GH3020_Write_Reg(GH3020_REG_CTRL2, 0x01);
delay_ms(100); // 传感器启动延时
}
// 读取心率、血氧、ADC原始数据
void GH3020_Read_Data(GH3020_Data_TypeDef *data)
{
uint8_t hr_h, hr_l;
uint8_t spo2_h, spo2_l;
uint8_t red_h, red_l;
uint8_t ir_h, ir_l;
// 读取设备ID
data->device_id = GH3020_Read_Reg(GH3020_REG_ID);
// 读取心率数据(16位:低字节+高字节)
hr_l = GH3020_Read_Reg(GH3020_REG_HR_DATA);
hr_h = GH3020_Read_Reg(GH3020_REG_HR_DATA + 1);
data->heart_rate = (hr_h << 8) | hr_l;
// 读取血氧数据(16位:低字节+高字节)
spo2_l = GH3020_Read_Reg(GH3020_REG_SPO2_DATA);
spo2_h = GH3020_Read_Reg(GH3020_REG_SPO2_DATA + 1);
data->spo2 = (spo2_h << 8) | spo2_l;
// 读取红光ADC值(16位)
red_l = GH3020_Read_Reg(GH3020_REG_RED_ADC);
red_h = GH3020_Read_Reg(GH3020_REG_RED_ADC + 1);
data->red_adc = (red_h << 8) | red_l;
// 读取红外光ADC值(16位)
ir_l = GH3020_Read_Reg(GH3020_REG_IR_ADC);
ir_h = GH3020_Read_Reg(GH3020_REG_IR_ADC + 1);
data->ir_adc = (ir_h << 8) | ir_l;
}
3. 主函数调用示例(main.c)
#include "stm32f10x.h"
#include "gh3020.h"
#include "delay.h"
int main(void)
{
GH3020_Data_TypeDef gh3020_data;
// 初始化延时函数
delay_init();
// 初始化GH3020的I2C外设
GH3020_I2C_Init();
// 初始化GH3020传感器
GH3020_Init();
while(1)
{
// 读取传感器数据
GH3020_Read_Data(&gh3020_data);
// 打印数据(需自行实现串口打印函数,如USART_SendString)
// 示例:心率=gh3020_data.heart_rate,血氧=gh3020_data.spo2
delay_ms(500); // 500ms读取一次
}
}
三、关键代码解释
-
I2C 初始化
配置 PB6/PB7 为复用开漏输出(I2C 总线要求),I2C 时钟设为 100KHz(GH3020 支持的速率)。
-
寄存器读写
-
写寄存器:先发送设备写地址→寄存器地址→要写入的数据→停止信号。
-
读寄存器:先发送设备写地址→寄存器地址→重复起始信号→设备读地址→读取数据→停止信号。
-
-
传感器配置
-
GH3020_REG_CTRL1 = 0x07开启心率 + 血氧检测模式。
-
GH3020_REG_CTRL2 = 0x01设置采样率 50Hz(平衡精度和功耗)。
-
-
数据读取
心率 / 血氧 / ADC 值均为 16 位数据,需读取低字节 + 高字节拼接。
四、使用注意事项
-
电源
GH3020 仅支持 3.3V 供电,接 5V 会烧毁传感器。
-
上拉电阻
SDA/SCL 引脚必须接 10KΩ 上拉电阻(I2C 总线要求)。
-
数据有效性
首次上电后需等待 1~2 秒,传感器完成初始化后再读取数据。
-
中断使用(可选)
若使用 INT 引脚,可配置 STM32 外部中断,当 INT 引脚拉低时读取数据(减少轮询功耗)。
总结
-
核心通信
GH3020 与 STM32 通过 I2C 总线通信,设备 7 位地址为 0x2F(写 0x5E / 读 0x5F)。
-
关键步骤
初始化 I2C→检测设备 ID→配置传感器工作模式→读取 16 位心率 / 血氧数据。
-
数据解析
心率值范围为 0~250 bpm,血氧值范围为 0~100 %,ADC 值为原始光电信号(可用于自定义算法)。