学习GY906(GY906-DCI)与STM32F1版程序

目录

模块介绍

工作原理:

代码介绍:

1.一些宏定义:

2.起始条件与终止条件:

3.发送一个字节:

4.发送一位:

5.接收一个字节:

6.接收一位:

7.初始化

8. 读取数据:

9.计算接收字节:

10,计算并返回温度值:

11.主函数:

演示:​


模块介绍

GY906是一款红外热像仪传感器模块,可测量目标物体的温度,常用于非接触式温度测量和温度控制,GY906采用了MLX90614红外热敏传感器芯片和SMBus数字接口协议。

可以测量的温度范围是-40℃至85℃

而答主我学习的模块是GY906-DCI,采用了I2C数字接口协议通信,所以学习该模块的时候可以与IIC进行比较学习。

引脚:

VCC: 供电引脚,输入电压范围为3.3-5V。

GND: 地线引脚,连接地线。

SDA: 数据引脚,用于传输数据。

SCL: 时钟引脚,用于同步数据传输。

工作原理:

GY906红外温度模块是一种基于热电偶原理的红外温度测量模块。它可以快速测量目标物体离开模块一定距离处的温度。

该模块内置了一个MLX90614型红外温度传感器,该传感器由杜邦公司生产。MLX90614利用红外线向目标物体发射电磁辐射,然后快速测量被测目标的温度。电磁辐射通过一个双电荷制电磁感应元件(DPP)进入传感器,该元件检测红外能量,并产生一个电荷,该电荷随后被放大并转换为数字输出。MLX90614还集成了一些其他的芯片和组件,如温度补偿电路、电源调节器、EEPROM等,能够提供更加准确和稳定的测量结果。

GY906红外温度模块利用STM32等微处理器,通过I2C/SMBus接口与MLX90614进行通信,可以读取基础温度、环境温度和目标温度等信息。

代码介绍:

为了方便与IIC比较学习,每个代码我会借用江科大的IIC的时序图,以下有关IIC时序的图片皆来自江科大

1.一些宏定义:

#include "mlx90614.h"

/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
#define ACK	 0 //应答
#define	NACK 1 //无应答
#define SA				0x00 //Slave address 单个MLX90614时地址为0x00,多个时地址默认为0x5a
#define RAM_ACCESS		0x00 //RAM access command RAM存取命令
#define EEPROM_ACCESS	0x20 //EEPROM access command EEPROM存取命令
#define RAM_TOBJ1		0x07 //To1 address in the eeprom 目标1温度,检测到的红外温度 -70.01 ~ 382.19度

#define SMBUS_PORT	GPIOB      //PB端口(端口和下面的两个针脚可自定义)
#define SMBUS_SCK		GPIO_Pin_6 //PB6:SCL
#define SMBUS_SDA		GPIO_Pin_7 //PB7:SDA

#define RCC_APB2Periph_SMBUS_PORT		RCC_APB2Periph_GPIOB

#define SMBUS_SCK_H()	    SMBUS_PORT->BSRR = SMBUS_SCK //置高电平
#define SMBUS_SCK_L()	    SMBUS_PORT->BRR = SMBUS_SCK  //置低电平
#define SMBUS_SDA_H()	    SMBUS_PORT->BSRR = SMBUS_SDA
#define SMBUS_SDA_L()	    SMBUS_PORT->BRR = SMBUS_SDA

#define SMBUS_SDA_PIN()	    SMBUS_PORT->IDR & SMBUS_SDA //读取引脚电平

2.起始条件与终止条件:

/*******************************************************************************
*函数名称:SMBus_StartBit
*说明:在SMBus上生成START起始条件
*输入:无
*输出:无
*返回:无
*******************************************************************************/
void SMBus_StartBit(void)
{
    SMBUS_SDA_H();		//释放SDA,将其拉高
    SMBus_Delay(5);	    // 等待几微秒
    SMBUS_SCK_H();		// 释放SCL,将其拉高
    SMBus_Delay(5);	    //在Stop信号之间生成总线空闲时间
    SMBUS_SDA_L();		// 拉低SDA
    SMBus_Delay(5);	    // Hold time after (Repeated) Start
    // 条件:在此周期后,生成第一个时钟。
    //(Thd:sta=4.0us min)在SCK=1时,检测到SDA由1到0表示通信开始(下降沿)
    SMBUS_SCK_L();	    // 拉低SCL
    SMBus_Delay(5);	    //等待几微秒
}

/*******************************************************************************
*函数名称:SMBus_StopBit
*描述:在SMBus上产生停止条件
*输入:无
*输出:无
*返回值:无
*******************************************************************************/
void SMBus_StopBit(void)
{
		/***先拉低SDA再释放SCL和SDA***/
    SMBUS_SCK_L();		
    SMBus_Delay(5);	
    SMBUS_SDA_L();		
    SMBus_Delay(5);	
    SMBUS_SCK_H();	
    SMBus_Delay(5);	  
    SMBUS_SDA_H();// Set SDA line在SCK=1时,检测到SDA由0到1表示通信结束(上升沿)
}

3.发送一个字节:

/*******************************************************************************
*函数名称:SMBus_SendByte
*描述:在SMBus上发送一个字节
*输入:Tx_buffer(要发送的字节)
*输出:无
*返回值:无
*******************************************************************************/
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)//与10000000。就是最高跟1与
        {
            bit_out=1;   // I如果Tx_buffer当前位为1,则将bit_out设置为1,否则将其设置为0。
        }
        else
        {
            bit_out=0;  // 否则将bit_out清零
        }
        SMBus_SendBit(bit_out);		// 发送SDA上的当前位
        Tx_buffer<<=1;				//获取下一个位以进行检查
    }

    Ack_bit=SMBus_ReceiveBit();		// 获取确认位
    return	Ack_bit;
}

4.发送一位:

/*******************************************************************************
函数名  : SMBus_SendBit
描述    : //发送一位
输入    : bit_out(要发送的位)
输出    : 无
返回    : 无
*******************************************************************************/
void SMBus_SendBit(u8 bit_out)
{
    if(bit_out==0)
    {
        SMBUS_SDA_L();
    }
    else
    {
        SMBUS_SDA_H();
    }
    SMBus_Delay(2);					//数据输入到时钟变化之间的最短延迟时间为250纳秒(了确保在输入数据变化后足够的时间内,系统可以采集和处理这些数据,以确保数据的准确性和稳定性)
    SMBUS_SCK_H();					// 拉高SCL
    SMBus_Delay(6);					// 时钟脉冲的高电平
    SMBUS_SCK_L();					// 拉低SCL
    SMBus_Delay(3);					//
//	SMBUS_SDA_H();				    // 拉高SDA
    return;
}

5.接收一个字节:

/*******************************************************************************
函数名:SMBus_ReceiveByte
描述:在SMBus上接收一个字节(byte)
输入:ack_nack
输出:无
返回值:RX_buffer
该函数用于在SMBus上接收一个字节(byte)。输入参数ack_nack表示是否返回应答位,取值为0或1,0表示不返回应答位,1表示返回应答位。
输出参数为空。返回值为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())			//从SDA线获取一个位
        {
            RX_buffer <<= 1;			// 如果位为1,则在RX_buffer中保存1。
            RX_buffer |=0x01;
        }
        else
        {
            RX_buffer <<= 1;			// 如果位为0,则在RX_buffer中保存0。
            RX_buffer &=0xfe;
        }
    }
    SMBus_SendBit(ack_nack);			// 发送确认比特。
    return RX_buffer;
}

获取SDA线的比特的过程如下:

  1. 主设备将SDA线设置为输入并释放对该线的控制。

  2. 主设备确保SCL线稳定。

  3. 从设备通过改变SDA线的状态发送一个比特。

  4. 主设备读取SDA线的状态。

  5. 主设备通过SDA线发送ACK / NACK消息来继续通信。

  6. 主设备为每个数据位重复此过程以完成SMBus通信。

6.接收一位:

/*******************************************************************************
*函数名:SMBus_ReceiveBit
*描述:在SMBus上接收一个位(bit)
*输入:无
*输出:无
*返回值:Ack_bit
该函数用于在SMBus上接收一个位(bit)。输入和输出参数都为空,返回值为Ack_bit,表示已成功接收一个位并返回应答位的值。
*******************************************************************************/
u8 SMBus_ReceiveBit(void)
{
    u8 Ack_bit;

    SMBUS_SDA_H();          //引脚靠外部电阻上拉,当作输入
	SMBus_Delay(2);			// 
    SMBUS_SCK_H();			// 拉高SCL
    SMBus_Delay(5);			//时钟脉冲的高电平
    if (SMBUS_SDA_PIN())
    {
        Ack_bit=1;
    }
    else
    {
        Ack_bit=0;
    }
    SMBUS_SCK_L();			// Clear SCL line
    SMBus_Delay(3);			// Low Level of Clock Pulse

    return	Ack_bit;
}

7.初始化

/*******************************************************************************
*函数名称  : SMBus_Init
*描述          : SMBus初始化
*输入          : 无
*输出          : 无
*返回值        : 无
*******************************************************************************/
void SMBus_Init()
{
    GPIO_InitTypeDef    GPIO_InitStructure;

	/* Enable SMBUS_PORT clocks */
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_SMBUS_PORT, ENABLE);

    /*配置SMBUS_SCK、SMBUS_SDA为集电极开漏输出*/
    GPIO_InitStructure.GPIO_Pin = SMBUS_SCK | SMBUS_SDA;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(SMBUS_PORT, &GPIO_InitStructure);

    SMBUS_SCK_H();
    SMBUS_SDA_H();
}

8. 读取数据:

/*******************************************************************************
*函数名称  : SMBus_ReadMemory
*描述          : 从RAM/EEPROM读取数据
*输入          : 从器件地址和命令字
*输出          : 无
*返回值        : 数据
*******************************************************************************/
u16 SMBus_ReadMemory(u8 slaveAddress, u8 command)
{
    u16 data;			//数据存储(DataH:DataL)
    u8 Pec;				// PEC字节存储
    u8 DataL=0;			// 低数据字节存储
    u8 DataH=0;			// 高数据字节存储。
    u8 arr[6];			// 发送字节的缓冲区
    u8 PecReg;			// 计算的PEC字节存储
    u8 ErrorCounter;	// 定义与MLX90614通信的尝试次数

    ErrorCounter=0x00;				// 初始化ErrorCounte
	slaveAddress <<= 1;	//2-7位表示从机地址
	
    do
    {
repeat:
        SMBus_StopBit();			    //如果从设备发送NACK停止通信
        --ErrorCounter;				    //预减ErrorCounter
        if(!ErrorCounter) 			    //ErrorCounter= 0?
        {
            break;					    //是的,退出do-while {}
        }

        SMBus_StartBit();				//起始条件
        if(SMBus_SendByte(slaveAddress))//发送SlaveAddress 最低位Wr=0表示接下来写命令
        {
            goto	repeat;			    //再次重复通信
        }
        if(SMBus_SendByte(command))	    //发送命令
        {
            goto	repeat;		    	//再次重复通信
        }

        SMBus_StartBit();					//重复起始条件
        if(SMBus_SendByte(slaveAddress+1))	//发送地址最低位Rd=1表示接下来读数据
        {
            goto	repeat;             	//再次重复通信
        }

        DataL = SMBus_ReceiveByte(ACK);	//读取低字节数据,主设备必须发送ACK应答
        DataH = SMBus_ReceiveByte(ACK); //取高字节数据,主设备必须发送ACK应答
        Pec = SMBus_ReceiveByte(NACK);	//读取PEC字节,主设备必须发送NACK非应答
        SMBus_StopBit();				//停止条件
        arr[5] = slaveAddress;		//
        arr[4] = command;			//
        arr[3] = slaveAddress+1;	//加载数组arr
        arr[2] = DataL;				//
        arr[1] = DataH;				//
        arr[0] = 0;					//
        PecReg=PEC_Calculation(arr);//计算CRC
    }
    while(PecReg != Pec);		//计算CRC

	data = (DataH<<8) | DataL;	//data=DataH:DataL
    return data;
}

解释代码:从SMBus设备的RAM或EEPROM中读取数据的函数。函数的输入参数为从设备地址和命令字。函数中首先定义了存储数据的变量data、PEC字节存储变量Pec、低数据字节存储变量DataL、高数据字节存储变量DataH、发送字节的缓冲区数组arr、计算的PEC字节存储变量PecReg和与SMBus设备通信的尝试次数变量ErrorCounter。

然后,初始化ErrorCounter并将从设备地址左移一位,将从设备地址的最低位Wr(写命令)设置为0。

在do-while循环中,如果ErrorCounter=0,即与SMBus设备通信尝试次数已经用完,则跳出循环。否则,发送起始条件、从设备地址和命令字,如果发送失败则跳到repeat处重复通信。

接着,发送重复起始条件和从设备地址,将低位数据、高位数据和PEC字节分别读取,如果主设备收到数据后无法应答,则跳到repeat处重新通信。读取完数据后,将6个字节(从设备地址、命令、低位数据、高位数据、0和PEC)分别加载到数组arr中,然后计算PEC,并将计算结果存储到PecReg变量中。

如果计算的PEC与读取的PEC相等,则退出循环,并将DataH和DataL合并成一个16位的数据存储到变量data中,并返回该变量。

注:PEC为Packet Error Code,是SMBus协议使用的一个错误检测字节。

9.计算接收字节:

/*******************************************************************************
*函数名称:PEC_calculation
*描述:计算接收字节的PEC
*输入:pec []
*输出:无
*返回值:pec [0]-此字节包含计算的crc值
*******************************************************************************/
u8 PEC_Calculation(u8 pec[])
{
    u8 	crc[6];
    u8	BitPosition=47;
    u8	shift;
    u8	i;
    u8	j;
    u8	temp;

    do
    {
        /*加载模式值 0x000000000107*/
        crc[5]=0;
        crc[4]=0;
        crc[3]=0;
        crc[2]=0;
        crc[1]=0x01;
        crc[0]=0x07;

        /*加载模式值0x000000000107*/
        BitPosition=47;

        /*将位移位置设为0*/
        shift=0;

        /*从MSByte字节5开始寻找传输消息中的第一个“1”。*/
        i=5;
        j=0;
        while((pec[i]&(0x80>>j))==0 && i>0)
        {
            BitPosition--;
            if(j<7)
            {
                j++;
            }
            else
            {
                j=0x00;
                i--;
            }
        }/*终止循环*/

        /*获取模式值的位移值*/
        shift=BitPosition-8;

        /*位移模式值 */
        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;
            }/*终止for循环*/
            shift--;
        }/*终止while循环*/

        /*PEC和CRC之间的异或运算*/
        for(i=0; i<=5; i++)
        {
            pec[i] ^=crc[i];
        }/*终止for循环*/
    }
    while(BitPosition>8); /*终止do-while*/

    return pec[0];
}

10,计算并返回温度值:

/*******************************************************************************
*函数名称:SMBus_ReadTemp
*描述:计算并返回温度值
*输入:无
*输出:无
*返回值:SMBus_ReadMemory(0x00, 0x07)*0.02-273.15
*******************************************************************************/
float SMBus_ReadTemp(void)
{   
	float temp;
	temp = SMBus_ReadMemory(SA, RAM_ACCESS|RAM_TOBJ1)*0.02-273.15;
	return temp;
}

/*********************************END OF FILE*********************************/

11.主函数:

#include "led.h"
#include "delay.h"
#include "key.h"
#include "sys.h"
#include "usart.h"
#include "mlx90614.h"
 int main(void)
 {		
	 float temp=0.0;
	delay_init();	    	 //延时函数初始化	  
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
	uart_init(9600);	 //串口初始化为115200
 	LED_Init();			     //LED端口初始化
	KEY_Init();          //初始化与按键连接的硬件接口
	SMBus_Init();
 	while(1)
	{
		LED0=!LED0;
		delay_ms(100);
		temp=SMBus_ReadTemp();
		printf("温度值为:%.2f\r\n",temp);
	}	 
 }

演示:

物联沃分享整理
物联沃-IOTWORD物联网 » 学习GY906(GY906-DCI)与STM32F1版程序

发表评论