一、GY-906的简单 介绍
GY-906是一种红外非接触式温度传感器模块,也称为非接触式红外温度传感器或红外测温模块。GY-906模块基于非接触式红外技术,可以用来测量物体表面的温度,而无需实际接触到物体。
GY-906模块通常包含一对红外传感器,其中一个传感器用于测量物体的红外辐射,另一个传感器用于测量环境的温度。通过同时测量物体和环境的温度,模块可以校准物体的真实温度,并输出一个相应的数字信号或模拟电压,使得用户可以读取物体的温度值。
我们只需要知道它,
遵循IIC协议,
能简单测个温度,
距离在10CM左右检测的精度最高,
然后套用个通信的信息接收代码,
OK!
二、效果图及原理
简单的做了界面调整,经过测试烙铁头和人体的温度,温度基本误差在1%左右,更精密的温度可另外详细调参!
本OLED的中文显示跑配置如图:
原理:
定时器在一定的周期内循环采集红外传感器的数据,经过一定的调参后输出较准确的温度值,可另外配置串口输出,本实验程序已经配置好串口和OLED显示输出。
三、代码
本文只给出主要的配置代码,详细完整的工程代码可移步到此下载,基于STM32F103C8T6最小系统的GY-90614ESF-DCC红外测温系统。
话不多说,上重点代码!
GY-906.c文件
#include "gy906.h"
/*******************************************************************************
* Function Name : Mlx90614_Configuration
* Description : Mlx90614_Configuration
* Input : None
* Output : None
* Return : None
*******************************************************************************/
void Mlx96014_Configuration(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB,ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15;//SCL
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
GPIO_Init(GPIOB,&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;//SDA
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
GPIO_Init(GPIOA,&GPIO_InitStructure);
SDA_H;
SCL_H;
}
/*******************************************************************************
* Function Name : SMBus_StartBit
* Description : 在SMBus上生成START条件
* Input : None
* Output : None
* Return : None
*******************************************************************************/
void SMBus_StartBit(void)
{
SDA_H; // Set SDA line
SMBus_Delay(1); // Wait a few microseconds
SCL_H; // Set SCK line
SMBus_Delay(5); // Generate bus free time between Stop
SDA_L; // Clear SDA line
SMBus_Delay(10); // Hold time after (Repeated) Start
// Condition. After this period, the first clock is generated.
//(Thd:sta=4.0us min)
SCL_L; // Clear SCK line
SMBus_Delay(2); // Wait a few microseconds
}
/*******************************************************************************
* Function Name : SMBus_StopBit
* Description : Generate STOP condition on SMBus
* Input : None
* Output : None
* Return : None
*******************************************************************************/
void SMBus_StopBit(void)
{
SCL_L; // Clear SCK line
SMBus_Delay(5); // Wait a few microseconds
SDA_L; // Clear SDA line
SMBus_Delay(5); // Wait a few microseconds
SCL_H; // Set SCK line
SMBus_Delay(10); // Stop condition setup time(Tsu:sto=4.0us min)
SDA_H; // Set SDA line
}
/*******************************************************************************
* Function Name : SMBus_SendByte
* Description : 在SMBus上发送一个字节
* Input : Tx_buffer
* Output : None
* Return : None
*******************************************************************************/
u8 SMBus_SendByte(u8 Tx_buffer)
{
u8 Bit_counter;
u8 Ack_bit;
u8 bit_out;
for(Bit_counter=8; Bit_counter; Bit_counter--)
{
if (Tx_buffer&0x80)
{
bit_out=1; // If the current bit of Tx_buffer is 1 set bit_out
}
else
{
bit_out=0; // else clear bit_out
}
SMBus_SendBit(bit_out); // Send the current bit on SDA
Tx_buffer<<=1; // Get next bit for checking
}
Ack_bit=SMBus_ReceiveBit(); // Get acknowledgment bit
return Ack_bit;
}
/*******************************************************************************
* Function Name : SMBus_SendBit
* Description : Send a bit on SMBus
* Input : bit_out
* Output : None
* Return : None
*******************************************************************************/
void SMBus_SendBit(u8 bit_out)
{
if(bit_out==0)
{
SDA_L;
}
else
{
SDA_H;
}
SMBus_Delay(2); // Tsu:dat = 250ns minimum
SCL_H; // Set SCK line
SMBus_Delay(10); // High Level of Clock Pulse
SCL_L; // Clear SCK line
SMBus_Delay(10); // Low Level of Clock Pulse
// SMBUS_SDA_H(); // Master release SDA line ,
return;
}
/*******************************************************************************
* Function Name : SMBus_ReceiveBit
* Description : 在SMBus上接收一位
* Input : None
* Output : None
* Return : Ack_bit
*******************************************************************************/
u8 SMBus_ReceiveBit(void)
{
u8 Ack_bit;
SDA_H; //?????????,????
SCL_H; // Set SCL line
SMBus_Delay(2); // High Level of Clock Pulse
if (SMBUS_SDA_PIN)
{
Ack_bit=1;
}
else
{
Ack_bit=0;
}
SCL_L; // Clear SCL line
SMBus_Delay(4); // Low Level of Clock Pulse
return Ack_bit;
}
/*******************************************************************************
* Function Name : SMBus_ReceiveByte
* Description : 在SMBus上接收一个字节
* Input : ack_nack
* Output : None
* Return : RX_buffer
*******************************************************************************/
u8 SMBus_ReceiveByte(u8 ack_nack)
{
u8 RX_buffer;
u8 Bit_Counter;
for(Bit_Counter=8; Bit_Counter; Bit_Counter--)
{
if(SMBus_ReceiveBit()) // Get a bit from the SDA line
{
RX_buffer <<= 1; // If the bit is HIGH save 1 in RX_buffer
RX_buffer |=0x01;
}
else
{
RX_buffer <<= 1; // If the bit is LOW save 0 in RX_buffer
RX_buffer &=0xfe;
}
}
SMBus_SendBit(ack_nack); // Sends acknowledgment bit
return RX_buffer;
}
/*******************************************************************************
* Function Name : SMBus_Delay
* Description : 1us
* Input : time
* Output : None
* Return : None
*******************************************************************************/
void SMBus_Delay(u16 time)
{
u16 i, j;
for (i=0; i<4; i++)
{
for (j=0; j<time; j++);
}
}
/*******************************************************************************
* Function Name : SMBus_ReadMemory
* Description : 从RAM/EEPROM读取数据
* Input : slaveAddress, command
* Output : None
* Return : Data
*******************************************************************************/
u16 SMBus_ReadMemory(u8 slaveAddress, u8 command)
{
u16 data; // Data storage (DataH:DataL)
u8 Pec; // PEC byte storage
u8 DataL=0; // Low data byte storage
u8 DataH=0; // High data byte storage
u8 arr[6]; // Buffer for the sent bytes
u8 PecReg; // Calculated PEC byte storage
u8 ErrorCounter; // Defines the number of the attempts for communication with MLX90614
ErrorCounter=0x00; // Initialising of ErrorCounter
slaveAddress <<= 1; //2-7???????
do
{
repeat:
SMBus_StopBit(); //If slave send NACK stop comunication
--ErrorCounter; //Pre-decrement ErrorCounter
if(!ErrorCounter) //ErrorCounter=0?
{
break; //Yes,go out from do-while{}
}
SMBus_StartBit(); //Start condition
if(SMBus_SendByte(slaveAddress))//Send SlaveAddress ???Wr=0????????
{
goto repeat; //Repeat comunication again
}
if(SMBus_SendByte(command)) //Send command
{
goto repeat; //Repeat comunication again
}
SMBus_StartBit(); //Repeated Start condition
if(SMBus_SendByte(slaveAddress+1)) //Send SlaveAddress ???Rd=1????????
{
goto repeat; //Repeat comunication again
}
DataL = SMBus_ReceiveByte(ACK); //Read low data,master must send ACK
DataH = SMBus_ReceiveByte(ACK); //Read high data,master must send ACK
Pec = SMBus_ReceiveByte(NACK); //Read PEC byte, master must send NACK
SMBus_StopBit(); //Stop condition
arr[5] = slaveAddress;
arr[4] = command;
arr[3] = slaveAddress+1; //Load array arr
arr[2] = DataL;
arr[1] = DataH;
arr[0] = 0;
PecReg=PEC_Calculation(arr); //Calculate CRC
}
while(PecReg != Pec); //If received and calculated CRC are equal go out from do-while{}
data = (DataH<<8) | DataL; //data=DataH:DataL
return data;
}
/*******************************************************************************
* Function Name : PEC_calculation
* Description : 计算接收字节的PEC
* Input : pec[]
* Output : None
* Return : pec[0]-this byte contains calculated crc value
*******************************************************************************/
u8 PEC_Calculation(u8 pec[])
{
u8 crc[6];
u8 BitPosition=47;
u8 shift;
u8 i;
u8 j;
u8 temp;
do
{
/*Load pattern value 0x000000000107*/
crc[5]=0;
crc[4]=0;
crc[3]=0;
crc[2]=0;
crc[1]=0x01;
crc[0]=0x07;
/*Set maximum bit position at 47 ( six bytes byte5...byte0,MSbit=47)*/
BitPosition=47;
/*Set shift position at 0*/
shift=0;
/*Find first "1" in the transmited message beginning from the MSByte byte5*/
i=5;
j=0;
while((pec[i]&(0x80>>j))==0 && i>0)
{
BitPosition--;
if(j<7)
{
j++;
}
else
{
j=0x00;
i--;
}
}/*End of while */
/*Get shift value for pattern value*/
shift=BitPosition-8;
/*Shift pattern value */
while(shift)
{
for(i=5; i<0xFF; i--)
{
if((crc[i-1]&0x80) && (i>0))
{
temp=1;
}
else
{
temp=0;
}
crc[i]<<=1;
crc[i]+=temp;
}/*End of for*/
shift--;
}/*End of while*/
/*Exclusive OR between pec and crc*/
for(i=0; i<=5; i++)
{
pec[i] ^=crc[i];
}/*End of for*/
}
while(BitPosition>8); /*End of do-while*/
return pec[0];
}
/*******************************************************************************
* Function Name : SMBus_ReadTemp
* Description : 计算并返回温度
* Input : None
* Output : None
* Return : SMBus_ReadMemory(0x00, 0x07)*0.02-273.15
*******************************************************************************/
float SMBus_ReadTemp(void)
{
return SMBus_ReadMemory(SA, RAM_ACCESS|RAM_TOBJ1)*0.02-273.15;//绝对零度
}
/*********************************END OF FILE*********************************/
GY-906.h文件
#ifndef _GY906_H_
#define _GY906_H_
#include "delay.h"
#define ACK 0 //应答信号
#define NACK 1 //否定应答
#define SA 0x00 //从设备地址
#define RAM_ACCESS 0x00 //内存访问命令
#define EEPROM_ACCESS 0x20 //EEPROM接入命令
#define RAM_TOBJ1 0x07 //eeprom中的To1地址
#define SDA_L GPIO_ResetBits(GPIOA, GPIO_Pin_8)
#define SDA_H GPIO_SetBits(GPIOA, GPIO_Pin_8)
#define SCL_H GPIO_SetBits(GPIOB, GPIO_Pin_15)
#define SCL_L GPIO_ResetBits(GPIOB, GPIO_Pin_15)
#define SMBUS_SDA_PIN GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_8)
void SMBus_StartBit(void);
void SMBus_StopBit(void);
void SMBus_SendBit(u8);
u8 SMBus_SendByte(u8);
u8 SMBus_ReceiveBit(void);
u8 SMBus_ReceiveByte(u8);
void SMBus_Delay(u16);
void SMBus_Init(void);
u16 SMBus_ReadMemory(u8, u8);
u8 PEC_Calculation(u8*);
float SMBus_ReadTemp(void);
void Mlx96014_Configuration(void);
#endif
定时器部分
#include "stm32f10x.h" // Device header
void Timer_Init(void)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
TIM_InternalClockConfig(TIM2);
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStructure.TIM_Period = 100 - 1;
TIM_TimeBaseInitStructure.TIM_Prescaler = 72 - 1;
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
TIM_ClearFlag(TIM2, TIM_FLAG_Update);
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_Init(&NVIC_InitStructure);
TIM_Cmd(TIM2, ENABLE);
}
void Timer3_Init(void)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
TIM_InternalClockConfig(TIM3);
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStructure.TIM_Period = 10000 - 1;
TIM_TimeBaseInitStructure.TIM_Prescaler = 720 - 1;
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructure);
TIM_ClearFlag(TIM3, TIM_FLAG_Update);
TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_Init(&NVIC_InitStructure);
TIM_Cmd(TIM3, ENABLE);
}
放在主函数部分
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "Timer.h"
#include "gy906.h"
#include "bsp_usart.h"
#include "iic.h"
#include <stdio.h>
#include <string.h> // 添加包含 string.h 头文件
static u8 i = 0;
static float add = 0.0f;
float Temperature = 0;
float Temperatures[24];
char temp[12];
uint16_t Num;
int main(void)
{
OLED_Init();
Timer_Init();
Timer3_Init();//定时72000000/(720*10000)S
USART_Config();
IIC_Init();
Mlx96014_Configuration();
while (1)
{
}
}
void TIM2_IRQHandler(void)
{
if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
{
i++;
add += SMBus_ReadTemp();//得到温度数据
if(i >= 50)
{
Temperature = add / 50 + 2.0; //平均加参数调整
add = 0;
i = 0;
}
snprintf((char *)Temperatures, sizeof(Temperatures), "%f", Temperature ); // 使用 snprintf转化,
OLED_ShowSignedNum(3, 5, Temperature, 3);
snprintf(temp, sizeof(temp), "%.2f", Temperature); // 使用 snprintf转化
OLED_ShowString(4, 4, temp );
OLED_ShowCN(80, 4, 5);//℃
OLED_ShowCN(80, 6, 5);//℃
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
}
}
void TIM3_IRQHandler(void)
{
if (TIM_GetITStatus(TIM3, TIM_IT_Update) == SET)
{
OLED_ShowCN(0 + 24, 0, 0); //温
OLED_ShowCN(16 + 24, 0, 1); //度
OLED_ShowCN(32 + 24, 0, 2); //检
OLED_ShowCN(48 + 24, 0, 3); //测
OLED_ShowCN(64 + 24, 0, 4); //仪
TIM_ClearITPendingBit(TIM3, TIM_IT_Update);
}
}
注:GY-906的寄存器通信非本人写,参考前辈所留下的代码进行学习和分享!