基于STM32的MLX90614人体红外测温枪
点击上方“果果小师弟”,选择“置顶/星标公众号”
那么在这里我就来写一写关于mlx90614红外测温的驱动代码和来说一说他的原理。主要是利用STM32F103C8T6单片机和mlx90614模块将测量的温度显示到0.96寸oled上面,如果你没有oled的话也可以用串口调试助手把温度信息打印出来。
一、所需材料
STM32F103C8T6最小系统板+mlx90614红外测温模块+0.96寸oled+一个jlink下载器。
二、mlx90614传感器介绍
MLX90614 系列模块是一组通用的红外测温模块。在出厂前该模块已进行校验及线性化,具有非接触、体积小、精度高,成本低等优点。被测目标温度和环境温度能通过单通道输出,并有两种输出接口,适合于汽车空调、室内暖气、家用电器、手持设备以及医疗设备应用等。测温方式可分为接触式和非接触式,接触式测温只能测量被测物体与测温传感器达到热平衡后的温度,所以响应时间长,且极易受环境温度的影响;而红外测温是根据被测物体的红外辐射能量来确定物体的温度,不与被测物体接触,具有影响动被测物体温度分布场,温度分辨率高、响应速度快、测温范围广、不受测温上限的限制、稳定性好等特点,所以我们选择mlx90614来作为红外测温模块。
单片机与mlx90614红外测温模块之间通信的方式是“类IIC”通信,意思就是通信方式跟IIC通信方式很像但又不是IIC,它有另外一个名字叫做SMBus。SMBus (System Management Bus)是 1995 年由 intel 公司提出的一种高效同步串行总线,SMBus 只有两根信号线:双向数据线和时钟信号线,容许 CPU 与各种外围接口器件以串行方式进行通信、交换信息,即可以提高传输速度也可以减小器件的资源占用,另外即使在没有SMBus 接口的单片机上也可利用软件进行模拟。
三、MLX90614工作原理
MLX90614由MLX81101红外热电堆传感器和包括含有稳压电路、低噪声放大器、A/D转换器、DSP单元、脉宽调制电路及逻辑控制电路的MLX90302信号处理芯片构成。
其工作原理为:红外热电堆传感器输出的温度信号经过内部低噪声、低失调的运算放大器(OPA)放大后经过A/D转换器(ADC)转换为17位数字信号通过可编程FIR及IIR低通数字滤波器(即DSP)处理后输出,输出结果存储在其内部RAM存储单元中。
Ta=RAM(006H)x0.02-273.15
To=RAM(007H)x0.02-273.15
四、IIC协议原理
IIC主从机之间通讯步骤如下:
5.主机给从机一个停止信号。
1.写时序
首先主机发送一个起始位,然后在发送从机的地址0x00和写标志位0,一共是8位。发送这8位之后,主机等待从机的响应,如果从机发送的是应答信号,主机就继续向从机发送一个字节的数据,同理再一次等待从机的响应,主机收到响应之后如果还有数据要发就继续发送第二段数据等待响应…直到发送完成;
写所用到的函数有:
1.void SMBus_StartBit(void);—主机发送一个起始位
2.u8 SMBus_ SendByte (u8 ack_nack);—主机发送从机的地址和写标志位
3.SMBus_ReceiveBit(ack_nack);—从机发送的是应答信号
4.u8 SMBus_SendByte(u8 ack_nack);—主机发送从机的一个字节的数据
5.void SMBus_StopBit(void);—主机发送一个停止位
2.读时序
首先主机发送一个起始位,然后在发送从机的地址0x00和读标志位1,一共是8位。发送这8位之后,主机等待从机的响应,如果从机发送的是应答信号,从机就向主机发送一个字节的数据。读时序就是主机从从机读数据,反过来就是从机向主机发送数据,同理,从机在发送完一个字节的数据之后也要向主机询问是否继续发送,如果主机让继续发送也就是发送了应答信号,那么从机就继续发送数据,每次发送完之后都要询问是否要继续发送,直到主机发送了非应答信号,从机才停止发送数据,最后主机再发送一个停止信号即可。
读所用到的函数有:
1.void SMBus_StartBit(void);—主机发送一个起始位
2.u8 SMBus_ SendByte (u8 ack_nack);—主机发送从机的地址和写标志位
3.SMBus_ReceiveBit(ack_nack);—从机发送的是应答信号
4.u8 SMBus_ReceiveByte (u8 ack_nack);—从机向主机发送的一个字节的数据
5.void SMBus_StopBit(void);—主机发送一个停止位
3.通信过程
1.起始信号—在时钟线SCL高电平期间数据线SDA发生下降沿跳变产生起始信号
2.应答信号—在时钟线SCL为高电平期间数据线SDA保持低电平为应答信号
3.非应答信号—在时钟线SCL为高电平期间数据线SDA保持高电平为非应答信号
4.结束信号—在时钟线SCL高电平期间数据线SDA发生上升沿跳变产生停止信号
5.数据信号—在数据传输期间,时钟线 SCL 为高电平期间,如果数据线 SDA 为高电平则代表二进制1,同理,时钟线 SCL 为高电平期间,如果数据线 SDA 为低电平则代表二进制 0。
6.上面1号框是SDA 数据有效期,2号框是数据改变期。
五、程序编写
1.起始信号与停止信号
void SMBus_StartBit(void)
{
SMBUS_SDA_H(); // 首先拉高数据线
SMBus_Delay(5); // 延时几微妙
SMBUS_SCK_H(); // 拉高时钟线
SMBus_Delay(5); // 延时几微妙
SMBUS_SDA_L(); // 拉低数据线
SMBus_Delay(5); // 延时几微妙
//在SCK=1时,检测到SDA由1到0表示通信开始(下降沿)
SMBUS_SCK_L(); // 拉低时钟线
SMBus_Delay(5); // 延时几微妙
}
void SMBus_StopBit(void)
{
SMBUS_SCK_L();// 拉低时钟线
SMBus_Delay(5);// 延时几微妙
SMBUS_SDA_L();// 拉低数据线
SMBus_Delay(5);// 延时几微妙
SMBUS_SCK_H();// 拉高时钟线
SMBus_Delay(5); // 延时几微妙
SMBUS_SDA_H(); // 拉高数据线
}
2.发送一个字节
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)//如果最高位为1
{
bit_out=1; // 把最高位置1
}
else //如果最高位为0
{
bit_out=0; // 把最高位置0
}
SMBus_SendBit(bit_out); // 把最高位发送出去
Tx_buffer<<=1;// 左移一位把最高位移出去等待下一个最高位,循环8次,每次都发最高位,就可把一个字节发出去了
}
Ack_bit=SMBus_ReceiveBit(); // Get acknowledgment bit
return Ack_bit;
}
发送一个字节也就是8个bit位,我们的做法就是循环8次,每次将最高位发送出去。如果最高位为1,我们将这一个字节Tx_buffer和0x80(10000000)进行"与运算",把最高位置为1。如果最高位为0,就把最高位置为0,再通过 SMBus_SendBit(bit_out); 把最高位发送出去,之后再通过Tx_buffer<<=1;左移一位把最高位移出去等待下一个最高位,循环8次,每次都发最高位,就可把一个字节发出去了。这里是SMBus发送一个字节,我们知道在从机发送完一个字节之后,主机要返回一个应答信号告诉从机是否继续发送下一个字节,所以这里我们 使用Ack_bit=SMBus_ReceiveBit();这条语句来告诉从机是否还要继续发送下一个字节,如果返回的是0就继续发送,如果返回的是1就停止发送。
3.接收一个字节
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;//如果Ack_bit=1,把收到应答信号1与0000 0001 进行或运算,确保为1
}
else
{
RX_buffer <<= 1;// If the bit is LOW save 0 in RX_buffer
RX_buffer &=0xfe;//如果Ack_bit=1,把收到应答信号0与1111 1110 进行与运算,确保为0
}
}
SMBus_SendBit(ack_nack);//把应答信号发出去,如果0,就进行下一次通信,如果为1,就拜拜了。
return RX_buffer;
}
从SMBus上接收一个字节也就是8位,我们也是用一个for循环将8位依次接受过来。首先问我们通过if函数来判断SMBus_ReceiveBit()是否是收到了应答信号,如果收到了的应答信号也就是1,我们就将收到的数据左移1位RX_buffer <<= 1;如果左移一位之后空出的那一位数据位1然后就与0x01也就是0000 0001 进行"或运算",确保为1。如果左移一位之后空出的那一位数据位0然后就与0xfe也就是1111 1110 进行"或运算",确保为0。最后SMBus把应答信号发出去,如果0,就进行下一次通信,如果为1,就拜拜了,就不接受数据了。
4.数据校验
u8 PEC_Calculation(u8 pec[])
{
u8 crc[6];//存放多项式
u8 BitPosition=47;//存放所有数据最高位,6*8=48 最高位就是47位
u8 shift;
u8 i;
u8 j;
u8 temp;
do
{
crc[5]=0;
crc[4]=0;
crc[3]=0;
crc[2]=0;
crc[1]=0x01;
crc[0]=0x07;
BitPosition=47;
shift=0;
i=5;
j=0;
while((pec[i]&(0x80>>j))==0 && i>0)
{
BitPosition--;
if(j<7)
{
j++;
}
else
{
j=0x00;
i--;
}
}
shift=BitPosition-8;
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;
}
shift--;
}
for(i=0; i<=5; i++)
{
pec[i] ^=crc[i];
}
}
while(BitPosition>8);
return pec[0];
}
这个数据校验是很重要的,目的就是来判断检测你采集的数据是否是是正确的。
也就是我们程序会把PEC的数据通过SA_W、Command、SA_R把LSByte、MSByte读出来。然后我们自己再写一个校验的函数来判断读到的PEC数据是否正确,如果正确了才能进行下一步的判断,如果不正确的话就继续读,直到正确为止。如何才能知道自己读到的数据是否正确呢?
这就需要自己单独建立一个C工程,把这个函数u8 PEC_Calculation(u8 pec[])放到里面运行一遍,看看自己输入的数据时候跟自己要得到数据数据是否一样就可以了。也就是把0x00、0x3a、0xd2、0xb5、0x07、0xb4放到这个函数运行,最终返回出来的数据是否是0x30就可以了,如果是就说明数据校验正确,如果不是就需要该函数,至于为啥返回的是0x30请看上面的时序表。
5.读取温度函数
u16 SMBus_ReadMemory(u8 slaveAddress, u8 command)
{
u16 data;
u8 Pec;
u8 DataL=0;
u8 DataH=0;
u8 arr[6];
u8 PecReg;
u8 ErrorCounter;
ErrorCounter=0x00;// Initialising of ErrorCounter
slaveAddress <<= 1; //2-7位表示从机地址 从机地址左移一位,把读写位空出来
do
{
repeat:
SMBus_StopBit();
--ErrorCounter;
if(!ErrorCounter) //ErrorCounter=0?
{
break; //如果为0就跳出do-while{}循环
}
SMBus_StartBit();
if(SMBus_SendByte(slaveAddress))//发送从机地址最低位Wr=0表示接下来写命令
{
goto repeat;
}
if(SMBus_SendByte(command))//发送命令
{
goto repeat;
}
SMBus_StartBit();
if(SMBus_SendByte(slaveAddress+1)) //发送从机地址+1最低位Rd=1表示接下来读数据
{
goto repeat;
}
DataL = SMBus_ReceiveByte(ACK); //读低位数据保存到DataL
DataH = SMBus_ReceiveByte(ACK); //读高位数据保存到DataH
Pec = SMBus_ReceiveByte(NACK); //读校验数据保存到Pec
SMBus_StopBit();
arr[5] = slaveAddress;
arr[4] = command;
arr[3] = slaveAddress+1;
arr[2] = DataL;
arr[1] = DataH;
arr[0] = 0;
PecReg=PEC_Calculation(arr);//Calculate CRC 数据校验
}
while(PecReg != Pec);
data = (DataH<<8) | DataL;
return data;
}
这个函数的输入参数就IIC设备的从机地址和command寄存器地址。首先从机地址左移一位,把读写位空出来,因为不知道最开始是啥状态,我们就需要发送一个停止信号,因为ErrorCounter=0x00;那么进行"--"操作之后就不等于0,就进行下面的操作。接下来就发送起始信号,发送从机设备地址,如果发送的从机地址正确,就接着发送操作从机地址的command这个地址,因为这里保存着温度数据。然后重新发起一个起始信号,开始读数据,把温度数据的低位DataL高位DataH 和PEC数据读出来,再进行PEC数据校验,判断读到的PEC数据和校验得到的PEC数据是否相等,相等的话就跳出循环,通过将高位数据左移8位再与低8位进行按位或就得到了最终的数据data。
6.得到最终温度值
float SMBus_ReadTemp(void)
{
float temp;
temp = SMBus_ReadMemory(0x00, 0x07)*0.02-273.15;
return temp;
}
通过数据手册我们知道将最终读取的数据*0.02-273.15就会得到最终的温度实际值,通过串口打印或者oled显示就可以得到传感器读取的温度了。
六、CRC8校验原理
1.模2除法
模2除法与算术除法类似,但每一位除的结果不影响其它位,即不向上一位借位,所以实际上就是异或。在循环冗余校验码(CRC)的计算中有应用到模2除法。CRC校验中有两个关键点,一是预先确定一个发送端和接收端都用来作为除数的二进制比特串(或多项式),可以随机选择,也可以使用国际标准,但是最高位和最低位必须为1;二是把原始帧与上面计算出的除数进行模2除法运算,计算出CRC码。
2.具体步骤
1.选择合适的多项式,确定除数。
3.实例1
现假设我们使用的多项式为:G(X) =X^ 8 + X^ 2+X^ 1+1,要求出0x1A的CRC8校验码。下面是具体的计算过程:
1.将多项式转化为二进制序列,由G(X) = X^ 8 + X^ 2+X^ 1+1可知二进制一种有9位,第8位、第2位、第1位和第0位分别为1,则序列为100000111。
2.原来要计算的数据为1 1010,多项式的最高次为8,则在数据的后面加上8位0,数据变为110100000 0000,然后使用模2除法除以除数100000111,最终得到的除不尽的余数,变为我们要求的CRC8结果。
3.最后除不尽的余数为0x46,所以0x1A按多项式G(X) = X8+X2+X+1计算得到的CRC8码为0x46。
4.示例2
也就是求0xb4 0x07 0xb5 0xd2 0x3a 的CRC8校验码是否是0x30
1.将多项式转化为二进制序列,由G(X) = X^8+X^2+X^1+1可知二进制一种有9位,第8位、第2位、第1位和第0位分别为1,则序列为100000111。
2.原来要计算的数据为b4 07 b5 d2 3a转换为2进制就是
1011010000000111101101011101001000111010
多项式的最高次为8,则在数据的后面加上8位0,数据变为
101101000000011110110101110100100011101000000000
然后使用模2除法除以除数100000111,最终得到的除不尽的余数,变为我们要求的CRC8结果。
5.读取温度函数
u16 SMBus_ReadMemory(u8 slaveAddress, u8 command)
{
u16 data;
u8 Pec;
u8 DataL=0;
u8 DataH=0;
u8 arr[6];
u8 PecReg;
u8 ErrorCounter;
ErrorCounter=0x00;// Initialising of ErrorCounter
slaveAddress <<= 1; //2-7位表示从机地址 从机地址左移一位,把读写位空出来
do
{
repeat:
SMBus_StopBit();
--ErrorCounter;
if(!ErrorCounter) //ErrorCounter=0?
{
break; //如果为0就跳出do-while{}循环
}
SMBus_StartBit();
if(SMBus_SendByte(slaveAddress))//发送从机地址最低位Wr=0表示接下来写命令
{
goto repeat;
}
if(SMBus_SendByte(command))//发送命令
{
goto repeat;
}
SMBus_StartBit();
if(SMBus_SendByte(slaveAddress+1)) //发送从机地址+1最低位Rd=1表示接下来读数据
{
goto repeat;
}
DataL = SMBus_ReceiveByte(ACK); //读低位数据保存到DataL
DataH = SMBus_ReceiveByte(ACK); //读高位数据保存到DataH
Pec = SMBus_ReceiveByte(NACK); //读校验数据保存到Pec
SMBus_StopBit();
arr[5] = slaveAddress;
arr[4] = command;
arr[3] = slaveAddress+1;
arr[2] = DataL;
arr[1] = DataH;
arr[0] = 0;
PecReg=PEC_Calculation(arr);//Calculate CRC 数据校验
}
while(PecReg != Pec);
data = (DataH<<8) | DataL;
return data;
}
这个函数的输入参数就IIC设备的从机地址和command寄存器地址。首先从机地址左移一位,把读写位空出来,因为不知道最开始是啥状态,我们就需要发送一个停止信号,因为ErrorCounter=0x00;那么进行"--"操作之后就不等于0,就进行下面的操作。接下来就发送起始信号,发送从机设备地址,如果发送的从机地址正确,就接着发送操作从机地址的command这个地址,因为这里保存着温度数据。然后重新发起一个起始信号,开始读数据,把温度数据的低位DataL高位DataH 和PEC数据读出来,再进行PEC数据校验,判断读到的PEC数据和校验得到的PEC数据是否相等,相等的话就跳出循环,通过将高位数据左移8位再与低8位进行按位或就得到了最终的数据data。
6.得到最终温度值
float SMBus_ReadTemp(void)
{
float temp;
temp = SMBus_ReadMemory(0x00, 0x07)*0.02-273.15;
return temp;
}
后台回复:红外测温,即可免费获取红外测温程序源码。