红外测温传感器MLX90614

【本文发布于https://blog.csdn.net/Stack_/article/details/132732671,未经许可不得转载,转载须注明出处】


1、MLX90614使用SMBus进行通讯,很多文章都没能说清楚I2C和SMBus的区别。反正在这里,这两者没什么区别,无需纠结。

2、貌似需要搭配透镜使用才能有更好的精度和更远的测量距离。

3、无地址选择引脚,内部也无唯一ID,要实现一条总线上挂接多个MLX90614,需要单独给每一个传感器的EEPROM写入不同的地址值之后才能挂到总线上.(暂无此需求,未研究)


#ifndef __MLX90614_H__
#define __MLX90614_H__

#include "public.h"


#define MLX90614_FIXED_BUS_ADDR              0x00    //不可修改的地址
#define MLX90614_EEPROM_BUS_ADDR             0x00    //可修改的地址,保存于eeprom的地址0eh处

                                            
#define MLX90614_MASK_RAM_ACCESS             0x00    //访问 RAM 或上此值
#define MLX90614_MASK_EEPROM_ACCESS          0x20    //访问 eeprom 或上此值                                            
#define MLX90614_MASK_READ_FLAG              0xf0    //访问 flag 或上此值                                            
#define MLX90614_ENTER_SLEEP_MODE            0xff    //                                          
                                            

#define MLX90614_EEPROM_ADDR___TO_MAX           (MLX90614_MASK_EEPROM_ACCESS | 0x00)                                            
#define MLX90614_EEPROM_ADDR___TO_MIN           (MLX90614_MASK_EEPROM_ACCESS | 0x01)
#define MLX90614_EEPROM_ADDR___PWM_CTRL         (MLX90614_MASK_EEPROM_ACCESS | 0x02)
#define MLX90614_EEPROM_ADDR___TA_RANGE         (MLX90614_MASK_EEPROM_ACCESS | 0x03)
#define MLX90614_EEPROM_ADDR___EMISSIVITY       (MLX90614_MASK_EEPROM_ACCESS | 0x04)
#define MLX90614_EEPROM_ADDR___CONFIG_REG1      (MLX90614_MASK_EEPROM_ACCESS | 0x05)
#define MLX90614_EEPROM_ADDR___SMBUS_ADDR       (MLX90614_MASK_EEPROM_ACCESS | 0x0E)
#define MLX90614_EEPROM_ADDR___ID_NUMBER1       (MLX90614_MASK_EEPROM_ACCESS | 0x1C)
#define MLX90614_EEPROM_ADDR___ID_NUMBER2       (MLX90614_MASK_EEPROM_ACCESS | 0x1D)
#define MLX90614_EEPROM_ADDR___ID_NUMBER3       (MLX90614_MASK_EEPROM_ACCESS | 0x1E)
#define MLX90614_EEPROM_ADDR___ID_NUMBER4       (MLX90614_MASK_EEPROM_ACCESS | 0x1F)


#define MLX90614_RAM_ADDR___RAW_DATA_IR_CH1           (MLX90614_MASK_RAM_ACCESS | 0x04)
#define MLX90614_RAM_ADDR___RAW_DATA_IR_CH2           (MLX90614_MASK_RAM_ACCESS | 0x05)
#define MLX90614_RAM_ADDR___TA                        (MLX90614_MASK_RAM_ACCESS | 0x06)     //环境温度
#define MLX90614_RAM_ADDR___TOBJ1                     (MLX90614_MASK_RAM_ACCESS | 0x07)     //物体温度
#define MLX90614_RAM_ADDR___TOBJ2                     (MLX90614_MASK_RAM_ACCESS | 0x08)





                                            
#define MLX90614_SCL_PORT               PORT1
#define MLX90614_SCL_PIN                PIN5
#define MLX90614_SCL_PIN_MODE           OPENDRAIN_OUTPUT


#define MLX90614_SDA_PORT               PORT1
#define MLX90614_SDA_PIN                PIN4
#define MLX90614_SDA_PIN_MODE_IN()           do {\
                                                *((volatile uint8_t*)(&PORT->PM0+MLX90614_SDA_PORT)) |= (1 << MLX90614_SDA_PIN);\
                                            } while (0)
#define MLX90614_SDA_PIN_MODE_OUT()          do {\
                                                *((volatile uint8_t*)(&PORT->PM0+MLX90614_SDA_PORT)) &= ~(1 << MLX90614_SDA_PIN);\
                                            } while (0)
#define MLX90614_SDA_PIN_GET()               PORT_GetBit(MLX90614_SDA_PORT, MLX90614_SDA_PIN)

                                            
                                            
typedef enum {
    MLX90164_WRITE = 0,
    MLX90164_READ
} mlx90164_cmdtype;    


#define MLX90164_NUM        1


typedef struct {
    uint8_t Online[MLX90164_NUM];    //是否在线
    uint8_t Valid[MLX90164_NUM];     //温度值是否有效
    int32_t Temperature_Ta[MLX90164_NUM];   //环境温度 2位小数
    int32_t Temperature_To[MLX90164_NUM];   //物体温度 2位小数
} MLX90164_t;    
                                                    
extern MLX90164_t   MLX90164_data;
                                                    
void MLX90614_Init(void);
void MLX90614_Proc(void);

#endif


/**
  ******************************************************************************
  * @copyright
  * @file      .c
  * @author
  * @version
  * @date      2023/1
  * @brief     红外测温传感器
  ******************************************************************************
  * @attention
  ******************************************************************************
  */

#include "MLX90614.h"

MLX90164_t   MLX90164_data;
uint8_t calc[32];
uint8_t *calc_p = calc;

#define DELAY_xUS       2   //


uint8_t Calc_PEC_CRC8(uint8_t *dat, uint8_t len)
{
    uint8_t i;
    uint8_t 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;
}
/**
  * @brief  起始信号
  * @note   SCL高电平期间,SDA跳变为低。需要保证start前sda和scl均为高
  * @param
  * @retval
  * @author PWH
  * @date   2022/7
  */
void MLX90614_Start(void)
{
    MLX90614_SDA_PIN_MODE_OUT();
    PORT_SetBit(MLX90614_SDA_PORT, MLX90614_SDA_PIN);
    PORT_SetBit(MLX90614_SCL_PORT, MLX90614_SCL_PIN);
    DELAY_US(DELAY_xUS);//
    PORT_ClrBit(MLX90614_SDA_PORT, MLX90614_SDA_PIN);
    DELAY_US(DELAY_xUS);//
    PORT_ClrBit(MLX90614_SCL_PORT, MLX90614_SCL_PIN);
    DELAY_US(DELAY_xUS);//
}
/**
  * @brief  结束信号
  * @note   SCL高电平期间,SDA跳变为高
  * @param
  * @retval
  * @author PWH
  * @date   2022/7
  */
void MLX90614_Stop(void)
{
    MLX90614_SDA_PIN_MODE_OUT();
    PORT_ClrBit(MLX90614_SDA_PORT, MLX90614_SDA_PIN);
    PORT_SetBit(MLX90614_SCL_PORT, MLX90614_SCL_PIN);
    DELAY_US(DELAY_xUS);//
    PORT_SetBit(MLX90614_SDA_PORT, MLX90614_SDA_PIN);
}
/**
  * @brief  主机发出应答
  * @note   发送完8bit后,第9bit发送前的SCL低电平期间,SDA拉低
  * @param
  * @retval
  * @author PWH
  * @date   2022/7
  */
void MLX90614_ACK(void)
{
    MLX90614_SDA_PIN_MODE_OUT();
    PORT_ClrBit(MLX90614_SDA_PORT, MLX90614_SDA_PIN);
    PORT_SetBit(MLX90614_SCL_PORT, MLX90614_SCL_PIN);
    DELAY_US(DELAY_xUS);//
    PORT_ClrBit(MLX90614_SCL_PORT, MLX90614_SCL_PIN);
    DELAY_US(DELAY_xUS);//
}
/**
  * @brief  主机发出非应答
  * @note   发送完8bit后,第9bit发送前的SCL低电平期间,SDA拉高
  * @param
  * @retval
  * @author PWH
  * @date   2022/7
  */
void MLX90614_NACK(void)
{
    MLX90614_SDA_PIN_MODE_OUT();
    PORT_SetBit(MLX90614_SDA_PORT, MLX90614_SDA_PIN);
    PORT_SetBit(MLX90614_SCL_PORT, MLX90614_SCL_PIN);
    DELAY_US(DELAY_xUS);//
    PORT_ClrBit(MLX90614_SCL_PORT, MLX90614_SCL_PIN);
    DELAY_US(DELAY_xUS);//
}
/**
  * @brief  等待从机应答
  * @note   每发送一个字节后,再产生一个scl低电平让从机把应答信号放到sda上
  * @param
  * @retval
  * @author PWH
  * @date   2022/7
  */
uint8_t MLX90614_WaitACK(void)
{
    uint8_t ack;

    MLX90614_SDA_PIN_MODE_IN();
    PORT_SetBit(MLX90614_SCL_PORT, MLX90614_SCL_PIN);
    ack = MLX90614_SDA_PIN_GET() ? 0 : 1;   //从机拉低sda应答
    DELAY_US(DELAY_xUS);//
    PORT_ClrBit(MLX90614_SCL_PORT, MLX90614_SCL_PIN);
    DELAY_US(DELAY_xUS);//
    return ack;
}
/**
  * @brief
  * @note
  * @param
  * @retval
  * @author PWH
  * @date   2022/7
  */
void MLX90614_SendByte(uint8_t _1byte)
{
    uint8_t i;

    MLX90614_SDA_PIN_MODE_OUT();
    for (i = 0; i < 8; i++)
    {
        if (_1byte & 0x80)
            PORT_SetBit(MLX90614_SDA_PORT, MLX90614_SDA_PIN);
        else
            PORT_ClrBit(MLX90614_SDA_PORT, MLX90614_SDA_PIN);
        PORT_SetBit(MLX90614_SCL_PORT, MLX90614_SCL_PIN);
        DELAY_US(DELAY_xUS);//
        _1byte <<= 1;
        PORT_ClrBit(MLX90614_SCL_PORT, MLX90614_SCL_PIN);   //时钟低电平期间才允许数据变化
        DELAY_US(DELAY_xUS);//
    }
}
/**
  * @brief
  * @note
  * @param
  * @retval
  * @author PWH
  * @date   2022/7
  */
void MLX90614_ReadByte(uint8_t *_1byte)
{
    uint8_t i;

    *_1byte = 0;
    MLX90614_SDA_PIN_MODE_IN();
    for (i = 0; i < 8; i++)
    {
        PORT_SetBit(MLX90614_SCL_PORT, MLX90614_SCL_PIN);
        DELAY_US(DELAY_xUS);//
        *_1byte <<= 1;
        if (MLX90614_SDA_PIN_GET())
            *_1byte |= 0x01;
        PORT_ClrBit(MLX90614_SCL_PORT, MLX90614_SCL_PIN);
        DELAY_US(DELAY_xUS);//
    }
}
/**
  * @brief
  * @note
  * @param
  * @retval
  * @author PWH
  * @date   2022/7
  */
void MLX90614_ReadWrite(uint8_t mlx_addr, mlx90164_cmdtype cmd, uint8_t reg_addr, uint8_t *bytes, uint8_t bytes_num)
{
    uint8_t i;
    calc_p = calc;  //缓存一帧用作校验计算

    *calc_p++ = (mlx_addr <<= 1) & 0xfe;   //器件地址

    MLX90614_Start();
    MLX90614_SendByte(mlx_addr & 0xfe); 
    MLX90614_WaitACK();
    MLX90614_SendByte(reg_addr);    //寄存器地址
    *calc_p++ = reg_addr;
    MLX90614_WaitACK();
    if (cmd == MLX90164_WRITE)
    {
        for (i = 0; i < bytes_num; i++) //发送数据
        {
            *calc_p++ = *bytes;
            MLX90614_SendByte(*bytes++);
            MLX90614_WaitACK();
        }
        MLX90614_SendByte(Calc_PEC_CRC8(calc, calc_p - calc));  //发送校验
    }
    else
    {
        MLX90614_Start();
        MLX90614_SendByte(mlx_addr | 0x01); //器件地址 读
        *calc_p++ = mlx_addr | 0x01;
        MLX90614_WaitACK();
        for (i = 0; i < bytes_num; i++)
        {
            MLX90614_ReadByte(bytes);
            
            if (i == bytes_num - 1) {
                MLX90614_NACK();
            }
            else {
                MLX90614_ACK();
            }
            
            *calc_p++ = *bytes++;
        }
    }
    MLX90614_Stop();
}



/**
  * @brief  初始化
  * @note
  * @param
  * @retval
  * @author PWH
  * @date   2022/7
  */
void MLX90614_Init(void)
{
    uint32_t delay;

    PORT_Init(MLX90614_SCL_PORT, MLX90614_SCL_PIN, MLX90614_SCL_PIN_MODE);
    PORT_ClrBit(MLX90614_SCL_PORT, MLX90614_SCL_PIN);   //拉低scl超过1.44ms,传感器将由pwm转为i2c工作模式
    UserTimer_Reset(&delay);
    while (UserTimer_Read(&delay) < 3);
    PORT_SetBit(MLX90614_SCL_PORT, MLX90614_SCL_PIN);

    PORT_Init(MLX90614_SDA_PORT, MLX90614_SDA_PIN, OPENDRAIN_OUTPUT);
    MLX90614_SDA_PIN_MODE_OUT();
    PORT_SetBit(MLX90614_SDA_PORT, MLX90614_SDA_PIN);

    memset(MLX90164_data.Online, 0, sizeof(MLX90164_data));
}


#define MLX_TEST    0
/**
  * @brief
  * @note   main while中调用
  * @param
  * @retval
  * @author PWH
  * @date   2023/2
  */
void MLX90614_Proc(void)
{
    static uint32_t timer = 0;
    uint8_t temp[3];
    uint16_t temperature;
    #if (MLX_TEST == 1)
    uint8_t i;
    #endif

    if (UserTimer_Read(&timer) > TIMEOUT_1S)	//1s
    {
        UserTimer_Reset(&timer);

        MLX90164_data.Online[0] = 0;
        MLX90164_data.Valid[0] = 0;

        MLX90614_ReadWrite(MLX90614_FIXED_BUS_ADDR, MLX90164_READ, MLX90614_RAM_ADDR___TA, temp, 3);
        #if (MLX_TEST == 1)
        for (i = 0; i < calc_p - calc; i++)
        {
            printf("calc[%d] = %#x\r\n", i, calc[i]);
        }
        printf("temp0=%#x,temp1=%#x,temp2=%#x\r\n", temp[0], temp[1], temp[2]);
        #endif
        if (Calc_PEC_CRC8(calc, calc_p - calc - 1) == temp[2])
        {
            MLX90164_data.Temperature_Ta[0] = (int32_t)(*((uint16_t *)temp) * 2) - (int32_t)27315;
            #if (MLX_TEST == 1)
            printf("ta成功temp0=%#x,temp1=%#x,temp2=%#x\r\n", temp[0], temp[1], temp[2]);
            #endif
//            MLX90164_data.Online[0] = 1;
//            MLX90164_data.Valid[0] = 1;
        }
        #if (MLX_TEST == 1)
        else
        {
            printf("ta失败-%#x\r\n", Calc_PEC_CRC8(calc, calc_p - calc - 1));
        }
        #endif

        MLX90614_ReadWrite(MLX90614_FIXED_BUS_ADDR, MLX90164_READ, MLX90614_RAM_ADDR___TOBJ1, temp, 3);
        #if (MLX_TEST == 1)
        for (i = 0; i < calc_p - calc; i++)
        {
            printf("calc[%d] = %#x\r\n", i, calc[i]);
        }
        printf("temp0=%#x,temp1=%#x,temp2=%#x\r\n", temp[0], temp[1], temp[2]);
        #endif
        if (Calc_PEC_CRC8(calc, calc_p - calc - 1) == temp[2])
        {
            //温度换算公式:寄存器值 * 0.02, 结果单位为开尔文
            MLX90164_data.Temperature_To[0] = (int32_t)(*((uint16_t *)temp) * 2) - (int32_t)27315;
            #if (MLX_TEST == 1)
            printf("to成功temp0=%#x,temp1=%#x,temp2=%#x\r\n", temp[0], temp[1], temp[2]);
            #endif
            MLX90164_data.Online[0] = 1;
            MLX90164_data.Valid[0] = 1;
        }
        #if (MLX_TEST == 1)
        else
        {
            printf("to失败-%#x\r\n", Calc_PEC_CRC8(calc, calc_p - calc - 1));
        }

        printf("环境温度%f℃,物体温度%f℃\r\n\r\n", (float)MLX90164_data.Temperature_Ta[0] / 100, (float)MLX90164_data.Temperature_To[0] / 100);
        #endif
    }
}






你可能感兴趣的:(笔记,MCU,物联网,单片机)