I2C协议和驱动框架分析(一)

一、I2C协议及硬件原理
1. I2C总线协议
1.1 概念
        I2C(Inter-Integrated Circuit)总线是由 Philips 公司开发的两线式串行总线,这两根线分别为时钟线(SCL)和双向数据线 (SDA)。I2C 在标准模式下传输速率最高 100K bits/s,在快速模式下最高可达 400K bits/s,高速模式下位速率高达 3.4M bits/s。在嵌入式系统中,I2C应用非常广泛。大多数 SoC 中都集成了 I2C 总线(I2C控制器),这是硬件基础。我们熟知的各sensor,TP,PMU,camera,Audio codec等都是I2C接口(对于 camera 和 Audio codec 来说是控制接口)的设备,所以熟悉I2C的协议和驱动框架对于从事 Linux 底层驱动开发的人来说无疑是非常必要的。

1.2 传输时序
        先上图:

 

        3 种类型信号:

      (1)开始信号(S):SCL 为高电平时,SDA 由高电平向低电平跳变,开始数据传送;

      (2)结束信号(P):SCL 为高电平时,SDA 由低电平向高电平跳变,结束数据传送;

      (3)响应信号(ACK):接收器接受到8位数据以后,在第9个时钟周期,拉低 SDA 电平。

        此外,还有一点可以 get 到的是:SDA 上传输的数据必须在 SCL 为高电平期间保持稳定,SDA 上的数据只能在 SCL 为低电平期间变化

1.3 传输格式
        读写的格式如下:

7位地址模式下的写操作格式:
S    从机的7位地址    0    ACK    8位数据    ACK    P
7位地址模式下的读操作格式:
S    从机的7位地址    1    ACK    8位数据    ACK    P
        SDA 线上的数据传输单位是字节,即 8 位的数据。首先传输的是最高位。启动一个传输时,主机先发出 S 信号,然后发出 8 位数据,高 7 位是从设备地址,第 8 位是读写位(0-W,1-R)。一般来说,每次一个字节的数据传输完,主机或者从机必须发出 ACK 信号,才能进行下一个数据的传输。当然,也有例外:

       (1) 当从机不能响应从机地址时(比如从机正在忙于处理其他事情而无法响应主机操作,或者是总线上并没有挂这个地址的从设备),也就是在第 9 个 SCL 周期内 SDA 线并没有被拉低,即没有 ACK 信号。这时主机发送一个 P 信号终止传输或者重新发出一个 S 信号开始新的传输。 

       (2) 如果从机在传输过程中不能接收更多的数据,它也不会发出 ACK 信号。这时主机必须终止传输,发送一个 P 信号或者重新发出一个 S 信号开始新的传输。

        10 位地址模式格式:

10位地址模式下的写操作格式:


S    从机的第一个7位地址(11110xx)    0    ACK    从机的第二个7位地址    ACK    8位数据     ACK    P
10位地址模式下的读操作格式:
S    从机的第一个7位地址(11110xx)    0    ACK    从机的第二个7位地址    ACK    8位数据     ACK    P
     

  10 位寻址不会影响已有的 7 位寻址,7 位和 10 位地址的器件可以连接到相同的 I2C 总线 。10 位从机地址由 S 信号后的头两个字节组成,采用了保留的 “1111XXX” 作为 S 信号后第一个字节的头 7 位。保留地址位 1111XXX 有 8 个组合,但是只有 4 个组合 11110XX 用于 10 位寻址,剩下的 4 个组合 11111XX 保留给后续增强的 I2C 总线。第一个字节的头 7 位是 11110XX 的组合,其中最后两位(XX) 是 10 位地址的两个最高位。

 

2. 硬件原理
2.1 连接模型:
        主控和从设备之间的连接示意图一般如下:

 

2.2 I2C控制器介绍
        以三星的 S3C2440(ARM9)为例来介绍,其他 ARM 架构的 SoC 里的 I2C 控制器,原理大同小异。S3C2440 的 I2C 控制器结构框图如下:

 

        先介绍一下各个寄存器:

      (1)IICCON 寄存器

        从下面的截图(截自韦东山老师所著《嵌入式Linux应用开发完全手册》一书)可以知道,IICCON 寄存器用于控制是否发出ACK 信号、设置 IIC 时钟、使能中断以及标识中断是否发生。是否发出 ACK 信号要根据实际情况而定,比如当主机处于接收模式时,在收到最后一个字节的数据前,需要设置 bit[7] 为 0,即在收到最后一个字节的数据后不发出 ACK 信号,这样从机就能知道传输结束了。SCL 线上的时钟频率由 bit[6] 和 bit[3:0] 共同决定。I2C 中断在以下三种情况下发生:①当发出从机地址信息或自己作为从机接收到一个和自己相同的从机地址时;②当总线仲裁失败时;③当发送或接收完一个字节的数据时。第二种情况中,所谓总线仲裁,是指当多个主机试图去控制总线时,通过仲裁可以保证只有一个主机获取到总线控制权,并且它传输的信息不会被破坏。

 

      (2)IICSTAT 寄存器

        bit[7:6] 用于选择该控制器处于什么工作模式;

        bit[5] 控制发出 S/P 信号,什么时候发哪个信号,都是软件可控的;

        bit[4] 控制接收/发送功能;

        bit[0] 标识是否接收到 ACK 信号。

 

      (3)IICADD 寄存器

        用到 bit[7:1],保存从机地址。该寄存器在串行输出使能位 IISTAT[4]=0 时,才能写入,这么做的目的是为了在传输功能使能后避免因修改该寄存器值而导致地址混乱。

     (4)IICDS 寄存器

        保存要发送或已经接收的 8 位数据。串行输出使能位 IISTAT[4]=1 时,才能写入。

        下面以主机模式下,介绍启动或恢复 I2C 传输的方法:

      (1)如果中断标志位 IICCON[4]=0,那么让 IICSTAT[5:4]=0b11,主机就会发出 S 信号及 IICDS 里的数据;

      (2)如果中断标志位 IICCON[4]=1,表示之前有数据传输,现在 I2C 传输被暂停。此时只要清掉中断标志位,即令IICCON[4]=0,就可以恢复I2C操作。在中断里(恢复I2C操作之前),我们需要将下一个发送的数据写入 IICDS 寄存器(发送模式),或者从 IICDS 里读取出接收到的数据(接收模式)。

2.3 I2C读写操作流程
        下面通过简单的 C 代码来介绍主机模式的寄存器级别读写操作逻辑流程。需要强调的是,要想搞清楚下面的代码操作流程,那么上面介绍的几个寄存器每一个 bit 位是什么含义都需要清楚,以及他们之间的关系。首先是对 I2C 控制器进行初始化:

准备工作就绪,随时准备读或者写操作,读写的代码结合上面的寄存器说明来分析,整个 I2C 通信过程就会更清楚。

 

 

 

 

       说明:这里清中断清的是中断控制器那边的寄存器,并不影响 I2CCON 的 bit[4],此时该标记位仍然为1,所以I2C还是处于暂停传输状态。

        请思考一下,读/写从设备的一个寄存器的流程应该是什么样的?

        首先需要说明一下的是,上面的 i2c_read 和 i2c_write 并不知道发送的到底是地址还是数据,发送的字节是什么意思,只有我们知道。上面两个函数地址和数据的逻辑关系是我们定的,内核的 I2C 驱动亦是如此。

写从设备的一个寄存器:

      (1)、i2c_init

      (2)、i2c_write(slave_addr, buf[2], 2)

先发出从设备地址,待从设备响应发出 ACK 之后,触发中断,进入中断处理程序。首先把 buf[0] 写入 I2CDS(此时g_s3c2440_i2c.len = 1),然后恢复传输。从设备收到数据后,知道这是寄存器地址,于是发出 ACK 信号,这时又触发中断,再次进入中断处理程序。程序继续把 buf[1] 写入 I2CDS(此时g_s3c2440_i2c.len = 0),然后恢复传输。从设备又收到数据,知道这是要写入刚才发过来的寄存器地址里,于是再次发出 ACK 信号,再次触发中断进入中断处理程序。此时if ((g_s3c2440_i2c.len--) == 0) 条件成立,直接恢复传输,发出 P 信号,同时 i2c_write 的循环条件也成立,跳出,i2c_write 执行完毕。

读从设备的一个寄存器:

      (1)、i2c_init

      (2)、i2c_write(slave_addr, buf[1], 1)

先写操作,buf[0] 是要读取的寄存器地址,发给从设备后,从设备在下一次读操作返回该寄存器的值。

      (3)、i2c_read(slave_addr, buf[1], 1)

读操作开始,注意此时会重新发出 S 信号。i2c_read 在发出从设备地址后,从设备发出 ACK 信号,触发中断进入中断处理程序。此时由于是发出的设备地址(任何时候 S 信号随后的第一个数据一定是设备地址),并不会接收到数据。如果只接收一个(或接收到的是最后)一个字节的数据,那么还要设置 I2CCON 的 bit[7] 为 0,即接收到数据后主机不发出 ACK 信号,这样从设备就知道数据传输结束了。当从设备响应并发出上次写操作发出的寄存器地址里的值后,再次触发中断。在中断里取出 I2CDS里接收到的数据,保存到 buf[0],恢复 I2C 传输并发出 P 信号。i2c_read 执行完毕。

        请再思考一下,如果是一次读/写多个寄存器呢?写好说,读呢?是(2)(3)(2)(3)重复这样还是(2)(3)(一次读多个数据)这样呢?

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值