【本文发布于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
}
}