STM32学习:一文搞懂I2C总线

目录

I2C总线的概念

I2C最重要的功能包括:

I2C的物理层

I2C主要特点:

I2C的高阻态

I2C物理层总结:

I2C的协议层

初始(空闲)状态

开始信号:

 停止信号

数据有效性

 应答信号

 I2C数据传送

数据传送格式

I2C写数据

I2C发送数据

I2C读数据:

以EEPROM的AT24C02为例子

 芯片的寻址:

 操作时序:

从AT24C02中读取数据

1、读取当前地址的数据

 2、读取随即地址的数据

3、连续读数据

软件I2C和硬件I2C


I2C总线的概念

I²C(Inter-Integrated Circuit),中文应该叫集成电路总线,它是一种串行通信总线,使用多主从架构,是由飞利浦公司在1980年代初设计的,方便了主板、嵌入式系统或手机与周边设备组件之间的通讯。由于其简单性,它被广泛用于微控制器与传感器阵列,显示器,IoT设备,EEPROM等之间的通信。

I2C总线支持任何IC生产过程(NMOS、CMOS、双极性)。两线——串行数据(SDA)和串行时钟(SCL)线存在连接到总线的器件间传递信息。每个器件都有唯一的地址识别(无论是微控制器、LCD驱动器、存储器或者键盘接口),而且都可以作为一个发送器或接收器(由器件的共能决定)。很明显,LCD驱动器只是一个接收器,而存储器则既可以接收又可以发送数据。除了发送器和接收器外,器件在执行数据传输时也可以被看作是主机或从机(见下表)。主机是初始化总线的数据传输并产生允许传输的时钟信号的器件。此时,任何被寻址的器件都被认为是从机。

术语 描述
发送器 发送数据到总线的器件
接收器 从总线接收数据的器件
主机

初始化发送、产生时钟信号和终止发送的器件

从机

被主机寻址的器件

多主机 同时有多于一个主机尝试控制总线,但不破坏报文
仲裁 是一个在有多主机同时尝试控制总线,但只允许其中一个控制总线并使报文不被破坏的过程
同步 两个或者多个器件同步时钟信号的过程

å¨è¿éæå¥å¾çæè¿°

I2C最重要的功能包括:

  • 只需要两条总线
  • 没有严格的波特率要求,例如使用RS232,主设备生成总线时钟
  • 所有组件之间都存在简单的主/从关系,连接到总线的每个设备均可通过唯一的地址进行软件寻址
  • I2C是真正的多主设备总线,可提供仲裁和冲突检测
  • 传输速度
  • 标准模式:100kbit/s
  • 快速模式:400kbit/s
  • 高速模式:3.4Mbit/s
  • 最大主设备数:无限制
  • 最大从机数:理论上是127
  • 以上是I2C的一些重要特点,下面会对I2C作进一步介绍。

    任何一个通信协议,分析起来主要分为物理层(硬件层)和协议层(软件层)

    I2C协议仅需要一个SDA和SCL引脚。SDA是串行数据总线的缩写,而SCL是串行时钟线的缩写。这两条数据线需要接上拉电阻。设备间的连接如下所示:

    使用I2C,可以将多个从机(Slave)连接到单个主设备(Master),并且还可以有多个主设备(Master)控制一个或者多个从机(Slave).

    假如希望有多个微控制器(MCU)将数据记录到单个存储卡或将文本显示到单个LCD时,这个功能就非常有用。 

    I2C总线(SDA,SCL)内部都使用漏极开路驱动器(开漏驱动),因此SDA和SCL可以被拉低为低电平,但是不能被驱动为高电平(PS:不知道原因的建议回去看下数电),所以每天线上都要使用一个上拉电阻,默认情况下将其保持在高电平。

    上拉电阻的取值取决于很多因素,德州仪器TI建议使用以下公式来计算正确的上拉电阻值:经验值

                                    Rp​(min)=VDD​−VOL​(max)​/IOL

                          Rp​(min)=0.8473xCb​tr​​

    其中VOL是逻辑低电压,IOL是逻辑低电流,tr是信号的最大上升时间

    Cb是总线电容

    I2C的物理层

    I2C一共只有两根总线:一条是双向的串行数据线SDA,一条是串行时钟线SCL

  • SDA(Serial data)是数据线,D代表Data也就是数据,Send Data也就是用来传输数据的
  • SCL (Serial clock line)是时钟线,C代表Clock也就是时钟,也就是控制数据发送的时序的
  • 所有连接到I2C总线设备上的串行数据SDA都接到总线的SDA上,各设备的时钟线SCL接到总线的SCL上。I2C总线上的每个设备都自己一个唯一的地址,来确保不同设备之间访问的准确性。

    [å¤é¾å¾ç转å­å¤±è´¥,æºç«å¯è½æé²çé¾æºå¶,建议å°å¾çä¿å­ä¸æ¥ç´æ¥ä¸ä¼ (img-fxNfPPim-1586571859509)(C:\Users\48013\AppData\Roaming\Typora\typora-user-images\image-20200331212342296.png)]

    I2C主要特点:

    通常我们为了方便把I2C设备分为主设备和从设备,基本上谁控制时钟线(即控制SCL的电平高低变换)谁就是主设备。

  • I2C主设备功能:主要产生时钟,产生起始信号和停止信号
  • I2C从设备功能:可编程的I2C地址检测,停止位检测
  • I2C的一个优点是它支持多主控(multi mastering),其中任何一个能够进行发送和接受的设备都可以成为主总线。一个主控能够控制信号的传输和时钟频率。当然,在任何时间点上只能有一个主控。
  • 支持不同速率的通讯速度,标准速度为100kbit/s,快速为400kbit/s,最大为3.4Mbit/s
  • SCL和SDA都需要借上拉电阻(大小由速度和容性负载决定,一般在3.3K到10K之间)保证数据的稳定性,减少干扰。
  • I2C是半双工,而不是全双工,同一时间只可以单向通信
  • 为了避免总线信号的混乱,要求各设备连接到总线的输出端时必须是漏极开路(OD)输出或者集电极开路(OC)输出,这一点在下面会进行讲解。
  • I2C的高阻态

    漏极开路(Open Drain)即高阻状态,适用于输入/输出,其可以独立输入/输出低电平和高阻状态,若需要产生高电平,则需使用外部上拉电阻。

    高阻状态:高阻状态是三态门电路的一种状态。逻辑门的输出除有高、低电平两种状态外,还有第三种状态——高阻状态的门电路。电路分析时高阻态可以做开路理解。

    我们知道I2C的所有设备是连接在一根总线上的,那么我们进行通信的时候往往只是几个设备进行通信,那么这时候其余的空闲设备可能会受到总线干扰,或者干扰总线,怎么办呢?

    为例避免总线信号的混乱,I2C的空闲状态只能有外部上拉,而此时空闲设备被拉到高阻态,也就是相当于断路,整个I2C总线也只有开启了的设备才会正常进行通信,而不会干扰到其他设备。

    å¨è¿éæå¥å¾çæè¿° I2C器件地址:每一个I2C器件都有一个器件地址,有的器件地址在出厂的时候就设定好了,用户不可以更改,比如OV7670的地址为0x42。有的器件例如EEPROM,前四个地址已经确定好为1010,后三个地址是由硬件连接确定的,所以I2C总线最多能够连8个EEPROM芯片。

    I2C物理层总结:

    I2C总线在物理连接上非常简单,分别由SDA和SCL及上拉电阻组成。通信原理是通过对SCL和SDA线高低电平时序的控制,来产生I2C总线协议所需要的信号进行数据的传输。在总线空闲状态时,SCL和SDA被上拉电阻Rp拉高,使得SDA和SCL线都保持高电平。

    I2C的通信方式为半双工,只有一根SDA线,同一时间只可以单向通信,485也为半双工,SPI和UART通信为全双工。

    主机和从机的概念:

    主机就是负责整个系统的任务协调与分配,从机一般是通过主机的指令从而完成某些特定的任务,主机和从机之间可以通过总线连接,进行数据通信。

  • 发布主要命令的称为主机
  • 接受命令的称为从机
  • I2C的协议层

    I2C总线在传递数据的过程中共有三种类型的信号,它们分别是:开始信号,结束信号,应答信号。

  • 开始信号:SCL为高电平,SDA由高电平向低电平跳变,开始传送数据
  • 结束信号:SCL为高电平,SDA由低电平向高电平跳变,结束传送数据
  • 应答信号:接收数据的IC在接收到8bit数据之后,向发送数据的IC发出特定的低电平脉冲,表示已经收到数据。CPU向受控单元发出一个信号之后,等待受控单元发出一个应答信号,CPU接收到应答信号后,根据实际情况作出是否继续传递信号的判断。若未收到应答信号,由判断为受控单元出现故障。
  • 这些信号中,起始信号是必需的,结束信号和应答信号,都可以不要。

    I2C总线时序图img

     下面我们来讲I2C的通信协议流程:

    初始(空闲)状态

    因为I2C的SCL和SDA都需要接上拉电阻,保证空闲状态的稳定性,所以I2C总线在空闲状态下的SCL和SDA都保持高电平

    /*产生I2C起始信号*/
    /*1、先拉高SDA,再拉高SCL,空闲状态*/
    /*2、拉低SDA*/
    void I2C_Start()        //启动信号
    {
        SDA = 1;        //确保SDA线为高电平
        
        delay_us(5);
    
        SCL = 1;    //确保SCL高电平
        
        delay_us(5);
    
        SDA = 0;        //在SCL为高时拉低SDA线,即为起始信号
    
        dealy_us(5);
    
    
    }

    开始信号:

    SCL保持高电平,SDA由高电平变为低电平后,延时(>4.7us),SCL变为低电平。

    å¨è¿éæå¥å¾çæè¿°

     停止信号

    停止信号:SCL保持高电平,SDA由低电平变为高电平

     在起始条件产生之后,总线处于忙状态,由本次数据传输的主从设备独占,其他I2C器件无法访问总线;而在停止条件产生之后,本次数据传输的主从设备将释放总线,总线再次处于空闲状态。

    å¨è¿éæå¥å¾çæè¿°

    数据有效性

    I2C信号在数据传输过程中,当SCL=1高电平时,SDA必须保持稳定状态,不允许有电平跳变,只有在时钟线上的信号为低电平期间,数据线上的高电平或低电平状态才允许变化。

    SCL=1时,数据线SDA的任何电平变换会看做是总线的起始信号或者停止信号。

    也就是在I2C传输数据的过程中,SCL时钟线会频繁的转换电平,以保证数据的传输。

     å¨è¿éæå¥å¾çæè¿°

    img

     应答信号

    每当主机向从机发送完一个字节的数据,主机总是需要等待从机给出一个应答信号,以确认从机是否成功接收到了数据。

    应答信号:主机SCL拉高,读取从机SDA的电平,为低电平表示产生应答

  • 应答信号为低电平时,规定为有效应答位(ACK,简称应答位),表示接收器已经成功的接收了该字节
  • 应答信号为高电平时,规定为非应答位(NACK),一般表示接收器接收该字节没有成功。
  • å¨è¿éæå¥å¾çæè¿°

     每发送一个字节(8bit)在一个字节传输的8个时钟后的第九个时钟期间,接收器接收数据后必须回一个ACK应答信号给发送器,这样才能进行数据传输。

    应答出现在每一次主机完成8个数据位传输后紧跟着的时钟周期,低电平0表示应答,1表示非应答

    å¨è¿éæå¥å¾çæè¿°

     I2C数据传送

    数据传送格式

    SDA线上的数据在SCL时钟“高”期间必须是稳定的,只有当SCL线上的时钟信号为低时,数据线上的“高”或者“低”状态才可以改变。输出到SDA线上的每个字节必须是8位的,数据传送时,先传送最高位(MSB),每个被传送的字节后面都必须跟随一位应答位(即一帧共有9位)。

    当一个字节数据位从高位到低位的顺序传输完成之后,紧接着从设备将拉低SDA线,回传给主设备一个应答位(ACK),此时才认为一个字节真正的被传输完成,如果一段时间内没有收到从机的应答信号,则自动认为从机已正确接收到数据。

    img

    I2C写数据

     img

    多数从设备的地址为7位或者10位,一般都用7位。

    八位设备地址=7位从机地址+读/写地址

    再给地址添加一个方向位用来表示接下来数据的传输方向

  • 0表示主设备向从设备(write)写数据
  • 1表示主设备向从设备(read)读数据
  • I2C的每一帧数据由9bit组成

    如果是发送数据,则包含8bit数据+1bit数据ACK

    如果是设备地址数据,则8bit包含7bit设备地址1bit方向(PS:其实地址就是一个特殊的数据)

     å¨è¿éæå¥å¾çæè¿°

     在起始信号后必须传送一个从机地址(7位)1~7位为7位接收器地址,第8位为读写位,用0表示主机发送数据,1表示主机接收数据,第九位为ACK应答位,紧接着的为第一个数据字节,然后是一位应答位,后面继续第二个数据字节

    I2C发送数据

    å¨è¿éæå¥å¾çæè¿°

     å¨è¿éæå¥å¾çæè¿°

    Start:I2C起始信号,表示开始传输

    DEVICE_ADDRESS:从设备地址,就是7位从机地址

    R/W:W为写,R为读

    WORD_ADDRESS:从机中对应的寄存器地址,比方说访问OLED中的某个寄存器

    DATA:发送的数据

    STOP:停止信号,结束I2C

     主机要向从机写数据时:

    1. 主机首先产生START信号
    2. 然后紧接着发送一个从机地址,这个地址共有7位,紧接着的第8位是数据方向位(R/W),0表示主机发送数据,1表示主机接收数据(读)
    3. 主机发送地址时,总线上的每个从机都将这7位地址码与自己的地址进行比较,若相同,则认为自己正在被主机寻址,根据R/T位将自己确定为发送器和接收器
    4. 这时候主机等待从机的应答信号(A)
    5. 当主机收到应答信号时,发送要访问从机的哪个地址,继续等待从机的应答信号
    6. 当主机接收到应答信号时,发送N个字节的数据,继续等待从机的N次应答信号
    7. 主机产生停止信号,结束传送过程

    I2C读数据:

    å¨è¿éæå¥å¾çæè¿°

     主机要从从机读数据时:

    1. 主机首先产生START信号
    2. 然后紧接着发送一个从机地址,注意此时该地址的第8位为0,表明是向从机写命令
    3. 这时候主机等待从机的应答信号(ACK)
    4. 当主机收到应答信号时,发送要访问的地址,继续等待从机的应答信号
    5. 当主机收到应答信号后,主机要改变通信模式(主机将由发送变为接收,从机将由接受变为发送)所以主机重新发送一个START信号,然后紧接着发送一个从机地址,注意此时该地址的第8位为1,表明将主机设置成接收模式开始读取数据
    6. 这时候主机等待从机的应答信号,当主机收到应答信号时,就可以接收一个字节的数据,当接受完成后,主机发送非应答信号,表示不再接收数据
    7. 主机进而产生停止信号,结束传送过程

    以EEPROM的AT24C02为例子

    AT24C02是一个2K bit的串行EEPROM存储器(掉电不丢失),内部含有256个字节。在这里面有一个8字节的页写缓冲器

    å¨è¿éæå¥å¾çæè¿°

     A0,A1,A2:硬件地址引脚

    WP:写保护引脚,接高电平只读,接地允许读和写

    SCL和SDA:I2C总线

    以看出对于不同大小的24Cxx,具有不同的从器件地址。由于24C02为2K容量,也就是说只需要参考图中第一行的内容:

    å¨è¿éæå¥å¾çæè¿°

     芯片的寻址:

    AT24C设备地址如下,前四位固定为1010,A2~A0由管脚电平决定。AT24Cxx EEPROM Board模块中默认接地。A2~A0为000,最后一位表示读写操作。所以AT24CXX的读地址为0xA1,写地址为0xA0.

    也就是说

    写24C02时,从器件的地址为10100000(0xA0)

    读24C02时,从器件的地址为10100001(0xA1)

    芯片内寻址可对内部的256byte的任意一个进行读/写操作,其寻址范围为00~FF,共256个寻址单位。

    对应的修改A2A1A0三位数据即可。

    å¨è¿éæå¥å¾çæè¿° 

    向AT24C02中写数据

    å¨è¿éæå¥å¾çæè¿°

     操作时序:

    1. MCU先发送一个开始信号(START)启动总线
    2. 接着跟上首字节,发送器件写操作地址(DEVICE_ADDRESS)+写数据(0XA0)
    3. 等待应答信号
    4. 发送数据的存储地址。24C02一共有256个字节的存储空间,地址从0x00~0xFF,想把数据存储在哪个位置,此刻写的就是哪个地址。
    5. 发送要存储的数据第一字节,第二字节,注意在写数据的过程中,E2PROM每个字节都会回应一个应答位0,告诉我们写E2PROM数据成功,如果没有应答位,说明写入不成功
    6. 发送结束信号,停止总线

     注意:在写数据的过程中,每写入一个字节,E2PROM存储空间地址就会加1,当加到0xFF后,再写入一个字节,地址就会溢出又变成0x00

    写数据的时候需要注意,E2PROM是先写入到缓冲区,然后再搬运到掉电非易失区。这个过程需要一定的时间,AT24C02这个过程是不超过5ms

    所以,当我们在写多个字节时,写入一个字节之后,再写入一个字节之前,必须延时,这里我用的另一种方法,不用延时。

    //等待EEPROM内部时序完成
    void EEPROM_WaitForWriteEnd(void)
    {
    	do{
    	//I2C发送起始信号
    	I2C_GenerateSTART(EEPROM_I2C, ENABLE);
    	//检测EV5事件
    	while(I2C_GetFlagStatus(EEPROM_I2C, I2C_FLAG_SB) == RESET);
    	//EV5事件被检测到,I2C发送设备地址
    	I2C_Send7bitAddress(EEPROM_I2C, EEPROM_ADDR, I2C_Direction_Transmitter);
    	
    	}while(I2C_GetFlagStatus(EEPROM_I2C, I2C_FLAG_ADDR) == RESET);
    	//内部时序完成	
    	I2C_GenerateSTOP(EEPROM_I2C,ENABLE);
    }	
    

    从AT24C02中读取数据

    1、读取当前地址的数据

    å¨è¿éæå¥å¾çæè¿°

     2、读取随即地址的数据

    å¨è¿éæå¥å¾çæè¿°

    1.  MCU先发送一个开始信号(START)启动总线
    2. 接着跟上首字节,发送器件写操作地址(DEVICE ADDRESS)+写数据(0xA0)

    注意:这里写操作是为了要把所要读的数据的存储地址先写进去,告诉E2PROM要读取哪个地址的数据。

         3.发送要读取内存的地址(WORD ADDRESS),通知E2PROM读取要哪个地址的信息。

        4.重新发送开始信号(START)

       5.发送设备读操作地址(DEVICE ADDRESS)对E2PROM进行读操作 (0xA1)
       6. E2PROM会自动向主机发送数据,主机读取从器件发回的数据,在读一个字节后,MCU会回应一个应答信号(ACK)后,E2PROM会继续传输下一个地址的数据,MCU不断回应应答信号可以不断读取内存的数据
       7.如果不想读了,告诉E2PROM不想要数据了,就发送一个“非应答位NAK(1)”。发送结束信号(STOP)停止总线

    3、连续读数据

    å¨è¿éæå¥å¾çæè¿°

     E2PROM支持连续写操作,操作和单个字节类似,先发送设备写操作地址(DEVICE ADDRESS),然后发送内存起始地址(WORD ADDRESS),MCU会回应一个应答信号(ACK)后,E2PROM会继续传输下一个地址的数据,MCU不断回应应答信号可以不断读取内存的数据。E2PROM的地址指针会自动递增,数据会依次保存在内存中。不应答发送结束信号后终止传输。

    软件I2C和硬件I2C

    I2C分为软件I2C和硬件I2C

    软件I2C:软件I2C通信指的是用单片机的两个I/O端口模拟出来的I2C,用软件控制管脚状态以模拟I2C通信波形,软件模拟寄存器的工作方式

    硬件I2C:一块硬件电路,硬件I2C对应芯片上的外设,有相应的I2C驱动电路,其所使用的I2C管脚也是专用的,硬件I2C直接调用内部寄存器进行配置。

    硬件I2C的效率要远高于软件的,而软件I2C由于不受管脚限制,接口比较灵活。

    物联沃分享整理
    物联沃-IOTWORD物联网 » STM32学习:一文搞懂I2C总线

    发表评论