目录
模块介绍
工作原理:
代码介绍:
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线的比特的过程如下:
-
主设备将SDA线设置为输入并释放对该线的控制。
-
主设备确保SCL线稳定。
-
从设备通过改变SDA线的状态发送一个比特。
-
主设备读取SDA线的状态。
-
主设备通过SDA线发送ACK / NACK消息来继续通信。
-
主设备为每个数据位重复此过程以完成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中,并返回该变量。文章来源:https://www.toymoban.com/news/detail-469802.html
注:PEC为Packet Error Code,是SMBus协议使用的一个错误检测字节。文章来源地址https://www.toymoban.com/news/detail-469802.html
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);
}
}
演示:
到了这里,关于学习GY906(GY906-DCI)与STM32F1版程序的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!