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 值为原始光电信号(可用于自定义算法)。

相关推荐
z20348315202 小时前
17届蓝桥杯嵌入式赛道开发板外设使用教程——LED
stm32·单片机·蓝桥杯
人还是要有梦想的2 小时前
C语言写一个贪吃蛇游戏
c语言·单片机·游戏
悠哉悠哉愿意13 天前
【单片机学习笔记】串口、超声波、NE555的同时使用
笔记·单片机·学习
Lester_110113 天前
STM32霍尔传感器输入口设置为复用功能输入口时,还能用GPIO函数直接读取IO的状态吗
stm32·单片机·嵌入式硬件·电机控制
LCG元13 天前
低功耗显示方案:STM32L0驱动OLED,动态波形绘制与优化
stm32·嵌入式硬件·信息可视化
三佛科技-1873661339713 天前
120W小体积碳化硅电源方案(LP8841SC极简方案12V10A/24V5A输出)
单片机·嵌入式硬件
z203483152013 天前
STM32F103系列单片机定时器介绍(二)
stm32·单片机·嵌入式硬件
古译汉书13 天前
【IoT死磕系列】Day 7:只传8字节怎么控机械臂?学习工业控制 CANopen 的“对象字典”(附企业级源码)
数据结构·stm32·物联网·http
Alaso_shuang13 天前
STM32 核心输入、输出模式
stm32·单片机·嵌入式硬件