使用MLX90640自制红外热像仪:MLX90640介绍与API库移植

Posted qlexcel

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用MLX90640自制红外热像仪:MLX90640介绍与API库移植相关的知识,希望对你有一定的参考价值。

  平时工作的时候会遇到如下场景:

  • 新焊接好的板子,通电后发现电流异常,需要看下板子上哪里发热异常。
  • 需要监控某个器件(比如三极管、MOS)的温度,以防过热烧掉。
  • 测量温升。

  因此需要一个红外热像仪,可是网上看了一下都要2000左右了,虽然分辨率很高,可是我并不需要那么好的性能,于是干脆自己做一个。AMG8833的像素点是8x8的,感觉太低了,于是选用了32x24像素点的MLX90640。

MLX90640介绍

特性和优点

  • 小尺寸,低功耗, 32*24 像素红外阵列
  • 方便集成
  • 标准的 TO39 封装
  • 出厂校准
  • 噪声等效温差( NETD)0.1K RMS@1Hz 速率
  • I2C 兼容数字接口
  • 可编程刷新速率 0.5Hz~60Hz
  • 供电电压 3.3V
  • 电流消耗:≤23mA
  • 两种视场角可选: 55° *30° 和 110° *75°
  • 工作温度: -45~85℃
  • 测温范围: -40~300℃
  • 符合 RoHS 标准

应用实例

  • 高精度非接触温度测量
  • 安防、入侵检测
  • 人体检测
  • 智能楼宇环境温度测控
  • 汽车空调控制
  • 微波炉、烤箱温度检测与控制
  • 工业零件温度监测
  • 可视化温度传感器
  • 驱动程序可从下面网页下载:
  • https://github.com/melexis/mlx90640-library

概述

  MLX90640 是工业标准并经过完全校准的 32*24像素热红外阵列传感器,采用 4 脚 TO39 封装以及I2C 兼容的数字接口。
  MLX90640 包含 768 个热红外像素点。内嵌自身环境温度传感器和 VDD 电压检测 ADC。通过 I2C 接口,可以访问存储于内部 RAM 中的红外阵列、 环境温度以及 VDD 实时数据。

管脚定义

A 型和 B 型


MLX90640 有两个型号, A 型和 B 型,型号全称为:MLX90640ESF_BAA/BAB。
他们区别在于:

  • 视场角不同: A 型为 110*75° , B 型为 55*35° ,通俗一点讲就是 A 型是广角,所以镜
    头矮一些,视野更宽,但对远处物体的捕捉能力更低, B 型更适于拍摄稍远的物体。
  • 精度不同: A 型的噪声比 B 型大,所以 B 型的绝对温度和灵敏度都好一些。

  做红外热像仪,一般用B型。
  A型尺寸:

  B型尺寸:

一般电气特性

像素位置对应关系

通信协议

该设备采用 I2C 协议,支持快速模式( FM+)(最高 1MHz时钟频率),只可作为从机在总线上使用。
SDA 和 SCL 端口 5V 兼容,可以直接连接到 5V 逻辑的 I2C 总线网络。
从地址可编程的,可以有多达 127 个不同的从地址。
使用标准的IIC时序:

测量模式

  测量完成的红外阵列数据更新到 RAM 区,占用 1536 字节, MLX90640 每次测量一半像素点,分两次完成所有 768 像素的测量,在逻辑上将每次测量完成的一半称为一个子页,故此数据帧有两个子页面(两个子页合并以后才是完整的 768 个像素数据), 每次完成一半像素点的分布模式有两种,可通过控制寄存器 1( 0x800D) 的 bit12 位来设置。

  • ( 1) 棋盘模式(默认),( 每个像素间隔排列-像素交错模式)
  • ( 2)电视交错模式,( 隔行排列-行交错模式)

注 1:标准的 MLX90640 出厂的时候是在棋盘模式下校准的,因此传感器在棋盘模式下具有更好的噪声滤除性能。为了得到最好的结果,建议使用默认的棋盘模式。


刷新率和噪声

  MLX90640 的测量速率最高可以达到 64Hz,但越快的速率时的噪声会越大,导致灵敏度下降,手册上给出的指标是 1Hz 时可以区分出 0.1℃。




精度、坏点

  测温精度和成像的区域有关,靠近中间位置是±0.5~1.0℃,最外侧 4 个角是±2.0℃,其它区域约是±1.0℃。

  传感器上电后有个热平衡时间,大约是 5 分钟,未达到热平衡时精度会差一些。
  手册里特别提到了每个 MLX90640 传感器可能存在最多 4 个不能使用或者精度达不到要求的像素,这也许和传感器的生产工艺有关吧,坏点都会在出厂时记录到传感器的EEPROM 里,实际使用时记得要读取一下并且在成像时特殊处理这种可能存在的像素点数据。

原理图

API库的移植

  从上面看好像MLX90640用起来比较复杂,其实不用担心,迈来芯官方已经提供了驱动库,只需要下载下来简单移植一下,然后调用库函数读取温度数据即可。
  驱动库下载链接:https://github.com/melexis/mlx90640-library
  下载下来后:

  functions文件夹中是源文件,headers文件夹中是头文件,把他们复制到自己的工程下。
  把源文件添加到自己的工程中,其中MLX90640_I2C_Driver.cpp是硬件IIC的,
MLX90640_SWI2C_Driver.cpp是软件模拟IIC的,根据自己的情况选择一个就行。

软件模拟IIC移植(stm32)

  假如使用软件模拟IIC,那么需要将functions文件夹下的MLX90640_API.cpp、MLX90640_SWI2C_Driver.cpp包含进工程,同时把headers文件夹下的MLX90640_API.h、MLX90640_I2C_Driver.h也包含进工程。
  效果如下:

  接下来只需要修改MLX90640_SWI2C_Driver.cpp。
  打开MLX90640_SWI2C_Driver.cpp,可以看到里面已经把模拟IIC的时序函数都实现了,只需要自己把IO口的初始化加上就可以了。
  修改后的样子:

/**
 * @copyright (C) 2017 Melexis N.V.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */
 /**
 * As the timings depend heavily on the MCU in use, it is recommended
 * to make sure that the proper timings are achieved. For that purpose
 * an oscilloscope might be needed to strobe the SCL and SDA signals.
 * The Wait(int) function could be modified in order to better 
 * trim the frequency. For coarse setting of the frequency or 
 * dynamic frequency change using the default function implementation, 
 * ‘freqCnt’ argument should be changed – lower value results in 
 * higher frequency.
 */
 
#include "MLX90640_I2C_Driver.h"

#define Wait(freqCnt) freqCnt++

int  I2CSendByte(int8_t);
void I2CReadBytes(int, char *);
void I2CStart(void);
void I2CStop(void);
void I2CRepeatedStart(void);
void I2CSendACK(void);
void I2CSendNack(void);
int  I2CReceiveAck(void);
//void Wait(int);

int freqCnt = 1;

void MLX90640_I2CInit()
{   
	GPIO_InitTypeDef GPIO_InitStructure;

	RCC_APB2PeriphClockCmd(	RCC_APB2Periph_GPIOA, ENABLE );	
	   
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9|GPIO_Pin_15;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP ;   //推挽输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
 
	IIC_SDA_HIGH();
	IIC_SCL_HIGH();
}
    
int MLX90640_I2CRead(uint8_t slaveAddr, uint16_t startAddress,uint16_t nMemAddressRead, uint16_t *data)
{
    uint8_t sa;
    int ack = 0;
    int cnt = 0;
    int i = 0;
    char cmd[2] = {0,0};
    char i2cData[1664] = {0};
    uint16_t *p;
    
    p = data;
    sa = (slaveAddr << 1);
    cmd[0] = startAddress >> 8;
    cmd[1] = startAddress & 0x00FF;
    
    I2CStop();
    Wait(freqCnt);  
    I2CStart();
    Wait(freqCnt);
    
    ack = I2CSendByte(sa)!=0;
    if(ack != 0)
    {
        return -1;
    } 
    
    ack = I2CSendByte(cmd[0])!=0;   
    if(ack != 0)
    {
        return -1;
    }
    
    ack = I2CSendByte(cmd[1])!=0;    
    if(ack != 0)
    {
        return -1;
    }  
    
    I2CRepeatedStart();
       
    sa = sa | 0x01;
    
    ack = I2CSendByte(sa);
    if(ack != 0)
    {
        return -1;
    } 
        
    I2CReadBytes((nMemAddressRead << 1), i2cData);
              
    I2CStop();   

    for(cnt=0; cnt < nMemAddressRead; cnt++)
    {
        i = cnt << 1;
        *p++ = (int)i2cData[i]*256 + (int)i2cData[i+1];
    } 
    return 0;
  
} 

void MLX90640_I2CFreqSet(int freq)
{
    freqCnt = freq>>1;
}

int MLX90640_I2CWrite(uint8_t slaveAddr, uint16_t writeAddress, uint16_t data)
{
    uint8_t sa;
    int ack = 0;
    char cmd[4] = {0,0,0,0};
    static uint16_t dataCheck;

    sa = (slaveAddr << 1);
    cmd[0] = writeAddress >> 8;
    cmd[1] = writeAddress & 0x00FF;
    cmd[2] = data >> 8;
    cmd[3] = data & 0x00FF;

    I2CStop();
    Wait(freqCnt);
    I2CStart();
    ack = I2CSendByte(sa);
    if (ack != 0x00)
    {
        return 1; 
    }  
    
    for(int i = 0; i<4; i++)
    {
        ack = I2CSendByte(cmd[i]);
    
        if (ack != 0x00)
        {
            return -1;
        }  
    }           
    I2CStop();   
    
    MLX90640_I2CRead(slaveAddr,writeAddress,1, &dataCheck);
    
    if ( dataCheck != data)
    {
        return -2;
    }    
    
    return 0;
}

int I2CSendByte(int8_t data)
{
   int ack = 1;
   int8_t byte = data; 
   
   for(int i=0;i<8;i++)
   {
       Wait(freqCnt);
       
       if(byte & 0x80)
       {
           IIC_SDA_HIGH();
       }
       else
       {
           IIC_SDA_LOW();
       }
       Wait(freqCnt);
       IIC_SCL_HIGH();
       Wait(freqCnt);
       Wait(freqCnt);
       IIC_SCL_LOW();
       byte = byte<<1;        
   }    
   
   Wait(freqCnt);
   ack = I2CReceiveAck();
   
   return ack; 
}

void I2CReadBytes(int nBytes, char *dataP)
{
    char data;
    for(int j=0;j<nBytes;j++)
    {
        Wait(freqCnt);
        IIC_SDA_HIGH();    
        
        data = 0;
        for(int i=0;i<8;i++){
            Wait(freqCnt);
            IIC_SCL_HIGH();
            Wait(freqCnt);
            data = data<<1;
            if(IIC_SDA_READ()){
                data = data+1;  
            }
            Wait(freqCnt);
            IIC_SCL_LOW();
            Wait(freqCnt);
        }  
        
        if(j == (nBytes-1))
        {
            I2CSendNack();
        }
        else
        {                  
            I2CSendACK();
        }
            
        *(dataP+j) = data; 
    }    
    
}
        
//void Wait(int freqCnt)
//{
    int i;
	i=0;
    for(i = 0;i<freqCnt;i++);
    {
        cnt = cnt++; 
    }    
	  while(freqCnt--);
    for(cnt=20;cnt>0;cnt--); 
//} 

void I2CStart(void)
{
    IIC_SDA_HIGH();
    IIC_SCL_HIGH();
    Wait(freqCnt);
    Wait(freqCnt);
    IIC_SDA_LOW();
    Wait(freqCnt);
    IIC_SCL_LOW();
    Wait(freqCnt);    
    
}

void I2CStop(void)
{
    IIC_SCL_LOW();
    IIC_SDA_LOW();
    Wait(freqCnt);
    IIC_SCL_HIGH();
    Wait(freqCnt);
    IIC_SDA_HIGH();
    Wait(freqCnt);
} 
 
void I2CRepeatedStart(void)
{
    IIC_SCL_LOW();
    Wait(freqCnt);
    IIC_SDA_HIGH();
    Wait(freqCnt);
    IIC_SCL_HIGH();
    Wait(freqCnt);
    IIC_SDA_LOW();
    Wait(freqCnt);
    IIC_SCL_LOW();
           
}

void I2CSendACK(void)
{
    IIC_SDA_LOW();
    Wait(freqCnt);
    IIC_SCL_HIGH();
    Wait(freqCnt);
    Wait(freqCnt);
    IIC_SCL_LOW();
    Wait(freqCnt);
    IIC_SDA_HIGH();
    
}

void I2CSendNack(void)
{
    IIC_SDA_HIGH();
    Wait(freqCnt);
    IIC_SCL_HIGH();
    Wait(freqCnt);
    Wait(freqCnt);
    IIC_SCL_LOW();
    Wait(freqCnt);
    IIC_SDA_HIGH();
    
}

int I2CReceiveAck(void)
{
    int ack;
    
    IIC_SDA_HIGH();
    Wait(freqCnt);
    IIC_SCL_HIGH();
    Wait(freqCnt);
    if(IIC_SDA_READ() == 0)
    {
        ack = 0;
    }
    else
    {
        ack = 1;
    }
    Wait(freqCnt);        
    IIC_SCL_LOW();
    IIC_SDA_LOW();
    
    return ack;    
}      

打开MLX90640_I2C_Driver.h文件,把SDA和SCL拉高拉低的宏定义实现:

/**
 * @copyright (C) 2017 Melexis N.V.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */
#ifndef _MLX90640_I2C_Driver_H_
#define _MLX90640_I2C_Driver_H_
//#ifdef __cplusplus
// extern "C" {
//#endif
#include <stdint.h>
#include "stm32f10x.h"
	 
//IO方向设置
#define SDA_IN()  {GPIOA->CRH&=0X0FFFFFFF;GPIOA->CRH|=8<<28;}
#define SDA_OUT() {GPIOA->CRH&=0X0FFFFFFF;GPIOA->CRH|=3<<28;}


#define GPIO_PORT_I2C	GPIOA			
#define RCC_I2C_PORT 	RCC_APB2Periph_GPIOA		
#define I2C_SCL_PIN		GPIO_Pin_9			/* 连接到SCL时钟线的GPIO */
#define I2C_SDA_PIN		GPIO_Pin_15			/* 连接到SDA数据线的GPIO */

#define IIC_SCL_HIGH()  GPIO_PORT_I2C->BSRR = I2C_SCL_PIN				/* SCL = 1 */
#define IIC_SCL_LOW()   GPIO_PORT_I2C->BRR = I2C_SCL_PIN				/* SCL = 0 */

#define IIC_SDA_HIGH()  GPIO_PORT_I2C->BSRR = I2C_SDA_PIN				/* SDA = 1 */
#define IIC_SDA_LOW()   GPIO_PORT_I2C->BRR = I2C_SDA_PIN				/* SDA = 0 */

#define IIC_SDA_READ()  ((GPIO_PORT_I2C->IDR & I2C_SDA_PIN) != 0)	    /* 读SDA口线状态 */



void MLX90640_I2CInit(void);
int  MLX90640_I2CRead(uint8_t slaveAddr,uint16_t startAddress, uint16_t nMemAddressRead, uint16_t *data);
int  MLX90640_I2CWrite(uint8_t slaveAddr,uint16_t writeAddress, uint16_t data);
void MLX90640_I2CFreqSet(int freq);
//#ifdef __cplusplus
//}
//#endif
#endif

硬件IIC移植

  假如使用硬件IIC,那么需要将functions文件夹下的MLX90640_API.cpp、MLX90640_I2C_Driver.cpp包含进工程,同时把headers文件夹下的MLX90640_API.h、MLX90640_I2C_Driver.h也包含进工程。
  效果如下:

  添加好以后我们还需要适配 I2C 驱动,这样才能使得 MLX90640_API.cpp 文件可以正确的通过MLX90640_I2C_Driver.cpp 文件提供的方法读取传感器的值,如下图,将 MLX90640_I2CRead()方法和MLX90640_I2CWrite()关联 STM32 的 I2C 读写函数即可:
  读函数

  写函数

以上是关于使用MLX90640自制红外热像仪:MLX90640介绍与API库移植的主要内容,如果未能解决你的问题,请参考以下文章

使用MLX90640自制红外热像仪:图像插值图像彩色编码

使用MLX90640自制红外热像仪:stm32f103c8t6刷320x240分辨率屏的时间测试

MLX90640 红外热成像仪测温模块开发笔记

MLX90640 红外热成像仪测温传感器模块开发笔记

MLX90640 红外热成像仪开发笔记

MLX90640 红外热成像仪测温模块开发笔记(完整篇)