嵌入式Linux系统中I2C总线设备的驱动设计
Linux下I2C驱动架构全面分析概要
Linux下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架构概述Linux的I2C体系结构分为3个组成部分:I2C核心:I2C核心提供了I2C总线驱动和设备驱动的注册,注销方法,I2C通信方法(”algorithm”)上层的,与具体适配器无关的代码以及探测设备,检测设备地址的上层代码等。
I2C总线驱动:I2C总线驱动是对I2C硬件体系结构中适配器端的实现,适配器可由CPU控制,甚至可以直接集成在CPU内部。
I2C设备驱动:I2C设备驱动(也称为客户驱动)是对I2C硬件体系结构中设备端的实现,设备一般挂接在受CPU控制的I2C适配器上,通过I2C适配器与CPU交换数据。
第二层:提供i2c adapter的algorithm,用具体适配器的xxx_xferf()函数来填充i2c_algorithm的master_xfer函数指针,并把赋值后的i2c_algorithm再赋值给i2c_adapter 的algo指针。
Linux下I2C驱动介绍
1、I2C概述I2C是philips公司提供的外设总线,I2C有两条数据线,一条是串行数据线SDA、一条是时钟线SCL,使用SDA和SCL实现了数据的交换,便于布线。
I2C总线方便用在EEPROM、实时钟、小型LCD等与CPU外部的接口上。
2、Linux下的驱动思路Linux系统下编写I2c驱动主要有两种方法:一种是把I2C当做普通字符设备来使用;另一种利用Linux下驱动的体系结构来实现。
第一种方法:优点:思路比较直接,不用花费大量时间去了解Linux系统下I2C体系结构缺点:不仅对I2C设备操作要了解,还有了解I2C的适配器操作不仅对I2C设备器和设备操作需要了解,编写的驱动移植性差,内核提供的I2C设备器都没有用上。
第二种方法:第一种的优点就是第二种的缺点,第一种的缺点就是第二种的优点。
3、I2C框架概述Linux的I2C体系结构分为3部分:1)I2C核心I2C核心提供了I2C总线驱动和设备驱动的注册和注销的方法,I2C 通信方法(algorithm)上层,与具体适配器无关的代码,检测设备上层的代码等。
2)I2C总线驱动I2C总线驱动是对I2C硬件体系结构中适配器端的实现,适配器可以直接受CPU来控制。
3)I2C设备驱动I2C设备驱动是对I2C硬件体系结构中设备端的实现,设备端挂在受CPU控制的适配器上,通过I2C适配器与CPU交换数据。
Linux下的I2C体系结构:1)Linux下的I2C体系结构4、I2C设备驱动编写方法首先让我们明白适配器驱动的作用是让我们能够通过它发出标准的I2C时序,在linux内核源代码中driver/I2C/buss包含一些适配器的驱动,例如s3c2410的驱动I2C-s3c2410.c,适配器被加载到内核中,接下的任务就是实现设备驱动的编写。
编写设备驱动的方法主要分为两种方法:第一种:利用设备提供的I2C-dev.c来实现I2C适配器设备文件,然后通过上层应用程序来操作I2C设备器来控制I2C设备。
Linux下I2C设备驱动的一种适配器层直接实现方法
适配器层驱动函数iic—LM75一read被映射为应用 层的接口函数read,以为应用层所调用。按上述方法
宅饥不应答,处 理接收到的第二 个温度字节 主机接收完毕,
写IICSTAT.
构建的12C驱动在Linux下以普通设备驱动的形式 加载,在加载的过程执行初始化操作,通过MKDEV ()函数创建主设备节点并分配设备号,通过12C—set— up—dev()函数实现驱动层到应用层函数的映射。在 应用层调用适配器层驱动时,须首先用open()函数打
sc《m一且A魅Juu~V崛
i{MsB
图1
基本12c总线时序
应答信号:由接收器在接到发送器发送的地址或有效 数据后发出,如果接收器配置为允许应答,则收到数据后 将自动应答,应答设置位在IICCON的最高位,写1为允 许应答,写0为禁止应答。 主机发送数据:待发送的数据需事先写入IICDS寄存 器中,当发送条件满足后(起始信号发出,或TX/RX中断 标志位清除后等),IICDS中的数据将自动移位发送。 主机接收数据:在收到总线上的一字节数据后,将存 储于IICDS中。主机可通过查询IICCON中的中断标志 位来确定是否接收成功一个字节。如果主机不发送应答 信号,则从机将一直处于等待状态。数据接收完毕后如果 不清除中断挂起位,12C通信将进入暂停状态。 停止信号:停止信号的发出由IICsTAT寄存器的第 5位清零来实现。 4
信号后都要接着发送从机地址字节,这个字节前7位代表 从设备物理地址,最低位R/W决定了数据流的方向,如 果是0,表示主机写数据到从机,1表示主机向从机读。发 送到SDA总线上的数据必须是8位,但是每次发送的字 节数不受限制。数据传输必须带应答(ACK),应答脉冲 由接收器产生,在应答的时钟脉冲期问,发送器释放SDA 线。通常被寻址的接收器在接收到每个字节后,必须产生 一个应答,才能正常通信。最后,需要由主机发出一个停 止信号,结束整个通信过程,使总线回到空闲状态,当 SCL是高电平时SDA线由低电平向高电平切换表示停 止条件。基本12C总线时序如图1所示。
I 2C总线驱动在嵌入式系统中的两种实现
摘 要 : C总 线 是 一种 用 于 I 器件 之 间 连接 的二 线制 总 线 , 嵌 入 式 系统 中有 广 泛 的 应 用 。 嵌 入 式 处理 器 本 身 携 带 I C 在 IC控 制 器 时 , 以 直接 通 过 配置 特 殊 寄 存 器 来 实 现 IC 的 功 能 ; 当缺 少 IC控 制 器 时 , 需要 通 过 软 件 模 拟 的 方 法 来 实 可 而 则
中图 分 类 号 : P 6 . T 38 1
文献标识码: B
文 章 编 号 :0 4 7X(0 7 O 0 6 3 10 —3 3 20 )8— 5 —0
Two Ap r a h s f r 1 Bu i e n Em b d e y t m p o c e o 2 C s Drv r : u sak n f2一wi u o n e —c n e tn h C s r me t , n d l s d i m b d e y t ms b t t I C b si i d o a r b sf rit r o n c ig t eI i t u n s a d wi ey u e n e e d d s s e . e n I h mb d e r c s o a h o t o lr t e I C ta s o t t n c n b c iv d b o f u ig t e s e i c r g se so ft ee e d d p o e s rh st eI C c n r l 。 h r n p ra i a ea h e e y c n i rn h p cf e it r f e o g i t e c n r l r Ot e wie t eI C S f n to s mu tb i lt d b o t r . n t i a e ,h b s p o o o n t i h o t o l . h r s 。h u c in s e smu a e y s f wa e I h s p p r t e IC u r t c l d ist e a me— o d ra e a a y e tf s . e t p r a h s f ri lme t g t eI C b s o e a i n i sr c i n n e h r e r n l z d a i t Th n,wo a p o c e o mpe n i h u p r t t u to su d r e ARM ¥ C4 1 r n o n t 3 5O p o e s ra e gv n An e a l a e n smu a i n i l s r t d a a t r c s o r i e . x mp e b s d o i lt Si u ta e tls . o l Ke wo d : C; mb d e y t m ; y rsI e e d d s s e ARM ; o t r i lt n s fwa e smu a i o
LinuxI2C驱动--用户态驱动简单示例
LinuxI2C驱动--⽤户态驱动简单⽰例1. Linux内核⽀持I2C通⽤设备驱动(⽤户态驱动:由应⽤层实现对硬件的控制可以称之为⽤户态驱动),实现⽂件位于drivers/i2c/i2c-dev.c,设备⽂件为/dev/i2c-02. I2C通⽤设备驱动以字符设备注册进内核的static const struct file_operations i2cdev_fops = {.owner = THIS_MODULE,.llseek = no_llseek,.read = i2cdev_read,.write = i2cdev_write,.unlocked_ioctl = i2cdev_ioctl,.open = i2cdev_open,.release = i2cdev_release,};res = register_chrdev(I2C_MAJOR, "i2c", &i2cdev_fops);3. 对设备⽂件进⾏读写时,可以调⽤read、write或者ioctl等⽅法,他们都是通过调⽤函数i2c_transfer来实现对I2C设备的操作的int i2c_transfer(struct i2c_adapter * adap, struct i2c_msg *msgs, int num){int ret;/* REVISIT the fault reporting model here is weak:** - When we get an error after receiving N bytes from a slave,* there is no way to report "N".** - When we get a NAK after transmitting N bytes to a slave,* there is no way to report "N" ... or to let the master* continue executing the rest of this combined message, if* that's the appropriate response.** - When for example "num" is two and we successfully complete* the first message but get an error part way through the* second, it's unclear whether that should be reported as* one (discarding status on the second message) or errno* (discarding status on the first one).*/if (adap->algo->master_xfer) {#ifdef DEBUGfor (ret = 0; ret < num; ret++) {dev_dbg(&adap->dev, "master_xfer[%d] %c, addr=0x%02x, ""len=%d%s\n", ret, (msgs[ret].flags & I2C_M_RD)'R' : 'W', msgs[ret].addr, msgs[ret].len,(msgs[ret].flags & I2C_M_RECV_LEN) ? "+" : "");}#endifif (in_atomic() || irqs_disabled()) {ret = mutex_trylock(&adap->bus_lock);if (!ret)/* I2C activity is ongoing. */return -EAGAIN;} else {mutex_lock_nested(&adap->bus_lock, adap->level);}ret = adap->algo->master_xfer(adap,msgs,num);mutex_unlock(&adap->bus_lock);return ret;} else {dev_dbg(&adap->dev, "I2C level transfers not supported\n");return -EOPNOTSUPP;}}4. i2c_transfer通过代码可以看出,i2c_transfer 通过调⽤相应的 adapter 的 master_xfer ⽅法实现的,⽽ master_xfer 主要是根据 struct i2c_msg 类型的msgs来进⾏处理的。
I2C总线驱动在嵌入式系统中的两种实现,I2C,嵌入式,AR.
I2C总线驱动在嵌入式系统中的两种实现,I2C,嵌入式,ARM,软件模拟1引言I2C总线(Inter-ICBus)是一种通用的串行总线,是用于IC器件之间连接的二线制总线。
他通过串行数据线(SerialDataLines,SDL)及串行时钟线(SerialClockLine,SCL)两线在连接到总线上的器件之间传送信息,并根据地址识别每个器件。
一个或多个微控制器以及外围器件可以通过I2C总线接口非常方便的连接在一起构成系统。
这种总线结构的连线和连接引脚少,器件间总线简单。
结构紧凑,因此其构成系统的成本较低;并且在总线上增加器件不1 引言I2C总线(Inter-IC Bus)是一种通用的串行总线,是用于IC器件之间连接的二线制总线。
他通过串行数据线(SerialData Lines,SDL)及串行时钟线(Serial ClockLine,SCL)两线在连接到总线上的器件之间传送信息,并根据地址识别每个器件。
一个或多个微控制器以及外围器件可以通过I2C总线接口非常方便的连接在一起构成系统。
这种总线结构的连线和连接引脚少,器件间总线简单。
结构紧凑,因此其构成系统的成本较低;并且在总线上增加器件不会影响系统的正常工作,所有的I。
C 器件共用一套总线,因此其系统修改和可扩展性好。
即使有不同时钟速度的器件连接到总线上,时间同步机制也能够很方便地确定总线时钟,因此在嵌入式系统中得到了广泛的应用。
2 I2C总线原理2.1 I2C工作原理I2C总线是由数据线SDA和时钟线SCL构成的串行总线,可发送和接收数据。
每个连接到总线的器件都可以通过惟一的地址与主机通讯,主机可以作为主机发送器或主机接收器。
他是一个真正的多主机总线,如果两个或更多主机同时初始化,数据传输可以通过冲突检测和仲裁防止数据被破坏。
串行的8位双向数据传输位速率在标准模式下可达100kb/s,快速模式下可达400 kb/s,高速模式下可达3.4 Mb/s。
linux 应用访问i2c设备原理
linux 应用访问i2c设备原理Linux是一个开放源代码的操作系统内核,支持许多硬件设备的驱动程序。
其中一种常用的硬件接口是I2C(Inter-Integrated Circuit)总线。
I2C总线允许多个设备通过共享同一组线路进行通信,这些设备可以是传感器、存储器、转换器等外围设备。
Linux提供了一种访问I2C设备的机制,通过此机制,应用程序可以与I2C设备进行通信。
在本文中,将介绍Linux应用程序访问I2C设备的原理及步骤。
1.硬件连接:首先,需要将I2C设备连接到主机上。
I2C总线由两根线路组成,即SDA(Serial Data Line)和SCL(Serial Clock Line)。
SDA用于传输数据,SCL用于提供时钟信号。
I2C设备通常具有一个I2C地址,用于在总线上识别不同的设备。
2. Linux内核驱动:Linux内核提供了I2C总线的驱动程序,用于与I2C设备进行通信。
通常,这些驱动程序已经包含在内核中,不需要单独安装。
但是,可能需要通过编译内核时打开相应的配置选项来启用I2C支持。
3. I2C设备驱动:每个I2C设备都需要一个设备驱动程序,用于与应用程序进行交互。
这些驱动程序通常由设备制造商提供,也有一些常见的驱动程序包含在Linux内核中。
设备驱动程序将设备上的读写操作映射到I2C总线上的读写操作。
4.用户空间库:为了更方便地编写应用程序,Linux提供了一些用户空间库,用于访问I2C设备。
其中最常用的库是libi2c-dev,它提供了一组API函数,可以通过文件描述符进行I2C通信。
此库通常已经安装在Linux系统中,可以通过编译时链接到应用程序中。
5.应用程序访问:应用程序需要通过打开I2C总线来访问I2C设备。
首先,需要获取I2C总线的文件描述符,可以通过调用open()函数并传递文件路径来实现。
常见的I2C总线路径为"/dev/i2c-x",其中"x"是总线号。
Linux设备驱动之I2C架构分析
Linux设备驱动之I2C架构分析一:前言I2c是philips提出的外设总线.I2C只有两条线,一条串行数据线:SDA,一条是时钟线SCL.正因为这样,它方便了工程人员的布线.另外,I2C是一种多主机控制总线.它和USB总线不同,USB是基于master-slave 机制,任何设备的通信必须由主机发起才可以.而 I2C 是基于multi master机制.一同总线上可允许多个master.关于I2C协议的知识,这里不再赘述.可自行下载spec阅读即可.二:I2C架构概述在linux中,I2C驱动架构如下所示:如上图所示,每一条I2C对应一个adapter.在kernel中,每一个adapter提供了一个描述的结构(struct i2c_adapter),也定义了adapter支持的操作(struct i2c_adapter).再通过i2c core层将i2c设备与i2c adapter关联起来.这个图只是提供了一个大概的框架.在下面的代码分析中,从下至上的来分析这个框架图.以下的代码分析是基于linux 2.6.26.分析的代码基本位于: linux-2.6.26.3/drivers/i2c/位置.三:adapter注册在kernel中提供了两个adapter注册接口,分别为i2c_add_adapter()和i2c_add_numbered_adapter().由于在系统中可能存在多个adapter,因为将每一条I2C总线对应一个编号,下文中称为 I2C总线号.这个总线号的PCI中的总线号不同.它和硬件无关,只是软件上便于区分而已.对于i2c_add_adapter()而言,它使用的是动态总线号,即由系统给其分析一个总线号,而i2c_add_numbered_adapter()则是自己指定总线号,如果这个总线号非法或者是被占用,就会注册失败.分别来看一下这两个函数的代码:int i2c_add_adapter(struct i2c_adapter *adapter){int id, res = 0;retry:if (idr_pre_get(&i2c_adapter_idr, GFP_KERNEL) == 0)return -ENOMEM;mutex_lock(&core_lock);/* "above" here means "above or equal to", sigh */res = idr_get_new_above(&i2c_adapter_idr, adapter,__i2c_first_dynamic_bus_num, &id);mutex_unlock(&core_lock);if (res < 0) {if (res == -EAGAIN)goto retry;return res;}adapter->nr = id;return i2c_register_adapter(adapter);}在这里涉及到一个idr结构.idr结构本来是为了配合page cache中的radix tree而设计的.在这里我们只需要知道,它是一种高效的搜索树,且这个树预先存放了一些内存.避免在内存不够的时候出现问题.所在,在往idr中插入结构的时候,首先要调用idr_pre_get()为它预留足够的空闲内存,然后再调用idr_get_new_above()将结构插入idr中,该函数以参数的形式返回一个id.以后凭这个id就可以在idr中找到相对应的结构了.对这个数据结构操作不太理解的可以查阅本站<< linux文件系统之文件的读写>>中有关radix tree的分析.注意一下 idr_get_new_above(&i2c_adapter_idr, adapter,__i2c_first_dynamic_bus_num, &id)的参数的含义,它是将adapter结构插入到i2c_adapter_idr中,存放位置的id必须要大于或者等于__i2c_first_dynamic_bus_num,然后将对应的id号存放在adapter->nr中.调用i2c_register_adapter(adapter)对这个adapter进行进一步注册.看一下另外一人注册函数: i2c_add_numbered_adapter( ),如下所示:int i2c_add_numbered_adapter(struct i2c_adapter *adap){int id;int status;if (adap->nr & ~MAX_ID_MASK)return -EINVAL;retry:if (idr_pre_get(&i2c_adapter_idr, GFP_KERNEL) == 0)return -ENOMEM;mutex_lock(&core_lock);/* "above" here means "above or equal to", sigh;* we need the "equal to" result to force the result*/status = idr_get_new_above(&i2c_adapter_idr, adap, adap->nr, &id);if (status == 0 && id != adap->nr) {status = -EBUSY;idr_remove(&i2c_adapter_idr, id);}mutex_unlock(&core_lock);if (status == -EAGAIN)goto retry;if (status == 0)status = i2c_register_adapter(adap);return status;}对比一下就知道差别了,在这里它已经指定好了adapter->nr了.如果分配的id不和指定的相等,便返回错误.过一步跟踪i2c_register_adapter().代码如下:static int i2c_register_adapter(struct i2c_adapter *adap){int res = 0, dummy;mutex_init(&adap->bus_lock);mutex_init(&adap->clist_lock);INIT_LIST_HEAD(&adap->clients);mutex_lock(&core_lock);/* Add the adapter to the driver core.* If the parent pointer is not set up,* we add this adapter to the host bus.*/if (adap->dev.parent == NULL) {adap->dev.parent = &platform_bus;pr_debug("I2C adapter driver [%s] forgot to specify ""physical device\n", adap->name);}sprintf(adap->dev.bus_id, "i2c-%d", adap->nr);adap->dev.release = &i2c_adapter_dev_release;adap->dev.class = &i2c_adapter_class;res = device_register(&adap->dev);if (res)goto out_list;dev_dbg(&adap->dev, "adapter [%s] registered\n", adap->name);/* create pre-declared device nodes for new-style drivers */if (adap->nr < __i2c_first_dynamic_bus_num)i2c_scan_static_board_info(adap);/* let legacy drivers scan this bus for matching devices */dummy = bus_for_each_drv(&i2c_bus_type, NULL, adap,i2c_do_add_adapter);out_unlock:mutex_unlock(&core_lock);return res;out_list:idr_remove(&i2c_adapter_idr, adap->nr);goto out_unlock;}首先对adapter和adapter中内嵌的struct device结构进行必须的初始化.之后将adapter内嵌的struct device注册.在这里注意一下adapter->dev的初始化.它的类别为i2c_adapter_class,如果没有父结点,则将其父结点设为platform_bus.adapter->dev的名字为i2c + 总线号.测试一下:[eric@mochow i2c]$ cd /sys/class/i2c-adapter/[eric@mochow i2c-adapter]$ lsi2c-0可以看到,在我的PC上,有一个I2C adapter,看下详细信息:[eric@mochow i2c-adapter]$ tree.`-- i2c-0|-- device -> ../../../devices/pci0000:00/0000:00:1f.3/i2c-0|-- name|-- subsystem -> ../../../class/i2c-adapter`-- uevent3 directories, 2 files可以看到,该adapter是一个PCI设备.继续往下看:之后,在注释中看到,有两种类型的driver,一种是new-style drivers,另外一种是legacy drivers New-style drivers是在2.6近版的kernel加入的.它们最主要的区别是在adapter和i2c driver的匹配上.3.1: new-style 形式的adapter注册对于第一种,也就是new-style drivers,将相关代码再次列出如下:if (adap->nr < __i2c_first_dynamic_bus_num)i2c_scan_static_board_info(adap);如果adap->nr 小于__i2c_first_dynamic_bus_num的话,就会进入到i2c_scan_static_board_info().结合我们之前分析的adapter的两种注册分式: i2c_add_adapter()所分得的总线号肯会不会小于__i2c_first_dynamic_bus_num.只有i2c_add_numbered_adapter()才有可能满足:(adap->nr < __i2c_first_dynamic_bus_num)而且必须要调用i2c_register_board_info()将板子上的I2C设备信息预先注册时才会更改__i2c_first_dynamic_bus_num的值.在x86上只没有使用i2c_register_board_info()的.因此,x86平台上的分析可以忽略掉new-style driver的方式.不过,还是详细分析这种情况下.首先看一下i2c_register_board_info(),如下:int __initi2c_register_board_info(int busnum,struct i2c_board_info const *info, unsigned len){int status;mutex_lock(&__i2c_board_lock);/* dynamic bus numbers will be assigned after the last static one */if (busnum >= __i2c_first_dynamic_bus_num)__i2c_first_dynamic_bus_num = busnum + 1;for (status = 0; len; len--, info++) {struct i2c_devinfo *devinfo;devinfo = kzalloc(sizeof(*devinfo), GFP_KERNEL);if (!devinfo) {pr_debug("i2c-core: can't register boardinfo!\n");status = -ENOMEM;break;}devinfo->busnum = busnum;devinfo->board_info = *info;list_add_tail(&devinfo->list, &__i2c_board_list);}mutex_unlock(&__i2c_board_lock);return status;}这个函数比较简单, struct i2c_board_info用来表示I2C设备的一些情况,比如所在的总线.名称,地址,中断号等.最后,这些信息会被存放到__i2c_board_list链表.跟踪i2c_scan_static_board_info():代码如下:static void i2c_scan_static_board_info(struct i2c_adapter *adapter){struct i2c_devinfo *devinfo;mutex_lock(&__i2c_board_lock);list_for_each_entry(devinfo, &__i2c_board_list, list) {if (devinfo->busnum == adapter->nr&& !i2c_new_device(adapter,&devinfo->board_info))printk(KERN_ERR "i2c-core: can't create i2c%d-%04x\n",i2c_adapter_id(adapter),devinfo->board_info.addr);}mutex_unlock(&__i2c_board_lock);}该函数遍历挂在__i2c_board_list链表上面的i2c设备的信息,也就是我们在启动的时候指出的i2c 设备的信息.如果指定设备是位于adapter所在的I2C总线上,那么,就调用i2c_new_device().代码如下:struct i2c_client *i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info){struct i2c_client *client;int status;client = kzalloc(sizeof *client, GFP_KERNEL);if (!client)return NULL;client->adapter = adap;client->dev.platform_data = info->platform_data;device_init_wakeup(&client->dev, info->flags & I2C_CLIENT_WAKE);client->flags = info->flags & ~I2C_CLIENT_WAKE;client->addr = info->addr;client->irq = info->irq;strlcpy(client->name, info->type, sizeof(client->name));/* a new style driver may be bound to this device when we* return from this function, or any later moment (e.g. maybe* hotplugging will load the driver module). and the device* refcount model is the standard driver model one.*/status = i2c_attach_client(client);if (status < 0) {kfree(client);client = NULL;}return client;}我们又遇到了一个新的结构:struct i2c_client,不要被这个结构吓倒了,其实它就是一个嵌入struct device的I2C设备的封装.它和我们之前遇到的struct usb_device结构的作用是一样的.首先,在clinet里保存该设备的相关消息.特别的, client->adapter指向了它所在的adapter.特别的,clinet->name为info->name.也是指定好了的.一切初始化完成之后,便会调用i2c_attach_client( ).看这个函数的字面意思,是将clinet关联起来.到底怎么样关联呢?继续往下看:int i2c_attach_client(struct i2c_client *client){struct i2c_adapter *adapter = client->adapter;int res = 0;//初始化client内嵌的dev结构//父结点为所在的adapter,所在bus为i2c_bus_typeclient->dev.parent = &client->adapter->dev;client->dev.bus = &i2c_bus_type;//如果client已经指定了driver,将driver和内嵌的dev关联起来if (client->driver)client->dev.driver = &client->driver->driver;//指定了driver, 但不是newstyle的if (client->driver && !is_newstyle_driver(client->driver)) {client->dev.release = i2c_client_release;client->dev.uevent_suppress = 1;} elseclient->dev.release = i2c_client_dev_release;//clinet->dev的名称snprintf(&client->dev.bus_id[0], sizeof(client->dev.bus_id),"%d-%04x", i2c_adapter_id(adapter), client->addr);//将内嵌的dev注册res = device_register(&client->dev);if (res)goto out_err;//将clinet链到adapter->clients中mutex_lock(&adapter->clist_lock);list_add_tail(&client->list, &adapter->clients);mutex_unlock(&adapter->clist_lock);dev_dbg(&adapter->dev, "client [%s] registered with bus id %s\n",client->name, client->dev.bus_id);//如果adapter->cleinet_reqister存在,就调用它if (adapter->client_register) {if (adapter->client_register(client)) {dev_dbg(&adapter->dev, "client_register ""failed for client [%s] at 0x%02x\n",client->name, client->addr);}}return 0;out_err:dev_err(&adapter->dev, "Failed to attach i2c client %s at 0x%02x ""(%d)\n", client->name, client->addr, res);return res;}参考上面添加的注释,应该很容易理解这段代码了,就不加详细分析了.这个函数的名字不是i2c_attach_client()么?怎么没看到它的关系过程呢?这是因为:在代码中设置了client->dev所在的bus为i2c_bus_type .以为只需要有bus为i2c_bus_type的driver注册,就会产生probe了.这个过程呆后面分析i2c driver的时候再来详细分析.3.2: legacy形式的adapter注册Legacy形式的adapter注册代码片段如下:dummy = bus_for_each_drv(&i2c_bus_type, NULL, adap,i2c_do_add_adapter);这段代码遍历挂在i2c_bus_type上的驱动,然后对每一个驱动和adapter调用i2c_do_add_adapter().代码如下:static int i2c_do_add_adapter(struct device_driver *d, void *data){struct i2c_driver *driver = to_i2c_driver(d);struct i2c_adapter *adap = data;if (driver->attach_adapter) {/* We ignore the return code; if it fails, too bad */driver->attach_adapter(adap);}return 0;}该函数很简单,就是调用driver的attach_adapter()接口.到此为止,adapter的注册已经分析完了.四:i2c driver注册在分析i2c driver的时候,有必要先分析一下i2c架构的初始化代码如下:static int __init i2c_init(void){int retval;retval = bus_register(&i2c_bus_type);if (retval)return retval;retval = class_register(&i2c_adapter_class);if (retval)goto bus_err;retval = i2c_add_driver(&dummy_driver);if (retval)goto class_err;return 0;class_err:class_unregister(&i2c_adapter_class);bus_err:bus_unregister(&i2c_bus_type);return retval;}subsys_initcall(i2c_init);很明显,i2c_init()会在系统初始化的时候被调用.在i2c_init中,先注册了i2c_bus_type的bus,i2c_adapter_class的class.然后再调用i2c_add_driver()注册了一个i2c driver.I2c_bus_type结构如下:static struct bus_type i2c_bus_type = {.name = "i2c",.dev_attrs = i2c_dev_attrs,.match = i2c_device_match,.uevent = i2c_device_uevent,.probe = i2c_device_probe,.remove = i2c_device_remove,.shutdown = i2c_device_shutdown,.suspend = i2c_device_suspend,.resume = i2c_device_resume,};这个结构先放在这里吧,以后还会用到里面的信息的.从上面的初始化函数里也看到了,注册i2c driver的接口为i2c_add_driver().代码如下: static inline int i2c_add_driver(struct i2c_driver *driver){return i2c_register_driver(THIS_MODULE, driver);}继续跟踪:int i2c_register_driver(struct module *owner, struct i2c_driver *driver){int res;/* new style driver methods can't mix with legacy ones *///如果是一个newstyle的driver.但又定义了attach_adapter/detach_adapter.非法 if (is_newstyle_driver(driver)) {if (driver->attach_adapter || driver->detach_adapter|| driver->detach_client) {printk(KERN_WARNING"i2c-core: driver [%s] is confused\n",driver->);return -EINVAL;}}/* add the driver to the list of i2c drivers in the driver core *///关联到i2c_bus_typesdriver->driver.owner = owner;driver->driver.bus = &i2c_bus_type;/* for new style drivers, when registration returns the driver core* will have called probe() for all matching-but-unbound devices.*///注册内嵌的driverres = driver_register(&driver->driver);if (res)return res;mutex_lock(&core_lock);pr_debug("i2c-core: driver [%s] registered\n", driver->);/* legacy drivers scan i2c busses directly *///遍历所有的adapter,对其都调用driver->attach_adapterif (driver->attach_adapter) {struct i2c_adapter *adapter;down(&i2c_adapter_class.sem);list_for_each_entry(adapter, &i2c_adapter_class.devices,dev.node) {driver->attach_adapter(adapter);}up(&i2c_adapter_class.sem);}mutex_unlock(&core_lock);return 0;}这里也有两种形式的区分,对于第一种,只需要将内嵌的driver注册就可以了,对于legacy的情况,对每一个adapter都调用driver->attach_adapter().现在,我们可以将adapter和i2c driver关联起来考虑一下了:1:如果是news style形式的,在注册adapter的时候,将它上面的i2c 设备转换成了struct client.struct client->dev->bus又指定了和i2c driver同一个bus.因为,它们可以发生probe.2:如果是legacy形式,就直接找到对应的对象,调用driver->attach_adapter().五: i2c_bus_type的相关操作I2c_bus_type的操作主要存在于new-style形式的驱动中.接下来分析一下对应的probe过程:5.1:match过程分析Match对应的操作函数为i2c_device_match().代码如下static int i2c_device_match(struct device *dev, struct device_driver *drv){struct i2c_client *client = to_i2c_client(dev);struct i2c_driver *driver = to_i2c_driver(drv);/* make legacy i2c drivers bypass driver model probing entirely;* such drivers scan each i2c adapter/bus themselves.*/if (!is_newstyle_driver(driver))return 0;/* match on an id table if there is one */if (driver->id_table)return i2c_match_id(driver->id_table, client) != NULL;return 0;}如果该驱动不是一个new-style形式的.或者driver没有定义匹配的id_table.都会匹配失败. 继续跟踪进i2c_match_id():static const struct i2c_device_id *i2c_match_id(const struct i2c_device_id *id,const struct i2c_client *client){while (id->name[0]) {if (strcmp(client->name, id->name) == 0)return id;id++;}return NULL;}由此可见.如果client的名字和driver->id_table[]中的名称匹配即为成功.5.2:probe过程分析Probe对应的函数为: i2c_device_probe()static int i2c_device_probe(struct device *dev){struct i2c_client *client = to_i2c_client(dev);struct i2c_driver *driver = to_i2c_driver(dev->driver);const struct i2c_device_id *id;int status;if (!driver->probe)return -ENODEV;client->driver = driver;dev_dbg(dev, "probe\n");if (driver->id_table)id = i2c_match_id(driver->id_table, client);elseid = NULL;status = driver->probe(client, id);if (status)client->driver = NULL;return status;}这个函数也很简单,就是将probe流程回溯到i2c driver的probe()六:其它的扩展分析完adapter和i2c driver的注册之后,好像整个架构也差不多了,其它,扩展的东西还有很多.我们举一个legacy形式的例子,这个例子是在kernel中随便搜索出来的:在linux-2.6.26.3/drivers/hwmon/ad7418.c中,初始化函数为:static int __init ad7418_init(void){return i2c_add_driver(&ad7418_driver);}i2c_driver ad7418_driver结构如下:static struct i2c_driver ad7418_driver = {.driver = {.name = "ad7418",},.attach_adapter = ad7418_attach_adapter,.detach_client = ad7418_detach_client,};该结构中没有probe()函数,可以断定是一个legacy形式的驱动.这类驱动注册的时候,会调用driver 的attach_adapter函数.在这里也就是ad7418_attach_adapter.这个函数代码如下:static int ad7418_attach_adapter(struct i2c_adapter *adapter){if (!(adapter->class & I2C_CLASS_HWMON))return 0;return i2c_probe(adapter, &addr_data, ad7418_detect);}在这里我们又遇到了一个i2c-core中的函数,i2c_probe().在分析这个函数之前,先来看下addr_data 是什么?#define I2C_CLIENT_MODULE_PARM(var,desc) \static unsigned short var[I2C_CLIENT_MAX_OPTS] = I2C_CLIENT_DEFAULTS; \static unsigned int var##_num; \module_param_array(var, short, &var##_num, 0); \MODULE_PARM_DESC(var,desc)#define I2C_CLIENT_MODULE_PARM_FORCE(name) \I2C_CLIENT_MODULE_PARM(force_##name, \"List of adapter,address pairs which are " \"unquestionably assumed to contain a `" \# name "' chip")#define I2C_CLIENT_INSMOD_COMMON \I2C_CLIENT_MODULE_PARM(probe, "List of adapter,address pairs to scan " \"additionally"); \I2C_CLIENT_MODULE_PARM(ignore, "List of adapter,address pairs not to " \"scan"); \static const struct i2c_client_address_data addr_data = { \.normal_i2c = normal_i2c, \.probe = probe, \.ignore = ignore, \.forces = forces, \}#define I2C_CLIENT_FORCE_TEXT \"List of adapter,address pairs to boldly assume to be present"由此可知道,addr_data中的三个成员都是模块参数.在加载模块的时候可以用参数的方式对其赋值.三个模块参数为别为probe,ignore,force.另外需要指出的是normal_i2c不能以模块参数的方式对其赋值,只能在驱动内部静态指定.从模块参数的模述看来, probe是指"List of adapter,address pairs to scan additionally"Ignore是指"List of adapter,address pairs not to scan "Force是指"List of adapter,address pairs to boldly assume to be present"事实上,它们里面的数据都是成对出现的.前面一部份表示所在的总线号,ANY_I2C_BUS表示任一总线.后一部份表示设备的地址.现在可以来跟踪i2c_probe()的代码了.如下:int i2c_probe(struct i2c_adapter *adapter,const struct i2c_client_address_data *address_data,int (*found_proc) (struct i2c_adapter *, int, int)){int i, err;int adap_id = i2c_adapter_id(adapter);/* Force entries are done first, and are not affected by ignoreentries *///先扫描force里面的信息,注意它是一个二级指针.ignore里的信息对它是无效的if (address_data->forces) {const unsigned short * const *forces = address_data->forces;int kind;for (kind = 0; forces[kind]; kind++) {for (i = 0; forces[kind] != I2C_CLIENT_END;i += 2) {if (forces[kind] == adap_id|| forces[kind] == ANY_I2C_BUS) {dev_dbg(&adapter->dev, "found force ""parameter for adapter %d, ""addr 0x%02x, kind %d\n",adap_id, forces[kind][i + 1],kind);err = i2c_probe_address(adapter,forces[kind][i + 1],kind, found_proc);if (err)return err;}}}}/* Stop here if we can't use SMBUS_QUICK *///如果adapter不支持quick.不能够遍历这个adapter上面的设备if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_QUICK)) {if (address_data->probe[0] == I2C_CLIENT_END&& address_data->normal_i2c[0] == I2C_CLIENT_END)return 0;dev_warn(&adapter->dev, "SMBus Quick command not supported, ""can't probe for chips\n");return -1;}/* Probe entries are done second, and are not affected by ignoreentries either *///遍历probe上面的信息.ignore上的信息也对它是没有影响的for (i = 0; address_data->probe != I2C_CLIENT_END; i += 2) {if (address_data->probe == adap_id|| address_data->probe == ANY_I2C_BUS) {dev_dbg(&adapter->dev, "found probe parameter for ""adapter %d, addr 0x%02x\n", adap_id,address_data->probe[i + 1]);err = i2c_probe_address(adapter,address_data->probe[i + 1],-1, found_proc);if (err)return err;}}/* Normal entries are done last, unless shadowed by an ignore entry */ //最后遍历normal_i2c上面的信息.它上面的信息不能在ignore中.for (i = 0; address_data->normal_i2c != I2C_CLIENT_END; i += 1) {int j, ignore;ignore = 0;for (j = 0; address_data->ignore[j] != I2C_CLIENT_END;j += 2) {if ((address_data->ignore[j] == adap_id ||address_data->ignore[j] == ANY_I2C_BUS)&& address_data->ignore[j + 1]== address_data->normal_i2c) {dev_dbg(&adapter->dev, "found ignore ""parameter for adapter %d, ""addr 0x%02x\n", adap_id,address_data->ignore[j + 1]);ignore = 1;break;}}if (ignore)continue;dev_dbg(&adapter->dev, "found normal entry for adapter %d, ""addr 0x%02x\n", adap_id,address_data->normal_i2c);err = i2c_probe_address(adapter, address_data->normal_i2c,-1, found_proc);if (err)return err;}return 0;}这段代码很简单,结合代码上面添加的注释应该很好理解.如果匹配成功,则会调用i2c_probe_address ().这个函数代码如下:static int i2c_probe_address(struct i2c_adapter *adapter, int addr, int kind,int (*found_proc) (struct i2c_adapter *, int, int)){int err;/* Make sure the address is valid *///地址小于0x03或者大于0x77都是不合法的if (addr < 0x03 || addr > 0x77) {dev_warn(&adapter->dev, "Invalid probe address 0x%02x\n",addr);return -EINVAL;}/* Skip if already in use *///adapter上已经有这个设备了if (i2c_check_addr(adapter, addr))return 0;/* Make sure there is something at this address, unless forced *///如果kind小于0.检查adapter上是否有这个设备if (kind < 0) {if (i2c_smbus_xfer(adapter, addr, 0, 0, 0,I2C_SMBUS_QUICK, NULL) < 0)return 0;/* prevent 24RF08 corruption */if ((addr & ~0x0f) == 0x50)i2c_smbus_xfer(adapter, addr, 0, 0, 0,I2C_SMBUS_QUICK, NULL);}/* Finally call the custom detection function *///调用回调函数err = found_proc(adapter, addr, kind);/* -ENODEV can be returned if there is a chip at the given addressbut it isn't supported by this chip driver. We catch it here asthis isn't an error. */if (err == -ENODEV)err = 0;if (err)dev_warn(&adapter->dev, "Client creation failed at 0x%x (%d)\n",addr, err);return err;}首先,对传入的参数进行一系列的合法性检查.另外,如果该adapter上已经有了这个地址的设备了.也会返回失败.所有adapter下面的设备都是以 adapter->dev为父结点的.因此只需要遍历adapter->dev下面的子设备就可以得到当前地址是不是被占用了.如果kind < 0.还得要adapter检查该总线是否有这个地址的设备.方法是向这个地址发送一个Read 的Quick请求.如果该地址有应答,则说明这个地址上有这个设备.另外还有一种情况是在24RF08设备的特例.如果adapter上确实有这个设备,就会调用驱动调用时的回调函数.在上面涉及到了IIC的传输方式,有疑问的可以参考intel ICH5手册的有关smbus部份.跟踪i2c_smbus_xfer().代码如下:s32 i2c_smbus_xfer(struct i2c_adapter * adapter, u16 addr, unsigned short flags,char read_write, u8 command, int size,union i2c_smbus_data * data){s32 res;flags &= I2C_M_TEN | I2C_CLIENT_PEC;if (adapter->algo->smbus_xfer) {mutex_lock(&adapter->bus_lock);res = adapter->algo->smbus_xfer(adapter,addr,flags,read_write,command,size,data);mutex_unlock(&adapter->bus_lock);} elseres = i2c_smbus_xfer_emulated(adapter,addr,flags,read_write,command,size,data);return res;}如果adapter有smbus_xfer()函数,则直接调用它发送,否则,也就是在adapter不支持smbus协议的情况下,调用i2c_smbus_xfer_emulated()继续处理.跟进i2c_smbus_xfer_emulated().代码如下:static s32 i2c_smbus_xfer_emulated(struct i2c_adapter * adapter, u16 addr,unsigned short flags,char read_write, u8 command, int size,union i2c_smbus_data * data){/* So we need to generate a series of msgs. In the case of writing, weneed to use only one message; when reading, we need two. We initializemost things with sane defaults, to keep the code below somewhatsimpler. *///写操作只会进行一次交互,而读操作,有时会有两次操作.//因为有时候读操作要先写command,再从总线上读数据//在这里为了代码的简洁.使用了两个缓存区,将两种情况统一起来.unsigned char msgbuf0[I2C_SMBUS_BLOCK_MAX+3];unsigned char msgbuf1[I2C_SMBUS_BLOCK_MAX+2];//一般来说,读操作要交互两次.例外的情况我们在下面会接着分析int num = read_write == I2C_SMBUS_READ?2:1;//与设备交互的数据,一般在msg[0]存放写入设备的信息,在msb[1]里存放接收到的//信息.不过也有例外的//msg[2]的初始化,默认发送缓存区占一个字节,无接收缓存struct i2c_msg msg[2] = { { addr, flags, 1, msgbuf0 },{ addr, flags | I2C_M_RD, 0, msgbuf1 }};int i;u8 partial_pec = 0;//将要发送的信息copy到发送缓存区的第一字节msgbuf0[0] = command;switch(size) {//quick类型的,其它并不传输有效数据,只是将地址写到总线上,等待应答即可//所以将发送缓存区长度置为0 .再根据读/写操作,调整msg[0]的标志位//这类传输只需要一次总线交互case I2C_SMBUS_QUICK:msg[0].len = 0;/* Special case: The read/write field is used as data */msg[0].flags = flags | (read_write==I2C_SMBUS_READ)?I2C_M_RD:0;num = 1;break;case I2C_SMBUS_BYTE://BYTE类型指一次写和读只有一个字节.这种情况下,读和写都只会交互一次//这种类型的读有例外,它读取出来的数据不是放在msg[1]中的,而是存放在msg[0]if (read_write == I2C_SMBUS_READ) {/* Special case: only a read! */msg[0].flags = I2C_M_RD | flags;num = 1;}break;case I2C_SMBUS_BYTE_DATA://Byte_Data是指命令+数据的传输形式.在这种情况下,写只需要一次交互,读却要两次//第一次将command写到总线上,第二次要转换方向.要将设备地址和read标志写入总线. //应回答之后再进行read操作//写操作占两字节,分别是command+data.读操作的有效数据只有一个字节//交互次数用初始化值就可以了if (read_write == I2C_SMBUS_READ)msg[1].len = 1;else {msg[0].len = 2;msgbuf0[1] = data->byte;}break;case I2C_SMBUS_WORD_DATA://Word_Data是指命令+双字节的形式.这种情况跟Byte_Data的情况类似//两者相比只是交互的数据大小不同if (read_write == I2C_SMBUS_READ)msg[1].len = 2;else {msg[0].len=3;msgbuf0[1] = data->word & 0xff;msgbuf0[2] = data->word >> 8;}break;case I2C_SMBUS_PROC_CALL://Proc_Call的方式与write 的Word_Data相似,只不过写完Word_Data之后,要等待它的应答//应该它需要交互两次,一次写一次读num = 2; /* Special case */read_write = I2C_SMBUS_READ;msg[0].len = 3;msg[1].len = 2;msgbuf0[1] = data->word & 0xff;msgbuf0[2] = data->word >> 8;break;case I2C_SMBUS_BLOCK_DATA://Block_Data:指command+N段数据的情况.//如果是读操作,它首先要写command到总线,然后再读N段数据.要写的command已经//放在msg[0]了.现在只需要将msg[1]的标志置I2C_M_RECV_LEN位,msg[1]有效长度为1字节.因为//adapter驱动会处理好的.现在现在还不知道要传多少段数据.//对于写的情况:msg[1]照例不需要.将要写的数据全部都放到msb[0]中.相应的也要更新 //msg[0]中的缓存区长度if (read_write == I2C_SMBUS_READ) {msg[1].flags |= I2C_M_RECV_LEN;msg[1].len = 1; /* block length will be added bythe underlying bus driver */} else {//data->block[0]表示后面有多少段数据.总长度要加2是因为command+count+N段数据 msg[0].len = data->block[0] + 2;if (msg[0].len > I2C_SMBUS_BLOCK_MAX + 2) {dev_err(&adapter->dev, "smbus_access called with ""invalid block write size (%d)\n",data->block[0]);return -1;}for (i = 1; i < msg[0].len; i++)msgbuf0 = data->block[i-1];}break;case I2C_SMBUS_BLOCK_PROC_CALL://Proc_Call:表示写完Block_Data之后,要等它的应答消息它和Block_Data相比,只是多了一部份应答而已num = 2; /* Another special case */read_write = I2C_SMBUS_READ;if (data->block[0] > I2C_SMBUS_BLOCK_MAX) {dev_err(&adapter->dev, "%s called with invalid ""block proc call size (%d)\n", __func__,data->block[0]);return -1;}msg[0].len = data->block[0] + 2;for (i = 1; i < msg[0].len; i++)msgbuf0 = data->block[i-1];msg[1].flags |= I2C_M_RECV_LEN;msg[1].len = 1; /* block length will be added bythe underlying bus driver */break;case I2C_SMBUS_I2C_BLOCK_DATA://I2c Block_Data与Block_Data相似,只不过read的时候,数据长度是预先定义好了的.另外//与Block_Data相比,中间不需要传输Count字段.(Count表示数据段数目)if (read_write == I2C_SMBUS_READ) {msg[1].len = data->block[0];} else {msg[0].len = data->block[0] + 1;if (msg[0].len > I2C_SMBUS_BLOCK_MAX + 1) {dev_err(&adapter->dev, "i2c_smbus_xfer_emulated called with ""invalid block write size (%d)\n",data->block[0]);return -1;}for (i = 1; i <= data->block[0]; i++)msgbuf0 = data->block;}break;default:dev_err(&adapter->dev, "smbus_access called with invalid size (%d)\n",size);return -1;}//如果启用了PEC.Quick和I2c Block_Data是不支持PEC的i = ((flags & I2C_CLIENT_PEC) && size != I2C_SMBUS_QUICK&& size != I2C_SMBUS_I2C_BLOCK_DATA);if (i) {/* Compute PEC if first message is a write */。
Linux IIC驱动
Linux IIC驱动在嵌入式系统中,IIC设备是非常常见的。
下面就讨论下在LINUX如何实现IIC设备的驱动。
因为IIC的主机适配驱动程序一般芯片厂家在BSP里面都会提供,这里就不详细说明了,下面主要介绍下客户驱动程序如何实现。
首先,我们需要在驱动的初始化函数中注册IIC。
我们需要调用i2c_add_driver 。
这个函数的原形是int i2c_add_driver(struct i2c_driver * IIC),在驱动的exit函数里面需要调用i2c_del_driver释放资源。
下面是注册和释放的例子:static const struct i2c_device_id tsc2003_id[] = {{ “ts_2003”, 0 },{ }};static struct i2c_driver tsc2003_driver = {.probe = tsc2003_probe,.remove = tsc2003_remove,.id_table = tsc2003_id,.driver = {.name = “ts_2003”,},};static int __init tsc2003_init(void){int res;res = i2c_add_driver(&tsc2003_driver);if(res){return res;}return 0;}void __exit tsc2003_exit(void){i2c_del_driver(&tsc2003_driver);}在tsc2003_init这个函数里面调用了i2c_add_driver注册IIC。
下面介绍下i2c_driver这个结构:这个结构里面定义了.probe = tsc2003_probe,那么系统在检测到IIC核心定义了.name指定的设备名。
那么系统会调用tsc2003_probe,并且将struct i2c_client的指针作为参数传递进来。
手把手教你写Linux I2C设备驱动
手把手教你写Linux I2C设备驱动Linux I2C驱动是嵌入式Linux驱动开发人员经常需要编写的一种驱动,因为凡是系统中使用到的I2C设备,几乎都需要编写相应的I2C驱动去配置和控制它,例如RTC实时时钟芯片、音视频采集芯片、音视频输出芯片、EEROM芯片、AD/DA转换芯片等等。
Linux I2C驱动涉及的知识点还是挺多的,主要分为Linux I2C的总线驱动(I2C BUS Driver)和设备驱动(I2C Clients Driver),本文主要关注如何快速地完成一个具体的I2C 设备驱动(I2C Clients Driver)。
关于Linux I2C驱动的整体架构、核心原理等可以在网上搜索其他相关文章学习。
本文主要参考了Linux内核源码目录下的 ./Documentation/i2c/writing-clients 文档。
以手头的一款视频采集芯片TVP5158为驱动目标,编写Linux I2C设备驱动。
1. i2c_driver结构体对象每一个I2C设备驱动,必须首先创造一个i2c_driver结构体对象,该结构体包含了I2C 设备探测和注销的一些基本方法和信息,示例如下:1.static struct i2c_driver tvp5158_i2c_driver = {2. .driver = {3. .name = "tvp5158_i2c_driver",4. },5. .attach_adapter = &tvp5158_attach_adapter,6. .detach_client = &tvp5158_detach_client,7. .command = NULL,8.};其中,name字段标识本驱动的名称(不要超过31个字符),attach_adapter和detac h_client字段为函数指针,这两个函数在I2C设备注册的时候会自动调用,需要自己实现这两个函数,后面将详细讲述。
Linux I2C设备驱动编写
Linux I2C设备驱动编写(一)在Linux驱动中I2C系统中主要包含以下几个成员:如果一个I2C适配器不支持I2C通道,那么就将master_xfer成员设为NULL。
如果适配器支持SMBUS 协议,那么需要去实现smbus_xfer,如果smbus_xfer指针被设为NULL,那么当使用SMBUS协议的时候将会通过I2C通道进行仿真。
master_xfer指向的函数的返回值应该是已经成功处理的消息数,或者返回负数表示出错了。
functionality指针很简单,告诉询问着这个I2C主控器都支持什么功能。
在内核的drivers/i2c/i2c-stub.c中实现了一个i2c adapter的例子,其中实现的是更为复杂的SMBUS。
SMBus 与I2C的区别通常情况下,I2C和SMBus是兼容的,但是还是有些微妙的区别的。
时钟速度对比:在电气特性上他们也有所不同,SMBus要求的电压范围更低。
I2C driver具体的I2C设备驱动,如相机、传感器、触摸屏、背光控制器常见硬件设备大多都有或都是通过I2C 协议与主机进行数据传输、控制。
结构体如下:如同普通设备的驱动能够驱动多个设备一样,一个I2C driver也可以对应多个I2C client。
以重力传感器AXLL34X为例,其实现的I2C驱动为:这里要说明一下module_i2c_driver宏定义(i2c.h):module_driver():理解上述宏定义后,将module_i2c_driver(adxl34x_driver)展开就可以得到:这一句宏就解决了模块module安装卸载的复杂代码。
这样驱动开发者在实现I2C驱动时只要将i2c_driver结构体填充进来就可以了,无需关心设备的注册与反注册过程。
I2C client即I2C设备。
I2C设备的注册一般在板级代码中,在解析实例前还是先熟悉几个定义:下面还是以adxl34x为例:这样ADXL34X的i2c设备就被注册到了系统中,当名字与i2c_driver中的id_table中的成员匹配时就能够出发probe匹配函数了。
I 2C总线及uClinux下的嵌入式设备驱动开发
操 作系统 的支持 。 I 总线 是 由数 据线 (D )和时 钟 (C )构 2 C SA SL
成 的 同步 串行 总线 ,可 发送 和接 收 数据 。以在 处 理 器 与被控 芯 片之 间 、芯片 与 芯片 之 间进 行双 向
中被 分 成 若 干 模 块 。 其 中最 主 要 的 文 件 是 i — 2 c cr.。它 负 责管 理 总线 设 备 驱 动 接 口 .用 于 实 oe c
维普资讯
第9o 第 1 卷 2 7 2 期 0 年12 月
避钸
V1 N. o o2 . 1 9
De .2 o c 07
舒 粤 ,金 炜 东 ,刘铭
( 南交通 大学 电气 工程 学院 ,四川 成都 西
摘
60 3) 10 1
要 :分析 了嵌入 式u l u I 驱 动程 序 的 结构 ,结合 具 体 的I 接 口芯 片A V 1 1 Ci x ? 2 n e C 2 C D 7 7 ,给
出了通 过A V 1 1 D 7 7 视频 编码芯 片在u l u 系统下开发I 设备驱 动程序 的基本 流程和部分代 码 。 Ci x n 2 C
关 键字 :I 总 线 ;u l u ;设备 驱 动 2 C Ci x n
O 引 言
I ℃总线 是嵌 入式设 备 中芯 片与 芯片之 间的互
。 ‘ - …
联 总线 。使 用I 总线必 须 为其设 计相 应 的驱 动程 C 序 。虽 然 在u l u 源 代 码 中也 提 供 了一 些 I 总 Ci x n 2 C
线 及设 备驱 动 程序 ,但 由于嵌 入式 系统 及 设 备 的 多 样性 .这 些 驱动 程序 很难 满 足开 发人 员 的所 有
1 I 总线 概 述 2 C
嵌入式Linux系统下I2C设备驱动程序的开发
嵌入式Linux系统下I2C设备驱动程序的开发【摘要】 I2C总线是一种很通用的总线,具有简单、高效等特点,广泛应用在各种消费类电子产品及音视频设备上,在嵌入式系统的开发中也经常用到。
本文分析了嵌入式 linux系统中I2C驱动程序的结构,并结合一个具体的I2C 时钟芯片DS1307,说明在嵌入式linux系统下开发I2C设备驱动程序的一般流程。
【关键字】I2C总线嵌入式linux 驱动开发1、I2C总线简介I2C (Inter-Integrated Circuit)总线是一种由PHILIPS公司开发的两线式串行总线,用于连接微控制器及其外围设备。
I2C总线最主要的优点就是简单性和有效性。
1.1 I2C总线工作原理I2C总线是由数据线SDA和时钟SCL构成的串行总线,各种被控制器件均并联在这条总线上,每个器件都有一个唯一的地址识别,可以作为总线上的一个发送器件或接收器件(具体由器件的功能决定) [1]。
I2C总线的接口电路结构如图1所示。
图1 I2C总线接口电路[1]1.2 I2C总线的几种信号状态[1]1. 空闲状态:SDA和SCL都为高电平。
2. 开始条件(S):SCL为高电平时,SDA由高电平向低电平跳变,开始传送数据。
3. 结束条件(P):SCL为低电平时,SDA由低电平向高电平跳变,结束传送数据。
4. 数据有效:在SCL的高电平期间, SDA保持稳定,数据有效。
SDA的改变只能发生在SCL的底电平期间。
5. ACK信号: 数据传输的过程中,接收器件每接收一个字节数据要产生一个ACK信号,向发送器件发出特定的低电平脉冲,表示已经收到数据。
1.3 I2C总线基本操作I2C总线必须由主器件(通常为微控制器)控制,主器件产生串行时钟(SCL),同时控制总线的传输方向,并产生开始和停止条件。
数据传输中,首先主器件产生开始条件,随后是器件的控制字节(前七位是从器件的地址,最后一位为读写位)。
接下来是读写操作的数据,以及 ACK响应信号。
嵌入式Linux 2.6内核下的I2C驱动架构及应用技术分析
嵌入式Linux 2.6内核下的I2C驱动架构及应用技术分析摘要:Linux系统下的设备-核心-总线三层的I2C驱动架构,是合理、高效开发I2C设备驱动程序的重要参考框架。
该文以结构化的视角深入分析了嵌入式Linux 系统下I2C驱动的层次结构、数据结构、驱动流程等,并着重分析了2种设备层驱动方法,对I2C驱动开发具有普遍的适用性。
关键词:嵌入式;Linux;I2C;驱动前言I2C总线是一种双线式总线,由PHILLIPS公司发明,由于其紧凑的尺寸及相对简单的时序,在嵌入式设备中获得了广泛的应用。
但是在Linux系统中,出于支持多设备、多任务的要求,I2C驱动架构变得非常复杂。
在此尝试以一种结构化的视角对嵌入式Linux下的I2C的架构及应用进行详细的阐述。
1 I2C驱动的分层结构1.1 驱动的层次构成Linux驱动按由调用层次可分为3层分别是:I2C设备层驱动、I2C核心层驱动、I2C总线(适配器)层驱动。
这3部分共同配合完成了适用性很强的I2C驱动框架。
I2C总线驱动和设备驱动通过内核驱动联系起来。
与驱动层次相对应的是I2C源码的文件层次结构。
1.2 I2C文件结构内核源码组织:I2C相关的源码位于linux kernel的i2c文件夹下:有i2c_core.c、i2c_dev.c及busses、chips、algorithm 等文件夹。
核心层功能由I2c_core.c实现。
设备层比较特殊,有2种等效的方法:①通过i2c_dev.c实现适配器文件接口(i2c_dev方法),即在应用层调用内核文件I2C_dev.C所创建的主设备节点(相当于适配器)接口函数如read、ioctl 等来访问设备。
这种方法相当于在应用层编写设备的驱动程序;②通过chips文件夹下c文件实现设备驱动文件接口(Dirver方法)。
即在chips下从设备对应的C文件中编写如XXX_command(xxx为自定义的从设备名称)等从功能函数完成设备的访问流程,并在应用层调用此功能函数。
Linux中I2C总线的EEPROM 驱动程序
Linux环境下基于I2C总线的EEPROM 驱动程序1 引言I2C (Inter-Integrated Circuit1总线是一种由Philips公司开发的2线式串行总线,用于连接微控制器及其外围设备。
它是同步通信的一种特殊形式,具有接口线少、控制方式简单、器件封装形式小、通信速率较高等优点。
在主从通信中,可有多个I2C总线器件同时接到I2C总线上,通过地址来识别通信对象。
笔者在开发基于MPC8250的嵌入式Linux系统的过程中发现I2C总线在嵌入式系统中应用广泛,I2C总线控制器的类型比较多,对系统提供的操作接口差别也很大。
与I2C总线相连的从设备主要有微控制器、EEPROM、实时时钟、A/D转换器等.MPC8250处理器正是通过内部的I2C总线控制器来和这些连接在I2C总线上的设备进行数据交换的。
由于I2C总线的特性,Linux的I2C总线设备驱动程序的设计者在设计驱动程序时采用了独特的体系结构。
使开发I2C总线设备驱动程序与开发一般设备驱动程序的方法具有很大差别。
因此,开发I2C总线设备驱动程序除了要涉及一般Linux内核驱动程序的知识外.还要对I2C总线驱动的体系结构有深入的了解。
笔者在开发过程中使用设备型号为AT24C01A的EEPROM 来测试I2C总线驱动。
2 工作原理概述在介绍I2C总线结构之前。
要搞清楚两个概念:I2C总线控制器和I2C设备。
I2C总线控制器为微控制器或微处理器提供控制I2C总线的接口,它控制所有I2C总线的特殊序列、协议、仲裁、时序,这里指MPC8250提供的I2C总线控制接口。
I2C设备是指通过I2C总线与微控制器或微处理器相连的设备,如EEPROM、LCD驱动器等,这里指EEPROM。
在一个串行数据通道中.I2C总线控制器可以配置成主模式或从模式。
开发过程中,MPC8250的I2C总线控制器工作在主模式,作为主设备;与总线相连的I2C设备为AT24C01A 型EEPROM,作为从设备。
Linux下I2C设备驱动的一种适配器层直接实现方法
Linux下I2C设备驱动的一种适配器层直接实现方法杨文铂;邢鹏康【期刊名称】《单片机与嵌入式系统应用》【年(卷),期】2011(11)6【摘要】The traditional I2C device drivers in embedded Linux normally uses a complex three-layer driver architecture that fulfills the most requirements when the I2C devices work in parallel mode. However , under the condition that the I2C devices work in serial mode, the device driver can be completed just in adapter layer. This method can simplify the development of I2C device driver efficiently. The common procedure of this method is illustrates in Linux, joined with an example of sampling temperatures using LM75 sensor.%传统嵌入式Linux下的I2C设备驱动通常采用较为复杂的3层架构,可满足多设备并行工作的要求;但是在多设备串行的情况下,可直接在适配器层一层实现I2C设备驱动,这将有效地简化I2C设备驱动的开发.本文结合用LM75传感器采集温度的实例,介绍这种方法在嵌入式Linux下的一般实现过程.【总页数】3页(P25-27)【作者】杨文铂;邢鹏康【作者单位】河南工业职业技术学院,南阳473000;河南工业职业技术学院,南阳473000【正文语种】中文【中图分类】TP391【相关文献】1.一种Windows下实现Linux教学环境的方法 [J], 刘会杰;郭敏2.Linux下基于I2C总线的CAT1025设备驱动实现 [J], 王莹3.Linux下语音实时通信的一种实现方法 [J], 程虹霞;朱珠;吴小强;龙建忠4.一种LINUX系统下隔离设备驱动故障的方法 [J], 严开元;欧中红5.一种丢包区分方法及其在Linux下的实现 [J], 叶进;王建新;袁银行;张向利因版权原因,仅展示原文概要,查看原文内容请购买。