STM32 IIC采集MLX90615无接触温度传感器
MLX90615 是用于非接触温度测量的红外温度计
电压使用3.3V
-40 到 85 ˚C 传感器温度范围
-40 到 115 ˚C 物体温度范围

有两种输出模式: SMBus 模式 和 PWM 模式
这里我们使用的是 SMBus 模式,故PWM模式就不介绍了。
RAM地址表

上图中我们要注意是的Ta和To的地址,Ta是环境温度(就是他封装、外壳的温度)地址是0X06,To是被测量物体温度,地址是0X07。
以下是示例时序图的相关术语图:

读取与写入的操作顺序

读取RAM的被测物体温度例子

1、先发送IIC起始信号(对应图中S)
2、发送IIC写命令( 写命令即 将器件地址(0X5B)左移一位空出最后一位再按位或上0或者1,最后一位=0为写命令(对应图中W),=1为读命令,而0x5B左移之后就是0xB6,0xb6|0 = 0xb6)
3、等待从机应答(对应图中A)(在SCL为高电平的状态下,SDA被从机拉为低电平,即为应答;为高电平为非应答)
4、从机应答后发送寄存器地址(这里的0X27代表的是从RAM中读取物体温度,RAM地址为2xH,其中x为未知;物体温度数据地址为07H,将两者相或 0x20|0x07 = 0x27)
2xH地址来源:

07H地址来源见之前的RAM地址表
5、等待从机应答
6、发送IIC读命令(0x5B<<1 | 1 = 0xb6 | 1 = 0xb7 )
7、等待从机应答
8、读取物体温度的低8位数据
9、主机发送应答
10、读取物体温度的高8位数据
11、主机发送应答
12、读取校验码
13、主机发送应答
14、IIC停止信号
上代码:
IIC.c
#include "iic.h"
//IIC起始信号
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停止信号
void IIC_Stop(void)
{
SDA_OUT();
SCL(0);
SDA(0);
SCL(1);
delay_us(5);
SDA(1);
delay_us(5);
}
//主机发送应答
//0应答
//1非应答
char 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);
return 0;
}
//等待从机应答
unsigned char IIC_Whit_Ack(void)
{
char ack = 0;
unsigned char ack_flag = 10;
// SCL(0);
// SDA(1);
SDA_IN();
delay_us(5);
SCL(1);
delay_us(5);
while( (GETSDA()==1) && ( ack_flag ) )
{
ack_flag--;
delay_us(5);
}
if( ack_flag <= 0 )
{
IIC_Stop();
return 1;
}
else
{
SCL(0);
SDA_OUT();
}
return ack;
}
//写一个字节
void IIC_Write(unsigned char dat)
{
int i = 0;
SDA_OUT();
SCL(0);//拉低时钟开始数据传输
for( i = 0; i < 8; i++ )
{
SDA( (dat & 0x80) >> 7 );
__nop();
SCL(1);
delay_us(5);
SCL(0);
delay_us(5);
dat<<=1;
}
}
//读1个字节
unsigned char IIC_Read(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( GETSDA() )
{
receive|=1;
}
delay_us(5);
}
SCL(0);
return receive;
}
//CRC校验
unsigned char crc8(const unsigned char *data, int len)
{
const unsigned char POLYNOMIAL = 0x31;
unsigned char crc = 0xFF;
int j, i;
for (j=0; j<len; j++)
{
crc ^= *data++;
for ( i = 0; i <8; i++ )
{
crc = ( crc & 0x80 ) ? (crc << 1) ^ POLYNOMIAL : (crc << 1);
}
}
return crc;
}
IIC.h
#ifndef _IIC_H_
#define _IIC_H_
//使用的是 STM32F405RGT6
#include "stm32f4xx.h"
#include "delay.h"
#include "usart.h"
//根据寄存器进行修改
#define SDA_IN() {GPIOB->MODER &= ~(0X03<<14);} //设置SDA为输入模式
#define SDA_OUT() {GPIOB->MODER |= (0X01<<14);} //设置SDA为输出模式
//置位SCL与SDA电平
#define SCL(BIT) GPIO_WriteBit( GPIOB, GPIO_Pin_6, BIT)
#define SDA(BIT) GPIO_WriteBit( GPIOB, GPIO_Pin_7, BIT)
//读取SDA的电平
#define GETSDA() GPIO_ReadInputDataBit( GPIOB, GPIO_Pin_7)
/////////////////// IIC ///////////////////
void IIC_Start(void);
void IIC_Stop(void);
char IIC_Send_Ack(unsigned char ack);
unsigned char IIC_Whit_Ack(void);
void IIC_Write(unsigned char dat);
unsigned char IIC_Read(void);
unsigned char crc8(const unsigned char *data, int len);
#endif
mlx90615.c
#include "mlx90615.h"
/************************************************************
* 函数名称:MLX90615_IIC_Init
* 函数说明:MLX90615引脚IIC初始化
* 型 参:无
* 返 回 值:无
* 备 注://我的IIC引脚是 PB6-SCL PB7-SDA
*************************************************************/
void MLX90615_IIC_Init(void)
{
GPIO_InitTypeDef GPIOINIT={0};
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB,ENABLE);
GPIOINIT.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7;
GPIOINIT.GPIO_OType = GPIO_OType_OD;
GPIOINIT.GPIO_PuPd = GPIO_PuPd_UP;
GPIOINIT.GPIO_Speed = GPIO_Speed_25MHz;
GPIOINIT.GPIO_Mode = GPIO_Mode_OUT;
GPIO_Init(GPIOB,&GPIOINIT);
SCL(1); //拉高
SDA(1); //拉高
}
/************************************************************
* 函数名称:I2C_CRC
* 函数说明:CRC校验
* 型 参:sp = 要校验的数据,len = 校验长度
* 返 回 值:校验值
* 备 注:无
*************************************************************/
unsigned char I2C_CRC(const unsigned char *sp, int len) //I2C:数据校验函数
{
const unsigned char PYM = 0x31; //const:关键字:只读
unsigned char crc = 0xff; //8 位都是 1
int i = 0, j = 0;
for(i=0; i<len; i++) //一共有16位:高8位,,低8位 len: 一般是 2
{
crc ^= *sp++; //sp 的地址自增
for(j=0; j<8; j++)
{
//真:执行:(crc << 1) ^ PYM ,,假:执行:(crc << 1)
crc = (crc & 0x80) ? (crc << 1) ^ PYM : (crc << 1);
}
}
return crc;
}
/************************************************************
* 函数名称:MXL90615_Read
* 函数说明:读取MXL90615的温度
* 型 参:SlaveAddr = 器件地址 RegAddr = 要操作的寄存器地址
* 返 回 值:温度值
* 备 注:MLX90615的从机地址为 0X5B,读取被测物体温度地址0X27,读取环境温度地址0X26
*************************************************************/
float MXL90615_Read(unsigned char SlaveAddr, unsigned char RegAddr)
{
int i = 0;
unsigned char buff[3]={0}; //保存温度高低位与校验码
unsigned char arr[6]={0}; //校验数据使用
short temp = 0; //高低位整合数据保存
float T=0.0; //换算出的实际温度
again:
IIC_STOP();
IIC_START();
IIC_WRITE((SlaveAddr<<1)|0); //写命令
if( IIC_WHIT_ACK() ) //等待响应
{
printf("ERROR CODE 1\r\n");
goto again;
}
IIC_WRITE(RegAddr); //写入要操作的寄存器地址
if( IIC_WHIT_ACK() )
{
printf("ERROR CODE 2\r\n");
goto again;
}
IIC_START(); //重新开始IIC
IIC_WRITE((SlaveAddr<<1)|1); //读命令
if( IIC_WHIT_ACK() )
{
printf("ERROR CODE 3\r\n");
goto again;
}
buff[0] = IIC_READ(); //保存温度数据的低8位
IIC_SEND_ACK(ACK); //主机发送应答
buff[1] = IIC_READ(); //保存温度数据的高8位
IIC_SEND_ACK(ACK); //主机发送应答
buff[2] = IIC_READ(); //保存校验码
IIC_SEND_ACK(ACK); //主机发送应答
//停止时序
IIC_Stop();
//使用校验
#if CRC_VERIFY_ENABLE
arr[4]=SlaveAddr; //写命令
arr[3]=RegAddr; //寄存器地址
arr[2]=SlaveAddr+1; //读命令
arr[1]=buff[0]; //低8位
arr[0]=buff[1]; //高8位
//校验
if( crc8(arr,2) != buff[2] )//校验正确
{
temp = (short)(buff[1]<<8) | buff[0]; //整合高低位
T = (temp*0.02)-273.15 ; //带入公式换算出实际温度
// printf("MLX90615=%.2f\r\n",T);
}
else
{
printf("ERROR CODE 4\r\n");
goto again;
}
#endif
//不使用校验
#if !CRC_VERIFY_ENABLE
temp = (short)(buff[1]<<8) | buff[0];
T = (temp*0.02)-273.15 ;
printf("MLX90615=%.2f\r\n",T);
#endif
return T;
}
mlx90615.h
#ifndef _MLX90615_H_
#define _MLX90615_H_
#include "stm32f4xx.h"
#include "delay.h"
#include "usart.h"
#include "iic.h"
//是否使用CRC校验 1使用 0不使用
//不使用也没有事只是有时候会出现数据不对的情况
//
#define CRC_VERIFY_ENABLE 1
#define ACK 0 //应答
#define NACK 1 //非应答
// 移植注意:
// 将自己的IIC时序函数对应替换即可
// 我自己的函数是:
// void IIC_Write(unsigned char com); //传入要写入的地址
// unsigned char IIC_Read(void); //返回IIC的数据
// unsigned char IIC_Whit_Ack(void); //返回从机是否有应答 ACK or NACK
// void IIC_Send_Ack(unsigned char ack); //主机发送应答信号 ACK or NACK
//
#define IIC_START IIC_Start //IIC开始信号
#define IIC_STOP IIC_Stop //IIC结束信号
#define IIC_WRITE IIC_Write //IIC写
#define IIC_READ IIC_Read //IIC读
#define IIC_WHIT_ACK IIC_Whit_Ack //IIC等待从机应答
#define IIC_SEND_ACK IIC_Send_Ack //IIC主机发送应答
void MLX90615_IIC_Init(void);//IIC引脚初始化
float MXL90615_Read(unsigned char SlaveAddr, unsigned char RegAddr);//读取温度
#endif
main.c
#include "stm32f4xx.h"
#include "usart.h"
#include "delay.h"
#include "iic.h"
#include "mlx90615.h"
/***************************************
编 译 版 本:MDK-keil5.37--CC5
编 辑 时 间:2022-12-12
编 辑 人 员:老怪.
****************************************/
int main(void)
{
float Temp = 0.0;
//中断分组 2组
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
//滴答定时器初始化
delay_init(168);
//串口1配置
USART1_Init();
//MLX90615的IIC引脚初始化
MLX90615_IIC_Init();
while(1)
{
//读取RAM中的被测物体温度数据
Temp = MXL90615_Read(0x5b,0x27);
printf("Temp = %.2f\r\n", Temp);
delay_ms(500);
}
}
效果:
当我的手放在传感器上方时,温度为30摄氏度左右

工程链接:
链接:https://pan.baidu.com/s/1a2Ox9xRNFDR_o1e6ldiMww?pwd=1234
提取码:1234
魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。
更多推荐


所有评论(0)