原创 嵌入式Linux系统中I2C总线设备的驱动设计[转]

2007-11-27 14:30 2763 7 7 分类: 汽车电子

嵌入式Linux系统中I2C总线设备的驱动设计


来源:电子产品世界  作者:新太科技股份有限公司(广州) 贾东耀 






摘要: 本文分析了Linux系统中I2C驱动程序的结构,并以AT91RM9200和X1227为例,介绍了如何在嵌入式Linux系统中实现I2C总线适配器及I2C设备驱动。
关键词: Linux;I2C总线;I2C设备;驱动


引言

    I2C总线是PHILIPS公司推出的两线式串行总线,用于连接微控制器及其外围设备,具有简单、高效等特点。由于其接口直接在组件之上,因此I2C总线占用的空间非常小,减少了电路板的空间和芯片引脚的数量,降低了互联成本,特别适用于嵌入式产品。

    而Linux系统具有开源、免费、网上资源丰富等优点,目前已成为嵌入式系统的主流选择。因此如何在嵌入式Linux系统中实现I2C功能成为实际开发中的问题。


I2C总线

    I2C 总线通过串行数据SDA 和串行时钟SCL线在连接到总线的器件间传递信息,每个器件都有一个唯一的地址识别。根据数据传输时的功能不同,把器件分为主机和从机。主机是初始化总线的数据传输并产生允许传输的时钟信号的器件,通常是微控制器。此时,任何被寻址的器件都被认为是从机,例如LCD驱动器、E2PROM等。

    I2C总线协议规定,各主机进行通信时都要有起始、结束、发送数据和应答信号。这些信号都是通信过程中的基本单元。起始信号就是在SCL线为高时SDA线从高变化到低;停止信号就是在SCL线为高时SDA线从低变化到高;应答信号是在SCL为高时SDA为低;非应答信号相反,是在SCL为高时SDA为高。

    总线传送的每1帧数据均是1个字节。协议规定,在启动总线后的第1个字节的高7位是对从机的寻址地址,第8位为方向位(“0”表示主机对从机的写操作;“1”表示主机对从机的读操作),其余的字节为操作数据。数据传送过程是:在I2C总线发送起始信号后,发送从机的7位寻址地址和1位表示这次操作性质的读写位,在有应答信号后开始传送数据,直到发送停止信号。主机每发送1个字节就要检测SDA线上有没有收到应答信号,有则继续发送,否则将停止发送数据。


Linux中I2C总线驱动结构

    Linux系统对I2C总线具有很好的支持。与硬件物理连接相对应的,Linux的I2C框架中各个部分的关系如图1所示。


070330103428681.jpg


图1 Linux内核I2C总线驱动程序构架

    内核中I2C相关代码可以分为三个层次:
    1. I2C core框架:提供了核心数据结构的定义和相关接口函数,用来实现I2C适配器驱动和设备驱动的注册、注销管理,以及I2C通信方法上层的、与具体适配器无关的代码,为系统中每个I2C总线增加相应的读写方法。

    2. I2C总线适配器驱动:定义描述具体I2C总线适配器的i2c_adapter数据结构、实现在具体I2C适配器上的I2C总线通信方法,并由i2c_algorithm数据结构进行描述。

    3. I2C 设备驱动:定义描述具体设备的i2c_client和可能的私有数据结构、借助I2C core提供的函数接口完成设备在内核的注册,并实现具体的功能,包括read, write以及ioctl等对用户层操作的接口。

    总体而言,Linux中I2C总线的驱动分为两个部分:总线驱动(BUS)和设备驱动(DEVICE)。I2C core与I2C总线适配器驱动完成了硬件上的主机总线驱动(BUS),而I2C driver则实现了从机设备驱动。在设计中,I2C core提供的接口是统一的,不需要修改,我们只需要实现特定I2C总线适配器驱动和I2C设备驱动,这样大大提高了系统的可移植性。
 
    笔者在某个产品中曾用AT91RM9200和X1227构成嵌入式系统的时钟模块。在该设计中,AT91RM9200作为I2C的主机部分,X1227作为从机。下面以此为例,具体介绍这两部分驱动的实现。


AT91RM9200 I2C总线驱动实现

    AT91RM9200是ARM920T处理器,它提供标准的两线接口TWI,即I2C接口,主机工作模式。通过TWI 控制寄存器TWI_CR设置I2C工作模式和状态。时钟由寄存器TWI_CWGR中编程值产生。该寄存器定义了TWCK 信号,使接口适应宽范围时钟。

    具体在linux中AT91RM9200 I2C总线适配器驱动的实现,首先初始化AT91RM9200 I2C的工作模式,然后装载I2C总线驱动,这需要两个结构模块来描述:struct i2c_adapter和struct i2c_algorithm。

    初始化i2c_adapter结构成员如下:
static struct i2c_adapter at91rm9200_adapter = {
name:           "AT91RM9200", 
id:             I2C_ALGO_SMBUS, 
algo:           &at91_algorithm, 
algo_data:      NULL, 
inc_use:        at91_inc, 
dec_use:        at91_dec, 
... ...
};

    这个模块并未提供读写函数,具体的读写方法由第二个模块struct i2c_algorithm提供。
static struct i2c_algorithm at91_algorithm = {
 name:  "at91 i2c",
 id:  I2C_ALGO_SMBUS,
 smbus_xfer: at91_smbus_xfer,
 master_xfer: at91_xfer,
 functionality: at91_func,
};

    通过调用I2C core中的接口函数i2c_add_adapter将这两个模块注册到操作系统里,总线驱动就算装上了。由此可见,i2c_algorithm实现了i2c通信具体方法。针对本文at91rm9200 I2C适配器, at91_xfer最为关键。分析内核可知,I2C core框架中提供给主机使用的数据传输接口:i2c_master_send,i2c_master_recv,i2c_transfer最终都是通过调用at91_xfer实现。

    数据传输处理如下:数据发送主机初始化Start状态后,向主机模式寄存器TWI_MMR中DADR发送一个7位从机地址,以通知从机器件。从机地址后的位表示传输方向(写或读)。该位为0,说明是写操作(发送操作);若该位为1,说明为数据读请求( 接收操作)。TWI 传输要求从机每收到一个字节后均要给出应答。在应答时钟脉冲中,主机释放数据线(HIGH),将从机拉低以产生应答。主机在该时钟脉冲中轮询数据线,可使用轮询或中断方式来检验状态位。若从机未应答该字节,将置位状态寄存器TWI_SR的NAK 位。

    写操作则发送数据至保持寄存器TWI_THR,设置TWI_CR的START 位以启动传输。数据在内部移位寄存器中移位,当检测到应答,TXRDY位置位,直到TWI_THR中有新数据写入,才清除该位。主机产生STOP 状态来结束传输。设置START 后开始读序列。当状态寄存器中RXRDY 位置位时,接收保持寄存器(TWI_RHR)以收到一个字符。当读TWI_RHR 时RXRDY 位复位。

    TWI接口可执行多种传输格式:7位从机地址和10位从机地址。通过主机模式寄存器TWI_MMR配置三个内部地址字节。若从机仅支持7 位地址,IADRSZ 必须置为0。若从机地址大于7 位,用户必须配置地址大小IADRSZ 并在内部地址寄存器TWI_IADR中设置其他从机地址位。


X1227的设备驱动实现

    X1227 是一个带有时钟、日历、CPU 监控电路和两路查询报警的实时时钟。时钟使用一个低成本的32.768kHz 的晶体作为输入,可精密地用秒、分钟、小时、日期、星期、月、年来显示时间,它可以自动调整闰年至2096年。同时X1227有一个看门狗定时器、3个超时时间可供选择。另外,X1227有一个4K位的EEPROM阵列,可用作某些用户配置数据的存储器。下面以X1227为例,说明一个具体的I2C设备驱动程序的设计要点。

    如前所述,I2C总线驱动只是提供了对一条总线的读写机制,本身并不会去做通信。通信是由I2C设备驱动来做的,设备驱动透过I2C总线同具体的设备进行通讯。一个设备驱动有两个模块来描述,struct i2c_client和struct i2c_driver。i2c_client用来描述一个具体的I2C设备,i2c_driver结构提供了i2c_adapter与i2c_client之间的通信方式。

struct i2c_driver x1227_driver = {
 name:  襒1227?
 id:  I2C_DRIVERID_X1227,
 flags:  I2C_DF_NOTIFY,
 attach_adapter: x1227_probe,
 detach_client: x1227_detach,
 command: x1227_command
};

    其中:attach_adapter利用适配器驱动提供的I2C总线访问方法,利用设备驱动程序模块中提供的地址线索信息,检测可能存在的设备及其地址。如果成功发现设备,则创建一个struct i2c_client来标识这个设备,并向该适配器的数据结构注册。detach_client用于从总线上注销设备、并释放i2c_client及相应的私有数据结构。command是用户接口中的ioctl功能的底层实现。

    I2C设备驱动需要实现两个方面的接口,一个是对I2C core框架的接口,设备初始化时通过函数i2c_add_driver调用,来实现驱动的注册。这个i2c_driver一旦装入完成,其中的attach_adapter函数就会被调用。

    另一个是对用户应用层的接口,提供用户程序访问I2C设备的接口,包括实现open,release,read,write以及最重要的ioctl等标准文件操作的接口函数。每个设备驱动程序都有一个称为file_operations的数据结构,用来实现接口函数。

static struct file_operations rtc_fops = {
 owner:  THIS_MODULE,
 ioctl:  x1227_rtc_ioctl,
 open:  x1227_rtc_open,
 release: x1227_rtc_release,
};

    其中open和release用来打开和关闭X1227,x1227_rtc_ioctl则向用户提供的一系列控制时钟芯片的具体命令:RTC_GET_TIME(以固定的数据格式读取实时时钟的时间)、RTC_SET_TIME(以固定的数据格式设定实时时钟的时间)以及E2PROM读写等。

    对于X1227,一般注册为一个miscdevice设备(所有miscdevice设备共同一个主设备号,不同的次设备号)。

static struct miscdevice x1227_rtc_miscdev = {
 RTC_MINOR,
 tc?
 &rtc_fops
};

    初始化时,通过misc_register (&x1227_rtc_miscdev)注册X1227,这样用户程序可以通过主设备号10 次设备号 135的设备节点/dev/rtc来访问X1227。

    要测试X1227的时钟功能,首先把AT91RM9200的I2C总线驱动模块和X1227模块在系统启动时先后加载。需要指出的是,Linux将时钟分为系统时钟和硬件时钟两种。系统时钟是指当前Linux Kernel中的时钟,而硬件时钟则是主板上由电池供电的那个主板硬件时钟,也就是本文中的X1227。

    在Linux中,用于时钟查看和设置的命令主要有date、hwclock。首先设置系统时钟,比如设置为2006年8月17日12点30分:date 081712302006,然后设置硬件时钟为当前系统时钟时间,使用命令/sbin/hwclock 衧ystohc,则X1227中的时间设置为当前系统时间。然后,通常在操作系统启动时设置启动脚本/sbin/hwclock 衕ctosys,利用X1227内的时间更新系统时钟,然后直到重启或关闭系统,由系统时钟来记录时间。


结语

    本文介绍了I2C总线适配器及I2C设备驱动的实现。该设计成功用于某网络测试设备的主控模块上,实现了设备的实时时钟功能,便于整个系统的监控。I2C总线在目前的嵌入式领域中应用非常广泛,如音/视频的控制,存储设备的通讯等,而Linux也已成为嵌入式系统的主流。从linux内核看,I2C的驱动程序具有清晰的层次结构,为编程者开发I2C相关驱动提供了规范的框架。


参考文献
1. lessandro Rubini,Jonathan Corbet. Linux Device Drivers,second edition[M].O誖eilly & Associates,2002.
2. 郑旭阳、李兵兵、黄新平,模拟I2C总线多主通信研究与软件设计,单片机与嵌入式系统应用,2005,12:29_32
3. Philips Corporation, I2C bus specification version 2.1, 2000
4. Atmel Corporation, AT91RM9200 Datasheet, version E, 2005
5.  Xicor Corporation, X1227 Datasheet, version1.3, 2004

PARTNER CONTENT

文章评论0条评论)

登录后参与讨论
EE直播间
更多
我要评论
0
7
关闭 站长推荐上一条 /3 下一条