MLX90614红外温度传感器使用指南:关键寄存器详解与STM32F1单片机软件IIC通信协议实践。

1 前言

本躯在使用MLX90614时,问商家要器件手册,他给我发一个毛用没有的示例程序,寄存器呢?客服说不知道您说的什么东西呢亲亲。

在CSDN上查这个器件,全是会员专享不说,给的内容还牛头不对马嘴。

本躯一气之下,决定亲自写文章,帮助后面的使用者快速学习并简单使用这个器件。

本文章代码运行环境:Keil;平台:STM32F103C8T6(注意:本文章代码与我自己测试的完整代码有部分改动,并且改动后的代码并没有测试,所以可能会出现细微的错误,如有错误请指正)

本文章主要内容:GY906硬件基础了解,器件连接,器件选型,STM32F1软件IIC,寄存器种类和配置。

2 MLX90614(GY906)红外传感器硬件

2.1 MLX90614(GY906)(以下全部称为GY906)器件连接

MLX90614支持IIC、UART等协议,一般我们使用更方便的IIC协议。

将你的IIC连接到MCU的IIC,VDD接3.3V,GND接地。

需要注意的是,GY906内部并没有集成上拉电阻,所以SDA和SCL都应该通过串联电阻连接到VCC。这里我推荐4.7KΩ电阻。

GY906的VDD按照器件手册上来说,可以接3-5V电源电压,但由于会出现较大的线性误差,推荐接3.3V电源。

图2-1 典型连接原理图

图2-2 误差与VDD的线性关系图,来源:MLX90614器件手册

如果需要更高的精度,可以采用以下滤波公式:T_C=T-(VDD-3)*0.6

2.2 GY906器件选型

相信大家在选择BAA、DCC等一堆器件时不清楚该选哪个,问客服又问不明白,先看一个图。

图2-3 不同型号的峰值和宽度域 来源:MLX90614器件手册

我们注意到除了xBA,其他型号宽度域是逐渐减小的,宽度域可以简单理解为视域,宽度域越大,红外检测的视野范围越广,但同时视野距离越近。

因此,想要满足长检测距离,应使用宽度域小的型号,比如xCC,甚至于xCI。

前面的x是什么意思呢,是测量精度,市面上常见的有B和D型,B型满足一般需求,D型满足医用级需求,比如红外测温。

在本文中,我们选用DCC型号,大约10CM测距,医用级精度。

2.3 GY906硬件简介和寄存器

GY906内部自动采集环境温度和目标物体温度,内置算法自动滤波。

通过FIR滤波器滤波、增益补偿和偏移补偿,IIR进一步滤波最后计算出最终温度数据。

用户在使用GY906时,使用到的寄存器很少,以下是主要寄存器和说明:

从机地址 说明 数据格式 功能
0x5A 单片机使用从机地址时应左移一位   8位 IIC从机地址
寄存器地址 名称 读写权限 数据格式 关键功能
0x06 RAM_TA 只读 16位(开尔文) 环境温度
0x07 RAM_TOBJ1 只读 16位(开尔文) 物体温度(主传感器)
0x08 RAM_TOBJ2 只读 16位(开尔文) 物体温度(副传感器,仅存在于双区域型号中)
0x04 EEPROM_EMISSIVITY 读写 16位(0~65535) 设置发射率(0.0~1.0)
0x05 EEPROM_CONFIG 读写 16位(位控制) 配置滤波、SMBus 地址等
0x2E EEPROM_SMBUS_ADDR 读写 16位(低7位)

修改 I2C 地址

2.3.1 温度数据寄存器

RAM_TA、RAM_TOBJ1、RAM_TOBJ2(DCC型号不存在)可以直接读取环境温度和物体温度,一共发送24位数据,先发送低八位数据,再发送高八位数据,最后发送PEC校验数据,每八位数据发送过来之后会发送一个ACK,所以我们只需要读取到两次ACK,就可以把全部的16位温度数据读取完毕。如果想使用PEC校验,只需要再读取一次ACK,读取到的PEC数据如果全部为0,则数据无误。

图2-4 接收数据格式 来源:MLX90614器件手册

写入数据与接收数据同理

图2-5 写入数据格式 来源:MLX90614器件手册

2.3.2 EEPROM_EMISSIVITY发射率设置寄存器

根据被测量物体发射率的不同,我们需要设置不同的发射率,比如如果测量人体,发射率应设置为0.96-0.98。

直接向发射率寄存器写入我们的发射率×65535,就可以设定发射率。(如0.98×65535)

2.3.3 EEPROM_CONFIG配置寄存器

在配置寄存器中,我们可以写入数据来控制寄存器是否上电复位(谨慎使用),是否开启强数字滤波(建议开启),和设置SMBus从机地址(一般默认为0x5A)。

名称 功能 默认值
15 POR(上电复位) 1 = 执行复位,0 = 正常 0
14 IIR_FILTER 数字滤波强度(0=弱,1=强) 0
13:8 保留 必须保持 0 0
7:0 SMBus 地址 设置 SMBus 从机地址(0x00~0x7F) 0x5A

 

2.3.4 EEPROM_SMBUS_ADDR SMBus从机地址寄存器

可以直接读取从机地址或改变从机地址,仅低7位有效。

3 STM32F1软件IIC

由于网上大把软件IIC,在此我不在赘述,直接给出代码(才不是因为我懒)。至于我为什么使用软件IIC,而不使用STM32F1单片机自带的硬件IIC,我只能说硬件IIC谁用谁知道,一个标志位能把你程序直接卡死。

3.1 Delay.c

#include "stm32f10x.h"

void Delay_us(uint32_t xus)
{
	SysTick->LOAD = 72 * xus;				
	SysTick->VAL = 0x00;					
	SysTick->CTRL = 0x00000005;				
	while(!(SysTick->CTRL & 0x00010000));	
	SysTick->CTRL = 0x00000004;				
}

void Delay_ms(uint32_t xms)
{
	while(xms--)
	{
		Delay_us(1000);
	}
}

void Delay_s(uint32_t xs)
{
	while(xs--)
	{
		Delay_ms(1000);
	}
} 

3.2 Delay.h

#ifndef __DELAY_H
#define __DELAY_H

void Delay_us(uint32_t us);
void Delay_ms(uint32_t ms);
void Delay_s(uint32_t s);

#endif

3.3 MyIIC.c

#include "stm32f10x.h"                  // Device header
#include "Delay.h"

//软件模拟I2C
//B10口为SCL;B11为SDA;可自行重新配置
void MyI2C_W_SCL(uint8_t BitValue)
{
	GPIO_WriteBit(GPIOB, GPIO_Pin_10, (BitAction)BitValue);
	Delay_us(10);
}

void MyI2C_W_SDA(uint8_t BitValue)
{
	GPIO_WriteBit(GPIOB, GPIO_Pin_11, (BitAction)BitValue);
	Delay_us(10);
}

uint8_t MyI2C_R_SDA(void)
{
	uint8_t BitValue;
	BitValue = GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11);
	Delay_us(10);
	return BitValue;
}

void MyI2C_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	
	GPIO_SetBits(GPIOB, GPIO_Pin_10 | GPIO_Pin_11);
}

void MyI2C_Start(void)
{
	MyI2C_W_SDA(1);
	MyI2C_W_SCL(1);
	MyI2C_W_SDA(0);
	MyI2C_W_SCL(0);
}

void MyI2C_Stop(void)
{
	MyI2C_W_SDA(0);
	MyI2C_W_SCL(1);
	MyI2C_W_SDA(1);
}

void MyI2C_SendByte(uint8_t Byte)
{
	uint8_t i;
	for (i = 0; i < 8; i ++)
	{
		MyI2C_W_SDA(Byte & (0x80 >> i));
		MyI2C_W_SCL(1);
		MyI2C_W_SCL(0);
	}
}

uint8_t MyI2C_ReceiveByte(void)
{
	uint8_t i, Byte = 0x00;
	MyI2C_W_SDA(1);
	for (i = 0; i < 8; i ++)
	{
		MyI2C_W_SCL(1);
		if (MyI2C_R_SDA() == 1){Byte |= (0x80 >> i);}
		MyI2C_W_SCL(0);
	}
	return Byte;
}

void MyI2C_SendAck(uint8_t AckBit)
{
	MyI2C_W_SDA(AckBit);
	MyI2C_W_SCL(1);
	MyI2C_W_SCL(0);
}

uint8_t MyI2C_ReceiveAck(void)
{
	uint8_t AckBit;
	MyI2C_W_SDA(1);
	MyI2C_W_SCL(1);
	AckBit = MyI2C_R_SDA();
	MyI2C_W_SCL(0);
	return AckBit;
}

3.4 MyIIC.h

#ifndef __MYI2C_H
#define __MYI2C_H

void MyI2C_Init(void);
void MyI2C_Start(void);
void MyI2C_Stop(void);
void MyI2C_SendByte(uint8_t Byte);
uint8_t MyI2C_ReceiveByte(void);
void MyI2C_SendAck(uint8_t AckBit);
uint8_t MyI2C_ReceiveAck(void);

#endif

4 STM32F1通过软件IIC读取MLX90614温度数据

4.1 MLX90614.c

#include "MyIIC.h"
#include "MLX90614_Address.h" 

float a_temp=0;    //环境温度
float o_temp=0;    //物体温度

uint32_t MLX90614_ReadReg(uint8_t RegAddress)
{
	uint8_t Data[2]={0};
	uint16_t Data_all=0;
	
	MyI2C_Start();
	MyI2C_SendByte(MLX90614_ADDRESS);
	MyI2C_ReceiveAck();
	MyI2C_SendByte(RegAddress);
	MyI2C_ReceiveAck();
	
	MyI2C_Start();
	MyI2C_SendByte(MLX90614_ADDRESS | 0x01);
	MyI2C_ReceiveAck();
	Data[0] = MyI2C_ReceiveByte();
	MyI2C_ReceiveAck();
	Data[1] = MyI2C_ReceiveByte();
	MyI2C_SendAck(1);
	MyI2C_Stop();
	
	Data_all=(Data[1]<<8)+Data[0];
	return Data_all;
}

void MLX90614_Init(void)
{
	MyI2C_Init();
}
void MLX90614_TO(void)
{
	int i=MLX90614_ReadReg(MLX90614_RAM_TOBJ1);
	i=(i*2)-27315;
	o_temp=i*0.01;
}
void MLX90614_TA(void)
{
	int i=MLX90614_ReadReg(MLX90614_RAM_TA);
	i=(i*2)-27315;
	a_temp=i*0.01;
}

4.2 MLX90614.h

#ifndef __MLX90614_H
#define __MLX90614_H

#define float a_temp;    //环境温度
#define float o_temp;    //物体温度

uint32_t MLX90614_ReadReg(uint8_t RegAddress);
void MLX90614_Init(void);
void MLX90614_TO(void);
void MLX90614_TA(void);

#endif

4.3 MLX90614_Address.h

#ifndef __MLX90614_Address_H
#define __MLX90614_Address_H

#define MLX90614_ADDRESS 			0x5A<<1 
#define MLX90614_RAM_TA				0x06	
#define MLX90614_RAM_TOBJ1			0x07	
#define MLX90614_RAM_TOBJ2			0x08	
#define MLX90614_EEPROM_EMISSIVITY	0x04	
#define MLX90614_EEPROM_CONFIG		0x05	
#define MLX90614_EEPROM_SMBUS_ADDR	0x2E	

#endif

5 主程序示例

#include "stm32f10x.h"                 
#include "Delay.h"
#include "MyI2C.h"
#include "MLX90614.h"
#include "MLX90614_Address.h"

int main(void)
{
	MLX90614_Init();
	Delay_ms(20);
	
    float ambient_temperature=0;    //环境温度储存于此
    float object_temperature=0;     //物体温度储存于此
    
	while (1)
	{
		MLX90614_TA();    //读取环境温度并储存到a_temp
		Delay_ms(20);
		MLX90614_TO();    //读取物体温度并储存到o_temp
		Delay_ms(20);
        
        ambient_temperature=a_temp;
        object_temperature=o_temp;
	}
}

6 结语

由此,就能简单得到环境温度和物体温度。

当然,很多内容我都没写,比如写入数据的代码,比如配置发射率和强数字滤波的代码。

再比如说,这样对着额头测体温依然是不准的,因为额头的温度实际上并不是人体核心温度。环境的温度、汗液的散热、血管的收缩和扩张,甚至于此时这个人的血压和心率,都非常影响额头温度和核心温度的差值。当然,这并不是完全无法解决,有兴趣的小伙伴可以自己试试。

并且我还删掉了大部分的注释。软件不难看懂,希望大家能好好学习基础知识,并且自己加上属于自己的注释,当然,如果能修改代码变成自己的代码习惯就更好了。

万事开头难,有了基础打底,剩下的进阶内容大家自己加油。

本躯水平有限,若有错误恳请斧正。

 

作者:史上最甜的躯

物联沃分享整理
物联沃-IOTWORD物联网 » MLX90614红外温度传感器使用指南:关键寄存器详解与STM32F1单片机软件IIC通信协议实践。

发表回复