I2C 总线协议分析

  由于大规模集成电路技术的发展,在单个芯片集成 CPU 以及组成一个单独工作系统所必须的 ROM、RAM、I/O 端口、A/D、D/A 等外围电路和已经实现,这就是常说的单片机或微控制器。目前,世界上许多公司生产单片机,品种很多:包括各种字长的 CPU,各种容量和品种的 ROM、RAM,以及功能各异的 I/O 等等。但是,单片机品种规格有限,所以只能选用某种单片机再进行扩展。扩展的方法有两种:一种是并行总线,另一种是串行总线。由于串行总线连线少,结构简单,往往不用专用的母板和插座而直接用导线连接各个设备即可。因此,采用串行总线大大简化了系统硬件设计。PHILIPS 公司早在十几年就前推出了I2C 串行总线,它是具备多主机系统所需的包括裁决和高低速设备同步等功能的高性能串行总线。

  总线对设备接口电路的制造工艺和电平都没有特殊的要求(NMOS、CMOS 都可以兼容)。数据传送率按I2C 总线可高达每秒十万位,高速方式可高达每秒四十万位。总线上允许连接的设备数以总线上的电容量不超过 400pF 为限。

  总线的运行(数据传输)由主机控制。所谓主机即启动数据的传送(发出启动信号),发出时钟信号,传送结束时发出停止信号的设备,通常主机是微处理器。被主机寻访的设备都称为从机。为了进行通讯,每个接到I2C 总线的设备都有一个唯一的地址,以便于主机寻访。主机和从机的数据传送,可以由主机发送数据到从机,也可以是从机发到主机。凡是发送数据到总线的设备称为发送器,从总线上接收数据的设备被称为接受器。

  I2C 总线上允许连接多个微处理器及各种外围设备,如存储器、LED 及 LCD驱动器、A/D 及 D/A 转换器等。为了保证数据可靠地传送,任一时刻总线只能有由某一台主机控制一个微处理器应该在总线空闲时发启动数据,为了妥善解决多台微处理器同时发启数据传送(总线控制权)的冲突,并决定由哪一台微处理器控制总线。I2C 总线允许连接不同传送速率的设备,多台设备之间时钟信号的同步过程称为同步化。 

  I2C 串行总线有两根信号线:一根双向的数据线 SDA;另一根是时钟线 SCL。所有接到I2C总线上的设备的串行数据都接到总线的SDA线,各设备的时钟线SCL接到总线的 SCL。

  为了避免总线信号的混乱,要求各设备连接到总线的输出端必须是开漏输出或集电极开路输出的结构。设备上的串行数据线 SDA 接口电路应该是双向的,输出电路用于向总线上发数据,输入电路用于接收总线上的数据。串行时钟线也应是双向的,作为控制总线数据传送的主机要通过 SCL 输出电路发送时钟信号,同时要检测总线上 SCL 上的电平以决定什么时候发下一个时钟脉冲电平;作为接受主机命令的从机,要按总线上的 SCL 的信号发出或接收 SDA 上的信号,也可以向 SCL 线发出低电平信号以延长总线时钟信号周期。总线空闲时,因各设备都是开漏输出,上拉电阻 RP 使 SDA 和 SCL 线都保持高电平。任一设备输出的低电平都使相应的总线信号线变低,也就是说各设备的 SDA 是“与”关系,SCL 也是“与”关系。

  在 I2C 总线通信的过程中,参与通信的双方互相之间所传输的信息种类归纳如下。主控器向被控器发送的信息种类有:启动信号、停止信号、7 位地址码、读/写控制位、10 位地址码、数据字节、重启动信号、应答信号、时钟脉冲。被控器向主控器发送的信息种类有:应答信号、数据字节、时钟低电平。下面对 I2C 总线通信过程中出现的几种信号状态和时序进行分析。

  ①总线空闲状态

  I2C总线总线的SDA和SCL两条信号线同时处于高电平时,规定为总线的空闲状态。此时各个器件的输出级场效应管均处在截止状态,即释放总线,由两条信号线各自的上拉电阻把电平拉高。

  ②启动信号

  在时钟线 SCL 保持高电平期间,数据线 SDA 上的电平被拉低(即负跳变),定义为 I2C 总线总线的启动信号,它标志着一次数据传输的开始。启动信号是一种电平跳变时序信号,而不是一个电平信号。启动信号是由主控器主动建立的,在建立该信号之前 I2C 总线必须处于空闲状态。

  ③停止信号

  在时钟线 SCL 保持高电平期间,数据线 SDA 被释放,使得 SDA 返回高电平(即正跳变),称为 I2C总线的停止信号,它标志着一次数据传输的终止。停止信号也是一种电平跳变时序信号,而不是一个电平信号,停止信号也是由主控器主动建立的,建立该信号之后,I2C 总线将返回空闲状态。

  ④数据位传送

  在 I2C 总线上传送的每一位数据都有一个时钟脉冲相对应(或同步控制),即在 SCL 串行时钟的配合下,在 SDA 上逐位地串行传送每一位数据。进行数据传送时,在 SCL 呈现高电平期间,SDA上的电平必须保持稳定,低电平为数据 0,高电平为数据 1。只有在 SCL 为低电平期间,才允许 SDA 上的电平改变状态。逻辑 0 的电平为低电压,而逻辑 1 的电平取决于器件本身的正电源电压 VDD(当使用独立电源时)。

  ⑤应答信号。

  I2C 总线上的所有数据都是以 8 位字节传送的,发送器每发送一个字节,就在时钟脉冲 9 期间释放数据线,由接收器反馈一个应答信号。应答信号为低电平时,规定为有效应答位(ACK简称应答位),表示接收器已经成功地接收了该字节;应答信号为高电平时,规定为非应答位(NACK),一般表示接收器接收该字节没有成功。对于反馈有效应答位 ACK 的要求是,接收器在第9个时钟脉冲之前的低电平期间将 SDA 线拉低,并且确保在该时钟的高电平期间为稳定的低电平。如果接收器是主控器,则在它收到最后一个字节后,发送一个 NACK 信号,以通知被控发送器结束数据发送,并释放 SDA 线,以便主控接收器发送一个停止信号P。

  ⑥插入等待时间

  如果被控器需要延迟下一个数据字节开始传送的时间,则可以通过把时钟线 SCL 电平拉低并且保持,使主控器进入等待状态。一旦被控器释放时钟线,数据传输就得以继续下
去,这样就使得被控器得到足够时间转移已经收到的数据字节,或者准备好即将发送的数据字节。带有CPU的被控器在对收到的地址字节做出应答之后,需要一定的时间去执行中断服务子程序,来分析或比较地址码,其间就把 SCL 线钳位在低电平上,直到处理妥当后才释放 SCL 线,进而使主控器继续后续数据字节的发送。

  ⑦重启动信号

  在主控器控制总线期间完成了一次数据通信(发送或接收)之后,如果想继续占用总线再进行一次数据通信(发送或接收),而又不释放总线,就需要利用重启动 Sr 信号时序。重启动信号 Sr 既作为前一次数据传输的结束,又作为后一次数据传输的开始。利用重启动信号的优点是,在前后两次通信之间主控器不需要释放总线,这样就不会丢失总线的控制权,即不让其他主器件节点抢占总线。

  ⑧时钟同步

  如果在某一I2C总线系统中存在两个主器件节点,分别记为主器件 1 和主器件 2,其时钟输出端分别为CLK1 和 CLK2,它们都有控制总线的能力。假设在某一期间两者相继向 SCL 线发出了波形不同的时钟脉冲序列 CLK1 和 CLK2(时钟脉冲的高、低电平宽度都是依靠各自内部专用计数器定时产生的),在总线控制权还没有裁定之前这种现象是可能出现的。鉴于 I2C 总线的“线与”特性,使得时钟线 SCL 上得到的时钟信号波形,既不像主器件 1 所期望的CLK1,也不像主器件 2 所期望的 CLK2,而是两者进行逻辑与的结果。
CLKI 和 CLK2 的合成波形作为共同的同步时钟信号,一旦总线控制权裁定给某一主器件,则总线时钟信号将会只由该主器件产生。

  ⑨总线冲突和总线仲裁

  假如在某 I2C 总线系统中存在两个主器件节点,分别记为主器件 1 和主器件 2,其数据输出端分别为DATA1 和 DATA2,它们都有控制总线的能力,这就存在着发生总线冲突(即写冲突)的可能性。假设在某一瞬间两者相继向总线发出了启动信号,鉴于:I2C 总线的“线与”特性,使得在数据线 SDA上得到的信号波形是 DATA1 和 DATA2 两者相与的结果,该结果略微超前送出低电平的主器件 1,其 DATA1的下降沿被当做 SDA 的下降沿。在总线被启动后,主器件 1 企图发送数据“101……”,主器件 2 企图发送数据据“100101……”。两个主器件在每次发出一个数据位的同时都要对自己输出端的信号电平进行抽检,只要抽检的结果与它们自己预期的电平相符,就会继续占用总线,总线控制权也就得不到裁定结果。主器件 1 的第 3 位期望发送“1”,也就是在第 3 个时钟周期内送出高电平。
  在该时钟周期的高电平期间,主器件 1 进行例行抽检时,结果检测到一个不相匹配的电平“0”,这时主器件 1 只好决定放弃总线控制杈;因此,主器件 2 就成了总线的惟一主宰者,总线控制权也就最终得出了裁定结果,从而实现了总线仲裁的功能。
  从以上总线仲裁的完成过程可以得出:仲裁过程主器件1和主器件2都不会丢失数据;各个主器件没有优先级别之分,总线控制权是随机裁定的,即使是抢先发送启动信号的主器件1最终也并没有得到控制杈。系统实际上遵循的是“低电平优先”的仲裁原则,将总线判给在数据线上先发送低电平的主器件,而其他发送高电平的主器件将失去总线控制权。

  ⑩总线封锁状态。

  在特殊情况下,如果需要禁止所有发生在 I2C 总线上的通信活动,封锁或关闭总线是一种可行途径,只要挂接于该总线上的任意一个器件将时钟线 SCL 锁定在低电平上即可。

最后,附上用STM32的GPIO口模拟的I2C总线协议的代码。

  1 #define     I2C_GPIO        GPIOB
  2 #define     I2C_SCL         GPIO_Pin_10
  3 #define     I2C_SDA         GPIO_Pin_11
  4 #define     I2C_RCC         RCC_APB2Periph_GPIOB
  5
  6 #define     SCL_H()         I2C_GPIO->BSRR  = I2C_SCL
  7 #define     SCL_L()         I2C_GPIO->BRR   = I2C_SCL
  8 #define     SDA_H()         I2C_GPIO->BSRR  = I2C_SDA
  9 #define     SDA_L()         I2C_GPIO->BRR   = I2C_SDA
 10 #define     SCL_Read()      I2C_GPIO->IDR   & I2C_SCL
 11 #define     SDA_Read()      I2C_GPIO->IDR   & I2C_SDA
 12
 13 void I2C_GPIO_Init(void)    //I2C引脚进行初始化
 14 {
 15     GPIO_InitTypeDef  GPIO_InitStructure;
 16     RCC_APB2PeriphClockCmd(I2C_RCC, ENABLE );
 17     GPIO_InitStructure.GPIO_Pin =  I2C_SCL;
 18     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
 19     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
 20     GPIO_Init(I2C_GPIO, &GPIO_InitStructure);
 21
 22     GPIO_InitStructure.GPIO_Pin =  I2C_SDA;
 23     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
 24     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
 25     GPIO_Init(I2C_GPIO, &GPIO_InitStructure);
 26 }
 27
 28 void I2C_Delay(void)    //延时函数,用空函数进行简单延时
 29 {
 30
 31 }
 32
 33 int I2C_Start(void)     //I2C启动信号
 34 {
 35     SDA_H();
 36     SCL_H();
 37     I2C_Delay();
 38     if(!SDA_Read)       //SDA线为低电平则总线忙,退出
 39     return 0;
 40     SDA_L();
 41     I2C_Delay();
 42     if(SDA_Read)        //SDA线为高电平则总线出错,退出
 43     return 0;
 44     SDA_L();
 45     I2C_Delay();
 46
 47     return 1;
 48 }
 49
 50 void I2C_Stop(void)    //I2C停止信号
 51 {
 52     SCL_L();
 53     I2C_Delay();
 54     SDA_L();
 55     I2C_Delay();
 56     SCL_H();
 57     I2C_Delay();
 58     SDA_H();
 59     I2C_Delay();
 60 }
 61
 62 void I2C_Ack(void)    //I2C应答信号
 63 {
 64     SCL_L();
 65     I2C_Delay();
 66     SDA_L();
 67     I2C_Delay();
 68     SCL_H();
 69     I2C_Delay();
 70     SCL_L();
 71     I2C_Delay();
 72 }
 73
 74 void I2C_NoAck(void)    //I2C无应答信号
 75 {
 76     SCL_L();
 77     I2C_Delay();
 78     SDA_H();
 79     I2C_Delay();
 80     SCL_H();
 81     I2C_Delay();
 82     SCL_L();
 83     I2C_Delay();
 84 }
 85
 86 int I2C_WaitAck(void)    //I2C等待应答信号 返回值:1有ACK,0无ACK
 87 {
 88     SCL_L();
 89     I2C_Delay();
 90     SDA_H();
 91     I2C_Delay();
 92     SCL_H();
 93     I2C_Delay();
 94     if(SDA_Read)
 95     {
 96           SCL_L();
 97           I2C_Delay();
 98           return 0;
 99     }
100     SCL_L();
101     I2C_Delay();
102
103     return 1;
104 }
105
106 void I2C_SendByte(unsigned int SendByte)      //I2C发送一个字节
107 {
108     unsigned int i=8;
109     while(i--)
110     {
111         SCL_L();
112         I2C_Delay();
113         if(SendByte&0x80)
114             SDA_H();
115         else
116             SDA_L();
117         SendByte<<=1;
118         I2C_Delay();
119         SCL_H();
120         I2C_Delay();
121     }
122     SCL_L();
123 }
124
125 unsigned int I2C_ReadByte(void)       //I2C读取一个字节
126 {
127     unsigned int i=8;
128     unsigned int ReceiveByte=0;
129
130     SDA_H();
131     while(i--)
132     {
133           ReceiveByte<<=1;
134           SCL_L();
135           I2C_Delay();
136           SCL_H();
137           I2C_delay();
138           if(SDA_Read)
139           {
140                ReceiveByte|=0x01;
141           }
142     }
143     SCL_L();
144
145     return ReceiveByte;
146 }
147
148 int I2C_Single_Write(unsigned int SlaveAddress,unsigned  int REG_Address,unsigned int REG_Data)        //I2C单字节写入
149 {
150       if(!I2C_Start())
151       return 0;
152       I2C_SendByte(SlaveAddress);
153       if(!I2C_WaitAck())
154       {
155         I2C_Stop();
156         return 0;
157       }
158       I2C_SendByte(REG_Address );   //设置低起始地址
159       I2C_WaitAck();
160       I2C_SendByte(REG_Data);
161       I2C_WaitAck();
162       I2C_Stop();
163
164       return 1;
165 }
166
167 int  I2C_Single_Read(unsigned int SlaveAddress,unsigned int REG_Address)    //I2C单字节读出
168 {
169     unsigned char REG_Data;
170     if(!I2C_Start())
171       return 0;
172     I2C_SendByte(SlaveAddress);
173     if(!I2C_WaitAck())
174     {
175         I2C_Stop();
176         return 0;
177     }
178     I2C_SendByte((unsigned int) REG_Address);
179     I2C_WaitAck();
180     I2C_Start();
181     I2C_SendByte(SlaveAddress+1);
182     I2C_WaitAck();
183
184     REG_Data= I2C_ReadByte();
185     I2C_NoAck();
186     I2C_Stop();
187
188     return REG_data;
189 }
190
191 int I2C_Mult_Read(unsigned int SlaveAddress,unsigned int REG_Address,unsigned int * ptChar,unsigned int size)    //I2C多字节读出
192 {
193     unsigned int i;
194
195     if(size < 1)
196             return 0;
197     if(!I2C_Start())
198             return 0;
199     I2C_SendByte(SlaveAddress);
200     if(!I2C_WaitAck())
201     {
202       I2C_Stop();
203       return 0;
204     }
205     I2C_SendByte(REG_Address);
206     I2C_WaitAck();
207
208     I2C_Start();
209     I2C_SendByte(SlaveAddress+1);
210     I2C_WaitAck();
211
212     for(i=1;i<size; i++)
213     {
214         *ptChar++ = I2C_ReadByte();
215         I2C_Ack();
216     }
217     *ptChar++ = I2C_ReadByte();
218     I2C_NoAck();
219     I2C_Stop();
220
221     return 1;
222 }
223
224
225
226
227                 
时间: 2024-10-22 13:54:16

I2C 总线协议分析的相关文章

[I2C]I2C总线协议图解

转自:http://blog.csdn.net/w89436838/article/details/38660631 1  I2C总线物理拓扑结构      I2C 总线在物理连接上非常简单,分别由SDA(串行数据线)和SCL(串行时钟线)及上拉电阻组成.通信原理是通过对SCL和SDA线高低电平时序的控制,来产生I2C总线协议所需要的信号进行数据的传递.在总线空闲状态时,这两根线一般被上面所接的上拉电阻拉高,保持着高电平. 2  I2C总线特征 I2C总线上的每一个设备都可以作为主设备或者从设备

I2C总线协议详解

1.1 I2C总线知识 1.1.1  I2C总线物理拓扑结构     I2C 总线在物理连接上非常简单,分别由SDA(串行数据线)和SCL(串行时钟线)及上拉电阻组成.通信原理是通过对SCL和SDA线高低电平时序的控制,来 产生I2C总线协议所需要的信号进行数据的传递.在总线空闲状态时,这两根线一般被上面所接的上拉电阻拉高,保持着高电平. 1.1.2  I2C总线特征    I2C总线上的每一个设备都可以作为主设备或者从设备,而且每一个设备都会对应一个唯一的地址(可以从I2C器件的数据手册得知)

I2C总线协议的软件模拟实现方法

I2C总线协议的软件模拟实现方法 在上一篇博客中已经讲过I2C总线通信协议,本文讲述I2C总线协议的软件模拟实现方法. 1. 简述 所谓的I2C总线协议的软件模拟实现方法,就是用软件控制GPIO的输入.输出和高低电平变化,来模拟I2C总线通讯过程中SCL.SDA的电平变化来实现的. 2. I2C总线的封装 每个处理器对应的GPIO操作都有差异,即使是同一款处理器,不同的人也会有不同的GPIO封装风格,就以我个人习惯用的GPIO方法为例来进行讲解.我习惯上将GPIO的组和位封装为一个结构体,这样使

【转】I2C总线协议

I2C总线(Inter Integrated-Circuit)是由PHILIPS公司在上世纪80年代发明的一种电路板级串行总线标准,通过两根信号线--时钟线SCL和数据线SDA--即可完成主从机的单工通信.总线硬件连接极其简单,不同I2C设备挂接在总线上,只需在信号线安装上拉电阻即可完成硬件线路的搭建.另外,I2C总线采用器件地址的硬件设置方法,通过软件寻址方式完全避免了片选寻址的弊端,从而使硬件系统扩展更为灵活. 由于简单有效,I2C在业界得到广泛应用.基于I2C衍生出来的标准有SMBus.P

I2C总线协议学习笔记 (转载)

1.I2C协议   2条双向串行线,一条数据线SDA,一条时钟线SCL.   SDA传输数据是大端传输,每次传输8bit,即一字节.   支持多主控(multimastering),任何时间点只能有一个主控.   总线上每个设备都有自己的一个addr,共7个bit,广播地址全0.   系统中可能有多个同种芯片,为此addr分为固定部分和可编程部份,细节视芯片而定,看datasheet. 1.1 I2C位传输   数据传输:SCL为高电平时,SDA线若保持稳定,那么SDA上是在传输数据bit: 

I2C总线协议

1.I2C协议   2条双向串行线,一条数据线SDA,一条时钟线SCL.   SDA传输数据是大端传输,每次传输8bit,即一字节.   支持多主控(multimastering),任何时间点只能有一个主控.   总线上每个设备都有自己的一个addr,共7个bit,广播地址全0.   系统中可能有多个同种芯片,为此addr分为固定部分和可编程部份,细节视芯片而定,看datasheet. 1.1 I2C位传输   数据传输:SCL为高电平时,SDA线若保持稳定,那么SDA上是在传输数据bit: 

SPI总线协议分析

一.概述 SPI, Serial Perripheral Interface, 串行外围设备接口, 是 Motorola 公司推出的一种同步串行接口技术. SPI 总线在物理上是通过接在外围设备微控制器(PICmicro) 上面的微处理控制单元 (MCU) 上叫作同步串行端口(Synchronous Serial Port) 的模块(Module)来实现的, 它允许 MCU 以全双工的同步串行方式, 与各种外围设备进行高速数据通信. SPI 主要应用在 EEPROM, Flash, 实时时钟(R

ADXL345经验总结,采用SPI和I2C总线操作

一. ADXL345简介 ADXL345是ADI公司推出的三轴(x,y,z)iMEMS数字加速度计(digital accelerometer),具有在16G下高分辨率(13Bit)测量能力,同时具备16Bit数字输出.ADXL345 适用于静态倾角测量以及动态加速度测量,高达4mg/LSB的灵敏度允许测量小于1度的倾角. 该传感器还具备单击 /双击探测,自由落体探测,并允许用户设置一个加速度阀值,当加速度值超过设定阀值后可以产生一个信号输出.所有这些功能都可以映射到2个中断上.内置的32级FI

17、I2C总线和CAT24WCxx存储器

1.I2C串行总线概述 I2C总线是PHLIPS公司推出的一种串行总线,是具备多主机系统所需的总线裁决和高低速器件同步功能的高性能串行总线.I2C总线只有两根双向信号线.一根是数据线SDA,另一根是时钟线SCL. 2.I2C总线通过上拉电阻接正电源.当总线空闲时,两根线均为高电平.连到总线上的任一器件输出的低电平,都将使总线的信号变低,即各器件的SDA及SCL都是线“与”关系. 3.每个接到I2C总线上的器件都有唯一的地址.主机与其它器件间的数据传送可以是由主机发送数据到其它器件,这时主机即为发