最近在做红外测温的项目,试了几个不同公司的红外产品,最早拿到手的是TI的这款芯片式的红外温度传感器TMP006,TI关于这款芯片的介绍是世界上首款单片数字IR MEMS温度传感器,首次为便携式消费电子产品实现非接触温度测量功能。TI这款芯片最大的优势在于它的体积,很难想象能够在那么小的一款芯片上能够集成各种器件,包括片上MEMS热电堆传感器、信号调节功能、16位模数转换器(ADC)、局部温度传感器以及各种电压参考。支持-40℃至+125℃宽范围工作温度,测温范围远远超过这个范围,因为红外测温测的是目标温度和环境温度的温差,只要温差电势不超过允许值(5.12mV)就可以,考虑到芯片对于layout和焊接要求很严格,所以直接买了模块回来测试,也买了几个芯片回来,买回来后就连接到开发板上测试。开发板用的是战舰V3,控制芯片是STM32F103ZET6。
先看看引脚说明:
一般的做法是数字地和模拟地是接在一起的,一根地线,ADR0和ADR1是地址选择引脚,接高电平接地或接SDA、SCA会表示不同的地址,最多可以同时连接8片TMP006,TI的数据手册上写的很清楚:
因为我只连了一片TMP006,地址选择引脚都接地,所以地址为0x70,发送地址时后面必须紧接着发送一位读写标志位。
这里我并没有使用DRDY引脚,所以直接将Data ready引脚接地,这样做的原因是配置寄存器中有一位是Data ready bit,通过读取该位的值来确定温度是否转换完成。
再来看看TMP006的寄存器,TMP006只有5个寄存器,所以使用起来还比较简单,5个寄存器中只有配置寄存器是可读写的,其他4个寄存器都是只读的:
其中,制造商ID和硬件ID都是固定的,只要你读出的这两个寄存器值没有问题,那么读取的温度也应该没什么问题了。 The manufacturer ID reads 5449h and the device ID is 0067h。
下面开始写软件,采用的通信协议是IIC兼容SMbus,关于IIC通信协议我这里就不用讲了,网上有非常详细的介绍,这里我使用的是软件IO口模拟IIC并没有使用芯片的硬件IIC,废话不多说,直接贴代码:
myiic.c
#include "myiic.h"
#include "delay.h"
/////////////////////////////////////////////////////////////////////////////////
//IIC驱动程序
//初始化IIC
void IIC_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE ); //使能GPIOB时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP ; //推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_SetBits(GPIOB,GPIO_Pin_6|GPIO_Pin_7); //PB6,PB7 输出高
}
//产生IIC起始信号
void IIC_Start(void)
{
SDA_OUT(); //sda线输出
IIC_SDA=1;
IIC_SCL=1;
delay_us(4);
IIC_SDA=0;//START:when CLK is high,DATA change form high to low
delay_us(4);
IIC_SCL=0;//钳住I2C信号,准备发送或接受数据
}
//产生IIC停止信号
void IIC_Stop(void)
{
SDA_OUT();//sda线输出
IIC_SCL=0;
IIC_SDA=0;//STOP:when CLK is high DATA change form low to high
delay_us(4);
IIC_SCL=1;
delay_us(4);
IIC_SDA=1;//发送I2C总线结束信号
delay_us(4);
}
//等待应答信号到来
//返回值 1,接受应答失败
// 0,接受应答成功
u8 IIC_Wait_Ack(void)
{
u8 ucErrTime=0;
SDA_IN(); //SDA设置为输入
IIC_SDA=1;delay_us(1);
IIC_SCL=1;delay_us(1);
while(READ_SDA)
{
ucErrTime++;
if(ucErrTime>250)
{
IIC_Stop();
return 1;
}
}
IIC_SCL=0;//时钟输出0
return 0;
}
//产生ACK应答
void IIC_Ack(void)
{
IIC_SCL=0;
SDA_OUT();
IIC_SDA=0;
delay_us(2);
IIC_SCL=1;
delay_us(2);
IIC_SCL=0;
}
//不产生ACK应答
void IIC_NAck(void)
{
IIC_SCL=0;
SDA_OUT();
IIC_SDA=1;
delay_us(2);
IIC_SCL=1;
delay_us(2);
IIC_SCL=0;
}
//IIC发送一个字节
//返回从机有无应答
//1,有应答
//0,无形的
void IIC_Send_Byte(u8 txd)
{
u8 t;
SDA_OUT();
IIC_SCL=0;//拉低时钟开始数据传输
for(t=0;t<8;t++)
{
//IIC_SDA=(txd&0x80)>>7;
if((txd&0x80)>>7)
IIC_SDA=1;
else
IIC_SDA=0;
txd<<=1;
delay_us(2);
IIC_SCL=1;
delay_us(2);
IIC_SCL=0;
delay_us(2);
}
}
//ׁ读1个字节,ack=1时,发送ACK,ack=0,发送NACK
u8 IIC_Read_Byte(unsigned char ack)
{
unsigned char i,receive=0;
SDA_IN();//SDA设置为输入
for(i=0;i<8;i++ )
{
IIC_SCL=0;
delay_us(2);
IIC_SCL=1;
receive<<=1;
if(READ_SDA)receive++;
delay_us(1);
}
if (!ack)
IIC_NAck();//发送nACK
else
IIC_Ack(); //发送ACK
return receive;
}//读TMP006寄存器值
u16 IIC_Read_Two_Byte(u8 daddr)
{
u16 temp;
IIC_Start();
IIC_Send_Byte(0X80); //发送写命令
IIC_Wait_Ack();
IIC_Send_Byte(daddr); //发送读地址指针
IIC_Wait_Ack();
IIC_Start();
IIC_Send_Byte(0X81); //发送读命令,进入接受模式
IIC_Wait_Ack();
temp = IIC_Read_Byte(1); //读高八位数据
temp = temp<<8;
temp += IIC_Read_Byte(0); //读低八位数据
IIC_Stop(); //产生一个停止条件
return temp;
}
读寄存器时序图:
#ifndef __MYIIC_H
#define __MYIIC_H
#include "sys.h"
//////////////////////////////////////////////////////////////////////////////////
//IO方向设置
#define SDA_IN() {GPIOB->CRL&=0X0FFFFFFF;GPIOB->CRL|=(u32)8<<28;}
#define SDA_OUT() {GPIOB->CRL&=0X0FFFFFFF;GPIOB->CRL|=(u32)3<<28;}
//IO操作函数
#define IIC_SCL PBout(6) //SCL
#define IIC_SDA PBout(7) //SDA
#define READ_SDA PBin(7) //输入SDA
//IIC所有操作函数
void IIC_Init(void); //初始化IIC所有IO口
void IIC_Start(void); //发送IIC开始信号
void IIC_Stop(void); //发送IIC停止信号
void IIC_Send_Byte(u8 txd); //IIC发送一个字节
u8 IIC_Read_Byte(unsigned char ack); //IIC读取一个字节
u8 IIC_Wait_Ack(void); //IIC等待ACK信号
void IIC_Ack(void); //IIC发送ACK信号
void IIC_NAck(void); //IIC不发送ACK信号
u16 IIC_Read_Two_Byte(u8 daddr); //读取TMP指定地址的寄存器值
#endif
#include "led.h"
#include "delay.h"
#include "sys.h"
#include "lcd.h"
#include "math.h"
int main(void)
{
u16 Tdie_Temp;
u16 Vobj_Read;
u16 Conf_Read;
float Vobj;
float Tdie;
float S,Vos,fVobj,Tobj;
float S0 = 6.4*pow(10,-14);
float a1 = 1.75*pow(10,-3);
float a2 = -1.678*pow(10,-5);
float Tref = 298.15;
float b0 = -2.94*pow(10,-5);
float b1 = -5.7*pow(10,-7);
float b2 = 4.63*pow(10,-9);
float c2 = 13.4;
delay_init(); //延时函数初始化
LED_Init(); //初始化LED
LCD_Init(); //初始化LCD
IIC_Init(); //IIC初始化¯
while(1)
{
LED0 = 0;
POINT_COLOR=RED;//设置字体为红色
do
{
// TODO: enter the block content here
Conf_Read = IIC_Read_Two_Byte(0x02);
} while ((Conf_Read&0x0080) != 0x0080); //等待温度转化完成,当配置寄存器的bit[7]=1时,表示Object voltage and ambient temperature results are ready to read
Tdie_Temp = IIC_Read_Two_Byte(0x01) //读取环境温度寄存器值,地址是0x01
///////////////////////////////////////////////////////////////////
//计算环境温度
if (Tdie_Temp >= 0x8000)
{
Tdie = -((Tdie_Temp>>2)|0xc000)*0.03125 + 273.15;
}
else
{
Tdie = (Tdie_Temp>>2)*0.03125 + 273.15;
}
do
{
// TODO: enter the block content here
Conf_Read = IIC_Read_Two_Byte(0x02);
} while ((Conf_Read&0x0080) != 0x0080); //等待转换完成,当配置寄存器的bit[7]=1时,表示Object voltage and ambient temperature results are ready to read
Vobj_Read = IIC_Read_Two_Byte(0x00); //读取目标电压寄存器值
if(Vobj_Read >= 0x8000)
{
Vobj = -(0xffff - Vobj_Read + 1)*156.25;
}else
{
Vobj = Vobj_Read*156.25;
}
Vobj *= pow(10,-9); //将目标电压寄存器值转化成电压值,单位V
/////////////////////////////////////////////////////////////////
//计算目标温度
S = S0*(1 + a1*(Tdie - Tref) + a2*(Tdie-Tref)*(Tdie - Tref));
Vos = b0 + b1*(Tdie - Tref) + b2*(Tdie - Tref)*(Tdie - Tref);
fVobj = Vobj - Vos + c2*(Vobj - Vos)*(Vobj - Vos);
Tobj = sqrt(sqrt(pow(Tdie,4) + fVobj/S));
Tobj -= 273.15;
LCD_ShowString(30,130,200,16,16,"The die temperature is:");
LCD_ShowxNum(30,150,Tdie - 273.15,8,16,0x00);
LCD_ShowString(10,170,240,16,16,"The object temperature is:");
LCD_ShowxNum(30,190,Tobj,8,16,0x00);
}
}
关于目标温度的计算:
S0的值需要自己根据所测物体的发射率以及视场距离等因素自己进行配置,这里我只是取得典型值,典型值为5.0*10-14至7.0*10-14.
最后介绍一下测试的情况,首先环境温度的读数是很准确的,室温25℃左右,误差很小,但是目标温度的读取就受到很多因素的影响,首先是距离的因素,能测到的距离确实很小,测量我手心的温度差不多要贴近到2CM以内才能测的准确,隔远一点温度就会衰减一点,并且读数不是很稳定,正常情况下手心的温度为32℃左右,但是离远了目标温度的读数跳变比较大,偶尔会跳到27读左右,另外一个就是受热气流的影响非常大,当我将传感器对准笔记本扇热器出风口时,出风口温度也就40多度,会发现目标温度会很快衰减到0,并且一直为0,移开测量环境温度才逐渐恢复正常读数。芯片式的红外传感器受其他外在因素的影响太大了,并没有做成探头式的MLX90614稳定,距离也比较近。虽然价格相对便宜不少,但是真正使用到产品中还需要很多要做的,包括布局和焊接,安装条件,目标物体的发射率,温度标定等等。最后不得佩服TI的文档,几乎你所有关于这个芯片的东西包括红外测温的原理都讲得非常清楚,关于芯片的各个方面都讲的很详细。所有关于红外测温的原理基本都是在这款芯片的数据手册上学到的。
TI官方技术文档:点击打开链接