总线接口与计算机通信(一)I2C总线
1. I2C总线的基本概念
1)发送器(Transmitter):发送数据到总线的器件
2)接收器(Receiver):从总线接收数据的器件
3)主机(Master):初始化发送、产生时钟信号和终止发送的器件
4)从机(Slave):被主机寻址的器件
2. I2C总线的信号传输
1) 3种速率可选择
标准模式100kbps、快速模式400kbps、最高速率3.4Mbps
2) 具有特定的传输起始、停止条件
a) 起始条件:当SCL 处于高电平期间时,SDA 从高电平向低电平跳变时产生起始条件。 起始条件常常简记为S
b) 停止条件:当SCL 处于高电平期间时,SDA 从低电平向高电平跳变时产生停止条件。 停止条件简记为P
3) 数据传输从确定从机地址开始
a)多个从机可连接到同一条I2C 总线上,它们之间通过各自唯一的器件地址来区分
b)一般从机地址由7 位地址位和一位读写标志R/W 组成,7 位地址占据高7 位,读写位在最后。读写位是0,表示主机将要向从机写入数据;读写位是1,则表示主机将要从从机读取数据
4) 以字节为单位的数据传输方式
a) I2C 总线以字节(Byte) 为单位收发数据,主机总是先发地址再发数据
b) 在I2C总线传输数据过程中,每传输一个字节,都要跟一个应答状态位。遵循"谁接收谁产生"的原则,即总是由接收器产生应答位,应答位为0 表示接收器应答 (ACK) ;为1 则表示非应答(NACK)
c) 如果接收器在接收完最后一个字节的数据,或者不能再接收更多的数据时,应当产生非应答来通知发送器
一次iic交互的例子,写AT24C08
写AT24C08: start信号 + addr + data[N] + stop信号 { 1. master发送start信号: [scl高+sda下降沿] 2.1. master发送addr: 7位设备地址 + R/W; 发完立刻把电平拉高,等待slave的ack; [sda在scl低电平的时候准备数据, 在scl上升沿时发送数据] * 8次 MSB---先发最高位数据sda. 2.2. slave收到master的start信号后,就开始监测master的数据了. 收到addr完毕并且发现addr就是自己的时候,马上拉低sda以表明传输有效(这就是所谓ack). 3.1 主机收到ack后接着发送数据data, 这里是写1个字节,那就和发送addr用同样的方法连续发送8个bit就是了. 同样,发完立刻把电平拉高,等待slave的ack; 3.2 slave收到8位data完毕,马上拉低sda以表明传输有效(ack). 4. master收到ack,马上发送stop信号以关闭通信. 说明: iic协议允许一次连接中传输多个字节,具体最多允许多少个字节看器件(AT24C08允许8个字节连续传输), 但是必须以8bit为单位, 接收方要给一个ack才会继续后面的传输. 明确start和stop是由master发起的. }
(灰色为主机的工作时隙,白色为从机的工作时隙)
S:起始位(START)
SA:从机地址(Slave Address),7 位从机地址
W:写标志位(Write),1 位写标志 R:读标志位(Read),1 位读标志
A:应答位(Acknowledge),1 位应答
A:非应答位(Not Acknowledge),1 位非应答
D:数据(Data),每个数据都必须是8 位
P:停止位(STOP)
3. I2C 总线具有如下特点
1) I2C 总线是双向传输的总线,因此主机和从机都可能成为发送器和接收器。
不论主机是发送器还是接收器,时钟信号SCL都要由主机来产生。
2) 读取数据是依据SCL为低时,SDA的电平.
3) 进行数据传送时,在SCL为高电平期间,SDA线上电平必须保持稳定,只有SCL为低时,才允许SDA线上电平改变状态。并且每个字节传送时都是高位在前。
4) 关于时钟:
由于通讯的时钟是由主机(master)控制的, 所以slave不需要有设置工作频率, master快, slave就快, master慢, slave就慢. (甚至可以时快时慢, 忽快忽慢). 但通常会有一个固定频率.
现在的I2C都是由集成的模块实现, 集成的模块往往能够满足I2C通讯的最大通讯速度, 所以从这个角度考虑, slave是完全不需要考虑通讯频率的.
5) . 关于应答信号:
ACK=0时为有效应答位,说明从机已经成功接收到该字节,若为1则说明接受不成功。
主机在读取从机数据时,也会产生ack,但是主机读取最后一个字节时不需要产生ack.
6) 只需要由两根信号线组成,一根是串行数据线SDA,另一根是串行时钟线SCL。
7) SDA 和SCL 信号线都必须要加上拉电阻Rp(Pull-Up Resistor)。上拉电阻一般取值3~ 10KΩ
8) SDA 和SCL 管脚都是漏极开路(或集电极开路)输出结构
9) 同一IIC总线上同一型号的IC只能最多共挂8片同种类芯片
4. I2C总线注意点
1) . 在特殊情况下,若需禁止所有发生在I2C总线上的通信,可采用封锁或关闭总线,具体操作为在总线上的任一器件将SCL锁定在低电平即可。
2) . 如果从机需要延迟下一个数据字节开始传送的时间,可以通过把SCL电平拉低并保持来强制主机进入等待状态。
3) . 主机完成一次通信后还想继续占用总线在进行一次通信,而又不释放总线,就要利用重启动信号。它既作为前一次数据传输的结束,又作为后一次传输的开始。
4) . 总线冲突时,按"低电平优先"的仲裁原则,把总线判给在数据线上先发送低电平的主器件。
5) . SDA仲裁和SCL时钟同步处理过程没有先后关系,而是同时进行的。
5. I2C总线在手机上的常见应用
所应用范围包括:摄像头、调频收音机芯片、音频编解码芯片、模拟电视、光电鼠标等
IIC 通讯协议 <http://blog.csdn.net/zmq5411/article/details/6085740>
由时序图写出IIC通信的Master代码, 不咋准确,仅作理解使用:
//准备: void init() // 初始化 { SCL=1; delay(); SDA=1; delay(); } /*从下面开始, delay()的主要作用是维护周期.*/ //generate Start signal void start() // 启动信号 { SDA=1; delay(); SCL=1; delay(); SDA=0; delay(); } //generate Stop signal void stop() // 停止信号 { SDA=0; delay(); SCL=1; delay(); SDA=1; delay(); } // ACK
// 个人感觉这个ACK处理的不太好, 半个周期内理想条件下SDA已经置为1,这个时候SCL拉高没有问题. 但如果从机响应较慢呢? 就会形成 SCL=1 + SDA下降沿, 那就形成了一个start信号.
// 如果只是一个slave,这没什么,但是对于带了多个slave的总线,其他slave就进入接收预备状态,不太符合情理.
// 应该在这里可以多等一下,保证主机拉高sda以及从机拉低sda的过程均在SCL=0的时候完成. 即在SCL=1之前再加入delay,并且在SCL=1之前就可以读SDA了,读到ack之后再拉高. void wait_ack() //等待回应信号 { uchar i=0; SCL=1; delay(); //这个delay是为了维持半个周期. while((SDA==1)&&(i<255)) //slave的ack会将sda拉低. 这里为什么要等255个循环? 是为了等待ack. { i++; } SCL=0; //读到ack之后又拉低SCL, 准备下一个周期 delay(); } // writebyte void writebyte(uchar date) // 写一个字节 { uchar i, temp; temp = date; for(i=0;i<8;i++) { Temp = temp<<1; SCL = 0; delay(); SDA = CY; delay(); SCL = 1; delay(); } SCL = 0; delay(); // 这个delay是为了等待SCL稳定过渡到低电平同时维持半个周期的时间. SDA = 1; // 发完立刻把电平拉高,等待slave的ack; delay(); // 这个delay是为了维持半个周期的时间. } //readbyte uchar readbyte()//读一个字节 { uchar i, j, k; SCL=0; delay(); SDA=1; for(i=0;i<8;i++) { SCL=1; delay(); if(SDA==1) j=1; else j=0; k=(k<<1)|j; SCL=0; delay(); } delay(); return k; } // write_add void write_add(uchar address, uchar info)//指定地址写一个字节数据 { start(); writebyte(0xa0);//这是一个从机芯片硬件的地址+ /W信号,不同芯片会不一样 wait_ack();//等待应答 writebyte(address);//这是指定从机芯片的内部存储地址 wait_ack(); writebyte(info); wait_ack(); stop(); } // read_add uchar read_add(uchar address)//指定地址读一个字节数据 { uchar dd; start(); writebyte(0xa0); //这是一个从机芯片硬件的地址+ /W 信号,不同芯片会不一样 wait_ack(); writebyte(address); wait_ack(); start(); writebyte(0xa1);//这是一个从机芯片硬件的地址+ R信号,不同芯片会不一样 wait_ack(); dd=readbyte(); stop(); return dd; }
参考:
-
普通IO口模拟实现I2C通信及应用解析 http://blog.csdn.net/bluewhaletech/article/details/38706093,bluewhaletech