嵌入式Linux内核I2C子系统详解

1.1 I2C总线知识

1.1.1  I2C总线物理拓扑结构
 
    I2C总线在物理连接上非常简单,分别由SDA(串行数据线)和SCL(串行时钟线)及上拉电阻组成。通信原理是通过对SCL和SDA线高低电平时序的控制,来产生I2C总线协议所需要的信号进行数据的传递。在总线空闲状态时,这两根线一般被上面所接的上拉电阻拉高,保持着高电平。

1.1.2  I2C总线特征
    I2C总线上的每一个设备都可以作为主设备或者从设备,而且每一个设备都会对应一个唯一的地址(可以从I2C器件的数据手册得知),主从设备之间就通过这个地址来确定与哪个器件进行通信,在通常的应用中,我们把CPU带I2C总线接口的模块作为主设备,把挂接在总线上的其他设备都作为从设备。
    I2C总线上可挂接的设备数量受总线的最大电容400pF 限制,如果所挂接的是相同型号的器件,则还受器件地址位的限制。
    I2C总线数据传输速率在标准模式下可达100kbit/s,快速模式下可达400kbit/s,高速模式下可达3.4Mbit/s。一般通过I2C总线接口可编程时钟来实现传输速率的调整,同时也跟所接的上拉电阻的阻值有关。
    I2C总线上的主设备与从设备之间以字节(8位)为单位进行双向的数据传输。

1.1.3  I2C总线协议
    I2C协议规定,总线上数据的传输必须以一个起始信号作为开始条件,以一个结束信号作为传输的停止条件。起始和结束信号总是由主设备产生。总线在空闲状态时,SCL和SDA都保持着高电平,当SCL为高电平而SDA由高到低的跳变,表示产生一个起始条件;当SCL为高而SDA由低到高的跳变,表示产生一个停止条件。在起始条件产生后,总线处于忙状态,由本次数据传输的主从设备独占,其他I2C器件无法访问总线;而在停止条件产生后,本次数据传输的主从设备将释放总线,总线再次处于空闲状态。如图所示:

在了解起始条件和停止条件后,我们再来看看在这个过程中数据的传输是如何进行的。前面我们已经提到过,数据传输以字节为单位。主设备在SCL线上产生每个时钟脉冲的过程中将在SDA线上传输一个数据位,当一个字节按数据位从高位到低位的顺序传输完后,紧接着从设备将拉低SDA线,回传给主设备一个应答位,此时才认为一个字节真正的被传输完成。当然,并不是所有的字节传输都必须有一个应答位,比如:当从设备不能再接收主设备发送的数据时,从设备将回传一个否定应答位。数据传输的过程如图所示:
 
    在前面我们还提到过,I2C总线上的每一个设备都对应一个唯一的地址,主从设备之间的数据传输是建立在地址的基础上,也就是说,主设备在传输有效数据之前要先指定从设备的地址,地址指定的过程和上面数据传输的过程一样,只不过大多数从设备的地址是7位的,然后协议规定再给地址添加一个最低位用来表示接下来数据传输的方向,0表示主设备向从设备写数据,1表示主设备向从设备读数据。如图所示:
 
1.1.4  I2C总线操作
    对I2C总线的操作实际就是主从设备之间的读写操作。大致可分为以下三种操作情况:
    第一,主设备往从设备中写数据。数据传输格式如下:

第二,主设备从从设备中读数据。数据传输格式如下:

第三,主设备往从设备中写数据,然后重启起始条件,紧接着从从设备中读取数据;或者是主设备从从设备中读数据,然后重启起始条件,紧接着主设备往从设备中写数据。数据传输格式如下:

第三种操作在单个主设备系统中,重复的开启起始条件机制要比用STOP终止传输后又再次开启总线更有效率。

1.2 I2C总线硬件接口电路示例

1.2.1 I2C总线硬件接口电路示例一
 
    这个电路是基于LPC2368 ARM7芯片进行设计的,使用其内部的I2C接口作为主设备,使用ADT75和SC16IS740作为两个从设备的I2C总线应用。

ADT75是一个带I2C接口的温度传感器器件,数据手册上对其地址的描述如下:
     
    由此,其地址跟A0、A1、A2引脚的接法有关,我们这里的实例是将A0、A1、A2全部接到高电平上,因此其地址是:1001111(即0x4F),又因根据协议再给地址添加一个最低位(方向位,默认给写方向),因此最后这个温度传感器作为从设备的地址是:10011110(即0x9E)。

SC16IS740是一个具有I2C或者SPI接口的扩展UART的器件(通过第8脚来决定使用I2C还是SPI接口,我们这里要求使用I2C接口,因此将第8脚接到高电平)。根据数据手册,我们同样的可以知道地址跟A0、A1的接法有关,我们这里的A0接高电平,A1接低电平。因此这个器件作为从设备的地址是:10010010(即0x92)。

1.2.2 I2C总线硬件接口电路示例二

这个电路是Mini2440开发板上I2C总线接口的应用。我们可以看到,SDA和SCL线上接了一个10K的上拉排阻。AT24C08是一个容量为8Kbit的EEPROM存储器件(注意是8Kbit,也就是1KB) ,根据数据手册中器件地址部分的描述,AT24C08的地址是:1010+A2A1A0+方向位,其中1010是EEPROM的类型识别符;仅仅使用A2来确定总线访问本器件的从设备地址,这里接的低电平,所以为0;A1和A0是器件内部页地址,在对器件擦除或者编程时使用,虽然这里也接的低电平,但器件内部并不使用引脚的输入值,也就是说A1和A0的值是由软件进行设定的。

1.3 脱离操作系统的I2C总线驱动示例(以电路示例一为例)

1.3.1 LPC2368中I2C接口寄存器描述
    LPC2368中有三个I2C总线接口,分别表示为I2C0、I2C1和I2C2,每个I2C接口都包含7个寄存器。它们分别是:
I2C控制置位寄存器(I2CONSET): 8位寄存器,各位不同的设置是对I2C总线不同的控制。

符号
描述
复位值
1:0
-
保留,用户软件不要向其写入1。从保留位读出的值未被定义
NA
2
AA
声明应答标志。为1时将为需要应答的情况产生一个应答
0
3
SI
I2C中断标志。当I2C状态改变时该位置位
0
4
STO
总线停止条件控制。1发出一个停止条件,当总线检测到停止条件时,STO自动清零
0
5
STA
总线起始条件控制。1进入主模式并发出一个起始条件
0
6
I2EN
总线使能控制。1为使能
0
7
-
保留,用户软件不要向其写入1。从保留位读出的值未被定义
NA

I2C控制清零寄存器(I2CONCLR): 8位寄存器,对I2CONSET寄存器中的相应为清零。

符号
描述
复位值
1:0
-
保留,用户软件不要向其写入1。从保留位读出的值未被定义
NA
2
AAC
声明应答标志清零位。向该位写入1清零I2CONSET寄存器中的AA位
0
3
SIC
中断标志清零位。向该位写入1清零I2CONSET寄存器中的SI位
0
4
-
保留,用户软件不要向其写入1。从保留位读出的值未被定义
NA
5
STAC
起始条件清零位。向该位写入1清零I2CONSET寄存器中的STA位
0
6
I2ENC
总线禁能控制。写入1清零I2CONSET寄存器中的I2EN位
0
7
-
保留,用户软件不要向其写入1。从保留位读出的值未被定义
NA
I2C状态寄存器(I2STAT): 8位只读寄存器,用于监控总线的实时状态(可能存在26种状态)。
符号
描述
复位值
2:0
-
这3个位不使用且总是为0 
0
7:3
Status
这些位给出I2C接口的实时状态,不同的值代表不同的状态,状态码请参考数据手册
0x1F
I2C数据寄存器(I2DAT): 8位寄存器,在SI置位期间,I2DAT中的数据保持稳定。
符号
描述
复位值
7:0
Data
该寄存器保留已经接收到或者准备要发送的数据值 
0
I2C从地址寄存器(I2ADR): 8位寄存器,I2C总线为从模式时才使用。主模式中该寄存器无效。
符号
描述
复位值
0
GC
通用调用使能位 
0
7:1
Address
从模式的I2C器件地址
0x00
SCH占空比寄存器(I2SCLH): 16位寄存器,用于定义SCL高电平所保持的PCLK周期数。
符号
描述
复位值
15:0
SCLH
SCL高电平周期选择计数
0x0004
SCL占空比寄存器(I2SCLL): 16位寄存器,用于定义SCL低电平所保持的PCLK周期数。
符号
描述
复位值
15:0
SCLL
SCL低电平周期选择计数
0x0004

在前面的I2C总线特征中我们提到过,I2C总线的速率通过可编程时钟来调整,即必须通过软件对I2SCLH和I2SCLL寄存器进行设置来选择合适的数据频率和占空比。 频率由下面的公式得出(fPCLK是PCLK的频率)。

1.3.2 LPC2368中I2C总线操作
    在1.1.4中我们已经讲过了对I2C总线的操作,但那只是从协议和时序上的描述,那我们如何从软件上去体现出来呢?接下来我们就讨论这个问题。
    对I2C总线上主从设备的读写可使用两种方法,一是使用轮询的方式,二是使用中断的方式。轮询方式即是在一个循环中判断I2C状态寄存器当前的状态值来确定总线当前所处的状态,然后根据这个状态来进行下一步的操作。中断方式即是使能I2C中断,注册I2C中断服务程序,在服务程序中读取I2C状态寄存器的当前状态值,再根据状态值来确定下一步的操作。
    不管使用哪种方法,看来I2C状态寄存器的值是至关重要的。这些状态值代表什么意思呢?下面我们描述一些常用的状态值(详细的状态值含义请参考数据手册)。

0x08: 表明主设备向总线已发出了一个起始条件;
0x10: 表明主设备向总线已发出了一个重复的起始条件;
0x18: 表明主设备向总线已发送了一个从设备地址(写方向)并且接收到从设备的应答;
0x20: 表明主设备向总线已发送了一个从设备地址(写方向)并且接收到从设备的非应答;
0x28: 表明主设备向总线已发送了一个数据字节并且接收到从设备的应答;
0x30: 表明主设备向总线已发送了一个数据字节并且接收到从设备的非应答;
0x40: 表明主设备向总线已发送了一个从设备地址(读方向)并且接收到从设备的应答;
0x48: 表明主设备向总线已发送了一个从设备地址(读方向)并且接收到从设备的非应答;
0x50: 表明主设备从总线上已接收一个数据字节并且返回了应答;
0x58: 表明主设备从总线上已接收一个数据字节并且返回了非应答;

1.3.3 示例代码
一、 轮询方式读写总线:

对于代码中从设备内部寄存器的操作请参考该设备的数据手册。例如,要读取温度传感器的温度值只需要调用:I2C0_ReadRegister(CHANNEL_TEMPERATURE, ADT75A_TEMP, &value),如果读取成功,则value中的数据就是通过I2C总线读取温度传感器中的温度数据。

二、 中断方式读写总线:
    这里的从设备地址定义、I2C控制寄存器宏定义和I2C初始化与上面轮询中的类似,只是要在初始化函数中加上中断申请的代码,中断服务程序名称为:I2C0_Exception。这里不再贴出以上代码了,这里只贴出关键性的代码。

时间: 2024-08-03 14:00:27

嵌入式Linux内核I2C子系统详解的相关文章

Linux的i2c驱动详解

目录(?)[-] 简介 架构 设备注册 I2C关键数据结构和详细注册流程 关键数据结构 详细注册流程 使用I2C子系统资源函数操作I2C设备 Gpio模拟i2c总线的通用传输算法 总结 理清i2c中的个结构体关系 i2c驱动的编写建议 1 简介 I2C 总线仅仅使用 SCL . SDA 两根信号线就实现了设备之间的数据交互,极大地简化对硬件资源和 PCB 板布线空间的占用.因此, I2C 总线被非常广泛地应用在 EEPROM .实时钟.小型 LCD 等设备与 CPU 的接口中. Linux I2

嵌入式Linux应用程序开发详解------(创建守护进程)

嵌入式Linux应用程序开发详解 华清远见 本文只是阅读文摘. 创建一个守护进程的步骤: 1.创建一个子进程,然后退出父进程: 2.在子进程中使用创建新会话---setsid(): 3.改变当前工作目录---chdir(): 4.重新设置文件权限掩码---umask(): 5.关闭所有的文件描述符---close(fdx): 6.设置daemon程序的任务---此例主要在while循环中体现. 下面是一个例子程序: /* daemon * how to create a daemon proce

Linux内核ROP姿势详解(二)

/* 很棒的文章,在freebuf上发现了这篇文章上部分的翻译,但作者貌似弃坑了,顺手把下半部分也翻译了,原文见文尾链接 --by JDchen */ 介绍 在文章第一部分,我们演示了如何找到有用的ROP gadget并为我们的系统(3.13.0-32 kernel –Ubuntu 12.04.5 LTS)建立了一个提权ROP链的模型.我们同时也开发了一个有漏洞的内核驱动来允许实现执行任意代码.在这一部分,我们将会使用这个内核模块来开发一个具有实践意义的ROP链:提权,修复系统,纯净退出到用户空

Yahoo Web 应用性能及linux内核优化黄金法则详解

Web 应用性能优化黄金法则:先优化前端程序(front-end) 的性能,因为这是80% 或以上的最终用户响应时间的花费所在. 法则1. 减少HTTP 请求次数80%的最终用户响应时间花在前端程序上,而其大部分时间则花在各种页面元素,如图像.样式表.脚本和Flash等,的下载上.减少页面元素将会减少HTTP请求次数.这是快速显示页面的关键所在.一种减少页面元素个数的方法是简化页面设计.但是否存在其他方式,能做到既有丰富内容,又能获得快速响应时间呢?以下是这样一些技术:Image maps 组合

linux内核I2C子系统学习(三)

写设备驱动: 四部曲: 构建i2c_driver 注册i2c_driver 构建i2c_client ( 第一种方法:注册字符设备驱动.第二种方法:通过板文件的i2c_board_info填充,然后注册) 注销i2c_driver 具体如下: ●    构建i2c_driver static struct i2c_driver pca953x_driver = {                 .driver = {                                     .n

Linux内核Section Mismatch详解

by falcon [email protected] of TinyLab.org 2014/01/22 Section Mismatch简介 Section Mismatch是非常严重的Bug,可能会导致无法预测的内存访问问题,建议谨慎对待,如果添加的驱动中有类似Warning,可能需要密切关注并解决掉. 下面就该问题的检测.原因.解决思路以及最新前沿进行分析. Section Mismatch的检测 CONFIG_DEBUG_SECTION_MISMATCH=y 打开上述选项,内核就会调用

linux 内核 RCU机制详解

RCU(Read-Copy Update)是数据同步的一种方式,在当前的Linux内核中发挥着重要的作用.RCU主要针对的数据对象是链表,目的是提高遍历读取数据的效率,为了达到目的使用RCU机制读取数据的时候不对链表进行耗时的加锁操作.这样在同一时间可以有多个线程同时读取该链表,并且允许一个线程对链表进行修改(修改的时候,需要加锁).RCU适用于需要频繁的读取数据,而相应修改数据并不多的情景,例如在文件系统中,经常需要查找定位目录,而对目录的修改相对来说并不多,这就是RCU发挥作用的最佳场景.

Linux基础学习--1:linux内核与发行详解

工欲善其事,必先利其器,linux从技术上面来说是一个内核,这个内核可以提供的功能有: 1:硬件抽象文件 2:磁盘及文件系统控制 3:多任务处理 内核是一个用来和硬件打交道并为用户程序提供一个有限服务集的低级支撑软件,我们完全可以把内核理解成与硬件进行交互的操作过程的封装,实际上一个内核不能算是一整套完整的操作系统,一套完整基于linux内核的操作系统才能叫做linux操作系统. linux内核的功能组成: 进程管理(process management).定时器(timer).中断管理(int

《Linux设备驱动开发详解:基于最新的Linux 4.0内核》china-pub预售

<Linux设备驱动开发详解:基于最新的Linux 4.0内核>china-pub今日上线进入预售阶段: http://product.china-pub.com/4733972 推荐序一 技术日新月异,产业斗转星移,滚滚红尘,消逝的事物太多,新事物的诞生也更迅猛.众多新生事物如灿烂烟花,转瞬即逝.当我们仰望星空时,在浩如烟海的专业名词中寻找,赫然发现,Linux的生命力之旺盛顽强,斗志之昂扬雄壮,令人称奇.它正以摧枯拉朽之势迅速占领包括服务器.云计算.消费电子.工业控制.仪器仪表.导航娱乐等