
目录
前言
最近用到了STM32驱动GY906体温检测传感器模块,我这里用的STM32是STM32F407IGT6,这个模块的精度能达到0.01℃,所以记录一下
一、工作原理
GY906 是一款基于MLX90614 芯片 的非接触式红外温度传感器模块,其工作原理基于红外辐射测温技术 和塞贝克效应(Seebeck Effect)。
核心原理:所有温度高于绝对零度 (-273.15℃) 的物体都会持续发射红外辐射,辐射强度与物体温度的四次方成正比 (斯特藩 - 玻尔兹曼定律)。GY906 通过检测目标物体发射的红外辐射能量,将其转换为电信号,经过处理后计算出物体表面温度。
GY906 通过I2C/SMBus 协议输出温度数据,也支持 PWM 输出模式:
- I2C 模式:16 位数据 (高 8 位 + 低 8 位),包含物体温度和环境温度,分辨率 0.02℃
- PWM 模式:SDA 引脚输出占空比代表温度的 10 位 PWM 信号
信号处理链
热电堆微弱电压信号 → 低噪声放大器(LNA) → 带通滤波器 → 17位ADC → DSP处理 → 温度值
二、接线方式
|-----|-----|
| VIN | 5V |
| GND | GND |
| SCL | PB6 |
| SDA | PB7 |
三、软件程序
gy906.h
#ifndef __GY906_H
#define __GY906_H
#include "stm32f1xx_hal.h" // 替换为对应型号的HAL头文件(如stm32f4xx_hal.h)
// GY906(MLX90614)I2C地址(默认0x5A,若未配置EEPROM则为0x00)
#define MLX90614_ADDR 0x5A << 1 // I2C地址左移1位(HAL库要求包含读写位)
// MLX90614 寄存器地址
#define MLX90614_TA_REG 0x06 // 环境温度寄存器
#define MLX90614_TOBJ1_REG 0x07 // 目标物体温度寄存器(主要测量)
// 函数声明
void GY906_Init(I2C_HandleTypeDef *hi2c); // 初始化(本质是I2C初始化,此处用于校验连接)
float GY906_ReadAmbientTemp(I2C_HandleTypeDef *hi2c); // 读取环境温度(℃)
float GY906_ReadObjectTemp(I2C_HandleTypeDef *hi2c); // 读取目标物体温度(℃)
uint8_t GY906_CheckDevice(I2C_HandleTypeDef *hi2c); // 检测传感器是否在线
#endif
gy906.c
#include "gy906.h"
#include "stdio.h"
/**
* @brief 检测GY906是否在线
* @param hi2c: I2C句柄指针
* @retval 0: 正常,1: 异常
*/
uint8_t GY906_CheckDevice(I2C_HandleTypeDef *hi2c)
{
HAL_StatusTypeDef status;
uint8_t dummy_data = 0x00;
// 尝试向传感器发送地址,检测ACK
status = HAL_I2C_Master_Transmit(hi2c, MLX90614_ADDR, &dummy_data, 1, 100);
if (status == HAL_OK)
return 0; // 传感器响应正常
else
return 1; // 传感器未连接或地址错误
}
/**
* @brief 初始化GY906(校验I2C连接)
* @param hi2c: I2C句柄指针
* @retval 无
*/
void GY906_Init(I2C_HandleTypeDef *hi2c)
{
if (GY906_CheckDevice(hi2c) == 0)
printf("GY906 Device OK!\r\n");
else
printf("GY906 Device Error!\r\n");
}
/**
* @brief 读取MLX90614寄存器数据
* @param hi2c: I2C句柄指针
* @param reg_addr: 寄存器地址
* @param data: 接收数据缓冲区(2字节)
* @retval HAL状态
*/
static HAL_StatusTypeDef MLX90614_ReadReg(I2C_HandleTypeDef *hi2c, uint8_t reg_addr, uint8_t *data)
{
HAL_StatusTypeDef status;
// 1. 发送寄存器地址
status = HAL_I2C_Master_Transmit(hi2c, MLX90614_ADDR, ®_addr, 1, 100);
if (status != HAL_OK)
return status;
// 2. 重新发送起始信号,读取2字节数据(温度值+校验位)
status = HAL_I2C_Master_Receive(hi2c, MLX90614_ADDR, data, 2, 100);
return status;
}
/**
* @brief 读取环境温度(℃)
* @param hi2c: I2C句柄指针
* @retval 环境温度值(失败返回-273.15)
*/
float GY906_ReadAmbientTemp(I2C_HandleTypeDef *hi2c)
{
uint8_t data[2];
int16_t temp_raw;
float temp_c;
if (MLX90614_ReadReg(hi2c, MLX90614_TA_REG, data) != HAL_OK)
return -273.15f; // 读取失败返回绝对零度(标识错误)
// 合并2字节数据(高8位+低8位)
temp_raw = (data[0] << 8) | data[1];
// 温度转换公式:原始值 * 0.02 - 273.15(MLX90614输出为绝对温度K)
temp_c = (temp_raw * 0.02f) - 273.15f;
return temp_c;
}
/**
* @brief 读取目标物体温度(℃)
* @param hi2c: I2C句柄指针
* @retval 目标温度值(失败返回-273.15)
*/
float GY906_ReadObjectTemp(I2C_HandleTypeDef *hi2c)
{
uint8_t data[2];
int16_t temp_raw;
float temp_c;
if (MLX90614_ReadReg(hi2c, MLX90614_TOBJ1_REG, data) != HAL_OK)
return -273.15f;
temp_raw = (data[0] << 8) | data[1];
// 排除无效数据(MLX90614无效温度值为0xFFFF)
if (temp_raw == 0xFFFF)
return -273.15f;
temp_c = (temp_raw * 0.02f) - 273.15f;
return temp_c;
}
main.c
#include "stm32f1xx_hal.h"
#include "gy906.h"
#include "stdio.h"
// 全局变量
I2C_HandleTypeDef hi2c1;
UART_HandleTypeDef huart1;
// 函数声明
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_I2C1_Init(void);
static void MX_USART1_UART_Init(void);
int fputc(int ch, FILE *f); // 重定向printf到串口
int main(void)
{
HAL_Init();
SystemClock_Config(); // 系统时钟配置(需根据实际硬件修改)
MX_GPIO_Init(); // GPIO初始化
MX_I2C1_Init(); // I2C1初始化
MX_USART1_UART_Init(); // USART1初始化(用于打印)
GY906_Init(&hi2c1); // 初始化GY906(检测设备)
while (1)
{
float ambient_temp = GY906_ReadAmbientTemp(&hi2c1); // 读取环境温度
float object_temp = GY906_ReadObjectTemp(&hi2c1); // 读取目标温度
// 打印温度数据(保留2位小数)
printf("Ambient Temp: %.2f ℃\r\n", ambient_temp);
printf("Object Temp : %.2f ℃\r\n", object_temp);
printf("-------------------------\r\n");
HAL_Delay(1000); // 1秒读取一次
}
}
/**
* @brief I2C1初始化配置(标准模式100kHz)
*/
static void MX_I2C1_Init(void)
{
hi2c1.Instance = I2C1;
hi2c1.Init.ClockSpeed = 100000; // I2C时钟频率100kHz
hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2;
hi2c1.Init.OwnAddress1 = 0; // STM32作为主机,无需自身地址
hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
hi2c1.Init.OwnAddress2 = 0;
hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
if (HAL_I2C_Init(&hi2c1) != HAL_OK)
{
Error_Handler();
}
}
/**
* @brief USART1初始化(115200波特率,8N1)
*/
static void MX_USART1_UART_Init(void)
{
huart1.Instance = USART1;
huart1.Init.BaudRate = 115200;
huart1.Init.WordLength = UART_WORDLENGTH_8B;
huart1.Init.StopBits = UART_STOPBITS_1;
huart1.Init.Parity = UART_PARITY_NONE;
huart1.Init.Mode = UART_MODE_TX_RX;
huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart1.Init.OverSampling = UART_OVERSAMPLING_16;
if (HAL_UART_Init(&huart1) != HAL_OK)
{
Error_Handler();
}
}
/**
* @brief GPIO初始化(I2C1引脚:PB6=SCL,PB7=SDA)
*/
static void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
// 使能GPIOB时钟
__HAL_RCC_GPIOB_CLK_ENABLE();
// 使能I2C1时钟
__HAL_RCC_I2C1_CLK_ENABLE();
// 使能USART1时钟
__HAL_RCC_USART1_CLK_ENABLE();
// I2C1 SCL(PB6)和SDA(PB7)配置:开漏输出、上拉、复用功能
GPIO_InitStruct.Pin = GPIO_PIN_6 | GPIO_PIN_7;
GPIO_InitStruct.Mode = GPIO_MODE_AF_OD; // 开漏复用模式
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
GPIO_InitStruct.Pull = GPIO_PULLUP; // 上拉电阻(无需外接时可开启,建议外接)
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
// USART1 TX引脚(PA9)配置
GPIO_InitStruct.Pin = GPIO_PIN_9;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}
/**
* @brief 重定向printf到串口1
*/
int fputc(int ch, FILE *f)
{
HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 100);
return ch;
}
/**
* @brief 系统时钟配置(需根据实际STM32型号修改,此处以STM32F103C8T6为例)
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
// 配置HSI作为时钟源(8MHz)
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
// 配置系统时钟:HSI→AHB→APB1/APB2
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2; // APB1最大36MHz
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK)
{
Error_Handler();
}
}
/**
* @brief 错误处理函数
*/
void Error_Handler(void)
{
while (1)
{
// 错误时可点亮LED提示(需自行添加LED配置)
HAL_Delay(500);
}
}
#ifdef USE_FULL_ASSERT
void assert_failed(uint8_t *file, uint32_t line)
{
printf("Assertion Failed: File %s, Line %d\r\n", file, line);
while (1);
}
#endif
总结
本文介绍了STM32驱动GY906红外温度传感器的实现方法。GY906基于MLX90614芯片,采用I2C通信协议,精度达0.01℃。硬件连接使用PB6(SCL)和PB7(SDA)引脚,软件部分包含初始化、环境温度和目标温度读取函数,通过串口输出温度数据。程序采用HAL库开发,实现了1秒间隔的温度采集与显示功能,为嵌入式系统温度监测提供了完整解决方案。