C 语言代码
MLX90614 和单片机STC12C5604AD 的SMBus,PWM 通信
1 目的
该文档的主要目的是描述怎样实现单片机和红外温度计 MLX90614 的 SMBus 通信,并简略介绍利用单片
机对 MLX90614 的 PWM 输出脉冲进行测量并计算相关温度。代码是基于 STC12C5604AD 单片机的。实例给
出了由 C 语言实现 MLX90614 读取物体温度,写入数据到 MLX90614、以及对 PWM 脉冲的测量和计算等功
能。
2 C 代码
2.1 由MLX90614 读取温度部分
实例给出了应用 C 语言来实现从 MLX90614 中读取物体温度的程序。为了使程序操作和运行简单起见,
整个项目被分为几个子项目。在该文档中整个 C 程序分为主文件 SMBus.c (用来实现对物体温度的读取、改
变 SMBus 地址、改变发射率、以及改变 PWMCTRL 配置参数功能);子文件 SMBus_CM.c(具体描述
SMBus 通信的起始状态,终止状态,发送和接受数据); 子文件 SMBus_OP.c(包含了利用 SMBus 通信由
MLX90614 读取数据,对 MLX90614 写入数据和 PEC 校验码计算的程序);子文件 Delay.c(调用延迟函
数);子文件 dec2hex.c(将十进制转换为十六进制);子文件 CalTem.c(根据十六进制数值计算温度读
数);子文件 digitalLED.c(在数字 LEDs 上显示温度读数)。在主文件的头文件中引用相应的子文件.h 文
件,以将所有功能综合、链接起来。
//----------------------------------------------------------------------------------------------------------------------------------------//
//----------------------------------------------------------------------------------------------------------------------------------------//
主文件 SMBus.c
//----------------------------------------------------------------------------------------------------------------------------------------//
//----------------------------------------------------------------------------------------------------------------------------------------//
红外温度计—MLX90614 中读取物体温度
描述:该文件是基于单片机 STC12C5604AD 编写的 C 语言程序,可通过 SMBus 实现对 MLX90614 温度数据
的读取,对 MLX90614 写入数据的功能。实例给出了读取物体温度的程序,以及可根据十六进制数值计算温度
数值并在数字 LEDs 上显示温度的程序,但是此为可选项,用户可根据自己的应用另行选择其他方式。
//----------------------------------------------------------------------------------------------------------------------------------------//
头文件
//----------------------------------------------------------------------------------------------------------------------------------------//
#include
#include "stdio.h"
#include "intrins.h"
#include "string.h"
#include "SMBus_CM.h"
#include "SMBus_OP.h"
#include "digitalLED.h"
#include "CalTem.h"
#include "dec2hex.h"
#include "Delay.h"
//----------------------------------------------------------------------------------------------------------------------------------------//
宏定义 I/O 端口和 SMBus 信号输入输出方向
//----------------------------------------------------------------------------------------------------------------------------------------//
#define _SDA_OUTPUT P1M0=0x10; P1M1=0x10;
#define _SDA_INPUT P1M0=0x10; P1M1=0x00;
#define _SCL_IO P1M0=0x08; P1M1=0x08;
//引用 SMBus_CM.h 文件(包含起始状态,终止状态,发送,接收字节等)
//引用 SMBus_OP.h 文件(包含由 MLX90614 中读取数据和写入数据)
//引用 digitalLED.h 文件以在数字 LEDs 上显示温度读数
//引用 CalTem.h 文件来根据十六进制数值计算温度
//引用 dec2hex.h 文件将十进制转换为十六进制
//引用 Delay.h 文件
//设置 SDA 为开漏输出
//设置 SDA 为高阻输入
//设置 SCL 为开漏输出的 I/O 端口
1 / 23
C 语言代码
MLX90614 和单片机STC12C5604AD 的SMBus,PWM 通信
sbit SDA = P1^4;
sbit SCL = P1^3;
//----------------------------------------------------------------------------------------------------------------------------------------//
主函数功能
作用:读取物体温度
//----------------------------------------------------------------------------------------------------------------------------------------//
void main()
{
//指定 SDA 线给 P14
//指定 SCL 线给 P13
unsigned char slaveaddress;
unsigned long int DATA;
unsigned int *mahm;
_SCL_IO;
_SDA_OUTPUT;
//引用宏定义-设置 SCL 为开漏式 I/O 口
//引用宏定义-设置 SDA 为开漏式输出
SCL=0;
Delay(1200);
SCL=1;
while(1)
{
//
//SMBus 请求时间,将 PWM 模式转换为 SMBus 模式(至少为 2ms)
//
slaveaddress=MEM_READ(0x00,0x2E);
//读取存于 MLX90614 EEPROM "0Eh"地址中的 SMBus 地址
DATA=MEM_READ(slaveaddress,0x07);
//基于上述地址由 MLX90614 的内存 07h 中读取物体温度
mahm=CALTEMP(DATA);
//基于所得的十六进制温度格式计算实际温度
show(mahm,5);
//在数字 LEDs 上显示计算所得温度
}
}
//----------------------------------------------------------------------------------------------------------------------------------------//
//----------------------------------------------------------------------------------------------------------------------------------------//
//----------------------------------------------------------------------------------------------------------------------------------------//
子文件 SMBus_CM.c
//----------------------------------------------------------------------------------------------------------------------------------------//
该文档包含了 SMBus 通信的起始状态,终止状态,发送和接受字节等
//----------------------------------------------------------------------------------------------------------------------------------------//
头文件
//----------------------------------------------------------------------------------------------------------------------------------------//
#include
#include "SMBus_CM.h"
#include "intrins.h"
#include "Delay.h"
//----------------------------------------------------------------------------------------------------------------------------------------//
宏定义 I/O 端口和 SMBus 信号的方向
//----------------------------------------------------------------------------------------------------------------------------------------//
#define _SDA_OUTPUT P1M0=0x10; P1M1=0x10;
#define _SDA_INPUT P1M0=0x10; P1M1=0x00;
#define _SCL_IO P1M0=0x08; P1M1=0x08;
sbit SDA = P1^4;
sbit SCL = P1^3;
//设置 SDA 为开漏输出
//设置 SDA 为高阻输入
//设置 SCL 为开漏式 I/O 端口
//指定 SDA 线给 P14
//指定 SCL 线给 P13
//头文件中引用本身
2 / 23
C 语言代码
MLX90614 和单片机STC12C5604AD 的SMBus,PWM 通信
//----------------------------------------------------------------------------------------------------------------------------------------//
函数名: start_bit
功能: 在 SMBus 总线上产生起始状态
注解: 参考“系统管理总线说明书-版本 2.0”
//----------------------------------------------------------------------------------------------------------------------------------------//
void start_bit()
{
//设置 SDA 为输出
//设置 SDA 线为高电平
_SDA_OUTPUT;
SDA=1;
_nop_();_nop_();
SCL=1;
Delay(5);
SDA=0;
Delay(5);
//(重复)开始状态后的保持时间,在该时间后,产生第一个时钟信号
//Thd:sta=4us 最小值
//设置 SCL 线为低电平
SCL=0;
_nop_();_nop_();
//设置 SCL 线为高电平
//在终止和起始状态之间产生总线空闲时间(Tbuf=4.7us 最小值)
//设置 SDA 线为低电平
}
//----------------------------------------------------------------------------------------------------------------------------------------//
函数名: stop_bit
功能: 在 SMBus 总线上产生终止状态
注解: 参考“系统管理总线说明书-版本 2.0”
//----------------------------------------------------------------------------------------------------------------------------------------//
void stop_bit()
{
_SDA_OUTPUT;
SCL=0;
Delay(5);
SDA=0;
Delay(5);
SCL=1;
Delay(5);
SDA=1;
//设置 SDA 为输出
//设置 SCL 线为低电平
//设置 SDA 线为低电平
//设置 SCL 线为高电平
//终止状态建立时间(Tsu:sto=4.0us 最小值)
//设置 SDA 线为高电平
}
//----------------------------------------------------------------------------------------------------------------------------------------//
函数名: send_bit
功能:在 SMBus 总线上发送一位数据
//----------------------------------------------------------------------------------------------------------------------------------------//
void send_bit(unsigned char bit_out)
{
_SDA_OUTPUT;
//设置 SDA 为开漏输出以在总线上传送数据
if(bit_out==0)
else
SDA=0;
SDA=1;
_nop_();
_nop_();
_nop_();
//核对字节的位
//如果 bit_out=1,设置 SDA 线为高电平
//如果 bit_out=0,设置 SDA 线为低电平
//
//Tsu:dat=250ns 最小值
//
3 / 23
C 语言代码
MLX90614 和单片机STC12C5604AD 的SMBus,PWM 通信
SCL=1;
Delay(4);
SCL=0;
Delay(4);
//设置 SCL 线为高电平
//时钟脉冲高电平脉宽(10.6us)
//设置 SCL 线为低电平
//时钟脉冲低电平脉宽
}
//----------------------------------------------------------------------------------------------------------------------------------------//
函数名: receive_bit
功能:在 SMBus 总线上接收一位数据
//----------------------------------------------------------------------------------------------------------------------------------------//
unsigned char receive_bit()
{
}
//----------------------------------------------------------------------------------------------------------------------------------------//
函数名: slave_ack
功能: 由受控器件 MLX90614 中读取确认位
返回值: unsigned char ack
1 - ACK
0 - NACK
//----------------------------------------------------------------------------------------------------------------------------------------//
unsigned char slave_ack()
{
}
//----------------------------------------------------------------------------------------------------------------------------------------//
发送一个字节
函数名: TX_byte
功能: 在 SMBus 总线上发送一个字节
参数: unsigned char TX_buffer (将要在总线上发送的字节)
注解: 先发送字节的高位
unsigned char bit_in;
_SDA_INPUT;
SCL=1;
Delay(2);
if(SDA==1)
bit_in=1;
else
bit_in=0;
Delay(2);
SCL=0;
Delay(4);
return bit_in;
unsigned char ack;
ack=0;
_SDA_INPUT;
SCL=1;
Delay(2);
if(SDA==1)
ack=0;
else
ack=1;
Delay(2);
SCL=0;
Delay(4);
return ack;
//设置 SDA 为高阻输入
//设置 SCL 线为高电平
//从总线上读取一位,赋给 bit_in
//设置 SCL 线为低电平
//返回 bit_in 值
//设置 SDA 为高阻输入
//设置 SCL 线为高电平
//从总线上读取一位,赋给 ack
//设置 SCL 线为低电平
4 / 23
C 语言代码
MLX90614 和单片机STC12C5604AD 的SMBus,PWM 通信
//----------------------------------------------------------------------------------------------------------------------------------------//
void TX_byte(unsigned char TX_buffer)
{
unsigned char Bit_counter;
unsigned char bit_out;
for(Bit_counter=8;Bit_counter;Bit_counter--)
{
if(TX_buffer&0x80)
else
bit_out=1;
bit_out=0;
send_bit(bit_out);
TX_buffer<<=1;
}
//如果 TX_buffer 的当前位是 1,设置 bit_out 为 1
//否则,设置 bit_out 为 0
//发送 SMBus 总线上的当前位
//核对下一位
}
//----------------------------------------------------------------------------------------------------------------------------------------//
接收一个字节
函数名: RX_byte
功能: 在 SMBus 总线上接收一个字节
参数: unsigned char ack_nack (确认位)
0 - 主控器件发送 ACK
1 - 主控器件发送 NACK
返回值: unsigned char RX_buffer (总线接收的字节)
注解: 先接收字节的高位
//----------------------------------------------------------------------------------------------------------------------------------------//
unsigned char RX_byte(unsigned char ack_nack)
{
unsigned char RX_buffer;
unsigned char Bit_counter;
for(Bit_counter=8;Bit_counter;Bit_counter--)
{
if(receive_bit()==1)
{
}
else
{
}
RX_buffer<<=1;
RX_buffer|=0x01;
RX_buffer<<=1;
RX_buffer&=0xfe;
}
send_bit(ack_nack);
return RX_buffer;
//由 SDA 线读取一位
//如果位为“1”,赋“1”给 RX_buffer
//如果位为“0”,赋“0”给 RX_buffer
//发送确认位
}
//----------------------------------------------------------------------------------------------------------------------------------------//
//----------------------------------------------------------------------------------------------------------------------------------------//
子文件 SMBus_OP.c
//----------------------------------------------------------------------------------------------------------------------------------------//
//----------------------------------------------------------------------------------------------------------------------------------------//
该文档包含了 SMBus 通信时从 MLX90614 读取数据,写入数据和 PEC 校验码计算的程序
5 / 23
C 语言代码
MLX90614 和单片机STC12C5604AD 的SMBus,PWM 通信
//头文件中引用本身
//引用 SMBus_CM.h 文件
//----------------------------------------------------------------------------------------------------------------------------------------//
头文件
//----------------------------------------------------------------------------------------------------------------------------------------//
#include
#include "SMBus_CM.h"
#include "intrins.h"
#include "SMBus_OP.h"
#include "Delay.h"
//----------------------------------------------------------------------------------------------------------------------------------------//
sbit SDA = P1^4;
sbit SCL = P1^3;
//----------------------------------------------------------------------------------------------------------------------------------------//
计算 PEC 包裹校验码
函数名: PEC_cal
功能: 根据接收的字节计算 PEC 码
参数: unsigned char pec[], int n
返回值: pec[0] - 该字节包含计算所得 crc 数值
注解: 参考“系统管理总线说明书-版本 2.0”和应用指南“MCU 和 MLX90614 的 SMBus 通信"
//----------------------------------------------------------------------------------------------------------------------------------------//
unsigned char PEC_cal(unsigned char pec[],int n)
{
//指定 MLX90614 的 SDA 线给单片机 P14 引脚
//指定 MLX90614 的 SCL 线给单片机 P13 引脚
unsigned char crc[6];
unsigned char Bitposition=47;
unsigned char shift;
unsigned char i;
unsigned char j;
unsigned char temp;
do{
crc[5]=0;
crc[4]=0;
crc[3]=0;
crc[2]=0;
crc[1]=0x01;
crc[0]=0x07;
Bitposition=47;
shift=0;
//在传送的字节中找出第一个“1”
i=5;
j=0;
while((pec[i]&(0x80>>j))==0 && (i>0))
//载入 CRC 数值 0x000000000107
//设置 Bitposition 的最大值为 47
//设置最高标志位 (包裹字节标志)
//字节位标志,从最低位开始
{
Bitposition--;
if(j<7)
{
j++;
}
else
{
j=0x00;
i--;
}
}//while 语句结束,并找出 Bitposition 中为“1”的最高位位置
shift=Bitposition-8;
//得到 CRC 数值将要左移/右移的数值“shift”
//对 CRC 数据左移“shift”位
6 / 23
C 语言代码
MLX90614 和单片机STC12C5604AD 的SMBus,PWM 通信
if((crc[i-1]&0x80) && (i>0))
while(shift)
for(i=5;i<0xFF;i--)
{
}
{
{
temp=1;
}
else
{
temp=0;
}
crc[i]<<=1;
crc[i]+=temp;
//核对字节的最高位的下一位是否为"1"
//是 - 当前字节 + 1
//否 - 当前字节 + 0
//实现字节之间移动“1”
shift--;
}
//pec 和 crc 之间进行异或计算
for(i=0;i<=5;i++)
pec[i]^=crc[i];
{
}
}while(Bitposition>8);
return pec[0];
//返回计算所得的 crc 数值
}
//----------------------------------------------------------------------------------------------------------------------------------------//
由 MLX90614 RAM/EEPROM 读取的数据
函数名: MEM_READ
功能: 给定受控地址和命令时由 MLX90614 读取数据
参数: unsigned char slave_addR (受控地址)
unsigned char cmdR (命令)
返回值: unsigned long int Data
//----------------------------------------------------------------------------------------------------------------------------------------//
unsigned long int MEM_READ(unsigned char slave_addR, unsigned char cmdR)
{
unsigned char DataL;
unsigned char DataH;
unsigned char PEC;
unsigned long int Data;
unsigned char Pecreg;
unsigned char arr[6];
unsigned char ack_nack;
unsigned char SLA;
SLA=(slave_addR<<1);
begin:
//
//由 MLX90614 读取的数据包
//
//由 MLX90614 返回的寄存器数值
//存储计算所得 PEC 字节
//存储已发送字节的缓冲器
start_bit();
TX_byte(SLA);
if(slave_ack()==0)
{
stop_bit();
goto begin;
}
TX_byte(cmdR);
if(slave_ack()==0)
//发送起始位
//发送受控器件地址,写命令
//发送命令
7 / 23
C 语言代码
MLX90614 和单片机STC12C5604AD 的SMBus,PWM 通信
//发送重复起始位
//发送受控器件地址,读命令
{
stop_bit();
goto begin;
}
start_bit();
TX_byte(SLA+1);
if(slave_ack()==0)
{
stop_bit();
goto begin;
}
DataL=RX_byte(0);
stop_bit();
goto begin;
}
stop_bit();
arr[5]=(SLA);
arr[4]=cmdR;
arr[3]=(SLA+1);
arr[2]=DataL;
arr[1]=DataH;
arr[0]=0;
Pecreg=PEC_cal(arr,6);
if(PEC==Pecreg)
ack_nack=0;
{
}
else
{
DataH=RX_byte(0);
PEC=RX_byte(ack_nack);
if(ack_nack==1)
//取决于 pec 计算,如果 PEC 是不正确的,发送 nack 并返回到 goto begin
{
//
//读取两个字节数据
//
//读取 MLX90614 的 PEC 码
//主控器件发送 ack 或是 nack
//发送终止位
//调用计算 CRC 的函数
ack_nack=1;
}
Data=(DataH*256)+DataL;
return Data;
}
//----------------------------------------------------------------------------------------------------------------------------------------//
MLX90614 EEPROM 中写入数据
函数名: EEPROM_WRITE
功能: 根据命令写入相关数据到给定受控器件地址的 MLX90614
参数: unsigned char slave_addW (受控器件地址)
unsigned char cmdW (命令)
unsigned char DataL
unsigned char DataH
//----------------------------------------------------------------------------------------------------------------------------------------//
void EEPROM_WRITE(unsigned char slave_addW,unsigned char cmdW,unsigned char DataL,unsigned char
DataH)
{
//存储计算所得 PEC 字节
unsigned char Pecreg;
unsigned char SLA;
8 / 23