CW32模块使用 MLX90614无接触测温传感器

描述

MLX90614 系列模块是一组通用的红外测温模块。在出厂前该模块已进行校验及线性化,具有非接触、体积小、精度高,成本低等优点。被测目标温度和环境温度能通过单通道输出,并有两种输出接口,适合于汽车空调、室内暖气、家用电器、手持设备以及医疗设备应用等。测温方式可分为接触式和非接触式,接触式测温只能测量被测物体与测温传感器达到热平衡后的温度,所以响应时间长,且极易受环境温度的影响;而红外测温是根据被测物体的红外辐射能量来确定物体的温度,不与被测物体接触,具有影响动被测物体温度分布场,温度分辨率高、响应速度快、测温范围广、不受测温上限的限制、稳定性好等特点,所以我们选择mlx90614来作为红外测温模块。

单片机与mlx90614红外测温模块之间通信的方式是“类IIC”通信,意思就是通信方式跟IIC通信方式很像但又不是IIC,它有另外一个名字叫做SMBus。SMBus (System Management Bus)是 1995 年由 intel 公司提出的一种高效同步串行总线,SMBus 只有两根信号线:双向数据线和时钟信号线,容许 CPU 与各种外围接口器件以串行方式进行通信、交换信息,即可以提高传输速度也可以减小器件的资源占用,另外即使在没有SMBus 接口的单片机上也可利用软件进行模拟。

1、模块来源

模块实物展示:

 

CW32


资料下载链接:
https://pan.baidu.com/s/1AsEBvVCiNAvTKqTeGSA60w
提取码:g06n

2、规格参数

工作电压:4.5~5.5V

工作电流:1.3~2.5mA

以上信息见厂家资料文件

3、移植过程

我们的目标是将例程移植至CW32F030C8T6开发板上【能够测量物体温度和环境温度的功能】。首先要获取资料,查看数据手册应如何实现读取数据,再移植至我们的工程。

3.1查看资料

MLX90614中有两个存储器,分别为EEPROM和RAM。

MLX90614中共有32个字长为16位的EEPROM存储单元,其地址为000H—01FH。EEPROM中所有的寄存器都是可以通过SMBus进行读取,但只有部分寄存器是可以进行改写的(地址为0x00, 0x01, 0x02, 0x03, 0x04, 0x05*,0x0E, 0x0F, 0x19)。可改写部分如下图所示。因在出厂前模块已进行校验及线性化,所以我们直接使用默认参数,不需要修改。

CW32

CW32

MLX90614中总共有32个17位的RAM存储单元,用户不能通过RAM来写入数据,只能读取RAM中的部分存储单元读取16位存储数据。其采集的环境温度数据保存在地址06H存储单元中,采集的被测物体温度数据存储在07H存储单元中。因此运用存储在RAM地址中的数据,通过公式的计算,可以得到环境温度Ta及被测物体温度数据To。

CW32

需要注意的是数据的低8位在前面,高8位在后。

CW32

器件地址(Slave Address)在数据手册中有说明,默认器件地址为0X5A;

CW32

命令(Command)是根据要控制的是RAM还是eeprom来决定一个字节中的BIT7~BIT5。剩余的BIT4~BIT0由要操控的地址决定。

CW32

例如,我要读取RAM的Ta温度数据,则命令组成见下表。其中RAM地址为000x_xxxx,Ta温度数据地址为0x06=0000_0110,只取低5位则为xxx0_0110。

CW32

PEC是一个多项式为X8+X2+X1+1的CRC-8校验数据。

在数据手册中举了两个例子。其中0xB4为器件地址左移一位后的值。

CW32

得到温度的原始数据后,根据数据手册的说明进行换算即可得到温度。

CW32

以上是手册中举了一个例子,如果读取到的温度数据是0X3AF7,其10进制为15095,将10进制数 除以50或者乘以0.02得到301.9,再减去273.15即可得到实际温度。

温度 = 温度原始数据 * 0.02 - 273.15

该温度换算公式对To和Ta都适用。

3.2引脚选择

CW32

模块接线图

3.3移植至工程

工程模板参考入门手册的工程模板

移植步骤中的导入.c和.h文件与【CW32模块使用】DHT11温湿度传感器相同,只是将.c和.h文件更改为bsp_mlx90614.c与bsp_mlx90614.h。这里不再过多讲述,移植完成后面修改相关代码。

在文件bsp_mlx90614.c中,编写如下代码。

 

/*
 * Change Logs:
 * Date           Author       Notes
 * 2024-06-20     LCKFB-ALP    first version
 */

#include "bsp_mlx90614.h"
#include "stdio.h"


/******************************************************************
 * 函 数 名 称:MLX90614_GPIO_Init
 * 函 数 说 明:MLX90614的引脚初始化
 * 函 数 形 参:无
 * 函 数 返 回:无
 * 作       者:LC
 * 备       注:MLX90614是5V,而且开发板开发板的引脚输出是3.3V,
 *             故设置引脚模式时,必须设置为开漏模式
******************************************************************/
void MLX90614_GPIO_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStruct; // GPIO初始化结构体

    RCC_MLX90614_ENABLE();        // 使能GPIO时钟

    GPIO_InitStruct.Pins = GPIO_SCL|GPIO_SDA;             // GPIO引脚
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;           // 开漏输出
    GPIO_InitStruct.Speed = GPIO_SPEED_HIGH;              // 输出速度高
    GPIO_Init(PORT_MLX90614, &GPIO_InitStruct);              // 初始化
}


/******************************************************************
 * 函 数 名 称:IIC_Start
 * 函 数 说 明:IIC起始时序
 * 函 数 形 参:无
 * 函 数 返 回:无
 * 作       者:LC
 * 备       注:无
******************************************************************/
void IIC_Start(void)
{
        SDA_OUT();

        SDA(1);
        delay_us(5);
        SCL(1);
        delay_us(5);

        SDA(0);
        delay_us(5);
        SCL(0);
        delay_us(5);

}
/******************************************************************
 * 函 数 名 称:IIC_Stop
 * 函 数 说 明:IIC停止信号
 * 函 数 形 参:无
 * 函 数 返 回:无
 * 作       者:LC
 * 备       注:无
******************************************************************/
void IIC_Stop(void)
{
        SDA_OUT();
        SCL(0);
        SDA(0);

        SCL(1);
        delay_us(5);
        SDA(1);
        delay_us(5);

}

/******************************************************************
 * 函 数 名 称:IIC_Send_Ack
 * 函 数 说 明:主机发送应答或者非应答信号
 * 函 数 形 参:0发送应答  1发送非应答
 * 函 数 返 回:无
 * 作       者:LC
 * 备       注:无
******************************************************************/
void IIC_Send_Ack(unsigned char ack)
{
        SDA_OUT();
        SCL(0);
        SDA(0);
        delay_us(5);
        if(!ack) SDA(0);
        else     SDA(1);
        SCL(1);
        delay_us(5);
        SCL(0);
        SDA(1);
}


/******************************************************************
 * 函 数 名 称:I2C_WaitAck
 * 函 数 说 明:等待从机应答
 * 函 数 形 参:无
 * 函 数 返 回:0有应答  1超时无应答
 * 作       者:LC
 * 备       注:无
******************************************************************/
unsigned char I2C_WaitAck(void)
{
        char ack = 0;
        unsigned char ack_flag = 10;
        SCL(0);
        SDA(1);
        SDA_IN();
        delay_us(5);
        SCL(1);
        delay_us(5);

        while( (SDA_GET()==1) && ( ack_flag ) )
        {
                        ack_flag--;
                        delay_us(5);
        }

        if( ack_flag <= 0 )
        {
                        IIC_Stop();
                        return 1;
        }
        else
        {
                        SCL(0);
                        SDA_OUT();
        }
        return ack;
}

/******************************************************************
 * 函 数 名 称:Send_Byte
 * 函 数 说 明:写入一个字节
 * 函 数 形 参:dat要写人的数据
 * 函 数 返 回:无
 * 作       者:LC
 * 备       注:无
******************************************************************/
void Send_Byte(uint8_t dat)
{
        int i = 0;
        SDA_OUT();
        SCL(0);//拉低时钟开始数据传输

        for( i = 0; i < 8; i++ )
        {
                SDA( (dat & 0x80) > > 7 );
                delay_us(1);
                SCL(1);
                delay_us(5);
                SCL(0);
                delay_us(5);
                dat< <=1;
        }
}

/******************************************************************
 * 函 数 名 称:Read_Byte
 * 函 数 说 明:IIC读时序
 * 函 数 形 参:无
 * 函 数 返 回:读到的数据
 * 作       者:LC
 * 备       注:无
******************************************************************/
unsigned char Read_Byte(void)
{
        unsigned char i,receive=0;
        SDA_IN();//SDA设置为输入
        for(i=0;i< 8;i++ )
        {
                SCL(0);
                delay_us(5);
                SCL(1);
                delay_us(5);
                receive< <=1;
                if( SDA_GET() )
                {
                        receive|=1;
                }
                delay_us(5);
        }
        SCL(0);
        return receive;
}


///******************************************************************
// * 函 数 名 称:PEC_Calculation
// * 函 数 说 明:PEC校验
// * 函 数 形 参:pec要校验的数据地址   len校验的长度
// * 函 数 返 回:校验后的值
// * 作       者:LC
// * 备       注:无
//******************************************************************/
//static unsigned char PEC_Calculation(unsigned char *dat , unsigned char len)
//{
//    unsigned char i;
//    unsigned char crc=0;
//    while( len-- )
//    {
//        crc ^= *dat++;
//        for( i=0 ; i< 8 ; i++ )
//        {
//            if( crc&0x80 )
//            {
//                crc = (crc< <1)^0x07;
//            }
//            else
//            {
//                crc = (crc< <1);
//            }
//        }
//    }
//    return crc;
//}

/************************************************************
 * 函数名称:MLX90615_Read
 * 函数说明:读取MLX90615的温度
 * 型    参:SlaveAddr = 器件地址  RegAddr = 要操作的寄存器地址
 * 返 回 值:温度值
 * 备    注:   SlaveAddr = 0X5A默认器件地址
 *              RegAddr   = 0X07读取被测量物体温度
 *              RegAddr   = 0X06读取环境温度
*************************************************************/
float MLX90614_Read(unsigned char SlaveAddr, unsigned char RegAddr)
{
    IIC_Start();
    Send_Byte((SlaveAddr < < 1) | 0); // 写命令
    if(I2C_WaitAck()) {
        IIC_Stop();
        return 0.0;
    }

    Send_Byte(RegAddr); // 寄存器地址
    if(I2C_WaitAck()) {
        IIC_Stop();
        return 0.0;
    }

//        delay_ms(1);

    IIC_Start();
    Send_Byte((SlaveAddr < < 1) | 1); // 读命令
    if(I2C_WaitAck()) {
        IIC_Stop();
        return 0.0;
    }

    uint8_t lowByte = Read_Byte(); // 低8位
    IIC_Send_Ack(0);
    uint8_t highByte = Read_Byte(); // 高8位
    IIC_Send_Ack(1);
    IIC_Stop();

    uint16_t tempRaw = (highByte < < 8) | lowByte;
    float temperature = (tempRaw * 0.02) - 273.15;

    return temperature;
}

 

在文件bsp_mlx90614.h中,编写如下代码。

 

/*
 * Change Logs:
 * Date           Author       Notes
 * 2024-06-20     LCKFB-ALP    first version
 */

#ifndef _BSP_MLX90614_H_
#define _BSP_MLX90614_H_

#include "board.h"

//端口移植
#define RCC_MLX90614_ENABLE()   __RCC_GPIOA_CLK_ENABLE()
#define PORT_MLX90614               CW_GPIOA


#define GPIO_SDA                    GPIO_PIN_1
#define GPIO_SCL                    GPIO_PIN_0

//设置SDA输出模式
#define SDA_OUT()   {        
                        GPIO_InitTypeDef GPIO_InitStruct;                
                        GPIO_InitStruct.Pins = GPIO_SDA;                 
                        GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;      
                        GPIO_InitStruct.Speed = GPIO_SPEED_HIGH;         
                        GPIO_Init(PORT_MLX90614, &GPIO_InitStruct);         
                     }
//设置SDA输入模式
#define SDA_IN()    {        
                        GPIO_InitTypeDef GPIO_InitStruct;                
                        GPIO_InitStruct.Pins = GPIO_SDA;                 
                        GPIO_InitStruct.Mode = GPIO_MODE_INPUT_PULLUP;   
                        GPIO_InitStruct.Speed = GPIO_SPEED_HIGH;         
                        GPIO_Init(PORT_MLX90614, &GPIO_InitStruct);         
                    }
//获取SDA引脚的电平变化
#define SDA_GET()       GPIO_ReadPin(PORT_MLX90614, GPIO_SDA)
//SDA与SCL输出
#define SDA(x)          GPIO_WritePin(PORT_MLX90614, GPIO_SDA, (x?GPIO_Pin_SET:GPIO_Pin_RESET) )
#define SCL(x)          GPIO_WritePin(PORT_MLX90614, GPIO_SCL, (x?GPIO_Pin_SET:GPIO_Pin_RESET) )



void MLX90614_GPIO_Init(void);
float MLX90614_Read(unsigned char SlaveAddr, unsigned char RegAddr);

void IIC_Stop(void);
#endif

 

4、移植验证

在自己工程中的main主函数中,编写如下。

 

/*
 * Change Logs:
 * Date           Author       Notes
 * 2024-06-20     LCKFB-ALP    first version
 */
#include "board.h"
#include "stdio.h"
#include "bsp_uart.h"
#include "bsp_mlx90614.h"

int32_t main(void)
{
    board_init();        // 开发板初始化

    uart1_init(115200);        // 串口1波特率115200

    MLX90614_GPIO_Init();
    printf("Startrn");
    while(1)
    {
        float t = MLX90614_Read(0X5A, 0X07);
        if( t != 0)
        {
            printf("temperature = %.2frn", t);
        }
        delay_ms(1000);
    }
}

 

移植现象:测量手心温度为36℃左右。

CW32

模块移植成功案例代码:

链接:https://pan.baidu.com/s/1Ts1A7Eqng7yCa2YabmI6_A?pwd=LCKF

提取码:LCKF

审核编辑 黄宇

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分