目录
IIC总线是一种由NXP公司开发的两线式串行总线,用于连接微控制器及其外围设备。多用于主控制器和从器件间的主从通信,在小数据量场合使用,传输距离短,任意时刻只能有一个主机。
在CPU与被控IC之间、IC与IC之间进行双向传送,通信速率一般在100kHZ-400kHZ(48.8kb/s),高速IIC总线一般可达400kbps以上。IIC是为了与低速设备通信而发明的,所以IIC的传输速率比不上SPI。
IIC一共只有两个总线,一条是双向的串行数据线SDA,一条是串行时钟线SCL。
SDA数据线:用来传输数据的
SCL时钟线:用来控制数据发送的时序的
所有接到IIC总线设备上的串行数据SDA都接到总线的SDA上,各设备的SCL都接到总线的SCL上。IIC总线上的每个设备都有自己唯一的地址,来确保不同设备间访问的准确性。
通信方式有异步和同步通信,IIC是同步通信。
异步:发送方要等接收方响应才继续通信,阻塞的通信方式
同步:不用管接收方是否有回应,是非阻塞的通信方式
通常为了方便把IIC设备分为主设备和从设备,基本上谁控制时钟线(即控制SCL时钟线的电平高低转换)谁就是主设备
IIC主设备功能:主要产生时钟,产生起始信号和停止信号
IIC从设备功能:可编程的IIC地址检测,停止位检测
IIC的一个优点是它支持多主控,其中任何一个能够进行发送和接收的设备都可以成为主总线。一个主控能够控制信号的传输和时钟频率。在任何时间点只能有一个主控。
SCL和SDA都需要接上拉电阻保证数据的稳定性,减少干扰
IIC是半双工通信,同一时间只可以单向通信
为了避免总线信号的混乱,要求各设备连接到总线的输出端时必须是漏极电路(OD)输出或集电极开路输出(OC)。
漏极电路(Open Drain)即高阻状态,适用于输入/输出,其可独立输入/输出低电平和高阻状态,若需要产生高电平,则需使用外部上拉电阻。
高阻状态:高阻状态是三态门电路的一种状态,逻辑门的输出有高低电平两种状态外,还有高阻状态的门电路。高阻态相当于隔断状态。三态门都有一个EN控制使能端来控制门电路的通断。处于高阻态的三态门是与总线隔离开的,这样总线可以同时被其他电路占用。EN=0时,Y为高阻状态。
IIC的所有设备是连接在一根总线上的,那么进行通信的时候往往只是几个设备进行通信,那这时候其余空闲的设备可能会受到总线干扰,或者干扰到总线。
为了避免总线信号的混乱,IIC的空闲状态只能有外部上拉,而此时空闲设备被拉到了高阻态,也就是相当于断路,整个IIC总线只有开启了的设备才会正常进行通信,而不会干扰到其他设备。
可随意设置,从设备必须配置地址
IIC从地址有三种类型,7位、8位和10位,厂商采用不同的地址约定。
7位寻址,如下图oled写入模式的从机地址
IIC总线规范规定,标准模式从机地址为7位长,其次是读写位。
在7位寻址过程中,从机地址在启动信号后的第一个字节开始传输,该字节的前7位为从机地址,第8位为读写位,0表示写,1表示读,它决定了传输的方向,第一个字节的第8位是0,表示主机会写信息到被选中的从机。
任何IIC设备都必须遵循这个标准,USB2xxx传输的从机地址即为这7bit地址,不含读写位,读写位会根据不同的函数自动添加进去。
保留地址:IIC规范保留了两组和8个地址,1111xxx和0000xxx。
这些地址用于特殊用途。
8位地址
一些厂商在提供从机地址的时候说的是包含了读写位的8bit地址,比如他说写地址为0x92,读地址为0x93,8位寻址的情况下需要将这个地址的前7bit提取出来,然后传入USB2xxx的接口函数,如图
判断厂商提供的地址是7bit模式地址还是8bit地址的方式:7bit地址模式下,地址的取值范围是0x07-0x78,超过了可能就是8bit。
10位寻址
IIC总线的10bit寻址和7bit寻址是兼容的,这样就可以在同一个总线上同时使用7bit地址和10bit地址模式的设备,在10bit寻址模式中,从机地址由7个有效位和3个可选位组成。有效位用于指定从机的地址,而可选位则用于扩展寻址范围。
在IIC总线上,主机发送起始位和读/写位后,紧接着发送7个有效位来指定从机地址。如果需要使用10位寻址模式,则在发送完7个有效位后,再发送3个可选位。
通过10位寻址模式,可以支持更多的从机地址,扩展了总线上可连接的设备数量。不过不是所有的设备都支持10位寻址模式,所以在使用时需要查看设备的规格说明书来确定支持的寻址模式。
每一个IIC器件都有一个器件地址,有的器件地址在出厂时地址就设定好了,用户不可以更改。有的器件例如EEPROM,前四个地址已经确定为1010,后三个地址由硬件链接确定的,所以IIC总线最多能连8个EEPROM芯片。
IIC总线最多可以挂多少个设备:有IIC地址决定,8位地址,减去一位广播地址,2^7=128,0x00不用,所以理论上可以挂127个从器件。
但是 IIC协议虽然没有规定总线设备最大数目,但是规定了总线电容不能超过400pF。管脚都是有输入电容的,PCB上也会有寄生电容,所以会有一个限制,有人测验大概是不超过8个器件。
总线时序图
初始(空闲)状态:SCL和SDA都保持高电平
起始信号:SCL为高电平,SDA由高电平变为低电平,数据开始发送
结束信号:SCL为高电平,SDA由低电平变为高电平,数据传送结束
有效的数据位传输:在IIC总线上传送的每一位数据都有一个时钟脉冲相对应(或同步控制),即在SCL串行时钟的配合下,数据在SDA上从高位向低位依次串行传送每一位数据。
应答信号:发送器每发送一个字节(8个bit),就在时钟脉冲9期间释放数据线,由接收器反馈一个应答信号。
应答信号为低电平时,规定为有效应答位(ACK,简称应答位),表示接收器已经成功地接收了该字节。
应答信号为高电平时,规定为非应答位(NACK),一般表示接收器接收该字节没有成功。
初始(空闲)状态
- void IIC_Init()
-
- {
-
- scl = 1;
-
- delay_us(5);
-
- sda = 1;
-
- delay_us(5);
-
- }
起始信号:SCL保持高电平,SDA由高电平(>4.7us)变为低电平(>4us),SCL变为低电平。
- //起始信号
-
- void IIC_Start()
-
- {
-
- scl = 1;
-
- sda = 1;
-
- delay_us(5);
-
- sda = 0;
-
- delay_us(5);
-
- scl = 0;
-
- }
结束信号:SCL保持高电平(>4us),SDA由低电平变为高电平(>4.7us),SDA变为低电平
- //结束信号
-
- void IIC_Stop()
-
- {
-
- scl = 1;
-
- sda = 0;
-
- delay_us(5);
-
- sda = 1;
-
- delay_us(5);
-
- sda = 0;
-
- }
数据有效性
IIC信号在数据传输中,当SCL=1高电平时,数据线SDA必须保持稳定状态,不允许有电平跳变,只有在时钟线上的信号为低电平期间,数据线上的电平状态才允许变化。
SCL=1时,数据线任何的电平状态变换会看做是总线的起始信号或者终止信号。所以在传输数据的过程中,SCL时钟线会频繁的转换电平以保证数据的稳定传输。
应答信号:主机SCL拉高,读取从机SDA的电平,为低电平表示产生应答
每发送一个字节(8个bit),在一个字节传输的8个时钟后的第九个时钟期间,接收器接收数据后必须返回一个ACK应答信号给发送器,这样才能进行数据传输。
- //应答
-
- void IIC_ACK()
-
- {
-
- scl = 0;
-
- sda = 0;
-
- delay_us(1);
-
- scl = 1;
-
- delay_us(5);
-
- scl = 0;
-
- }
- //非应答
-
- void IIC_NACK()
-
- {
-
- scl = 0;
-
- sda = 1;
-
- delay_us(1);
-
- scl = 1;
-
- delay_us(5);
-
- scl = 0;
-
- }
- //应答信号sda为低电平,非应答为高电平
-
- //所以读取SDA的状态来判断是什么信号
-
- void IIC_read_ACK()
-
- {
-
- char flag;//标志位,记录sda的状态
-
- sda = 1;//在时钟脉冲9期间释放数据线
-
- delay_us(2);
-
- scl = 1;
-
- delay_us(5);
-
- flag = sda;//在这个期间记录下sda的电平状态
-
- delay_us(5);
-
- scl = 0;
-
- return flag;
-
- }
数据传送时,先传送最高位(MSB),每一个被传送的字节后面都必须跟随一位应答位(即一帧共有9位)。
当一个字节按数据位从高到低的顺序传输完成后,紧接着从设备将拉低SDA线,回传给主设备一个应答位ACK,此时才认为一个字节真正的被传输完成,如果一段时间内没有收到从机的应答信号,则自动认为从机已正确接收到数据。
IIC写数据
IIC的每一帧数据由9bit组成
如果是发送数据,则包含8bit数据+1bitACK
如果是设备地址数据,则包含7bit设备地址+1bit方向位+1bitACK
IIC总线数据格式:
IIC写数据:
- //在SCL为高电平期间,发送数据,发送8次,,数据从高到低
-
- //传输期间保持传输稳定,数据线仅可以在SCL为低电平时改变
-
- void IIC_Write_Byte(chra dataSend)
-
- {
-
- int i;
-
- for(i = 0;i < 8;i++){
-
- //拉低时钟开始数据传输
-
- scl = 0;
-
- //获取最高位给sda
-
- sda = dataSend & 0x80;//与1000 0000获得dataSend最高位
-
- delay_us(5);//发送数据建立时间
-
- scl = 1;//拉高时钟开始数据发送
-
- delay_us(5);
-
- scl = 0;//发送完毕拉低
-
- delay_us(5);//准备下一个数据位
-
- dataSend = dataSend << 1;//从高到低发送,左移获得下一位数据
-
- }
-
- }
主机向从机发送数据时:
IIC读数据
- //读数据
-
- void IIC_Read_Byte()
-
- {
-
- int i,value = 0;
-
- delay_us(1);
-
- for(i = 0;i < 8;i++){
-
- value <<= 1;
-
- scl = 1;
-
- delay_us(1);
-
- if(sda = 1){
-
- value++;
-
- }
-
- delay_us(1);
-
- scl = 0;
-
- delay_us(1);
-
- }
-
- return value;
-
- }
主机从 从机读数据时: