今天学习了一下AT89C52编写IIC总线通讯,通讯时序方面还可以接受,但是容易记混,重复了两遍过程,熟悉了很多,以后害的多看多学多记多用。使用的是AT24c02来模拟的I2C的通讯时序
原理讲解:
/*====================================
函数 :At24c02Write(uchar ADDR, DAT)
参数 :ADDR 单元地址0-255,DAT 需要输入的数据0-255
返回值 :无
描述 :At24c02指定单元写入一个字节数据
====================================*/
void AT24c02Write(uchar ADDR,DAT)//写进AT24c02
{
I2cStart(); //I2C起始信号
I2cSendByte(AT24c02ADDR + I2cWrite);//IC发送一个字节**************************
if(ReadACK()) //读从机应答
AckFlag =1; //无应答
else
AckFlag =0; //应答
I2cSendByte(ADDR);//发送储存单元地址字节**************************************
if(ReadACK())
AckFlag =1; //无应答
else
AckFlag =0; //应答
I2cSendByte(DAT);//IC发送一个字节
if(ReadACK())
AckFlag =1; //无应答
else
AckFlag =0; //应答
I2cStop(); //I2C停止信号
}
/*====================================
函数 :At24c02Read(uchar ADDR)
参数 :ADDR 单元地址 0-255
返回值 :返回指定单元的数据
描述 :读AT24C02指定单元内数据
====================================*/
uchar AT24c02Read(uchar ADDR)
{
uchar DAT; //返回数据
I2cStart();//I2C起始信号
I2cSendByte(AT24c02ADDR +I2cWrite);//IC发送一个字节//发送器件地址加写方向位
if(ReadACK()) //读从机应答
AckFlag =1; //无应答
else
AckFlag =0; //应答
I2cSendByte(ADDR);//IC发送一个字节
ReadACK(); //读从机应答
I2cStart(); //再次产生I2C起始信号
I2cSendByte(AT24c02ADDR +I2cRead);//IC读取
if(ReadACK()) //读从机应答
AckFlag =1; //无应答
else
AckFlag =0; //应答
DAT=I2cReadByte();//读取一个字节
SendACK(1); //主机发送非应答
I2cStop(); //I2C停止信号
return(DAT); //返回读出数据
}
/*====================================
函数 :I2cStart()
参数 :无
返回值 :无
描述 :I2C总线起始信号
====================================*/
void I2cStart()//起始信号的函数
{
SCL =1;
SDA =1;
delay5us();
SDA =0;
delay5us();
}
/*====================================
函数 :I2cStop()
参数 :无
返回值 :无
描述 :I2C总线停止信号
====================================*/
void I2cStop()//停止信号的函数
{
SCL =0;
SDA =0;
SCL =1;
delay5us();
SDA =1;
delay5us();
}
/*====================================
函数 :ReadACK()
返回值 :1非应答,0应答
描述 :I2C总线读从机应答信号
====================================*/
bit ReadACK()//主机读从机应答
{
SCL =0; //拉低时钟总线,允许从机控制SDA
SCL =1; //拉高,读SDA
delay5us();
if (SDA) //非应答
{
SCL =0;
return(1);
}
else //应答
{
SCL =0;
return(0);
}
}
/*====================================
函数 :SendACK(bit i)
参数 :1主机发送非应答,0发送应答
返回值 :无
描述 :主机发送应答信号
====================================*/
void SendACK(bit i)//主机发送应答
{
SCL =0; //拉低时钟总线,允许主机控制SDA
if(i) //发送非应答 SDA=1
SDA =1;
else //发送应答 SDA=0
SDA =0;
SCL =1; //拉高总线 让从机读SDA
delay5us(); //延时5us
SCL =0;//拉低时钟总线,允许SDA释放
SDA =1;//释放数据总线
}
/*IIC通信,AT24C02读写数据,数码管显示数据*/
#include <REGX52.H>
#include <intrins.h>
#define uint unsigned int
#define uchar unsigned char
//宏定义
#define AT24c02ADDR 0xA0 //AT24C02硬件地址
#define I2cRead 1 //I2C读方向位
#define I2cWrite 0 //I2C写方向位
sbit DU =P2^6;//段选
sbit WE =P2^7;//位选
sbit SCL =P2^1;//时钟线
sbit SDA =P2^0;//数据线
uchar num;//数码管显示的值
bit AckFlag;
uchar ms_50,s; //记录50ms的个数 20个为1s
//共阴极数码管0-9
uchar code duanxuan[]= {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F,};
//位选前4个数码管
uchar code weixuan[] ={0xfe,0xfd,0xfb,0xf7};
void delay(uint x) //@12.000MHz 延迟函数1毫秒
{
unsigned char i, j;
while(x--)
{
i = 2;
j = 239;
do
{
while (--j);
} while (--i);
}
}
void delay5us() //@12.000MHz
{
_nop_();
}
void display(uchar i) //数码管显示函数
{
static uchar wei;
uchar qian,bai, shi, ge;
qian =i/1000;
bai = i / 100%10;
shi = i /10%10;
ge = i % 10;
P0 = 0XFF;//清除断码
WE = 1; //位选打开
P0 = weixuan[wei];
WE = 0; //关闭位选寄存器
switch(wei)
{
case 0:DU = 1;P0 = duanxuan[qian];DU = 0;break;
case 1:DU = 1;P0 = duanxuan[bai];DU = 0;break;
case 2:DU = 1;P0 = duanxuan[shi];DU = 0;break;
case 3:DU = 1;P0 = duanxuan[ge];DU = 0;break;
}
wei++;
if(wei ==4)
wei =0;
}
void timer0Init() //定时器0初始化
{
EA =1; //打开总中断
ET0 =1; //打开定时器0中断
TR0 =1; //启动定时器0
//TCON中的打开定时器
TMOD |=0x01; //TMOD不能位寻址 0000 0001中 0 01表示定时器模式 16位定时
TH0 =0xed; //(65535-46082)/256; //定时器 50ms 65536-50000/1.085/256 高8位
TL0 =0xff; //(65535-46082)%256; //低8位
}
/*******************************************************************
//IIC通信代码
*******************************************************************/
/*====================================
函数 :I2cStart()
参数 :无
返回值 :无
描述 :I2C总线起始信号
====================================*/
void I2cStart()//起始信号的函数
{
SCL =1;
SDA =1;
delay5us();
SDA =0;
delay5us();
}
/*====================================
函数 :I2cStop()
参数 :无
返回值 :无
描述 :I2C总线停止信号
====================================*/
void I2cStop()//停止信号的函数
{
SCL =0;
SDA =0;
SCL =1;
delay5us();
SDA =1;
delay5us();
}
/*====================================
函数 :ReadACK()
返回值 :1非应答,0应答
描述 :I2C总线读从机应答信号
====================================*/
bit ReadACK()//主机读从机应答
{
SCL =0; //拉低时钟总线,允许从机控制SDA
SCL =1; //拉高,读SDA
delay5us();
if (SDA) //非应答
{
SCL =0;
return(1);
}
else //应答
{
SCL =0;
return(0);
}
}
/*====================================
函数 :SendACK(bit i)
参数 :1主机发送非应答,0发送应答
返回值 :无
描述 :主机发送应答信号
====================================*/
void SendACK(bit i)//主机发送应答
{
SCL =0; //拉低时钟总线,允许主机控制SDA
if(i) //发送非应答 SDA=1
SDA =1;
else //发送应答 SDA=0
SDA =0;
SCL =1; //拉高总线 让从机读SDA
delay5us(); //延时5us
SCL =0;//拉低时钟总线,允许SDA释放
SDA =1;//释放数据总线
}
//发送一个字节8位,从最高位开始***********************************************发送1个字节
/*====================================
函数 :I2cSendByte(uchar DAT)
参数 :DAT需要发送的数据
返回值 :无
描述 :I2C发送一个字节数据
====================================*/
void I2cSendByte(uchar DAT)
{
uchar i;
for(i=0;i<8;i++) //分别写8次,每次写1位
{
SCL =0; //时钟线为0
if(DAT & 0x80) //1011 0110&1000 0000 1为真所以发送1 否则为0则发送0
SDA =1;
else
SDA =0;
SCL =1;
DAT =DAT<<1; //向左移位 每次移位1次 一共8次
}
SCL =0; //拉低时钟
SDA = 1; //释放数据总线
}
/*====================================
函数 :At24c02Write(uchar ADDR, DAT)
参数 :ADDR 单元地址0-255,DAT 需要输入的数据0-255
返回值 :无
描述 :At24c02指定单元写入一个字节数据
====================================*/
void AT24c02Write(uchar ADDR,DAT)//写进AT24c02
{
I2cStart(); //I2C起始信号
I2cSendByte(AT24c02ADDR + I2cWrite);//IC发送一个字节**************************
if(ReadACK()) //读从机应答
AckFlag =1; //无应答
else
AckFlag =0; //应答
I2cSendByte(ADDR);//发送储存单元地址字节**************************************
if(ReadACK())
AckFlag =1; //无应答
else
AckFlag =0; //应答
I2cSendByte(DAT);//IC发送一个字节
if(ReadACK())
AckFlag =1; //无应答
else
AckFlag =0; //应答
I2cStop(); //I2C停止信号
}
/*====================================
函数 :I2cReadByte()
参数 :无
返回值 :返回读出的一字节数据
描述 :I2C总线读一字节数据
====================================*/
uchar I2cReadByte()//读一个字节
{
uchar i,DAT;
for(i =0;i<8;i++)//分别读8次,每次读一位
{
DAT<<=1; //数据左移1位,准备接收一位******************************************
SCL =0; //拉低时钟总线,允许从机控制SDA变化
SCL =1; //拉高时钟总线,读取SDA上的数据
if(SDA)
DAT |=0x01; //为1则写1,否则不行执行写1,通过左移补0
}
return(DAT);
}
/* 从机要发送的值为 1010 0000
DAT的初始值为 0000 0000
DAT<<1 =0000 0000
DAT|=0x01;
0000 0001
DAT<<1 =0000 0010
DAT<<1 =0000 0100
DAT|=0x01 =0000 0101
最后成功发送1010 0000*/
//读取AT24c02函数
/*====================================
函数 :At24c02Read(uchar ADDR)
参数 :ADDR 单元地址 0-255
返回值 :返回指定单元的数据
描述 :读AT24C02指定单元内数据
====================================*/
uchar AT24c02Read(uchar ADDR)
{
uchar DAT; //返回数据
I2cStart();//I2C起始信号
I2cSendByte(AT24c02ADDR +I2cWrite);//IC发送一个字节//发送器件地址加写方向位
if(ReadACK()) //读从机应答
AckFlag =1; //无应答
else
AckFlag =0; //应答
I2cSendByte(ADDR);//IC发送一个字节
ReadACK(); //读从机应答
I2cStart(); //再次产生I2C起始信号
I2cSendByte(AT24c02ADDR +I2cRead);//IC读取
if(ReadACK()) //读从机应答
AckFlag =1; //无应答
else
AckFlag =0; //应答
DAT=I2cReadByte();//读取一个字节
SendACK(1); //主机发送非应答
I2cStop(); //I2C停止信号
return(DAT); //返回读出数据
}
void timer0() interrupt 1 //定时器0中断
{
TH0 =0xed;
TL0 =0xff;
display(num);
}
void main()
{
timer0Init(); //定时器0初始化
EA =0; //屏蔽中断
AT24c02Write(55,119); //给第55单元写入数据“119”
delay(5); //延时等待AT24C02处理*******************************************
num =AT24c02Read(55); //读出第55单元内数据送给显示变量 返回值为DAT
if(AckFlag) //当从机非应答
P1 = 0; //亮P1所有灯
else
P1 = 0XFF; //灭P1所有灯
EA =1; //开总中断
while(1);
}