如果想制作一个热成像仪,或者红外图像相关的什么东西,首先就需要找到一个好用的热成像模块。
假设你希望这个模块价格不要太贵(500元以下),精度也不用太高也不要太低(单帧数据为一个几十×几十的矩阵),那么MLX90640
基于这款芯片制作的热成像模块就是你的不二选择。
在说明驱动方式之前,咱先看看这款芯片的特性和基于它制作的一种模块GYMCU90640
MLX90640
是Melexis
公司发布的一款热成像芯片,共有长镜头和短镜头两种封装。长镜头视角小、成像距离远;短镜头则视角大,成像距离近。
上图是从MLX90640
的数据手册(DataSheet
)里面截取的,这里给出Melexis
公司的官网:https://www.melexis.com/zh,相关的技术文档可以从这里获取到。
从数据手册里可以看到芯片的特性参数——支持使用I2C
通信协议读取数据,需要3.3V
的电源电压。
MXL90640
的引脚如下图所示,共VDD,GND,SCL,SDA
四个引脚,两个用于接电源的引脚(VDD,GND
)、两个支持IIC
通信的引脚(SCL,SDA
)。
GYMCU90640
是基于MLX90640
设计的热成像模块,这个模块在芯片原有的I2C
协议之外还附加了一个USRT
通信接口。
借助CH340
等串口转USB
的模块,GYMCU90640
模块可以接到电脑上配合上位机显示镜头前的热辐射图。
和MPU6050
一样,MLX90640
可以被挂载到I2C总线上。像下图那样,如果它被当作DEVICE n
挂到I某个I2C
总线上,我们使用的STM32
等MCU
就可以通过蓝色的SCL
线和红色的SDA
线这两条线对MLX90640
进行读写操作啦。
这部分是I2C
通信的知识,有疑问的话可以自行补一补哈。这里给一个链接:I2C总线_百科
与MPU6050
不一样的是,这个热成像芯片的配置和数据解算比较复杂,自行写驱动就比较麻烦啦。所以呢,Melexis
公司官方也就给出了驱动,需要的时候移植官方的驱动就可以了。
Melexis
公司把驱动放在GIthub上仓库里,链接是: Mlx90640官方驱动
Github
可能比较慢,可以使用GitCode的镜像仓库:Mlx90640官方驱动镜像仓库链接
上面链接给的官方驱动仓库里面包含了驱动的源文件、头文件和驱动的说明书。
驱动包含了I2C
驱动和MLX90640
读写API
两部分,functions
文件夹中包含三个.c文件——MLX90640_API.cpp
、MLX90640_I2C_Driver.cpp
和MLX90640_SWI2C_Driver.cpp
。
MLX90640_I2C_Driver.cpp
是硬件I2C
;MLX90640_SWI2C_Driver.cpp
是软件I2C
。
为了方便大伙理解API
和I2C
的关系,特意画了下面这张图哈。API
是建立在I2C
驱动基础上的,只要I2C
的驱动写好(不管是软件还是硬件),那么编写代码调用API
就可以对MLX90640
进行读写操作啦。
到这里,我们就知道,根据我们的硬件改好驱动就行,那接下来咱就看看怎么改驱动。
怎么修改呢?有个说明书(MLX90640.pdf
)嘛,咱先翻翻看看。
主要看第二章I2C Driver
和第三章MLX90640 API
。下面这张图是说明书第二章的概述,写的很清楚吧,主要改驱动的硬件参数。
6.1 删函数
虽然前面说不要改API文件,但我们要删一个函数:
int MLX90640_TriggerMeasurement(uint8_t slaveAddr);
这个我们用不上,不用担心,如果有问题加回来就行,所以建议注释,不要直接删除了。
6.2 改软件驱动.c文件
考虑到代码的通用性,并且为了规避STM32
里硬件I2C
的BUG
,建议使用软件I2C
,这里我们也只说软件I2C
的修改。
先看MLX90640_SWI2C_Driver.cpp
,下面这张图指出了需要删除的部分。这个源文件包含了“mbed.h”这个头文件,咱们没有,当然要把它删了吧,第二个红框框里面的也一样,全部删掉哈。
然后注意哟,里面有一个void Wait(int)
函数,这是控制I2C时序的延时函数,打开看看:
void Wait(int freqCnt)
{
int cnt;
for(int i = 0;i<freqCnt;i++)
{
cnt = cnt++;
}
}
只是个软件延时函数,由freqCnt
来控制延时时间。实际上,I2C
驱动里每次调用Wait()
函数,输入的参数都是同一个freqCnt
,所以咱们只需要改那个
static int freqCnt;
让他随便等于一个数(2、3或者4)就行啦,
static int freqCnt=2;
如果这个不管的话,软件I2C
通信是用不了的。
6.3 改软件驱动头文件
为了定义SCL和SDA线的引脚,咱们在MLX90640_I2C_Driver.h
这个头文件里加上:
#define SCL_PIN GPIO_Pin_6
#define SCL_GPIO_PORT GPIOB
#define SDA_PIN GPIO_Pin_7
#define SDA_GPIO_PORT GPIOB
#define SCL_HIGH GPIO_SetBits(SCL_GPIO_PORT , SCL_PIN)
#define SCL_LOW GPIO_ResetBits(SCL_GPIO_PORT , SCL_PIN)
#define SDA_HIGH GPIO_SetBits(SDA_GPIO_PORT , SDA_PIN )
#define SDA_LOW GPIO_ResetBits(SDA_GPIO_PORT , SDA_PIN)
#define SCL_read GPIO_ReadInputDataBit(SCL_GPIO_PORT , SCL_PIN)
#define SDA_read GPIO_ReadInputDataBit(SDA_GPIO_PORT , SDA_PIN)
把PB6
设置为SCL
,PB7
设置为SDA
。
然后加上一些常量宏定义和枚举,把整个头文件改成
#ifndef _MLX90640_I2C_Driver_H_
#define _MLX90640_I2C_Driver_H_
#include "stm32f10x.h"
#include "stm32f10x_gpio.h"
#include
enum REFRESHRATE{
FPS2HZ = 0x02,
FPS4HZ = 0x03,
FPS8HZ = 0x04,
FPS16HZ = 0x05,
FPS32HZ = 0x06
};
#define RefreshRate FPS32HZ
#define WAIT_TIC 2 //软件I2C频率控制
#define MLX90640_ADDR 0x33
#define TA_SHIFT 8 //Default shift for MLX90640 in open air
#define SCL_PIN GPIO_Pin_6
#define SCL_GPIO_PORT GPIOB
#define SDA_PIN GPIO_Pin_7
#define SDA_GPIO_PORT GPIOB
#define SCL_HIGH GPIO_SetBits(SCL_GPIO_PORT , SCL_PIN)
#define SCL_LOW GPIO_ResetBits(SCL_GPIO_PORT , SCL_PIN)
#define SDA_HIGH GPIO_SetBits(SDA_GPIO_PORT , SDA_PIN )
#define SDA_LOW GPIO_ResetBits(SDA_GPIO_PORT , SDA_PIN)
#define SCL_read GPIO_ReadInputDataBit(SCL_GPIO_PORT , SCL_PIN)
#define SDA_read GPIO_ReadInputDataBit(SDA_GPIO_PORT , SDA_PIN)
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);
int MLX90640_I2CGeneralReset(void);
#endif
首先包含两个头文件
#include "MLX90640_I2C_Driver.h"
#include "MLX90640_API.h"
初始化I2C
MLX90640_I2CInit();
初始化设备
paramsMLX90640 mlx90640;
MlxConfig_Init(&mlx90640);
初始化函数定义为
uint16_t eeMLX90640[832];
float mlx90640To[768];
uint16_t frame[834];
int status;
void MlxConfig_Init(paramsMLX90640* params)
{
MLX90640_SetRefreshRate(MLX90640_ADDR, RefreshRate);
printf("MLX90640_SetRefreshRate done\n");
MLX90640_SetChessMode(MLX90640_ADDR);
printf("MLX90640_SetChessMode done\n");
MlxConfig_Check(params);
}
paramsMLX90640
是一个存放配置信息的结构体,定义在头文件MLX90640_API.h
中
typedef struct
{
int16_t kVdd;
int16_t vdd25;
float KvPTAT;
float KtPTAT;
uint16_t vPTAT25;
float alphaPTAT;
int16_t gainEE;
float tgc;
float cpKv;
float cpKta;
uint8_t resolutionEE;
uint8_t calibrationModeEE;
float KsTa;
float ksTo[5];
int16_t ct[5];
uint16_t alpha[768];
uint8_t alphaScale;
int16_t offset[768];
int8_t kta[768];
uint8_t ktaScale;
int8_t kv[768];
uint8_t kvScale;
float cpAlpha[2];
int16_t cpOffset[2];
float ilChessC[3];
uint16_t brokenPixels[5];
uint16_t outlierPixels[5];
} paramsMLX90640;
获取、解算并发送数据
testShowThermal(&mlx90640);
这个函数定义为
void testMlx90640Value(paramsMLX90640* params)
{
printf("\n---------------start--------------------\n");
int status = MLX90640_GetFrameData(MLX90640_ADDR, frame);
if (status < 0)
{
printf("GetFrame Error: %d\r\n",status);
}
float vdd = MLX90640_GetVdd(frame, params);
float Ta = MLX90640_GetTa(frame, params);
float tr = Ta - TA_SHIFT; //Reflected temperature based on the sensor ambient temperature
printf("vdd: %f Tr: %f\r\n",vdd,tr);
MLX90640_CalculateTo(frame, params, emissivity , tr, mlx90640To);
for(int i = 0; i < 768; i++){
if(i%32 == 0 && i != 0){
printf("\r\n");
}
printf("%2.2f ",mlx90640To[i]);
}
}
计算MLX90640_CalculateTo()
需要的一个参数tr
时,
float tr = Ta - TA_SHIFT; //Reflected temperature based on the sensor ambient temperature
用到的TA_SHIFT
定义在头文件MLX90640_I2C_Driver.h
中,是我们刚才添加进去的:
#define TA_SHIFT 8 //Default shift for MLX90640 in open air
好啦,这样就可以用啦,更多的就自己看看驱动说明书吧,API
很丰富的。