1、Linux 下 I2C 设备驱动开发和实现摘 要 I 2C 总线具有结构简单使用方便的特点。本文描述了 linux 下 I2C 驱动的结构,并在此基础上给出了 I2C 设备驱动和应用的实现。关键词 I 2C;驱动;应用1 引言I2C (InterIntegrated Circuit)总线是一种由 PHILIPS 公司开发的两线式串行总线,用于连接微控制器及其外围设备。I 2C 总线最主要的优点是其简单性和有效性。由于接口直接在组件之上,因此 I2C 总线占用的空间非常小,减少了电路板的空间和芯片管脚的数量,降低了互联成本。I 2C 总线最初为音频和视频设备开发,现已应用于各种服务与管理场合,来
2、实现配置或掌握组件的功能状态,如电源、系统风扇、系统温度等参数,增加了系统的安全性,方便了管理。2 I2C 总线概述I2C 总线是由数据线 SDA 和时钟 SCL 构成的串行总线,可发送和接收数据,每个器件都有一个惟一的地址识别。I 2C 规程运用主/从双向通讯。器件发送数据到总线上,则定义为发送器,器件接收数据则定义为接收器。主器件和从器件都可以工作于接收和发送状态。总线必须由主 器件(通常为微控制器)控制,主器件产生串行时钟(SCL)控制总线的传输方向,并产生起始和停止条件。SDA 线上的数据状态仅在 SCL 为低电平的期间才 能改变,SCL 为高电平的期间,SDA 状态的改变被用来表示起
3、始和停止条件。I2C 总线在传送数据过程中共有三种类型信号,它们分别是:开始信号、结束信号和应答信号。开始信号:SCL 为高电平时,SDA 由高电平向低电平跳变,开始传送数据。结束信号:SCL 为低电平时,SDA 由低电平向高电平跳变,结束传送数据。应答信号:接收数据的 IC 在接收到 8bit 数据后,向发送数据的 IC 发出特定的低电平脉冲,表示已收到数据。CPU 向受控单元发出一个信号后,等待受控单 元发出一个应答信号,CPU 接收到应答信号后,根据实际情况作出是否继续传递信号的判断。若未收到应答信号,由判断为受控单元出现故障。3 Linux 的 I2C 驱动架 Linux 中 I2C
4、总线的驱动分为两个部分,总线驱动(BUS)和设备驱动(DEVICE)。其中总线驱动的职责,是为系统中每个 I2C 总线增加相应的读写方法。但是总线驱动本身并不会进行任何的通讯,它只是存在那里,等待设备驱动调用其函数,参见图 1。设备驱动则是与挂在 I2C 总线上的具体的设备通讯的驱动。通过 I2C 总线驱动提供的函数,设备驱动可以忽略不同总线控制器的差异,不考虑其实现细节地与硬件设备通讯。图 1 Linux 内核 I2C 总线驱动程序构架在我们的 Linux 驱动的 i2c 文件夹下有 algos,busses,chips 三个文件夹,另外还有 i2c-core.c 和 i2c-dev.c 两
5、个文件。其中 i2c-core.c 文件实现了 I2C core 框架,是 Linux 内核用来维护和管理的 I2C 的核心部分,其中维护了两个静态的 List,分别记录系统中的 I2C driver 结构和 I2C adapter 结构。I 2C core 提供接口函数,允许一个 I2C adatper,I 2C driver 和 I2C client 初始化时在 I2C core 中进行注册,以及退出时进行注销。同时还提供了 I2C 总线读写访问的一般接口,主要应用在 I2C 设备驱动中。Busses 文件夹下的 i2c-mpc.c 文件实现了 PowerPC 下 I2C 总线适配器驱动,
6、定义描述了具体的 I2C 总线适配器的 i2c_adapter 数据结构,实现比较底层的对 I2C 总线访问的具体方法。I 2C adapter 构造一个对 I2C core 层接口的数据结构,并通过接口函数向 I2C core 注册一个控制器。I 2C adapter 主要实现对I2C 总线访问的算法,iic_xfer() 函数就是 I2C adapter 底层对 I2C 总线读写方法的实现。同时 I2C adpter 中还实现了对 I2C 控制器中断的处理函数。i2c-dev.c 文件中实现了 I2C driver,提供了一个通用的 I2C 设备的驱动程序,实现了字符类型设备的访问接口,实
7、现了对用户应用层的接口,提供用户程序访问 I2C 设备的接口,包括实现 open,release,read,write 以及最重要的 ioctl 等标准文件操作的接口函数。我们可以通过 open 函数打开 I 2C 的设备文件,通过 ioctl 函数设定要访问从设备的地址,然后就可以通过 read 和write 函数完成对 I2C 设备的读写操作。通过 I2C driver 提供的通用方法可以访问任何一个 I2C 的设备,但是其中实现的 read,write 及 ioctl 等功能完全是基于一般设备的实现,所有的操作数据都是基于字节流,没有明确的格式和意义。为了更方便和有效地使用 I2C设备,
8、我们可以为一个具体的 I2C 设备开发特定的 I2C 设备驱动程序,在驱动中完成对特定的数据格式的解释以及实现一些专用的功能。4 Linux 下 I2C 具体驱动开发TMP75 是 TI 公司推出的基于 I2C 总线的数字温度传感器,具有低的功耗,高数字分辨率,广泛应用于电源温度监控,计算机外设保护,笔记本和蜂窝电话中。针对该设备开发驱动程序,由于 linux 系统下已经实现了 I2C core 框架,I2C 总线适配器驱动,同时通过 i2c-dev.c 文件提供了一个通用的 I2C 设备的驱动程序,因此我们的驱动程序的开发主要集中在 TMP75 设备驱动程序这一层,用来实现针对 TMP75
9、设备的数据格式的解释以及实现一些专用的功能。根据 TMP75 的具体寄存器地址和功能定义:#define TMP75_REG_TEMP 0x00 /温度寄存器地址#define TMP75_REG_CONF 0x01 /配置寄存器地址#define TMP75_REG_TEMP_LOW 0x02 /低温阈值寄存器地址#define TMP75_REG_TEMP_HIGH 0x03 /高温阈值寄存器地址定义一个 TMP75_data 结构体和一系列函数实现总线初始化时的设备检测加载、设备删除时的数据操作。struct TMP75_data struct i2c_client client;str
10、uct semaphore update_lock;char valid; /* !=0 if following fields are valid */unsigned long last_updated; /* In jiffies */u16 temp_input; /* Register values */u16 temp_max;u16 temp_hyst;static int TMP 75_attach_adapter(struct i2c_adapter *adapter);static int TMP 75_detect(struct i2c_adapter *adapter,
11、int address,int kind);static void TMP 75_init_client(struct i2c_client *client);static int TMP 75_detach_client(struct i2c_client *client);static int TMP 75_read_value(struct i2c_client *client,u8 reg);static int TMP 75_write_value(struct i2c_client *client,u8 reg,u16 value);static struct TMP 75_dat
12、a *tmp75_update_device(struct device *dev);其中针对 TMP75 设备寄存器的特定格式定义 TMP75 寄存器读写的两个函数如下:static int TMP75_write_value(struct i2c_client *client,u8 reg,u16 value)if (reg = TMP75_REG_CONF)return i2c_smbus_write_byte_data(client,reg,value);elsereturn i2c_smbus_write_word_data(client,reg,swab16(value);stat
13、ic int TMP75_read_value(struct i2c_client *client,u8 reg)if (reg = TMP 75_REG_CONF)return i2c_smbus_read_byte_data(client,reg);elsereturn swab16(i2c_smbus_read_word_data(client,reg);具体的设备驱动程序完成之后将 TMP75 设备驱动的配置选项添加到 chips文件夹下的 kconfig 文件中,这样在配置内核选项时就可以把 TMP75 设备驱动添加到内核中。5 Linux 下 I2C 应用程序开发Linux 中应用
14、程序要使用本驱动来访问外部 I2C 器件,首先要通过 open()来打开其驱动,使用完毕后使用 close()将其关闭。int fd;fd = open(“/dev/i2c/0“,O_RDWR); close(fd);I2C 总线控制器驱动提供的 API 函数提供了 ioctl()函数用于设定 I2C 总线控制器的一些参数,本应用程序调用 ioctl 函数将 I2C 总线设置为 7 位地址模式,同时设置 I2C 从机地址。ioctl(fd,I2C_TENBIT,0)ioctl(fd,I2C_SLAVE,SLAVE_ADDR) 对 TMP75 的初始化工作通过调用 write()函数实现,通过调
15、用该函数实现对配置寄存器、高温阈值和低温阈值寄存器的初始化配置。/配置寄存器的初始化senbuf0=0x01;senbuf1=I2C_CONF_INITDATA;write(fd,sendbuf,2);对 TMP75 当前工作温度的读取通过调用 write()函数先写入温度寄存器的地址,然后调用 read()函数读取寄存器 2 字节的温度数据实现。write(fd,0x0,1);read(fd,recbuf,2);6 总结 I2C 总线结构简单使用方便。linux 系统下 I2C 的驱动程序具有清晰的层次结构,借助于成熟的驱动的例子用户很容易开发出针对自己产品的相应驱动。本文分析了 Linux 系统下 I2C 驱动结构,并在此基础上实现了一个具体的 I2C设备的驱动,并在此基础上给出了对 I2C 总线实现访问的用户应用实现。参考文献1Philips Corporation,I2C bus specification version 2.1,20002Aless and Robin 著,魏永明等译LINUX 设备驱动程序(第二版)北京:中国电力出版社,2004 年3Texas Instruments,inc . USA . TMP75 Datasheet,20044郑旭阳,李兵兵,黄新平模拟 I2C 总线多主通信研究与软件设计单片机与嵌入式系统应用,2005