I2c串行总线组成及其工作原理

采用串行总线技术可以使系统的硬件设计大大简化 , 系统的体积减小 , 可靠性提高 , 同时系统更容易更改和扩充
常用的串行扩展总线有:I2c总线 , 单总线 , SPI总线 , 以及microwire、Plus等等
I2c总线只有两根双向信号线 , 一根是数据线SDA , 另一根是时钟线SCL
 
I2c总线通过上拉电阻接正电源 。因此I2C总线的设备都要接上拉电阻
当总线闲置的时候 , 两根线均为高电平 , 连接到总线上的任何一个器件输出的低电平 , 都将使得总线得到信号变低 , 及各个器件的SDA和SCL都是线与的关系
每个接入到I2C总线都有唯一的地址 , 主机与其他器件间的数据传送可以是由主机发送数据到其他器件 , 这时主机即是发送器 , 由总线上接收数据的器件称为是接收器 。
在多主机系统中 , 可能同时由几个主机企图启动总线传送数据 , 为了避免混乱 , I2C总线要通过总线仲裁 , 已决定由哪台主机控制总线
数据位的有效性
I2C总线进行数据传送时 , 时钟信号为高电平期间 , 数据线上的数据必须保持稳定 , 只有时钟线上的信号为低电平期间 , 数据线上的高电平和低电平状态才允许变化
 
起始信号和终止信号
SCL线为高电平期间 , SDA线由高电平向低电平的变化表示起始信号 , SCL线为高电平期间 , SDA线由低电平向高电平变化表示终止信号
 
数据传送的格式
(1)字节传送与应答
每一个字节必须保证是8位长度 , 数据传送时 , 先传送的是最高位(MSB) , 每一个被传送的字节后面都必须跟随一位应答位 , 即(一帧共有9位) , 应答信号由从机发送给主机
 
每次数据传送总是由主机产生的终止信号结束 , 但是若主机希望继续占用总线进行新的数据传送 , 则可以不产生终止信号 , 马上再次发出起始信号对另一个从机进行寻址
在总线的一个数据传上过程中 , 可以有一下几种传送方式的组合方式
a , 主机向从机发送数据 , 数据传送的方向在整个传送过程中不变
 
A表示应答 , A非表示非应答 , s表示其实信号 , p表示终止信号
主机发送地址时 , 总线上的每一个从机都将这7位地址码与自己的地址进行比较 , 如果相同 , 则认为自己正在被主机寻址 , 根据R/T位将自己确定为发送器或接收器
从机地址由固定部分和可编程部分组成 , 可编程的部分决定了可接入总线该器件的最大数目 。
 
由操作时序可知要进行必要的延时
起始操作示例代码:
void T2CStart(void)
{
SDA = 1;
SomeNop();//大于微秒级别
SCL = 1;
SomeNop();
SDA = 0;
SomeNop();
}
终止指令:
void I2CStop(void)
{
SDA = 0;//data由0变到1为终止指令
SomeNop();
SCL = 1;
SomeNop();
SDA = 1;
SomeNop();
}
I2C总线扩展
串行E2PROM的扩展
(2)写入过程:AT24CEEPROM的固定地址为1010 , A2,A1A0引脚接入高低电平可以得到确定的3位编码 , 形成的7位编码即为该器件的地址码
单片机进行写操作的时候 , 首先
发送该器件的7位地址吗和写方向的方向码0 , 发送完以后释放SDA线并在SCL线上产生第九个时钟信号 , 被选中的存储器再确认自己的地址后 , 在SDA上产生一个应答信号作为响应
 , 单片机接收到信号就可以传送数据了
传送数据时 , 单片机首先发送一个字节的被写入器件的存储区的首地址 , 收到存储器器件的应答后 , 单片机就逐个发送各个数据的字节 , 但是每次发送一个字节后都要等待应答
收到每个字节的地址后 , 芯片上的地址会自动加一
写入n个字节的数据格式
 
读出过程
单片机首先发送该器件的7位地址码和写方向位0(伪写) , 发送完后释放SDA线并在SCL线上产生9个时钟信号 , 被选中的存储器器件在确认自己的地址之后 , 在SDA上产生一个应答信号作为回应
然后在发送一个字节的要读出存储去的首地址 , 收到应答 , 单片机要重复一次起始信号并发出器件地址的读方向位(1) , 收到器件应答就可以读出字节 , 每次读出一个字节 , 单片机都要回复一个应答信号 , 但最后读出一个字节 , 单片机应返回非应答信号(高电平)并发出终止信号以结束读出操作
 
示例代码:
#include
#define uchar unsigned char
#define uint unsigned int
sbit sda = P2^3;
sbit scl = P2^2;
sbit wp = P2^1;
void delay()//微妙级别的延时函数
{;;}
void start()//开始信号
{
sda = 1;
delay();
scl = 1;
delay();
sda = 0;
delay();
}
void stop()//停止信号
{
sda = 0;
delay();
scl = 1;
delay();
sda = 1;
delay();
}
void respons()//应答信号
{
uchar i;
scl = 1;
delay();
while((sda ==1)&&(i<250))//等到第九个时钟周期的时候 , 还没有变为0 , 
//那么scl将自动的变为0,表示收到信号
{
i++;
}
scl = 0;
}
void init()
{
sda = 1;
scl = 1;//把线全部释放
}
void write_byte(uchar date)
{
uchar i,temp;
temp = date;
scl = 0;
delay();
for(i = 0;i<8;i++)//写8次
{
temp = temp<<1;//表示将temp左移1位 , 将最高位移入psw寄存器中的cy位 , 
//然后将最高位赋值给sda , 送走数据
scl = 1;//数据稳定了
delay();
sda = CY;
delay();
scl = 0;//读走数据
delay();
}
sda = 1;//注意养成释放总线的习惯
【I2c串行总线组成及其工作原理】delay();
}
uchar read_byte()
{
uchar i,j,k;
scl = 0;
delay();
sda = 1;//释放数据总线
delay();
for(i=0;i<8;i++)
{
scl = 1;
delay();
j = sda ;//读取数据
k =(k<<1)|j;
scl = 0;
delay();
}
return k;
}
uchar read_add(uchar address)
{
uchar date;
start();
write_byte(0xa0);//表示写入器件的地址
respons();
write_byte(address);
respons();
start();
write_byte(0xa1);
respons();
date=read_byte();
stop();
return date;
}
void write_add(uchar address,uchar date)
{
init();//初始化信号总线和地址总线
start();//启动信号
write_byte(0xa0);//表示写入器件的地址
respons();
write_byte(address);//表示往这个器件内部的第三个地址处写入地址
respons();
write_byte(date);//表示器件内部的数据
respons();
stop();
}
void delay1(uint z)
{
uint x,y;
for(x= z;x>0;x--)
for(y=110;y>0;y--);
}
void main()
{
init();
write_add(23,125);
delay1(100);
P1=read_add(23);
while(1);
}

    推荐阅读