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

 引言

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

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

  I2C总线

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

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

 

A为低;非应答信号相反,是在SCL为高时SDA为高。

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

  LinuxI2C总线驱动结构

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

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等对用户层操作的接口。

  总体而言,LinuxI2C总线的驱动分为两个部分:总线驱动(BUS)和设备驱动(DEVICE)I2C coreI2C总线适配器驱动完成了硬件上的主机总线驱动(BUS),而I2C driver则实现了从机设备驱动。在设计中,I2C core提供的接口是统一的,不需要修改,我们只需要实现特定I2C总线适配器驱动和I2C设备驱动,这样大大提高了系统的可移植性。

  笔者在某个产品中曾用AT91RM9200X1227构成嵌入式系统的时钟模块。在该设计中,AT91RM9200作为I2C的主机部分,X1227作为从机。下面以此为例,具体介绍这两部分驱动的实现。

  AT91RM9200 I2C总线驱动实现

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

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

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

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

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

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

  其中openrelease用来打开和关闭X1227x1227_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的时钟功能,首先把AT91RM9200I2C总线驱动模块和X1227模块在系统启动时先后加载。需要指出的是,Linux将时钟分为系统时钟和硬件时钟两种。系统时钟是指当前Linux Kernel中的时钟,而硬件时钟则是主板上由电池供电的那个主板硬件时钟,也就是本文中的X1227

  在Linux中,用于时钟查看和设置的命令主要有datehwclock。首先设置系统时钟,比如设置为20068171230分: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].Oeilly & Associates,2002.

  2. 郑旭阳、李兵兵、黄新平,模拟I2C总线多主通信研究与软件设计,单片机与嵌入式系统应用,20051229_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

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值