文章目录
-
- 一、所需材料
- 二、mlx90614传感器介绍
- 三、MLX90614工作原理
- 四、IIC协议原理
-
- 1.写时序
- 2.读时序
- 3.通信过程
- 五、编程
-
- 1.起始信号和停止信号
- 2.发送一个字节
- 3.接收一个字节
- 4.数据校验
- 5.读取温度函数
- 6.获得最终温度值
- 六、CRC-8校验原理
-
- 1.模2除法
- 2.具体步骤
- 3.实例1
- 4.示例2
- 5.读取温度函数
- 6.获得最终温度值

一、所需材料
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信号处理芯片构成。 ??其工作原理如下:滤波器(即DSP)输出处理后,输出结果存储在其内部RAM存储单元中。
??MLX90614中有两个存储器EEPROM和RAM。 ??MLX90614共有32个字长为16位EEPROM存储单元的地址为0000H—01FH。 ??EEPROM所有寄存器都可以通过SMBus读取,但只有一些寄存器可以重写(地址为0x00, 0x01, 0x02, 0x03, 0x04, 0x05*,0x0E, 0x0F, 0x09)。可重写部分如左表所示。 ??Tomax和Tomin测量物体温度的上下限,Ta范围是环境温度范围。 ??测量温度上限的计算方法如下:Tomax=100×(To MAX 273.15)通过计算将结果写入000H;温度下限计算方法与上限计算方法相同,将计算结果写入001H。 ??MLX90614中共有32个17个RAM用户无法通过存储单元RAM写入数据只能读取RAM部分存储单元读取16位存储数据。收集的环境温度数据保存在地址006H在存储单元中,被测物体的温度数据存储在007中H存储单元中。所以使用存储RAM地址中的数据,通过公式的计算,可以得到环境温度Ta以及被测物体的温度数据To。 ??Ta和To既可通过SMBus读取RAM单元(分辨率0.02°C ,也可以通过固定范围输出PWM数字模式输出(10位分辨率,范围可配置)。由于变电所测温度的范围和范围MLX90614出厂时校准的温度范围一致,可直接使用SMBus温度数据的方法Ta和To读取输出。 ??Ta=RAM(006H)x0.02-273.15 ??To=RAM(007H)x0.02-273.15
四、IIC协议原理
IIC主从机之间的通信步骤如下: 1.主机发送起始信号,通知各从机就位。 2.主机发送从机地址和读写标志位(写0,读1) 从机地址和读写标志位共占8位,地址占7位,读写标志位占最低, 33从机给主机回复响应(ACK) 4.如果是写作模式,主机会发送一个字节数据,等待从机响应。主机收到响应后,如果有数据要发送,继续发送第二段数据等待响应……直到发送完成; 如果是阅读模式,此时主机STM32读取从机发送的数据并给出从机响应。如果从机发送数据(然后报告第二段),主机将读取并向从机发送响应… 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.读时序
??首先,主机发送起始位置,然后发送从机地址0x读标志位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.上面
是SDA 数据有效期, 是数据改变期。
五、程序编写
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
{
//Load pattern value 0x00 00 00 00 01 07
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位的0,然后用得到的数据以模2除法的方式除上面确定的除数,得到的余数就是该数的CRC校验码。注意,余数的位数一定只比除数位数少一位,也就是CRC校验码位数比除数位数少一位,如果前面位是0也不能省略。
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
- 将多项式转化为二进制序列,由G(X) = X8+X2+X^1+1可知二进制一种有9位,第8位、第2位、第1位和第0位分别为1,则序列为100000111。
- 原来要计算的数据为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;
}
通过数据手册我们知道将【】就会得到最终的温度实际值,通过串口打印或者oled显示就可以得到传感器读取的温度了。 后台回复:【红外测温】,即可免费获取红外测温程序源码。
好书不厌百回读,熟读自知其中意。将学习成为习惯,用知识改变命运,用博客见证成长,用行动证明努力。 如果我的博客对你有帮助、如果你喜欢我的博客内容,请 “点赞” “评论” “收藏” 一键三连哦! 更多精彩内容请前往 果果小师弟的微信公众号
如果以上内容有任何错误或者不准确的地方,欢迎在下面 👇 留个言。或者你有更好的想法,欢迎一起交流学习~~~