51单片机——IIC总线

51单片机——IIC总线

IIC串行总线协议及组成:

I2C:同步串行2线方式进行通信(一条时钟线SCL(serial clock),一条数据线SDA(serial data I/O))。

程序中的使用:在这里插入图片描述

IIC原理:

如下图所示,IIC总线上可以挂多个器件,而每个器件都有唯一的地址,这样可以标识通信目标。数据的通信的方式采用主从方式,主机负责主动联系从机,而从机则被动回应数据。
在这里插入图片描述
I2C总线通过上拉电阻接正电源。当总线空闲时,两根线均为高电平。连到总线上的任一器件输出的低电平,都将使总线的信号变低,即各器件的SDA及SCL都是线“与”关系。
在这里插入图片描述

IIC总线传输协议:

数据位的有效性规定:
SCL为高电平期间,数据线上的数据必须保持稳定,只有SCL信号为低电平期间,SDA状态才允许变化。
在这里插入图片描述

IIC的起始和终止信号:

SCL线为高电平期间,SDA线由高电平向低电平的变化表示起始信号;SCL线为高电平期间,SDA线由低电平向高电平的变化表示终止信号。
在这里插入图片描述
起始和终止信号都是由主机发出的,在起始信号产生后,总线就处于被占用的状态;在终止信号产生后,总线就处于空闲状态。

连接到I2C总线上的器件,若具有I2C总线的硬件接口,则很容易检测到起始和终止信号。

接收器件收到一个完整的数据字节后,有可能需要完成一些其它工作,如处理内部中断服务等,可能无法立刻接收下一个字节,这时接收器件可以将SCL线拉成低电平,从而使主机处于等待状态。直到接收器件准备好接收下一个字节时,再释放SCL线使之为高电平,从而使数据传送可以继续进行。

IIC字节的传送与应答:

每一个字节必须保证是8位长度。数据传送时,先传送最高位(MSB),每一个被传送的字节后面都必须跟随一位应答位(即一帧共有9位)。
在这里插入图片描述
程序使用:

IIC发送一个字节数据:
在这里插入图片描述

应答位的作用:

主机在发送数据时,每次发送一字节数据,都需要读取从机应答位,当从机空闲可以接收该字节数据时,从机会发出应答(一帧数据的第9位为“0”),当从机正忙于其他工作的处理来不及接收主机发送的数据时,从机会发出非应答(一帧数据的第9位为“1”)主机则应发出终止信号以结束数据的继续传送,主机通过从机发出的应答位来判断从机是否成功接收数据。
程序实现:
在这里插入图片描述

当主机接收数据时,它收到最后一个数据字节后,必须向从机发出一个结束传送的信号。这个信号是由对从机的“非应答”来实现的。然后,从机释放SDA线,以允许主机产生终止信号。

数据帧格式

IIC总线上传送的数据信号是广义的,既包括地址信号,又包括真正的数据信号。
在起始信号后必须传送一个从机的地址(7位),第8位是数据的传送方向位(R/T),用“0”表示主机发送数据(T),“1”表示主机接收数据(R)。每次数据传送总是由主机产生的终止信号结束。但是,若主机希望继续占用总线进行新的数据传送,则可以不产生终止信号,马上再次发出起始信号对另一从机进行寻址。

数据传送时的组合方式:
1、主机向从机发送数据,数据传送方向在整个传送过程中不变:在这里插入图片描述

2、主机在第一个字节后,立即从从机读数据:在这里插入图片描述
3、在传送过程中,当需要改变传送方向时,起始信号和从机地址都被重复产生一次,但两次读/写方向位正好反相。在这里插入图片描述
注:有阴影部分表示数据由主机向从机传送,无阴影部分则表示数据由从机向主机传送。
A表示应答, A非表示非应答(高电平)。S表示起始信号,P表示终止信号。

总线的寻址:

I2C总线协议有明确的规定:采用7位的寻址字节(寻址字节是起始信号后的第一个字节)
(1)寻址字节的位定义在这里插入图片描述( D7~D1位组成从机的地址。D0位是数据传送方向位,为“0”时表示主机向从机写数据,为“1”时表示主机由从机读数据。)

主机发送地址时,总线上的每个从机都将这7位地址码与自己的地址进行比较,如果相同,则认为自己正被主机寻址,根据R/T位将自己确定为发送器或接收器。
从机的地址由固定部分和可编程部分组成。在一个系统中可能希望接入多个相同的从机,从机地址中可编程部分决定了可接入总线该类器件的最大数目。如一个从机的7位寻址位有4位是固定位,3位是可编程位,这时仅能寻址8个同样的器件,即可以有8个同样的器件接入到该I2C总线系统中。

模拟I2C通信时序:

为保证数据的可靠性,I2C总线的数据传送有严格的时序要求。I2C总线的起始信号、终止信号、发送“0”及发送“1”的模拟时序 :
在这里插入图片描述
程序实现:
在这里插入图片描述
在这里插入图片描述

IIC都从机应答信号(读0或1)在这里插入图片描述
IIC通信流程:(可改变部分顺序)

1.声明头文件。
2.声明所使用的函数。
3.定义变量及声明地址。(使用数码管时,要定义数组)
4.延时函数。
5.(若使用数码管,写数码管动态显示函数)
6.定时器初始化(此处使用定时器0)
7.写IIC总线起始信号函数。
8.写IIC总线终止信号函数。
9.写IIC总线读从机应答信号函数(1非应答,0应答)。
10.写主机发送应答信号函数(1主机发送非应答,0发送应答)。
11.写IIC发送一个字节数据函数。
12.写At24c02指定单元写入一个字节数据函数。
13.写IIC总线读一字节数据函数。
14.写读AT24C02指定单元内数据函数。
15.主函数(写出你想实现的要求)。
16.中断函数(此处使用定时器0中断函数)。

程序实现:

#include <reg52.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;//I2C时钟总线
sbit SDA = P2^0;//I2C数据总线
uchar num;//数码管显示的值
bit AckFlag;//应答标志位

//共阴数码管段选表0-9
uchar code SMGduan[]= {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F,};
//数码管位选码
uchar code SMGwei[] = {0xfe, 0xfd, 0xfb};

/====================================
函数 : delay(uint z)
参数 :z 延时毫秒设定,取值范围0-65535
返回值 :无
描述 :12T/Fosc11.0592M毫秒级延时
====================================
/
void delay(uint z)
{
uint x,y;
for(x = z; x > 0; x–)
for(y = 114; y > 0 ; y–);
}

/====================================
函数 :display(uchar i)
参数 :i 显示数值,取值范围0-255
返回值 :无
描述 :三位共阴数码管动态显示
====================================
/
void display(uchar i)
{
static uchar wei;
P0 = 0XFF;//清除断码
WE = 1;//打开位选锁存器
P0 = SMGwei[wei];
WE = 0;//锁存位选数据
switch(wei)
{
case 0: DU = 1; P0 = SMGduan[i / 100]; DU = 0; break;
case 1: DU = 1; P0 = SMGduan[i % 100 / 10]; DU = 0; break;
case 2: DU = 1; P0 = SMGduan[i % 10]; DU = 0; break;
}
wei++;
if(wei == 3)
wei = 0;
}
//定时器0初始化
void timer0Init()
{
EA = 1; //打开总中断
ET0 = 1;//打开定时器0中断
TR0 = 1; //启动定时器0
TMOD |= 0X01; //定时器工作模式1,16位定时模式
TH0 = 0xED;
TL0 = 0xFF; //定时5ms
}
/****************************************************
IIC通信代码
****************************************************/

/====================================
函数 :delay5us()
参数 :无
返回值 :无
描述 :5us延时函数
====================================
/
void delay5us()
{
nop();
}

/====================================
函数 :I2cStart()
参数 :无
返回值 :无
描述 :I2C总线起始信号
====================================
/
void I2cStart()
{
//时钟总线为高电平期间数据总线又高变低产生起始型号
SCL = 1;
SDA = 1;
delay5us();//状态保持5us
SDA = 0;
delay5us();//状态保持5us
}

/====================================
函数 :I2cStop()
参数 :无
返回值 :无
描述 :I2C总线停止信号
====================================
/
void I2cStop()
{
//时钟总线为高电平期间,数据总线从高变低产生终止信号
SCL = 0;
SDA = 0;
SCL = 1;
delay5us();//状态保持5us
SDA = 1;
delay5us();//状态保持5us
}

/====================================
函数 :ReadACK()
参数 :无
返回值 :1非应答,0应答
描述 :I2C总线读从机应答信号
====================================
/
bit ReadACK()
{
SCL = 0;//拉低时钟总线,允许从机控制SDA
SCL = 1;//拉高,读SDA
delay5us();
if(SDA)//NOACK
{
SCL = 0;
return(1);//返回1
}
else//ACK
{
SCL = 0;
return(0);//返回0
}
}

/====================================
函数 :SendACK(bit i)
参数 :1主机发送非应答,0发送应答
返回值 :无
描述 :主机发送应答信号
====================================
/
void SendACK(bit i)
{
SCL = 0;//拉低时钟总线,允许主机控制SDA
if(i) //发非应答
SDA = 1;
else //发应答
SDA = 0;
SCL = 1; //拉高总线,让从机读SDA
delay5us();//保持5us
SCL = 0; //拉低时钟总线,允许SDA释放
SDA = 1;//释放数据总线
}

/====================================
函数 :I2cSendByte(uchar DAT)
参数 :DAT需要发送的数据
返回值 :无
描述 :I2C发送一个字节数据
====================================
/
void I2cSendByte(uchar DAT)
{
uchar i;
for(i=0; i<8; i++) //分别写8次,每次写1位
{
SCL = 0;//拉低时钟总线,允许SDA变化
if(DAT & 0x80)//先写数据最高位
SDA = 1; //写1
else
SDA = 0; //写0
SCL = 1; //拉高时钟,让从机读SDA
DAT <<= 1; //为发送下一位左移1位
}
SCL = 0; //拉低时钟总线,允许SDA释放
SDA = 1;//释放数据总线
}

/====================================
函数 :At24c02Write(uchar ADDR, DAT)
参数 :ADDR 单元地址0-255,DAT 需要输入的数据0-255
返回值 :无
描述 :At24c02指定单元写入一个字节数据
====================================
/
void At24c02Write(uchar ADDR, DAT)
{
I2cStart();//I2C起始信号
I2cSendByte(At24c02ADDR + I2cWrite);//发送器件地址加读写方向位
if(ReadACK()) //读从机应答
AckFlag = 1; //NOACK
else
AckFlag = 0; //ACK
I2cSendByte(ADDR);//发送储存单元地址字节
if(ReadACK())//读从机应答
AckFlag = 1; //NOACK
else
AckFlag = 0; //ACK
I2cSendByte(DAT);//发送一字节数据
if(ReadACK())//读从机应答
AckFlag = 1; //NOACK
else
AckFlag = 0; //ACK
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); //返回读出的数据
}

/====================================
函数 :At24c02Read(uchar ADDR)
参数 :ADDR 单元地址 0-255
返回值 :返回指定单元的数据
描述 :读AT24C02指定单元内数据
====================================
/
uchar At24c02Read(uchar ADDR)
{
uchar DAT;
I2cStart();//I2C起始信号
I2cSendByte(At24c02ADDR + I2cWrite);//发送器件地址加读写方向位
if(ReadACK())//读从机应答
AckFlag = 1; //NOACK
else
AckFlag = 0; //ACK
I2cSendByte(ADDR);//I2C发送一个字节
ReadACK();//读从机应答
I2cStart();//再次产生I2C起始信号
I2cSendByte(At24c02ADDR + I2cRead);//发送器件地址加读写方向位 读
if(ReadACK())//读从机应答
AckFlag = 1; //NOACK
else
AckFlag = 0; //ACK
DAT = I2cReadByte();//读一字节
SendACK(1);//主机发送非应答
I2cStop(); //I2C停止信号
return(DAT);//返回读出数据

}

void main()//main函数自身会循环
{
timer0Init();//定时器0初始化
EA = 0;//屏蔽中断
At24c02Write(3, 188);//给第3单元写入数据“188”
delay(1);//延时等待AT24C02处理
num = At24c02Read(3);//读出第3单元内数据送给显示变量
if(AckFlag)//当从机非应答
P1 = 0;//亮P1所有灯
else
P1 = 0XFF;//灭P1所有灯
EA = 1;//开中断
while(1);
}

//定时器0中断函数
void timer0() interrupt 1
{
TH0 = 0xED;
TL0 = 0xFF; //定时5ms
display(num); //数码管显示函数
}

  • 11
    点赞
  • 40
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值