好贷网好贷款

I2C

发布时间:2016-12-5 20:33:13 编辑:www.fx114.net 分享查询网我要评论
本篇文章主要介绍了"I2C",主要涉及到I2C方面的内容,对于I2C感兴趣的同学可以参考一下。

一:I2C 概述 I2C是philips提出的外设总线.I2C只有两条线,一条串行数据线:SDA,一条是时钟线SCL ,使用SCL,SDA这两根信号线就实现了设备之间的数据交互,它方便了工程师的布线。因此,I2C总线被非常广泛地应用在EEPROM,实时钟,小型LCD等设备与CPU的接口中。 二:在linux下的驱动思路 谈到在linux系统下编写I2C驱动,目前主要有两种方法,一种是把I2C设备当作一个普通的字符设备来处理,另一种是利用linux下I2C驱动体系结构来完成。下面比较下这两种方法: 第一种方法: 优点:思路比较直接,不需要花很多时间去了解linux中复杂的I2C子系统的操作方法。 第一种方法的缺点: 要求工程师不仅要对I2C设备的操作熟悉,而且要熟悉I2C的适配器(I2C控制器)操作。 要求工程师对I2C的设备器及I2C的设备操作方法都比较熟悉,最重要的是写出的程序可以移植性差。 对内核的资源无法直接使用,因为内核提供的所有I2C设备器以及设备驱动都是基于I2C子系统的格式。 第一种方法的优点就是第二种方法的缺点, 第一种方法的缺点就是第二种方法的优点。 三.I2C架构概述 1.Linux的I2C体系结构分为3个组成部分: I2C核心 I2C核心提供了I2C总线驱动和设备驱动的注册,注销方法,I2C通信方法(”algorithm”)上层的,与具体适配器无关的代码以及探测设备,检测设备地址的上层代码等。 I2C总线驱动 I2C总线驱动是对I2C硬件体系结构中适配器端的实现,适配器可由CPU控制,甚至可以直接集成在CPU内部。 I2C设备驱动 I2C设备驱动(也称为客户驱动)是对I2C硬件体系结构中设备端的实现,设备一般挂接在受CPU控制的I2C适配器上,通过I2C适配器与CPU交换数据。 2.Linux I2C体系结构 Linux I2C体系结构如下图所示 四. I2C设备驱动程序编写 首先要明确适配器驱动的作用是让我们能够通过它发出符合I2C标准协议的时序。 在linux内核源代码中drivers/i2c/busses目录下包含着一些适配器的驱动。如S3C2440的驱动i2c-s3c2410.c。适配器加载到内核后,接下来的工作就是要针对具体的设备编写驱动了。 编写I2C设备驱动也有两种方法:一种是利用系统给我们提供的i2c-dev.c来实现一个i2c适配器的设备文件。然后通过在应用层操作I2C适配器来控制i2c设备。另一种是为i2c设备,独立编写一个设备驱动。注意:在后面一种情况下,是不需要使用i2c-dev.c的。 五:Linux下I2C体系文件构架 在Linux内核源代码中的driver目录下包含一个i2c目录,而在i2c目录下又包含如下文件和文件夹: i2c-core.c这个文件实现了I2C核心的功能以及/proc/bus/i2c*接口。 i2c-dev.c实现了I2C适配器设备文件的功能,每一个I2C适配器都被分配一个设备。通过适配器访设备时的主设备号都为89,次设备号为0-255。I2c-dev.c并没有针对特定的设备而设计,只是提供了通用的read(),write(),和ioctl()等接口,应用层可以借用这些接口访问挂接在适配器上的I2C设备的存储空间或寄存器,并控制I2C设备的工作方式。 Chips这个目录包含了一些特定的I2C设备驱动。在具体的I2C设备驱动中,调用的都是I2C核心提供的API,因此,这使得具体的I2C设备驱动不依赖于CPU的类型和I2C适配器的硬件特性。 busses文件夹这个文件中包含了一些I2C总线的驱动,如针对S3C2410,S3C2440,S3C6410等处理器的I2C控制器驱动为i2c-s3c2410.c. algos文件夹实现了一些I2C总线适配器的algorithm. 六:重要的结构体 1.各个结构体 在内核中的i2c.h这个头文件中对i2c_driver,i2c_client,i2c_adapter和i2c_algorithm这个四个结构体进行了定义。理解这4个结构体的作用十分关键。 i2c_adapter结构体 struct i2c_adapter { struct module *owner;//所属模块 unsigned int id;//algorithm的类型,定义于i2c-id.h, unsigned int class; const struct i2c_algorithm *algo; //总线通信方法结构体指针 void *algo_data;//algorithm数据 struct rt_mutex bus_lock;//控制并发访问的自旋锁 int timeout; int retries;//重试次数 struct device dev; //适配器设备 int nr; char name[48];//适配器名称 struct completion dev_released;//用于同步 struct list_head userspace_clients;//client链表头 }; I2c_algorithm结构体 struct i2c_algorithm { int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs, int num);//I2C传输函数指针 int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,unsigned short flags, char read_write,u8 command, int size, union i2c_smbus_data *data);//smbus传输函数指针 u32 (*functionality) (struct i2c_adapter *);//返回适配器支持的功能 }; SMbus大部分基于I2C总线规范,SMbus不需要增加额外引脚。与I2C总线相比,SMbus增加了一些新的功能特性,在访问时序也有 一定的差异。 i2c_driver结构体 struct i2c_driver { unsigned int class; int (*attach_adapter)(struct i2c_adapter *);//依附i2c_adapter函数指针 int (*detach_adapter)(struct i2c_adapter *);//脱离i2c_adapter函数指针 int (*probe)(struct i2c_client *, const struct i2c_device_id *); int (*remove)(struct i2c_client *); void (*shutdown)(struct i2c_client *); int (*suspend)(struct i2c_client *, pm_message_t mesg); int (*resume)(struct i2c_client *); void (*alert)(struct i2c_client *, unsigned int data); int (*command)(struct i2c_client *client, unsigned int cmd, void*arg);//命令列表 struct device_driver driver; const struct i2c_device_id *id_table;//该驱动所支持的设备ID表 int (*detect)(struct i2c_client *, struct i2c_board_info *); const unsigned short *address_list; struct list_head clients; }; i2c_client结构体 struct i2c_client { unsigned short flags;//标志 unsigned short addr; //低7位为芯片地址 char name[I2C_NAME_SIZE];//设备名称 struct i2c_adapter *adapter;//依附的i2c_adapter struct i2c_driver *driver;//依附的i2c_driver struct device dev;//设备结构体 int irq;//设备所使用的结构体 struct list_head detected;//链表头 }; 2:各结构体的作用与它们之间的关系 i2c_adapter对应于物理上的一个适配器,而i2c_algorithm对应一套通信方法。一个i2c适配器需要i2c_algorithm中提供的通信函数来控制适配器上产生特定的访问周期。缺少i2c_algorithm的i2c_adapter什么也做不了,因此i2c_adapter中包含其使用的i2c_algorithm的指针。i2c_algorithm中的关键函数master_xfer()用于产生I2C访问周期需要的信号,以i2c_msg(即I2C消息)为单位。i2c_msg也很重要,代码清单如下: struct i2c_msg { __u16 addr;//设备地址 __u16 flags;//标志 __u16 len;//消息长度 __u8 *buf;//消息数据 }; i2c_driver与i2c_clienti2c_driver对应一套驱动方法,其主要成员函数是probe(),remove(),suspend(),resume()等,另外id_table是该驱动所支持的I2C设备的ID表。i2c_client对应于真实的物理设备,每个I2C设备都需要一个i2c_client来描述。i2c_driver与i2c_client的关系是一对多,一个i2c_driver上可以支持多个同等类型的i2c_client。i2c_client信息通常在BSP的板文件中通过i2c_board_info填充。一般在arch/arm目录下的板文件中。在I2C总线驱动i2c_bus_type的match()函数i2c_device_match()中,会调用i2c_match_id()函数匹配板文件中的ID和i2c_driver所支持的ID表。 i2c_adpater与i2c_client i2c_adpater与i2c_client的关系与I2C硬件体系中适配器和设备的关系一致,即i2c_client依附与i2c_adpater.由于一个适配器上可以连接多个I2C设备,所以就一个i2c_adpter也可以被多个i2c_client依附,i2c_adpter中包括依附与它的i2c_client的链表。 3.编写驱动需要完成的工作 编写具体的I2C驱动时,工程师需要处理的主要工作如下: 1).提供I2C适配器的硬件驱动,探测,初始化I2C适配器(如申请I2C的I/O地址和中断号),驱动CPU控制的I2C适配器从硬件上产生。 2).提供I2C控制的algorithm, 用具体适配器的xxx_xfer()函数填充i2c_algorithm的master_xfer指针,并把i2c_algorithm指针赋给i2c_adapter的algo指针。 3).实现I2C设备驱动中的i2c_driver接口,用具体yyy的yyy_probe(), yyy_remove(),yyy_suspend(),yyy_resume()函数指针和i2c_device_id设备ID表赋给i2c_driver的probe,remove,suspend,resume和id_table指针。 4)实现I2C设备所对应类型的具体驱动,i2c_driver只是实现设备与总线的挂接。 上面的工作中前两个属于I2C总线驱动,后面两个属于I2C设备驱动。 七:I2C协议 数据传送:SCL线呈现高电平期间,SDA线上的电平必须保持稳定,低电平表示0(此时的线电压为地电压),高电平表示1(此时的电压由元器件的VDD决定)。只有在SCL线为低电平期间,SDA上的电平允许变化。 应答信号ACK:I2C总线的数据都是以字节(8位)的方式传送的,发送器件每发送一个字节之后,在时钟的第9个脉冲期间释放数据总线,由接收器发送一个ACK(把数据总线的电平拉低)来表示数据成功接收。 无应答信号NACK: 在时钟的第9个脉冲期间发送器释放数据总线,接收器不拉低数据总线表示一个NACK,NACK有两种用途: a. 一般表示接收器未成功接收数据字节; b. 当接收器是主控器时,它收到最后一个字节后,应发送一个NACK信号,以通知被控发送器结束数据发送,并释放总线,以便主控接收器发送一个停止信号STOP。 开始与停止信号的时序图 I2C的读写时序 读过程 写过程 整个数据发送与接收时序 ACK时序 八.适配器驱动程序分析 在linux系统中,适配器驱动位于linux目录下的\drivers\i2c\busses下,不同的处理器的适配器驱动程序设计有差异,但是总体思路不变,在适配器的驱动中,实现两个结构体非常关键,也是整个适配器驱动的灵魂。下面以某个适配器的驱动程序为例进行说明: static struct platform_driver tcc_i2c_driver = { .probe = tcc_i2c_probe, .remove = tcc_i2c_remove, .suspend = tcc_i2c_suspend_late, .resume = tcc_i2c_resume_early, .driver = { .owner = THIS_MODULE, .name = "tcc-i2c", }, }; 看见这个结构体应该不会陌生,说明这个驱动是基于平台总线的,这样实现的目的是与CPU紧紧联系起来。 static const struct i2c_algorithm tcc_i2c_algorithm = { .master_xfer = tcc_i2c_xfer, .functionality = tcc_i2c_func, }; 这个结构体也是非常的关键,这个结构体里面的函数tcc_i2c_xfer是适配器算法的实现,这个函数实现了适配器与I2C CORE的连接。 tcc_i2c_func是指该适配器所支持的功能。tcc_i2c_xfer这个函数实质是实现I2C数据的发送与接收的处理过程。不同的处理器实 现的方法不同,主要表现在寄存器的设置与中断的处理方法上。把握上面的两点去分析适配器程序就简单多了。更深一步的分析见代码注释。 九.I2C-core驱动程序分析 在I2C-core.c这个函数中,把握下面的几个关键函数就可以了。 增加/删除i2c_adapter int i2c_add_adapter(struct i2c_adapter *adapter) int i2c_del_adapter(struct i2c_adapter *adap) 增加/删除i2c_driver int i2c_register_driver(struct module *owner, struct i2c_driver *driver) void i2c_del_driver(struct i2c_driver *driver) i2c_client依附/脱离 int i2c_attach_client(struct i2c_client *client) 增加/删除i2c_driver int i2c_register_driver(struct module *owner, struct i2c_driver *driver) void i2c_del_driver(struct i2c_driver *driver) i2c_client依附/脱离 int i2c_attach_client(struct i2c_client *client) int i2c_detach_client(struct i2c_client *client) I2C传输,发送和接收 int i2c_master_send(struct i2c_client *client,const char *buf ,int count) int i2c_master_recv(struct i2c_client *client, char *buf ,int count) int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) I2c_transfer这个函数实现了core与adapter的联系。更深一步的分析见代码注释。 十:AT24XXEEPROM驱动 在linux目录下的\drivers\misc\eeprom中实现了大部分EEPROM的驱动。对于EEPROM而言,设备本身的驱动以bin_attribute二进制sysfs结点形式呈现。分析这个驱动首先看关键的结构体 static struct i2c_driver at24_driver = { .driver = { .name = "at24", .owner = THIS_MODULE, }, .probe = at24_probe, .remove = __devexit_p(at24_remove), .id_table = at24_ids, }; 从上面看说明这个驱动又是基于平台总线的。再进一步看at24_bin_read()与at24_bin_write()这两个函数,这两个函数 会调用I2C_core.c中的i2c_transfer()函数,从而实现了设备,core,适配器这三者的联系。 更深一步的分析见代码注释。

上一篇:摘录C语言的学习路线
下一篇:各种排序

相关文章

关键词: I2C

相关评论