linux_rtc_i2c_driver

合集下载

linux_RTC

linux_RTC

Linux的RTC驱动相对还是比较简单的,可以将它作为一个普通的字符型设备,或者一个misc设备,也可以是一个平台设备,这都没有关系,主要还是对rtc_ops这个文件操作结构体中的成员填充,这里主要涉及到两个方面比较重要:1.在Linux中有硬件时钟与系统时钟等两种时钟。

硬件时钟是指主机板上的时钟设备,也就是通常可在BIOS画面设定的时钟。

系统时钟则是指kernel 中的时钟。

当Linux启动时,系统时钟会去读取硬件时钟的设定,之后系统时钟即独立运作。

所有Linux相关指令与函数都是读取系统时钟的设定。

系统时钟的设定就是我们常用的date命令,而我们写的RTC驱动就是为硬件时钟服务的,它有属于自己的命令hwclock,因此使用date命令是不可能调用到我们的驱动的(在这点上开始把我郁闷到了,写完驱动之后,傻傻的用date指令来测试,当然结果是什么都没有),我们可以通过hwclock的一些指令来实现更新rtc时钟——也就是系统时钟和硬件时钟的交互。

hwclock –r 显示硬件时钟与日期hwclock –s 将系统时钟调整为与目前的硬件时钟一致。

hwclock –w 将硬件时钟调整为与目前的系统时钟一致。

2. 第二点就是内核空间和用户空间的交互,在系统启动结束,我们实际是处在用户态,因此我们使用指令输入的内容也是在用户态,而我们的驱动是在内核态的,内核态和用户态是互相不可见的,因此我们需要特殊的函数来实现这两种形态的交互,这就是以下两个函数:copy_from_user(从用户态到内核态)copy_to_user (从内核态到用户态)当然这两个函数需要我们在内核驱动中实现。

RTC最基本的两个命令就是设置时间,读取时间。

设置时间——设置时间会调用系统默认的RTC_SET_TIME,很显然就是处在用户态的用户将自己所要设置的时间信息传递给内核态,case RTC_SET_TIME:{struct rtc_time rtc_tm;if (copy_from_user(&rtc_tm, (struct rtc_time*)arg,sizeof(struct rtc_time)))return -EFAULT;sep4020_rtc_settime(&rtc_tm);//把用户态得到的信息传递给设置时间这个函数return 0;}读取时间——设置时间会调用系统默认的RTC_RD_TIME,很显然就是需要通过内核态的驱动将芯片时钟取出,并传递给用户态case RTC_RD_TIME: /* Read the time/date from RTC */{sep4020_rtc_gettime(&septime);//通过驱动的读函数读取芯片时钟copy_to_user((void *)arg, &septime, sizeof septime);//传递给用户态}。

Linux下I2C驱动介绍

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设备。

LinuxI2C驱动--用户态驱动简单示例

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来进⾏处理的。

linux i2c驱动

linux i2c驱动

linux i2c驱动1. i2c-dev interfaceI2C dev-interface通常,i2c设备由某个内核驱动控制。

但是在用户空间,也可以访问某个I2C设备:你需要加载i2c-dev模块。

每个被注册的i2c适配器(控制器)会获得一个数字号,从0开始。

你可以检查/sys/class/i2c-dev,来查看适配器对应哪个数字号。

你也可以通过命令"i2cdetect -l"获取你的当前系统的所有I2c适配器的列表。

i2cdetct是i2c-tool包中的一个工具。

i2c设备文件是字符设备,主设备号是89,次设备号的分配如上所述。

设备文件名通常被规定为"i2c-%d"(i2c-0, i2c-1, ...,i2c-10, ...)i2c设备文件是字符设备,主设备号是89,次设备号的分配如上所述。

设备文件名通常被规定为"i2c-%d"(i2c-0, i2c-1, ...,i2c-10, ...).所有256个次设备号都保留给i2c使用。

C example=========假定你要在你的C应用程序中访问i2c适配器。

第一件事情就是包含头文件"#include<linux/i2c-dev.h>"。

注意,存在两个"i2c-dev.h"文件: 一个属于Linux kernel,用于内核驱动中;一个由i2c-tools发布,用于用户程序。

显然,这里需要使用第二个i2c-dev.h文件。

现在,你需要确定访问哪个适配器。

你需要通过查看/sys/class/i2c-dev/或者运行"i2cdetect -l"确定。

适配器号时常是动态分配的,你无法预先假定某个值。

因为它们甚至会在系统重启后变为不同的值。

下一步,打开设备文件,如下:int file;int adapter_nr = 2; /*probably dynamically determined */char filename[20];snprintf(filename, 19, "/dev/i2c-%d", adapter_nr);file = open(filename, O_RDWR);if (file < 0) {/* ERROR HANDLING; you can check errno to see what went wrong */ exit(1);}当你打开设备后,你必须明确说明你相与哪个设备地址进行通信:int addr = 0x40;if (ioctl(file, I2C_SLAVE, addr) < 0) {/* ERROR HANDLING; you can check errno to see what went wrong */ exit(1);}Well, 你现在准备好与I2C设备通信了。

linux 应用访问i2c设备原理

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设备驱动

手把手教你写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设备注册的时候会自动调用,需要自己实现这两个函数,后面将详细讲述。

i2c_register_driver函数详解

i2c_register_driver函数详解

i2c_register_driver函数详解在嵌入式软件开发中,I2C(Inter-Integrated Circuit)总线是一种常用的串行通信接口,用于在微控制器和外部设备之间传输数据。

i2c_register_driver函数是Linux内核中一个重要的函数,用于注册I2C 驱动程序。

本文将详细解析i2c_register_driver函数的功能、参数和应用。

一、i2c_register_driver函数概述i2c_register_driver函数是在Linux内核中注册一个I2C驱动程序的函数。

它的作用是将驱动程序与对应的I2C适配器绑定,使得操作系统能够正确地识别和管理该驱动程序。

在驱动程序注册后,当相应的I2C设备连接到系统时,驱动程序将会自动加载并为该设备提供服务。

二、i2c_register_driver函数参数i2c_register_driver函数包含一个结构体参数,该结构体用于指定驱动程序的相关信息和功能。

1. struct i2c_driverstruct i2c_driver是一个定义I2C驱动程序的结构体,包含了以下重要的成员:- .driver:指向内核的struct device_driver结构体,用于描述驱动程序的信息,如名称、文件操作方法等。

- .probe:指向I2C设备探测函数的指针,用于在设备连接时进行初始化和配置。

- .remove:指向I2C设备移除函数的指针,用于在设备断开连接时进行清理和释放资源。

- .id_table:指向I2C设备ID表的指针,用于匹配设备和驱动程序。

2. I2C设备探测函数(probe函数)I2C设备探测函数是I2C驱动程序的核心功能之一,在I2C设备连接到系统时被调用。

该函数的作用是检测和初始化I2C设备,并将设备与驱动程序进行绑定。

在probe函数中,可以执行一系列必要的操作,如配置寄存器、分配内存、注册字符设备等。

28.Linux-IIC驱动(详解)

28.Linux-IIC驱动(详解)

28.Linux-IIC驱动(详解)上⼀节我们学习了:IIC接⼝下的24C02 驱动分析:接下来本节, 学习Linux下如何利⽤linux下I2C驱动体系结构来操作24C021. I2C体系结构分析1.1⾸先进⼊linux内核的driver/i2c⽬录下,如下图所⽰:其中重要的⽂件介绍如下:1)algos⽂件夹(algorithms)⾥⾯保存I2C的通信⽅⾯的算法2)busses⽂件夹⾥⾯保存I2C总线驱动相关的⽂件,⽐如i2c-omap.c、 i2c-versatile.c、 i2c-s3c2410.c等。

3)chips⽂件夹⾥⾯保存I2C设备驱动相关的⽂件,如下图所⽰,⽐如m41t00,就是RTC实时钟4) i2c-core.c这个⽂件实现了I2C核⼼的功能(I2C总线的初始化、注册和适配器添加和注销等相关⼯作)以及/proc/bus/i2c*接⼝。

5) i2c-dev.c提供了通⽤的read()、 write()和ioctl()等接⼝,实现了I2C适配器设备⽂件的功能,其中I2C设备的主设备号都为89,次设备号为0~255。

应⽤层可以借⽤这些接⼝访问挂接在适配器上的I2C设备的存储空间或寄存器,并控制I2C设备的⼯作⽅式显然,它和前⼏次驱动类似, I2C也分为总线驱动和设备驱动,总线就是协议相关的,它知道如何收发数据,但不知道数据含义,设备驱动却知道数据含义1.2 I2C驱动架构,如下图所⽰:如上图所⽰,每⼀条I2C对应⼀个adapter适配器,在kernel中, adapter适配器是通过struct adapter结构体定义,主要是通过i2c core层将i2c设备与i2c adapter关联起来.在kernel中提供了两个adapter注册接⼝,分别为i2c_add_adapter()和i2c_add_numbered_adapter().由于在系统中可能存在多个adapter,因为将每⼀条I2C 总线对应⼀个编号,下⽂中称为I2C总线号.这个总线号的PCI中的总线号不同.它和硬件⽆关,只是软件上便于区分⽽已.对于i2c_add_adapter()⽽⾔,它使⽤的是动态总线号,即由系统给其分析⼀个总线号,⽽i2c_add_numbered_adapter()则是⾃⼰指定总线号,如果这个总线号⾮法或者是被占⽤,就会注册失败.2.接下来便来分析I2C总线驱动参考 drivers/i2c/busses/i2c-s3c2410.c先进⼊init⼊⼝函数,如下图所⽰:在init函数中,注册了⼀个“s3c2440-i2c”的platform_driver平台驱动,我们来看看probe函数做了些什么3.进⼊s3c24xx_i2c_probe函数struct i2c_adapter adap;static int s3c24xx_i2c_probe(struct platform_device *pdev){ struct s3c24xx_i2c *i2c = &s3c24xx_i2c;... .../*获取,使能I2C时钟*/i2c->clk = clk_get(&pdev->dev, "i2c"); //获取i2c时钟clk_enable(i2c->clk); //使能i2c时钟... ..../*获取资源*/res = platform_get_resource(pdev, IORESOURCE_MEM, 0);i2c->regs = ioremap(res->start, (res->end-res->start)+1);... ..../*设置i2c_adapter适配器结构体, 将i2c结构体设为adap的私有数据成员*/ i2c->adap.algo_data = i2c; //i2c_adapter适配器指向s3c24xx_i2c;i2c->adap.dev.parent = &pdev->dev; /* initialise the i2c controller *//*初始化2440的I2C相关的寄存器*/ret = s3c24xx_i2c_init(i2c);if (ret != 0)goto err_iomap;... .../*注册中断服务函数*/ret = request_irq(res->start, s3c24xx_i2c_irq, IRQF_DISABLED,pdev->name, i2c);... .../*注册i2c_adapter适配器结构体*/ret = i2c_add_adapter(&i2c->adap);... ...}其中i2c_adapter结构体是放在s3c24xx_i2c->adap下,如下图所⽰:4.接下来我们进⼊i2c_add_adapter()函数看看,到底如何注册的int i2c_add_adapter(struct i2c_adapter *adapter){int id, res = 0;retry:if (idr_pre_get(&i2c_adapter_idr, GFP_KERNEL) == 0) //调⽤idr_pre_get()为i2c_adapter预留内存空间return -ENOMEM;mutex_lock(&core_lists);/* "above" here means "above or equal to", sigh */res = idr_get_new_above(&i2c_adapter_idr, adapter,__i2c_first_dynamic_bus_num, &id);//调⽤idr_get_new_above()将结构插⼊i2c_adapter_idr中,并将插⼊的位置赋给id,以后可以通过id在i2c_adapter_idr中找到相应的i2c_adapter结构体 mutex_unlock(&core_lists);if (res < 0) {if (res == -EAGAIN)goto retry;return res;}adapter->nr = id;return i2c_register_adapter(adapter); //调⽤i2c_register_adapter()函数进⼀步来注册.}其中i2c_register_adapter()函数代码如下所⽰:static int i2c_register_adapter(struct i2c_adapter *adap){struct list_head *item; //链表头,⽤来存放i2c_driver结构体的表头struct i2c_driver *driver; //i2c_driver,⽤来描述⼀个IIC设备驱动list_add_tail(&adap->list, &adapters); //添加到内核的adapter链表中... ...list_for_each(item,&drivers) { //for循环,从drivers链表⾥找到i2c_driver结构体的表头driver = list_entry(item, struct i2c_driver, list); //通过list_head表头,找到i2c_driver结构体if (driver->attach_adapter)/* We ignore the return code; if it fails, too bad */driver->attach_adapter(adap); //调⽤i2c_driver的attach_adapter函数来看看,这个新注册的设配器是否⽀持i2c_driver}}在i2c_register_adapter()函数⾥主要执⾏以下⼏步:①将adapter放⼊i2c_bus_type的adapter链表②将所有的i2c设备调出来,执⾏i2c_driver设备的attach_adapter函数来匹配其中, i2c_driver结构体会在后⾯讲述到⽽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; //存放在i2c_adapter_idr⾥的位置号char name[48]; //适配器名称struct completion dev_released; //⽤于同步struct list_head userspace_clients; //client链表头};i2c_adapter表⽰物理上的⼀个i2C设备(适配器), 在i2c-s3c2410.c中,是存放在s3c24xx_i2c结构体下的(struct i2c_adapter adap)成员中5.其中s3c24xx_i2c的结构体成员如下所⽰static const struct i2c_algorithm s3c24xx_i2c_algorithm = {.master_xfer = s3c24xx_i2c_xfer, //主机传输.functionality = s3c24xx_i2c_func,};static struct s3c24xx_i2c s3c24xx_i2c = {.lock = __SPIN_LOCK_UNLOCKED(s3c24xx_i2c.lock),.wait = __WAIT_QUEUE_HEAD_INITIALIZER(s3c24xx_i2c.wait),.tx_setup = 50, //⽤来延时,等待SCL被释放.adap = { // i2c_adapter适配器结构体.name = "s3c2410-i2c",.owner = THIS_MODULE,.algo = &s3c24xx_i2c_algorithm, //存放i2c_algorithm算法结构体.retries = 2, //重试次数.class = I2C_CLASS_HWMON,},};显然这⾥是直接设置了i2c_adapter结构体,所以在s3c24xx_i2c_probe ()函数中没有分配i2c_adapter适配器结构体,其中, i2c_adapter结构体的名称等于"s3c2410-i2c",它的通信⽅式等于s3c24xx_i2c_algorithm,重试次数等于2PS:如果缺少i2c_algorithm的i2c_adapter什么也做不了,就只是个I2C设备,⽽没有通信⽅式s3c24xx_i2c_algorithm中的关键函数master_xfer()就是⽤于产⽣i2c访问周期需要的start stop ack等信号⽐如,在s3c24xx_i2c_algorithm中的关键函数master_xfer()⾥,调⽤了:s3c24xx_i2c_xfer -> s3c24xx_i2c_doxfer()->s3c24xx_i2c_message_start()来启动传输message信息, 其中s3c24xx_i2c_message_start()函数代码如下:static void s3c24xx_i2c_message_start(struct s3c24xx_i2c *i2c, struct i2c_msg *msg){unsigned int addr = (msg->addr & 0x7f) << 1; //IIC从设备地址的最低位为读写标志位... ...stat = 0;stat |= S3C2410_IICSTAT_TXRXEN; //设置标志位启动IIC收发使能if (msg->flags & I2C_M_RD) { //判断是读,还是写stat |= S3C2410_IICSTAT_MASTER_RX;addr |= 1; //设置从IIC设备地址为读标志} elsestat |= S3C2410_IICSTAT_MASTER_TX;s3c24xx_i2c_enable_ack(i2c); //使能ACK信号 iiccon = readl(i2c->regs + S3C2410_IICCON); //读出IICCON寄存器writel(stat, i2c->regs + S3C2410_IICSTAT); //写⼊IICSTAT寄存器,使能IIC的读或写标志dev_dbg(i2c->dev, "START: %08lx to IICSTAT, %02x to DS\n", stat, addr);writeb(addr, i2c->regs + S3C2410_IICDS); //将IIC从设备地址写⼊IICDS寄存器/* delay here to ensure the data byte has gotten onto the bus* before the transaction is started */ndelay(i2c->tx_setup); //延时,等待SCL被释放,下⾯便可以发送起始信号+IIC设备地址值dev_dbg(i2c->dev, "iiccon, %08lx\n", iiccon);writel(iiccon, i2c->regs + S3C2410_IICCON);stat |= S3C2410_IICSTAT_START;writel(stat, i2c->regs + S3C2410_IICSTAT); //设置IICSTAT寄存器的bit5=1,开始发送起始信号+IIC从设备地址值,并回应ACK}通过上⾯的代码和注释,发现主要是写⼊IIC从设备地址,然后发送起始信号+IIC从设备地址值,并回应ACK显然IIC总线驱动i2c-s3c2410.c,主要设置适配器adapter,⾥⾯帮我们做好了IIC通信的架构,就是不知道发什么内容我们进⼊driver/i2c/chips中,看看eeprom设备驱动是如何写的参考: driver/i2c/chips/eeprom.c6.还是⾸先来看它的init⼊⼝函数:其中struct i2c_driver eeprom_driver的成员如下:static struct i2c_driver eeprom_driver = {.driver = {.name = "eeprom", //名称},.id = I2C_DRIVERID_EEPROM, //IIC设备标识ID.attach_adapter = eeprom_attach_adapter, //⽤来与总线驱动的适配器匹配,匹配成功添加到适配器adapter中.detach_client = eeprom_detach_client, //与总线驱动的适配器解绑,分离这个IIC从设备};如下图所⽰, eeprom_driver结构体的ID成员在i2c-id.h中,⾥⾯还定义了⼤部分常⽤I2C设备驱动的设备ID显然,在init函数中通过i2c_add_driver()注册i2c_driver结构体,然后通过i2c_driver ->attach_adapter来匹配内核中的各个总线驱动的适配器, 发送这个设备地址,若有ACK响应,表⽰匹配成功7.接下来,我们进⼊i2c_add_driver()来看看是不是这样的int i2c_add_driver(struct module *owner, struct i2c_driver *driver){driver->driver.owner = owner;driver->driver.bus = &i2c_bus_type; //将i2c_driver放在i2c_bus_type链表中res = driver_register(&driver->driver); //注册⼀个i2c_driver... ...if (driver->attach_adapter) {struct i2c_adapter *adapter; //定义⼀个i2c_adapter适配器list_for_each_entry(adapter, &adapters, list) //for循环提取出adapters链表中所有的i2c_adapter适配器,放⼊到adapter结构体中 {driver->attach_adapter(adapter); //来匹配取出来的i2c_adapter适配器}}... ...return0;}在i2c_add_driver ()函数⾥主要执⾏以下⼏步:①放⼊到i2c_bus_type链表②取出adapters链表中所有的i2c_adapter,然后执⾏i2c_driver->attach_adapter()所以i2c_adapter适配器和i2c_driver设备驱动注册框架如下所⽰:这⾥调⽤了i2c_driver ->attach_adapter(adapter),我们看看⾥⾯是不是通过发送IIC设备地址,等待ACK响应来匹配的8.以struct i2c_driver eeprom_driver 为例,进⼊i2c_driver ->eeprom_attach_adapter()函数如下图所⽰,⾥⾯调⽤了i2c_probe(adapter, &addr_data, eeprom_detect)函数上图的第1个参数就是i2c_adapter适配器,第2个参数addr_data变量,⾥⾯存放了IIC设备地址的信息,第3个参数eeprom_detect就是具体的设备探测回调函数i2c_probe()函数,会通过adapter适配器发送IIC设备地址addr_data,如果收到ACK信号,就调⽤eeprom_detect()回调函数来注册i2c_client结构体,该结构体对应真实的物理从设备,⽽i2c_driver对应的是设备驱动,也就是说,只有当适配器⽀持这个设备驱动,才会注册i2c_client从设备,后⾯会讲这个回调函数如何注册i2c_client⽽在i2c_driver ->detach_client()中,则注销i2c_client结构体其中addr_data变量是struct i2c_client_address_data结构体,它的成员如下所⽰:struct i2c_client_address_data {unsigned short *normal_i2c; //存放正常的设备⾼7位地址数据unsigned short *probe; //存放不受*ignore影响的⾼7位设备地址数据unsigned short *ignore; //存放*ignore的⾼7位设备地址数据unsigned short **forces; //forces表⽰适配器匹配不了该设备,也要将其放⼊适配器中};当上⾯结构体的数组成员以I2C_CLIENT_END结尾,则表⽰地址已结束,⽐如at24c02设备为例,看这个结构体如何定义的:#define AT24C02_ADDR (0xA0>>1) //AT24C02地址static unsigned short ignore[] = { I2C_CLIENT_END };static unsigned short normal_addr[] = { AT24C02_ADDR, I2C_CLIENT_END };static unsigned short force_addr[] = {ANY_I2C_BUS, AT24C02_ADDR ,2C_CLIENT_END};static unsigned short * forces[] = {force_addr, NULL}; //ANY_I2C_BUS:表⽰⽀持所有适配器总线,若填指定的适配器总线ID,则表⽰该设备只⽀持指定的那个适配器static struct i2c_client_address_data addr_data = {.normal_i2c = normal_addr, //存放at24c02地址.probe = ignore, //表⽰⽆地址.ignore = ignore, //表⽰⽆地址. forces = forces, //存放强制的at24c02地址,表⽰强制⽀持};⼀般⽽⾔,都不会设置.forces成员,这⾥只是打个⽐⽅8.1接下来继续进⼊i2c_probe()函数继续分析,如下所⽰:int i2c_probe(struct i2c_adapter *adapter,struct i2c_client_address_data *address_data,int (*found_proc) (struct i2c_adapter *, int, int)){... ...err = i2c_probe_address(adapter,forces[kind][i + 1],kind, found_proc);}⾥⾯调⽤了i2c_probe_address()函数,从名称上来看,显然它就是⽤来发送起始信号+设备地址,来探测IIC设备地址⽤的8.2进⼊i2c_probe_address()函数:static int i2c_probe_address(struct i2c_adapter *adapter, int addr, int kind,int (*found_proc) (struct i2c_adapter *, int, int)){/*判断设备地址是否有效,addr⾥存放的是设备地址前7位,⽐如AT24C02=0xA0,那么addr=0x50*/if (addr < 0x03 || addr > 0x77) {dev_warn(&adapter->dev, "Invalid probe address 0x%02x\n",addr); //打印地址⽆效,并退出return -EINVAL;}/*查找链表中其它IIC设备的设备地址,若这个设备地址已经被使⽤,则return*/if (i2c_check_addr(adapter, addr))return0;if (kind < 0) {if (i2c_smbus_xfer(adapter, addr, 0, 0, 0,I2C_SMBUS_QUICK, NULL) < 0) //进⼊I2C传输函数 return0;... ...}8.3 其中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) { //如果adapter适配器有smbus_xfer这个函数mutex_lock(&adapter->bus_lock); //加互斥锁res = adapter->algo->smbus_xfer(adapter,addr,flags,read_write,command,size,data); //调⽤adapter适配器⾥的传输函数mutex_unlock(&adapter->bus_lock); //解互斥锁} else//否则使⽤默认函数传输设备地址res = i2c_smbus_xfer_emulated(adapter,addr,flags,read_write,command,size,data);return res;}看了上⾯代码后,显然我们的s3c2410-i2c适配器没有algo->smbus_xfer函数,⽽是使⽤i2c_smbus_xfer_emulated()函数,如下图所⽰:PS:通常适配器都是不⽀持的,使⽤默认的i2c_smbus_xfer_emulated()函数8.4 接下来看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) {unsigned char msgbuf0[I2C_SMBUS_BLOCK_MAX+3]; //属于 msg[0]的buf成员unsigned char msgbuf1[I2C_SMBUS_BLOCK_MAX+2]; //属于 msg[1]的buf成员int num = read_write == I2C_SMBUS_READ?2:1; //如果为读命令,就等于2,表⽰要执⾏两次数据传输struct i2c_msg msg[2] = { { addr, flags, 1, msgbuf0 }, { addr, flags | I2C_M_RD, 0, msgbuf1 }}; //定义两个i2c_msg结构体,msgbuf0[0] = command; //IIC设备地址最低位为读写命令... ...if (i2c_transfer(adapter, msg, num) < 0)return -1;/*设置i2c_msg结构体成员*/if (read_write == I2C_SMBUS_READ)switch(size) {... ...case I2C_SMBUS_BYTE_DATA: //如果是读字节if (read_write == I2C_SMBUS_READ)msg[1].len = 1;else {msg[0].len = 2;msgbuf0[1] = data->byte;}break;... ...}... ...if (i2c_transfer(adapter, msg, num) < 0) //将 i2c_msg结构体的内容发送给I2C设备return -1;... ...}其中i2c_msg结构体的结构,如下所⽰:struct i2c_msg {__u16 addr; //I2C从机的设备地址__u16 flags; //当flags=0表⽰写, flags= I2C_M_RD表⽰读__u16 len; //传输的数据长度,等于buf数组⾥的字节数__u8 *buf; //存放数据的数组};上⾯代码中之所以读操作需要两个i2c_msg,写操作需要⼀个i2c_msg,是因为读IIC设备是两个流程在上⼀节IIC接⼝下的24C02 驱动分析:⾥就已经分析到了,只要发送⼀个S起始信号则就是⼀个i2c_msg,如下两个读写操作图所⽰:⽽在i2c_transfer()函数中,最终⼜是调⽤了之前分析的i2c_adapter->algo->master_xfer()发送函数,如下图所⽰:其中i2c_transfer()的参数*adap表⽰通过哪个适配器传输出去,msgs表⽰I2C消息,num表⽰msgs的数⽬内核每发送⼀个Msg都会先发出S开始信号和设备地址.直到所有Msg传输完毕,最后发出P停⽌信号。

Linux 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匹配函数了。

linux驱动学习i2c驱动架构分析

linux驱动学习i2c驱动架构分析

linux驱动学习i2c驱动架构davinc dm368 i2c驱动分析但是Linux的i2c驱动体系结构却有相当的复杂度,不管是叫linux i2c驱动还是单片机i2c驱动,其根本还是操作soc芯片内部的i2c模块(也叫i2c adapter)(读写i2c相关的寄存器)来产生start、stop还有ack信号而已。

linux设备驱动到底复杂在什么地方?假设soc芯片dm368有两个i2c adapter(368内部真正只有一个i2c模块):i2c_adapter1,i2c_adapter1;然后外部有三个i2c接口的设备i2c_device1,i2c_device2,i2c_device3。

现在要求在裸机下写出他们的驱动函数。

那么肯定要写出6个不同的驱动函数:i2c_adapter1_ReadWrite_i2c_device1();i2c_adapter1_ReadWrite_i2c_device2()i2c_adapter1_ReadWrite_i2c_device3()i2c_adapter2_ReadWrite_i2c_device1()i2c_adapter2_ReadWrite_i2c_device2()i2c_adapter2_ReadWrite_i2c_device3()设想一共有m个i2c adapter和n个外设i2c device,那么将需要m*n个驱动。

并且这m*n个驱动程序必要会有很大部分重复的代码,而且不利于驱动程序的移植。

如果采用adapter和device分离的思想来写这样的驱动会是怎样呢?图1这样分离之后,只需要m+n个驱动,而且Adapter和Device的几乎没有耦合性,增加一个Adapter或者device并不会影响其余的驱动。

这就是分离思想带来的好处。

除此之外,linux虽然是C写的,但是大量使用了面向对象的变成方法(可以理解为分层的思想),仅仅分离细想和分层思想的引入,就大大增加了linux设备驱动的复杂度。

iic--rtc驱动

iic--rtc驱动

这几天主要做I2c驱动和RTC驱动关于I2c其实控制器的核心代码已经写好了,我要做的就是移植到现在的linux3.0内核架构中一.关于I2c总线架构谈到在linux系统下编写I2C驱动,目前主要有两种方式,一种是把I2C设备当作一个普通的字符设备来处理,另一种是利用linux I2C驱动体系结构来完成而最新的内核3.0使用的是最新的Probe方式,I2c裸机程序很简单,按照它的时序收发,但是内核中的I2c架构有点麻烦,但是万变不离其宗,内核都是严格遵循分层思想的I2c子系统中最上面的层次i2c-dev.c,主要处理的是应用层接口中间层是i2c-core.c,主要处理的是通用层和具体设备和算法之间的接口下层是I2c主要适配器的算法(读写方式)我需要移植的就是适配器算法让其和中间层对接起来适配器算法已经由gpio方式实现了,只需要让其挂接在中间层的通用接口上就可以了具体下层和中间层的对接代码我是参考S3c2410的代码架构的最后I2c driver 能和I2c 总线设备match的话,该I2c总线驱动基本完成二.关于DS3231驱动架构接下来是做I2c 设备驱动,已经做好总线驱动了,设备驱动很简单了,我这里参考的DS3232代码驱动架构,两款芯片差不多,因为现在用的DS3231没有用到报警器之类的功能,在DS3232,的代码基础上,做了大量的裁剪,只留下基本的驱动框架,在驱动注册到I2c总线后,会i2c总线上match相应的device设备。

如果匹配成功了,就会自动调用probe了我们就可以在probe中注册普通字符驱动设备(其实在这里字符设备早已经在rtc子系统初始化注册了,在这里只是做些初始化分配资源而已),设置普通字符设备的read,write(其实就是从I2c总线收发数据),我们在这里的read,write中注意要调用I2c中间层的提供的函数,I2c core中有许多供I2c适配器算法使用的通用接口,这里我只挑选了2个,master_i2c_send和master_i2c_recv,其实这么多接口归根结底都是调用i2c_transfert,I2c_trandfer才是转向下层i2c总线适配器算法的真正接口.三.关于温度接口实现问题最后按照需求增加对读RTC温度的接口这个实现比较简单只要在DS3231中增加接口读温度寄存器的值即可,我在这里选用的接口是IOCTL,这个接口,在上册字符驱动还需要增加调用DS3231接口的代码,这里实在上层的IOCTL中添加新的命令。

LinuxI2C驱动整理(以RK3399Pro+Kernel4.4为例)

LinuxI2C驱动整理(以RK3399Pro+Kernel4.4为例)

LinuxI2C驱动整理(以RK3399Pro+Kernel4.4为例)⼀. Linux I2C驱动架构Linux内核⾥,I2C驱动框架可以分为两层,adapter驱动和deivce驱动。

Adapter驱动也可以理解为I2C总线驱动,指的是SOC⾥的I2C控制器驱动。

⼀个SOC可能包含多个I2C控制器,⽽每个控制器的使⽤⽅式是相同的(寄存器参数、收发数据的⽅法等),因此多个控制器可以共⽤⼀套adapter驱动;Deivce驱动,对应的是SOC外围的I2C设备,不同类型I2C设备需要开发不同的设备驱动,同⼀类型的I2C设备可以使⽤⼀种驱动,但是每⼀个I2C设备都由⼀个唯⼀的client来描述。

⼆. Adapter配置DTSI⽂件(kernel/arch/arm64/boot/dts/rockchip/rk3399.dtsi)描述了RK3399Pro所有的I2C控制器信息:i2c0: i2c@ff3c0000 {compatible = "rockchip,rk3399-i2c";reg = <0x00xff3c00000x00x1000>;clocks = <&pmucru SCLK_I2C0_PMU>, <&pmucru PCLK_I2C0_PMU>;clock-names = "i2c", "pclk";interrupts = <GIC_SPI 57 IRQ_TYPE_LEVEL_HIGH 0>;pinctrl-names = "default";pinctrl-0 = <&i2c0_xfer>;#address-cells = <1>;#size-cells = <0>;status = "disabled";};i2c1: i2c@ff110000 {compatible = "rockchip,rk3399-i2c";reg = <0x00xff1100000x00x1000>;clocks = <&cru SCLK_I2C1>, <&cru PCLK_I2C1>;clock-names = "i2c", "pclk";interrupts = <GIC_SPI 59 IRQ_TYPE_LEVEL_HIGH 0>;pinctrl-names = "default";pinctrl-0 = <&i2c1_xfer>;#address-cells = <1>;#size-cells = <0>;status = "disabled";};i2c2: i2c@ff120000 {compatible = "rockchip,rk3399-i2c";reg = <0x00xff1200000x00x1000>;clocks = <&cru SCLK_I2C2>, <&cru PCLK_I2C2>;clock-names = "i2c", "pclk";interrupts = <GIC_SPI 35 IRQ_TYPE_LEVEL_HIGH 0>;pinctrl-names = "default";pinctrl-0 = <&i2c2_xfer>;#address-cells = <1>;#size-cells = <0>;status = "disabled";};i2c3: i2c@ff130000 {compatible = "rockchip,rk3399-i2c";reg = <0x00xff1300000x00x1000>;clocks = <&cru SCLK_I2C3>, <&cru PCLK_I2C3>;clock-names = "i2c", "pclk";interrupts = <GIC_SPI 34 IRQ_TYPE_LEVEL_HIGH 0>;pinctrl-names = "default";pinctrl-0 = <&i2c3_xfer>;#address-cells = <1>;#size-cells = <0>;status = "disabled";};i2c5: i2c@ff140000 {compatible = "rockchip,rk3399-i2c";reg = <0x00xff1400000x00x1000>;clocks = <&cru SCLK_I2C5>, <&cru PCLK_I2C5>;clock-names = "i2c", "pclk";interrupts = <GIC_SPI 38 IRQ_TYPE_LEVEL_HIGH 0>;pinctrl-names = "default";pinctrl-0 = <&i2c5_xfer>;#address-cells = <1>;#size-cells = <0>;status = "disabled";};i2c6: i2c@ff150000 {compatible = "rockchip,rk3399-i2c";reg = <0x00xff1500000x00x1000>;clocks = <&cru SCLK_I2C6>, <&cru PCLK_I2C6>;clock-names = "i2c", "pclk";interrupts = <GIC_SPI 37 IRQ_TYPE_LEVEL_HIGH 0>;pinctrl-names = "default";pinctrl-0 = <&i2c6_xfer>;#address-cells = <1>;#size-cells = <0>;status = "disabled";};i2c7: i2c@ff160000 {compatible = "rockchip,rk3399-i2c";reg = <0x00xff1600000x00x1000>;clocks = <&cru SCLK_I2C7>, <&cru PCLK_I2C7>;clock-names = "i2c", "pclk";interrupts = <GIC_SPI 36 IRQ_TYPE_LEVEL_HIGH 0>;pinctrl-names = "default";pinctrl-0 = <&i2c7_xfer>;#address-cells = <1>;#size-cells = <0>;status = "disabled";};i2c4: i2c@ff3d0000 {compatible = "rockchip,rk3399-i2c";reg = <0x00xff3d00000x00x1000>;clocks = <&pmucru SCLK_I2C4_PMU>, <&pmucru PCLK_I2C4_PMU>;clock-names = "i2c", "pclk";interrupts = <GIC_SPI 56 IRQ_TYPE_LEVEL_HIGH 0>;pinctrl-names = "default";pinctrl-0 = <&i2c4_xfer>;#address-cells = <1>;#size-cells = <0>;status = "disabled";};i2c8: i2c@ff3e0000 {compatible = "rockchip,rk3399-i2c";reg = <0x00xff3e00000x00x1000>;clocks = <&pmucru SCLK_I2C8_PMU>, <&pmucru PCLK_I2C8_PMU>;clock-names = "i2c", "pclk";interrupts = <GIC_SPI 58 IRQ_TYPE_LEVEL_HIGH 0>;pinctrl-names = "default";pinctrl-0 = <&i2c8_xfer>;#address-cells = <1>;#size-cells = <0>;status = "disabled";};可以看出,该SOC共有9个I2C控制器,分别为I2C0~I2C8, 每个控制器对应了不同的寄存器基地址(例如I2C0对应0xff3c0000),它们的compatible匹配属性都是"rockchip,rk3399-i2c",也就是对应了同⼀个adapter驱动。

i2c_register_driver函数详解

i2c_register_driver函数详解

i2c_register_driver函数详解一、概述i2c_register_driver函数是Linux内核中用于注册I2C设备驱动的函数之一。

它用于将设备驱动程序注册到内核,以便其他模块可以使用该驱动程序来与I2C设备进行通信。

二、函数原型int i2c_register_driver(struct i2c_driver *driver);三、函数参数说明* driver:指向i2c_driver结构体的指针,该结构体包含了设备驱动的相关信息,如设备名称、设备ID等。

四、函数返回值如果函数成功注册驱动程序,则返回0;否则返回-E...等错误代码。

五、函数功能详解i2c_register_driver函数将指定的i2c_driver结构体注册到内核中,使其成为可用的I2C设备驱动。

在注册驱动程序之前,需要确保驱动程序已经正确编译并包含在内核中。

在注册驱动程序时,i2c_register_driver函数会执行以下操作:1. 检查驱动程序是否符合内核的要求,如设备ID、设备名称等是否正确。

2. 将驱动程序的信息添加到内核的设备列表中,以便其他模块可以找到并使用该驱动程序。

3. 注册成功后,内核会为该驱动程序分配相应的资源,如内存、端口等,并建立与I2C总线的连接。

六、使用示例```c#include <linux/module.h>#include <linux/i2c.h>struct i2c_driver my_driver;int main(){int ret;ret = i2c_register_driver(&my_driver);if (ret) {printk(KERN_ERR "Failed to register driver: %d\n", ret);return ret;}// 其他操作...return 0;}```在上述示例中,首先定义了一个i2c_driver结构体变量my_driver,用于存储驱动程序的相关信息。

rk linux下i2c总线驱动读写原理

rk linux下i2c总线驱动读写原理

RK Linux下I2C总线驱动读写原理RK Linux,一个开源的、强大的、可定制的操作系统,广泛应用于各种嵌入式系统。

I2C总线是一种常用的通信协议,常用于连接低速外围设备,如传感器、EEPROM等。

在RK Linux下,I2C总线驱动的读写原理是什么呢?首先,我们来了解下I2C总线的基本概念。

I2C总线是一种双线串行通信总线,由数据线SDA和时钟线SCL组成。

通过这两根线,多个设备可以在同一总线上进行通信。

每个设备都有一个唯一的地址,主机可以通过发送设备的地址来选择与之通信的设备。

在RK Linux下,I2C总线驱动的读写操作主要依赖于内核提供的API。

这些API 包括i2c_read()、i2c_write()等,它们提供了与I2C设备通信的接口。

那么,这些API是如何实现读写操作的呢?在内核中,I2C驱动程序负责管理I2C总线上所有的设备和它们的通信。

当需要从设备读取数据时,驱动程序首先会向设备发送读请求。

设备接收到请求后,会将数据写入SDA线。

驱动程序会持续监听SDA线,一旦接收到数据,就会将其保存并通知应用程序。

同样地,当需要向设备写入数据时,驱动程序会向设备发送写请求。

设备接收到请求后,会准备好接收数据。

驱动程序会将数据写入SDA线,设备接收到数据后,会将数据保存到内部寄存器中。

需要注意的是,I2C总线的读写操作都是通过驱动程序来完成的。

应用程序只需要调用内核提供的API即可与I2C设备进行通信。

这样设计的好处是应用程序可以专注于自己的业务逻辑,而不需要关心底层的通信细节。

同时,这也使得应用程序与具体的I2C设备无关,具有更好的可移植性和扩展性。

linux下i2c驱动源码详解

linux下i2c驱动源码详解

Linux i2c驱动详细分析.首先在S3C2440平台的初始化函数中,主要是将开发平台的设备注册进了系统,也就是将device注册到了platform虚拟的总线上,并进行了一些初始化的工作,这里我们只关注I2C的部分。

static void __init smdk2440_machine_init(void){s3c24xx_fb_set_platdata(&smdk2440_fb_info);s3c_i2c0_set_platdata(NULL);platform_add_devices(smdk2440_devices,ARRAY_SIZE(smdk2440_devices));smdk_machine_init();}s3c_i2c0_set_platdata()函数将S3C2440上的I2C控制器进行了一些初始化,但是并没有写入硬件寄存器,仅仅是保存在了s3c2410_platform_i2c结构体中。

void __init s3c_i2c0_set_platdata(struct s3c2410_platform_i2c *pd){struct s3c2410_platform_i2c *npd;if (!pd)pd = &default_i2c_data0;npd = kmemdup(pd, sizeof(struct s3c2410_platform_i2c),GFP_KERNEL);if (!npd)printk(KERN_ERR "%s: no memory for platform data\n",__func__);else if (!npd->cfg_gpio)npd->cfg_gpio = s3c_i2c0_cfg_gpio;/* s3c_i2c0_cfg_gpio为 配置I2C控制器GPIO函数指针 */ s3c_device_i2c0.dev.platform_data = npd;/*最后将struct device 中的platform_data指针直指向了初始化后的 s3c2410_platform_i2c结构体 */}函数s3c_i2c0_cfg_gpio()很简单,实际上就是配置GPIO为I2C的工作模式void s3c_i2c0_cfg_gpio(struct platform_device *dev){s3c2410_gpio_cfgpin(S3C2410_GPE(15), S3C2410_GPE15_IICSDA);s3c2410_gpio_cfgpin(S3C2410_GPE(14), S3C2410_GPE14_IICSCL); }s3c_i2c0_set_platdata(struct s3c2410_platform_i2c *pd)函数实际上就是把初始化数据段中的default_i2c_data0结构体复制过来,然后对GPIO进行配置的函数指针进行了初始化。

linux i2c注册流程 -回复

linux i2c注册流程 -回复

linux i2c注册流程-回复Linux I2C 注册流程I2C (Inter-Integrated Circuit) 是一种常用于在嵌入式系统中连接多个设备的串行通信协议。

在Linux 操作系统中,内核提供了I2C Bus 管理子系统,允许开发人员通过I2C 接口与各种外部设备进行通信。

本篇文章将详细介绍Linux I2C 注册流程,为读者提供一步一步的指导。

1. 硬件准备首先,我们需要确保硬件上已经连接了I2C 设备。

通常,I2C 总线由一个主设备(通常是嵌入式系统中的MCU 或CPU),以及一个或多个从设备组成。

这些设备通过I2C 数据线(SDA)和时钟线(SCL)相连。

2. Linux 内核配置接下来,我们需要确保Linux 内核已经正确配置了I2C 支持。

可以通过检查内核配置文件(通常是`.config` 文件)来确认是否启用了I2C 支持。

需要确保以下配置项被设置为`y` 或`m`:CONFIG_I2C=yCONFIG_I2C_CHARDEV=y3. I2C 驱动程序加载在Linux 系统中,I2C 驱动程序被视为内核模块。

因此,在执行I2C 设备注册之前,需要加载适当的I2C 驱动程序。

可以使用`modprobe` 命令加载驱动程序,如下所示:modprobe i2c-dev4. 查找I2C 地址和设备ID每个I2C 设备都有一个独立的7 位地址,用于在总线上进行寻址。

在注册I2C 设备之前,我们需要确定要使用的I2C 地址。

可以通过执行以下命令来列出连接到I2C 总线的所有设备及其地址:i2cdetect -li2cdetect -y <i2c_bus_number>5. 创建I2C 客户端设备结构体在Linux 内核中,I2C 设备通常通过struct i2c_client 结构表示。

该结构体用于描述I2C 设备的各种属性和特征。

创建一个新的I2C 客户端结构体,并通过填充必要的字段来初始化它。

linux i2c协议类型 -回复

linux i2c协议类型 -回复

linux i2c协议类型-回复Linux I2C协议类型I2C(Inter-Integrated Circuit)是一种串行通信协议,用于连接微控制器和外围设备。

在Linux操作系统中,支持多种I2C协议类型。

本文将详细介绍Linux中常用的I2C协议类型,并一步一步回答相关问题。

第一步:了解I2C协议I2C是一种全双工的串行通信协议,使用两根线进行数据传输:SDA(Serial Data Line)和SCL(Serial Clock Line)。

SDA线用于传输数据,SCL线用于同步时钟。

I2C协议支持多主机和多从机的连接,并能够在不同电压和数据速率下工作。

第二步:设置I2C驱动在Linux系统中,需要配置I2C驱动,以便与外围设备进行通信。

首先,确保内核已经编译了适当的I2C驱动。

然后,加载I2C核心模块,例如i2c-dev模块。

可以使用以下命令加载该模块:sudo modprobe i2c-dev第三步:查看I2C适配器在Linux系统中,I2C适配器负责实际的物理层传输。

可以使用`i2cdetect`工具来查看可用的I2C适配器。

例如,使用以下命令可以列出所有的I2C 适配器:i2cdetect -l该命令将显示适配器号、适配器类型和所使用的总线号。

第四步:选择合适的I2C协议类型在Linux系统中,支持多种I2C协议类型,每种类型有不同的驱动和功能。

以下是一些常见的I2C协议类型:1. SMBus(System Management Bus)是一种基于I2C的通信协议,用于管理系统中的各种设备。

SMBus提供了一组标准的功能和命令,例如读取和写入数据、查询设备ID等。

大多数传感器和监控设备都支持SMBus协议。

2. I2C-ALGOBIT(I2C Algorithm Bit-Banging)是一种软件实现的I2C 协议类型。

它不依赖于硬件特性,可以在任何GPIO引脚上使用。

ALGOBIT允许用户定义I2C协议的时序和速率,但相比硬件实现的I2C,其性能较低。

Linux下I2C设备驱动开发和实现

Linux下I2C设备驱动开发和实现

Linux下I2C设备驱动开发和实现2008-05-16嵌入式在线收藏|打印I2C总线具有结构简单使用方便的特点。

本文描述了Linux下I2C驱动的结构,并在此基础上给出了I2C设备驱动和应用的实现。

1引言I2C(Inter-Integrated Circuit)总线是一种由PHILIPS公司开发的两线式串行总线,用于连接微控制器及其外围设备。

I2C总线最主要的优点是其简单性和有效性。

由于接口直接在组件之上,因此I2C总线占用的空间非常小,减少了电路板的空间和芯片管脚的数量,降低了互联成本。

I2C总线最初为音频和视频设备开发,现已应用于各种服务与管理场合,来实现配置或掌握组件的功能状态,如电源、系统风扇、系统温度等参数,增加了系统的安全性,方便了管理。

2I2C总线概述I2C总线是由数据线SDA和时钟SCL构成的串行总线,可发送和接收数据,每个器件都有一个惟一的地址识别。

I2C规程运用主/从双向通讯。

器件发送数据到总线上,则定义为发送器,器件接收数据则定义为接收器。

主器件和从器件都可以工作于接收和发送状态。

总线必须由主器件(通常为微控制器)控制,主器件产生串行时钟(SCL)控制总线的传输方向,并产生起始和停止条件。

SDA 线上的数据状态仅在SCL为低电平的期间才能改变,SCL为高电平的期间,SDA 状态的改变被用来表示起始和停止条件。

I2C总线在传送数据过程中共有三种类型信号,它们分别是:开始信号、结束信号和应答信号。

开始信号:SCL为高电平时,SDA由高电平向低电平跳变,开始传送数据。

结束信号:SCL为低电平时,SDA由低电平向高电平跳变,结束传送数据。

应答信号:接收数据的IC在接收到8bit数据后,向发送数据的IC发出特定的低电平脉冲,表示已收到数据。

CPU向受控单元发出一个信号后,等待受控单元发出一个应答信号,CPU接收到应答信号后,根据实际情况作出是否继续传递信号的判断。

若未收到应答信号,由判断为受控单元出现故障。

【免费下载】linux内核I2C总线驱动实现

【免费下载】linux内核I2C总线驱动实现
i2c-dev.c 并没有针对特定的设备而设计,只是提供了通用的 read()、write()和 ioctl()等 接口,应用层可以借用这些接口访问挂接在适配器上的 i2c 设备的存储空间或寄存器,并 控制 I2C 设备的工作方式。
需要特别注意的是:i2c-dev.c 的 read()、write()方法都只适合于如下方式的数据格式 (可查看内核相关源码)
1.2 从芯片的 I2C 的驱动
下面主要分析从芯片的 I2C 驱动(也有 2 种方式,第一个是利用内核提供的 i2c-dev.c 来构建,另一个是自己写) 主要分析第一种方式:
利用系统给我们提供的 i2c-dev.c 来实现一个 i2c 适配器的设备文件。然后通过在应用 层操作 i2c 适配器来控制 i2c 设备。
static const struct i2c_algorithm s3c24xx_i2c_algorithm = { .master_xfer = s3c24xx_i2c_xfer, .functionality = s3c24xx_i2c_func,
};
另外一方面需要确定为了实现对 AT24C02 及读写访问时序。 ● AT24C02 地址的确定
原理图上将 A2、A1、A0 都接地了,所以地址是 0x50。 ● AT24C02 任意地址字节写的时序
对全部高中资料试卷电气设备,在安装过程中以及安装结束后进行高中资料试卷调整试验;通电检查所有设备高中资料电试力卷保相护互装作置用调与试相技互术关,系电,力根保通据护过生高管产中线工资敷艺料设高试技中卷术资配0料不置试仅技卷可术要以是求解指,决机对吊组电顶在气层进设配行备置继进不电行规保空范护载高高与中中带资资负料料荷试试下卷卷高问总中题体资,配料而置试且时卷可,调保需控障要试各在验类最;管大对路限设习度备题内进到来行位确调。保整在机使管组其路高在敷中正设资常过料工程试况中卷下,安与要全过加,度强并工看且作护尽下关可都于能可管地以路缩正高小常中故工资障作料高;试中对卷资于连料继接试电管卷保口破护处坏进理范行高围整中,核资或对料者定试对值卷某,弯些审扁异核度常与固高校定中对盒资图位料纸置试,.卷保编工护写况层复进防杂行腐设自跨备动接与处地装理线置,弯高尤曲中其半资要径料避标试免高卷错等调误,试高要方中求案资技,料术编试交写5、卷底重电保。要气护管设设装线备备置敷4高、调动设中电试作技资气高,术料课中并中3试、件资且包卷管中料拒含试路调试绝线验敷试卷动槽方设技作、案技术,管以术来架及避等系免多统不项启必方动要式方高,案中为;资解对料决整试高套卷中启突语动然文过停电程机气中。课高因件中此中资,管料电壁试力薄卷高、电中接气资口设料不备试严进卷等行保问调护题试装,工置合作调理并试利且技用进术管行,线过要敷关求设运电技行力术高保。中护线资装缆料置敷试做设卷到原技准则术确:指灵在导活分。。线对对盒于于处调差,试动当过保不程护同中装电高置压中高回资中路料资交试料叉卷试时技卷,术调应问试采题技用,术金作是属为指隔调发板试电进人机行员一隔,变开需压处要器理在组;事在同前发一掌生线握内槽图部内纸故,资障强料时电、,回设需路备要须制进同造行时厂外切家部断出电习具源题高高电中中源资资,料料线试试缆卷卷敷试切设验除完报从毕告而,与采要相用进关高行技中检术资查资料和料试检,卷测并主处且要理了保。解护现装场置设。备高中资料试卷布置情况与有关高中资料试卷电气系统接线等情况,然后根据规范与规程规定,制定设备调试高中资料试卷方案。
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

RTC 实时时钟驱动-------I2C软件模拟通信内核版本: linux-2.4.21文档设计: 侯辉华版本: 1.01时间: 2007/06/10内容简介: 介绍接在I2C总线上RTC实时时钟设备的驱动, 使用软件模拟的方法完成I2C的通信; 介绍了Linux下的时钟系统, 以及I2C的层次结构.目录索引:一.Linux下的时钟系统简介.二.Linux对时间的表示.三.Linux时钟中断的初始化及处理.四.RTC设备驱动程序.五.I2C总线读写.六. Linux下的I2C驱动层次结构..一. Linux下的时钟系统简介实际上,linux系统有两个时钟:一个是由主板电池驱动的“Real Time Clock”也叫做RTC 或者叫CMOS时钟,硬件时钟。

当操作系统关机的时候,用这个来记录时间,但是对于运行的系统是不用这个时间的。

另一个时间是“System clock”也叫内核时钟或者软件时钟,是由软件根据时间中断来进行计数的,内核时钟在系统关机的情况下是不存在的,所以,当操作系统启动的时候,内核时钟是要读取RTC时间来进行时间同步.linux的内核时间实际上是记录从1970年1月1日距离现在的秒数,并且以GMT(格林尼治时间)(或者叫UTC- Coordinated Universal Time)为标准, UTC是不随着DST(夏令时)变换,需要有变化的是由应用程序自身来完成时间的转换。

二. Linux对时间的表示通常,操作系统可以使用三种方法来表示系统的当前时间与日期:①最简单的一种方法就是直接用一个64位的计数器来对时钟滴答进行计数。

②第二种方法就是用一个32位计数器来对秒进行计数,同时还用一个32位的辅助计数器对时钟滴答计数,之子累积到一秒为止。

因为2的32次方超过136年,因此这种方法直至22世纪都可以让系统工作得很好。

③第三种方法也是按时钟滴答进行计数,但是是相对于系统启动以来的滴答次数,而不是相对于相对于某个确定的外部时刻;当读外部后备时钟(如RTC)或用户输入实际时间时,根据当前的滴答次数计算系统当前时间。

Linux通常都采用第三种方法来维护系统的时间与日期, 通过时钟点滴进行计时的基础原理, 可以参看下面介绍的参考文档, 主要原理是通过硬件的中断累积来计时, 但必要要设置硬件中断一次所须的时间, 一般具体的不同的芯片都不同, EP9302系统具体设置如下:1.EP93xx系列芯片有四个Timer计时器, 使用的是Timer1, 与具体芯片相关的内容在如下两个文件:linux-2.4.21\arch\arm\mach-ep93xx\time.c,linux-2.4.21\arch\arm\mach-ep93xx\time.h2.针对整个Arm体系的时钟相关文件为:linux-2.4.21\arch\arm\kernel\time.c时钟中断计时的主要相关函数为如下两个:1.ep93xx_gettimeoffset()的作用就是返回距最近一次时钟中断发生后, Timer已经累积的时间(但还未满足引发一次时钟中断), 这个时间值单位为微秒, 获取当前时间时, 就是累计已经发生的Timer中断次数所经历的时间, 然后加上这个即将要发生中断所过去的时间, 这样取得当前时间的精度是相当高的. TCH的含义是指一次时钟中断要经过多少个Timer时钟周期, TIMER1LOAD寄存器设置的就是这个值, ep93xx的计时器Timer1会将这个值一直递减直至0, 如此就引发一次时钟中断, 然后又重LATCH重头开始递减.3.xtime记载的即为系统自开机以来的当前时间, 单位为秒, 精确度为微秒. 因此在开机时必须从RTC当中取得真实的时间来赋此初值, EP93xx系列直接初始此值为其自身所带RTC模块的时间值, RTCDR寄存器是EP93xx所自带的RTC模块的寄存器, 其值单位为秒,基准为相对1970年, 此处即为我们要改动的地方.将其值从i2c的RTC实时芯片中取回赋给它.static unsigned long ep93xx_gettimeoffset(void){unsigned long hwticks;hwticks = LATCH - (inl(TIMER1V ALUE) & 0xffff);return ((hwticks * tick) / LA TCH);}void __init ep93xx_setup_timer(void) //初始化Timer,设定时钟中断周期…{gettimeoffset = ep93xx_gettimeoffset;outl(0, TIMER1CONTROL);outl(LA TCH - 1, TIMER1LOAD); //设定Timer经多少个Timer时钟周期后产生中断…outl(0xc8, TIMER1CONTROL);_sec = inl(RTCDR); //从ep93xx内部RTC模块读取时间,后将重新从cmos读.}以下详细介绍一下时钟点滴计时的几个基本参数, 以下定义的出处, 除非特别指出, 一般是位于各自不同的平台的文件夹下定义:linux-2.4.21\include\asm-arm\arch-ep93xxlinux-2.4.21\arch\arm\mach-ep93xx\1.时钟周期(clock cycle)的频率:计时器Timer晶体振荡器在1秒时间内产生的时钟脉冲个数就是时钟周期的频率, 要注意这个Timer的时钟周期频率要与时钟中断的频率区别开来, Linux用宏CLOCK_TICK_RATE来表示计时器的输入时钟脉冲的频率(此值在EP93xx上是508KHZ),该宏定义在timex.h头文件中:#define CLOCK_TICK_RATE 508000 /* Underlying HZ */2.时钟中断(clock tick):我们知道当计数器减到0值时,它就在IRQ0上产生一次时钟中断,也即一次时钟中断, 计数器的初始值决定了要过多少时钟周期才产生一次时钟中断,因此也就决定了一次时钟滴答的时间间隔长度. 在EP93xx系统中, Timer1的TIMER1LOAD 的值决定过多少时钟周期后产生一次时钟中断.3.时钟中断的频率(HZ):也即1秒时间内Timer所产生的时钟中断次数。

确定了时钟中断的频率值后也就可以确定Timer的计数器初值。

Linux内核用宏HZ来表示时钟中断的频率,而且在不同的平台上HZ有不同的定义值。

对于SPARC、MIPS、ARM和i386等平台HZ的值都是100。

该宏在ARM平台上的定义如下(param.h):#ifndef HZ#define HZ 100#endif据HZ值,可知每隔(1000ms/HZ)=10ms发生一次时钟中断.4.时钟中断的时间间隔:Linux用全局变量tick来表示时钟中断的时间间隔长度,其实定义了HZ之后, 即决定了此间隔值, tick变量的单位是微妙(μs), 该变量定义在kernel/timer.c文件中,如下:long tick = (1000000 + HZ/2) / HZ; /* timer interrupt period */.5.宏LATCH:Linux用宏LATCH来定义要设置到Timer中的值,它表示TImer将没隔多少个时钟周期产生一次时钟中断。

显然LATCH应该由下列公式计算:LATCH=(1秒之内的Timer时钟周期个数)÷(1秒之内的时钟中断次数)=(CLOCK_TICK_RATE)÷(HZ).三. Linux时钟中断的初始化及处理以下着重描述一下时钟中断时与RTC相关的修改:文件:linux-2.4.21\arch\arm\kernel\time.c描述:时钟初始化化, 在start_kernel()当中调用.void __init time_init(void){_usec = 0;_sec = 0;setup_timer();}文件:linux-2.4.21\arch\arm\mach-ep93xx\time.h描述:安装时钟中断服务程序,从RTC更新初始化系统时钟, 在time_init()当中调用./** Set up timer interrupt, and return the current time in seconds.*/static inline void setup_timer(void){ep93xx_setup_timer();//houhh 20070713.../////////_sec = get_cmos_time(); //从RTC设备中读取当前时间…set_rtc = set_cmos_time; //初始化更新系统时间到RTC的函数, 在do_set_rtc()中调用…/////////timer_irq.handler = ep93xx_timer_interrupt; //时钟中断服务程序…setup_arm_irq(IRQ_TIMER1, &timer_irq); //安装时钟中断处理…}文件:linux-2.4.21\arch\arm\mach-ep93xx\time.h描述:读写I2C的RTC设备,软件模拟方式, 引自i2c-ep93xx.c, 将在下面详细介绍. extern uchar pcf8563_readdata(uchar address);extern int pcf8563_writedata(uchar address, uchar mdata);#define CMOS_READ(addr) pcf8563_readdata(addr)#define CMOS_WRITE(data, addr) pcf8563_writedata(addr, data)文件:linux-2.4.21\arch\arm\mach-ep93xx\time.h描述:设置及读写RTC, 主要通过宏CMOS_READ/ CMOS_WRITE完成功能.unsigned long get_cmos_time(void)static int set_cmos_time(unsigned long nowtime)文件: linux-2.4.21\arch\arm\mach-ep93xx\time.h描述:时钟中断服务程序,并检测更新系统时钟到RTC, 在setup_timer()当中调用./** IRQ handler for the timer*/static void ep93xx_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs){outl( 1, TIMER1CLEAR );do_leds();do_set_rtc(); //houhh 20070713, 检测是否要更新系统当前时间到cmos rtc设备...do_timer(regs);do_profile(regs);}一般认为时钟中断计时的精度较高,所以在时钟中断服务程序中会每隔11分钟(660秒)就检测一次是否须要将此时的系统时间写回到RTC当中, 所以在ep93xx的时钟中断服务程序中,须要加上do_set_rtc(),关于这个函数具体的功能请具体参见源码.四. RTC设备驱动程序主要指出RTC设备及相关操作,这一块相当简单,RTC时钟处理成一简单字符设备.基础文件结构:static struct file_operations pcf8563_fops = {owner: THIS_MODULE,ioctl: pcf8563_ioctl,open: pcf8563_open,release: pcf8563_release,};文件: linux-2.4.21\drivers\char\pcf8563_rtc.c描述: RTC设备的文件结构,指文件打开及释放操作,其实最核心的还是ioctl,这里可以进行时间读取以及时间设置操作, 具体使用示例可以参考rtctest.c示例文件.文件: linux-2.4.21\drivers\char\pcf8563_rtc.c描述:时间设置与读取.get_rtc_time(struct rtc_time *tm);Set_rtc_time(struct rtc_time *tm);文件: linux-2.4.21\drivers\char\ pcf8563_rtc.c描述: I2C读写, 这两个函数是从文件i2c-ep93xx.c中引用的,定义成宏.extern int pcf8563_writedata(uchar address, uchar mdata);extern uchar pcf8563_readdata(uchar address);#define rtc_reg_read(x) pcf8563_readdata(x)#define rtc_reg_write(x,y) pcf8563_writedata(x, y)文件: linux-2.4.21\drivers\char\ pcf8563_rtc.c描述:模块初始化,负责注册/注消RTC字符设备.int __init pcf8563_init(void)void __exit pcf8563_exit(void)五. I2C总线读写i2c总线读写方式为软件模拟方式,因为ep93xx没有相关的i2c总线控制器,因此只能通过软件方式来模拟I2C总线的读写, 在调试过程中遇到如下问题,注意如下即可:1.注意延时的时间,I2C的开始条件与读写时都须要一定的延时,根据主设备(即ep93xx cpu)的运行速度,此延时必须是一个稳定的时间,通常采取读取特定外设i/o以达此目的. 2.注意在改变I2C的SDA数据线状态时,必须是在SCL时钟线为低的时候,因为根据开始与结束条件的要求,开始与结束条件是在SCL时钟线高的时候SDA拉高或者拉低,所以如果在传送数据时,SCL为高,则会被当成开始或结束条件,通信失败.3.在读写I2C总线时,每传送或接收一BYTE数据,必须要进行回应.写回应:主设备写完八位数据后,在第九个周期等待从设备来拉低SDA作为回应,因此须先将SDA在第八周期SCL低时拉高,之后拉高SCL等从设备回应,等到从设备回应后拉低SCL,第九周期结束,一个BYTE传送完成.读回应:主设备读从设备八位数据后,也应该在第九周期进行回应,分如下两种情况: 连续读n个字节时,前n-1个字节以拉低作回应,第n个字节则为拉高SDA回应,因此如若是每次只读一个字节,则回应为拉高.4.操作SDA/SCL PIN时,注意在读SDA时,将其设置成输入状态.文件: linux-2.4.21\drivers\i2c\ i2c-ep93xx.c描述:SDA/SCL PIN脚定义, 为EP93xx的GPIO口G口的第0, 1位.#define I2C_SDA_PORT GPIO_PGDR#define I2C_SDA_DIR GPIO_PGDDR#define I2C_SDA_MASK 0x2 //EEDAT...#define I2C_SCL_PORT GPIO_PGDR#define I2C_SCL_DIR GPIO_PGDDR#define I2C_SCL_MASK 0x1 //EECLK...文件: linux-2.4.21\drivers\i2c\ i2c-ep93xx.c描述:SDA/SCL输入输出.static void bit_ep93xx_setscl(void* data, int state){unsigned long flags;save_flags(flags);outl(inl(I2C_SCL_DIR) | I2C_SCL_MASK, I2C_SCL_DIR); // tristate pinif (state){cli();outl(inl(I2C_SCL_PORT) | I2C_SCL_MASK, I2C_SCL_PORT); // drive pin}else{cli();outl(inl(I2C_SCL_PORT) & ~I2C_SCL_MASK, I2C_SCL_PORT); // drive pin}restore_flags(flags);}//=========================================================================== /// write SCL pin//=========================================================================== static void bit_ep93xx_setsda(void* data, int state){unsigned long flags;save_flags(flags);outl(inl(I2C_SDA_DIR) | I2C_SDA_MASK, I2C_SDA_DIR); // output...if (state){cli();outl(inl(I2C_SDA_PORT) | I2C_SDA_MASK, I2C_SDA_PORT); // drive pin }else{cli();outl(inl(I2C_SDA_PORT) & ~I2C_SDA_MASK, I2C_SDA_PORT); // drive pin }restore_flags(flags);}具体有关I2C总线软件模拟通信部分,参考源码.六. Linux下的I2C驱动层次结构.Linux最大的特点,就是将一系列的驱动中共有的东西抽象出来,大大提高代码的共享与利用率,这样使具体硬件设备的驱动作者无须关注驱动中共性的部分,I2C驱动同样如此,这里我不打算详细的分析I2C的层次结构,只是简单的陪析一下其大略的层次, 并指出几个不易理解的地方。

相关文档
最新文档