今日学习配置MLX90614红外非接触温度计 与 STM32 F103C8T6 单片机的通信
文章提供测试代码讲解、完整工程下载、测试效果图
**本文需要用到的大概基础知识:**1.3寸OLED配置通信显示、IIC通信、 定时器配置使用
这里就只贴出我的 OLED驱动方面的网址链接了:
其余的在我STM32 F103C8T6专栏里找吧.......
STM32 F103C8T6学习笔记16:1.3寸OLED的驱动显示日历-CSDN博客
目录[MLX90614 存储器:](#MLX90614 存储器:)
[MLX90614 的 SMBus 协议:](#MLX90614 的 SMBus 协议:)
MLX90614相关基础概念:
红外测温优势:
一般来说,测温方式可分为接触式和非接触式
接触式测温只能测量被测物体与测温传 感器达到热平衡后的温度,所以响应时间长,且极易受环境温度的影响;
而红外测温是根据 被测物体的红外辐射能量来确定物体的温度,不与被测物体接触,具有影响动被测物体温度 分布场,温度分辨率高、响应速度快、测温范围广、不受测温上限的限制、稳定性好等特点, 近年来在家庭自动化、汽车电子、航空和军事上得到越来越广泛的应用。
MLX90614介绍:
MLX90614系列模块是一组通用的红外测温模块。
在出厂前该模块已进行校验及线 性化,具有非接触、体积小、精度高,成本低等优点。被测目标温度和环境温度能通过单通 道输出,并有两种输出接口,适合于汽车空调、室内暖气、家用电器、手持设备以及医疗设 备应用等。
MLX90614 是一款红外非接触温度计。TO-39 金属封装里同 时集成了红外感应热电堆探测器芯片和信号处理专用集成芯 片。 由于集成了低噪声放大器、17位模数转换器和强大的数字信 号处理单元,使得高精度和高分辨度的温度计得以实现。 温度计具备出厂校准化,有数字PWM和SMBus(系统管理 总线)输出模式。
作为标准,配置为 10 位的 PWM 输出格式用于连续传送温 度范围为-20...120 ˚C 的物体温度,其分辨率为 0.14 ˚C。 POR 默认模式是SMBus 输出格式
MLX90614 存储器:
EEPROM 只有某些存储单元用户能够写入,但是可以读出全部存储单元。
MLX90614 的 EEPROM 有32 个16 位存储单元,
其中存储单元
Tomax,Tomin,Ta 分别是 用户物体温度上下限和环境温度范围,
PWMCTRL是PWM配置寄存器。
RAM 用户不能向RAM写入数据,但是可以读一些存储单元。MLX90614 的RAM有 32 个17位存储单元,
其中TA,TOBJ1是环境温度和物体温度
在SMBus方式下,可以从这几个存储单元读出环境和被测物体的温度。
MLX90614 的 SMBus 协议:
单片机与MLX90614红外测温模块之间通信的方式是 "类IIC" 通信
意思就是通信方式跟IIC通信方式很像但又不是IIC,它有另外一个名字叫做SMBus。
SMBus (System Management Bus)是1995年由 intel公司提出的一种高效同步串行总线,SMBus只有两根信号线:双向数据线和时钟信号线,容许CPU与各种外围接口器件以串行方式进行通信、交换信息,既可以提高传输速度也可以减小器件的资源占用,另外即使在没有SMBus 接口的单片机上也可利用软件进行模拟。。MLX90614 SMBus时钟的最大频率为100KHz,最小为 10KHz。
起始信号与停止信号:
宏定义:
这里直接贴出所有需要的宏定义供查阅了:
#define ACK 0
#define NACK 1
#define SA 0x00 //Slave address ??MLX90614????0x00,????????0x5a
#define RAM_ACCESS 0x00 //RAM access command
#define EEPROM_ACCESS 0x20 //EEPROM access command
#define RAM_TA 0x06 //环境
#define RAM_TOBJ1 0x07 //To1 address in the eeprom 物体
#define RAM_TOBJ2 0x08 //
#define SMBUS_PORT GPIOB
#define SMBUS_SCK GPIO_Pin_10
#define SMBUS_SDA GPIO_Pin_11
#define RCC_APB2Periph_SMBUS_PORT RCC_APB2Periph_GPIOB
#define SMBUS_SCK_H() SMBUS_PORT->BSRR = SMBUS_SCK
#define SMBUS_SCK_L() SMBUS_PORT->BRR = SMBUS_SCK
#define SMBUS_SDA_H() SMBUS_PORT->BSRR = SMBUS_SDA
#define SMBUS_SDA_L() SMBUS_PORT->BRR = SMBUS_SDA
#define SMBUS_SDA_PIN() SMBUS_PORT->IDR & SMBUS_SDA
cpp
//在SMBus上生成启动条件
void SMBus_StartBit(void)
{
SMBUS_SDA_H(); // Set SDA line
SMBus_Delay(1); // Wait a few microseconds
SMBUS_SCK_H(); // Set SCL line
SMBus_Delay(5); // Generate bus free time between Stop
SMBUS_SDA_L(); // Clear SDA line
SMBus_Delay(10); // Hold time after (Repeated) Start
// Condition. After this period, the first clock is generated.
//(Thd:sta=4.0us min)
SMBUS_SCK_L(); // Clear SCL line
SMBus_Delay(2); // Wait a few microseconds
}
//在SMBus上生成停止条件
void SMBus_StopBit(void)
{
SMBUS_SCK_L(); // Clear SCL line
SMBus_Delay(5); // Wait a few microseconds
SMBUS_SDA_L(); // Clear SDA line
SMBus_Delay(5); // Wait a few microseconds
SMBUS_SCK_H(); // Set SCL line
SMBus_Delay(10); // Stop condition setup time(Tsu:sto=4.0us min)
SMBUS_SDA_H(); // Set SDA line
}
发送读取与PEC:
cpp
//延时
void SMBus_Delay(u16 time)
{
u16 i, j;
for (i=0; i<4; i++)
{
for (j=0; j<time; j++);
}
}
//从 RAM/EEPROM 读取数据
u16 SMBus_ReadMemory(u8 slaveAddress, u8 command)
{
u16 data; // Data storage (DataH:DataL)
u8 Pec; // PEC byte storage
u8 DataL=0; // Low data byte storage
u8 DataH=0; // High data byte storage
u8 arr[6]; // Buffer for the sent bytes
u8 PecReg; // Calculated PEC byte storage
u8 ErrorCounter; // Defines the number of the attempts for communication with MLX90614
ErrorCounter=0x00; // Initialising of ErrorCounter
slaveAddress <<= 1; //2-7???????
do
{
repeat:
SMBus_StopBit(); //If slave send NACK stop comunication
--ErrorCounter; //Pre-decrement ErrorCounter
if(!ErrorCounter) //ErrorCounter=0?
{
break; //Yes,go out from do-while{}
}
SMBus_StartBit(); //Start condition
if(SMBus_SendByte(slaveAddress))//Send SlaveAddress ???Wr=0????????
{
goto repeat; //Repeat comunication again
}
if(SMBus_SendByte(command)) //Send command
{
goto repeat; //Repeat comunication again
}
SMBus_StartBit(); //Repeated Start condition
if(SMBus_SendByte(slaveAddress+1)) //Send SlaveAddress ???Rd=1????????
{
goto repeat; //Repeat comunication again
}
DataL = SMBus_ReceiveByte(ACK); //Read low data,master must send ACK
DataH = SMBus_ReceiveByte(ACK); //Read high data,master must send ACK
Pec = SMBus_ReceiveByte(NACK); //Read PEC byte, master must send NACK
SMBus_StopBit(); //Stop condition
arr[5] = slaveAddress; //
arr[4] = command; //
arr[3] = slaveAddress+1; //Load array arr
arr[2] = DataL; //
arr[1] = DataH; //
arr[0] = 0; //
PecReg=PEC_Calculation(arr);//Calculate CRC
}
while(PecReg != Pec); //If received and calculated CRC are equal go out from do-while{}
data = (DataH<<8) | DataL; //data=DataH:DataL
return data;
}
u8 SMBus_SendByte(u8 Tx_buffer)
{
u8 Bit_counter;
u8 Ack_bit;
u8 bit_out;
for(Bit_counter=8; Bit_counter; Bit_counter--)
{
if (Tx_buffer&0x80)
{
bit_out=1; // If the current bit of Tx_buffer is 1 set bit_out
}
else
{
bit_out=0; // else clear bit_out
}
SMBus_SendBit(bit_out); // Send the current bit on SDA
Tx_buffer<<=1; // Get next bit for checking
}
Ack_bit=SMBus_ReceiveBit(); // Get acknowledgment bit
return Ack_bit;
}
void SMBus_SendBit(u8 bit_out)
{
if(bit_out==0)
{
SMBUS_SDA_L();
}
else
{
SMBUS_SDA_H();
}
SMBus_Delay(2); // Tsu:dat = 250ns minimum
SMBUS_SCK_H(); // Set SCL line
SMBus_Delay(10); // High Level of Clock Pulse
SMBUS_SCK_L(); // Clear SCL line
SMBus_Delay(10); // Low Level of Clock Pulse
// SMBUS_SDA_H(); // Master release SDA line ,
return;
}
u8 SMBus_ReceiveBit(void)
{
u8 Ack_bit;
SMBUS_SDA_H(); //?????????,????
SMBUS_SCK_H(); // Set SCL line
SMBus_Delay(2); // High Level of Clock Pulse
if (SMBUS_SDA_PIN())
{
Ack_bit=1;
}
else
{
Ack_bit=0;
}
SMBUS_SCK_L(); // Clear SCL line
SMBus_Delay(4); // Low Level of Clock Pulse
return Ack_bit;
}
u8 SMBus_ReceiveByte(u8 ack_nack)
{
u8 RX_buffer;
u8 Bit_Counter;
for(Bit_Counter=8; Bit_Counter; Bit_Counter--)
{
if(SMBus_ReceiveBit()) // Get a bit from the SDA line
{
RX_buffer <<= 1; // If the bit is HIGH save 1 in RX_buffer
RX_buffer |=0x01;
}
else
{
RX_buffer <<= 1; // If the bit is LOW save 0 in RX_buffer
RX_buffer &=0xfe;
}
}
SMBus_SendBit(ack_nack); // Sends acknowledgment bit
return RX_buffer;
}
//计算接收字节的PEC
u8 PEC_Calculation(u8 pec[])
{
u8 crc[6];
u8 BitPosition=47;
u8 shift;
u8 i;
u8 j;
u8 temp;
do
{
/*Load pattern value 0x000000000107*/
crc[5]=0;
crc[4]=0;
crc[3]=0;
crc[2]=0;
crc[1]=0x01;
crc[0]=0x07;
/*Set maximum bit position at 47 ( six bytes byte5...byte0,MSbit=47)*/
BitPosition=47;
/*Set shift position at 0*/
shift=0;
/*Find first "1" in the transmited message beginning from the MSByte byte5*/
i=5;
j=0;
while((pec[i]&(0x80>>j))==0 && i>0)
{
BitPosition--;
if(j<7)
{
j++;
}
else
{
j=0x00;
i--;
}
}/*End of while */
/*Get shift value for pattern value*/
shift=BitPosition-8;
/*Shift pattern value */
while(shift)
{
for(i=5; i<0xFF; i--)
{
if((crc[i-1]&0x80) && (i>0))
{
temp=1;
}
else
{
temp=0;
}
crc[i]<<=1;
crc[i]+=temp;
}/*End of for*/
shift--;
}/*End of while*/
/*Exclusive OR between pec and crc*/
for(i=0; i<=5; i++)
{
pec[i] ^=crc[i];
}/*End of for*/
}
while(BitPosition>8); /*End of do-while*/
return pec[0];
}
传感器与单片机引脚接线:
MLX90614配置应用设计函数:
类IIC引脚初始化:
cpp
//MLX90614 SMBus通信 初始化
void SMBus_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
/* Enable SMBUS_PORT clocks */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SMBUS_PORT, ENABLE);
/*??SMBUS_SCK?SMBUS_SDA????????*/
GPIO_InitStructure.GPIO_Pin = SMBUS_SCK | SMBUS_SDA;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(SMBUS_PORT, &GPIO_InitStructure);
SMBUS_SCK_H();
SMBUS_SDA_H();
}
定时器实时刷新OLED打印数据与BMP图像的标志:
cpp
//刷新时间标志
uint16_t TDisplay_cnt,TDisplay;
//刷新BMP图像
uint16_t BMP_cnt,BMP_FLAG,BMPDisplay;
cpp
//定时器2中断服务函数
void TIM2_IRQHandler(void)
{
if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
{
if(++TDisplay_cnt==15) //定时器刷新温度
{
TDisplay_cnt=0;
TDisplay=1;
}
if(++BMP_cnt==9) //定时器 刷新太空人图片
{
BMP_cnt=0;BMP_FLAG++;BMPDisplay=1;
if(BMP_FLAG==29){BMP_FLAG=1;}
}
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);//清出中断寄存器标志位,用于退出中断
}
}
数据读取与打印处理:
cpp
//OLED打印读取到的温度值
void Print_temperature(void)
{
if(TDisplay==1)
{
char buf[20]; //用于暂存oled数据
float temp; //读取温度
// huanjing=temp*100; //浮点数扩大100倍存入整数,方便显示
// wuti=temp*100; //浮点数扩大100倍存入整数,方便显示
temp=SMBus_ReadTemp(RAM_TA); //读取环境温度
OLED_ShowCHinese(65+16*0,0,2); //打印中文"环"
OLED_ShowCHinese(65+16*1,0,3); //打印中文"境"
OLED_ShowCHinese(65+16*2,0,0); //打印中文"温"
OLED_ShowCHinese(65+16*3,0,1); //打印中文"度"
//打印环境温度的值
sprintf(buf,"%.2f C",temp);
OLED_ShowString(70,2,(u8 *)buf,16);
temp=SMBus_ReadTemp(RAM_TOBJ1);//读取物体温度
OLED_ShowCHinese(65+16*0,4,4); //打印中文"物"
OLED_ShowCHinese(65+16*1,4,5); //打印中文"体"
OLED_ShowCHinese(65+16*2,4,0); //打印中文"温"
OLED_ShowCHinese(65+16*3,4,1); //打印中文"度"
//打印物体温度的值
sprintf(buf,"%.2f C",temp);
OLED_ShowString(70,6,(u8 *)buf,16);
TDisplay=0;
}
}
测试效果图与视频:
测试效果还行,能明显区分不同温度的物体:
类IIC通信---MLX90614红外非接触温度计