STM32 驱动心率血氧传感器--GH3020

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读取一次
    }
}

三、关键代码解释

  1. I2C 初始化

    配置 PB6/PB7 为复用开漏输出(I2C 总线要求),I2C 时钟设为 100KHz(GH3020 支持的速率)。

  2. 寄存器读写

    • 写寄存器:先发送设备写地址→寄存器地址→要写入的数据→停止信号。

    • 读寄存器:先发送设备写地址→寄存器地址→重复起始信号→设备读地址→读取数据→停止信号。

  3. 传感器配置

    • GH3020_REG_CTRL1 = 0x07

      开启心率 + 血氧检测模式。

    • GH3020_REG_CTRL2 = 0x01

      设置采样率 50Hz(平衡精度和功耗)。

  4. 数据读取

    心率 / 血氧 / ADC 值均为 16 位数据,需读取低字节 + 高字节拼接。

四、使用注意事项

  1. 电源

    GH3020 仅支持 3.3V 供电,接 5V 会烧毁传感器。

  2. 上拉电阻

    SDA/SCL 引脚必须接 10KΩ 上拉电阻(I2C 总线要求)。

  3. 数据有效性

    首次上电后需等待 1~2 秒,传感器完成初始化后再读取数据。

  4. 中断使用(可选)

    若使用 INT 引脚,可配置 STM32 外部中断,当 INT 引脚拉低时读取数据(减少轮询功耗)。

总结

  1. 核心通信

    GH3020 与 STM32 通过 I2C 总线通信,设备 7 位地址为 0x2F(写 0x5E / 读 0x5F)。

  2. 关键步骤

    初始化 I2C→检测设备 ID→配置传感器工作模式→读取 16 位心率 / 血氧数据。

  3. 数据解析

    心率值范围为 0~250 bpm,血氧值范围为 0~100 %,ADC 值为原始光电信号(可用于自定义算法)。

相关推荐
balance_rui2 小时前
FreeRTOS
笔记·stm32
LCG元2 小时前
STM32实战案例:基于HC-SR04的超声波测距与倒车雷达系统
stm32·单片机·嵌入式硬件
华清远见IT开放实验室3 小时前
智能手表完整项目实现,比赛求职双向加分,基于嵌入式大赛推荐开发板(STM32U5)
stm32·单片机·嵌入式硬件·学习·智能手表·嵌入式大赛
BackCatK Chen3 小时前
STM32保姆级入门教程|第8章:PT100高精度测温实战 + ADS1232驱动 + 24位ADC数据解析(功能超详细+CubeIDE手把手)
stm32·stm32cubeide·高精度测温·ads1232·pt100·24位adc·工业实战
危桥带雨3 小时前
FLASH理论基础
stm32·单片机·嵌入式硬件
进击的小头4 小时前
第18篇:嵌入式电机控制专用外设:正交编码脉冲模块原理与闭环控制应用
arm开发·单片机·嵌入式硬件
feifeigo1234 小时前
STM32 LCD彩色液晶屏显示汉字、英文、数字
stm32·单片机·嵌入式硬件
实在太懒于是不想取名6 小时前
STM32N6的开发日记(4):快速上手LTDC显示图片-让屏幕刷新丝滑流畅
stm32·单片机·嵌入式硬件
实在太懒于是不想取名6 小时前
STM32N6的开发日记(1):上手难度拉满的N6有哪些不同?
stm32·单片机·嵌入式硬件
LingLong_roar6 小时前
keil未指定 PY32F0 具体芯片型号导致编译报错及无法烧录问题
单片机·嵌入式硬件