Linux I2C驱动完全分析(一)

发布者:李国永最新更新时间:2021-11-22 来源: eefocus关键字:Linux  I2C  驱动 手机看文章 扫描二维码
随时随地手机看文章

其实老早就想写这个I2C的了,期间有各种各样的事情给耽误了。借着五一放假的时间把这个写出来,供同志们参考。以后会花一些时间深入研究下内核,虽然以前对内核也有所了解,但是还不系统。I2C的硬件结构并不复杂,一个适配器加几个设备而已。Linux下驱动的体系结构看着挺复杂,实际也是比较简单的。在本文中我还是使用实际的例子,结合硬件和软件两个方面来介绍。希望能给初学的同志们一些帮助,另外抛砖引玉,希望高手能给一些指点。话不多说,开整!~


本文用到的一些资源:


   1. Source Insight软件


   2. mini2440原理图。 下载地址http://wenku.baidu.com/view/0521ab8da0116c175f0e48fe.html


   3. S3C2440 datasheet


   4. AT24C08 datasheet


   5. Bq27200 datasheet


   6. kernel 2.6.31中的At24.c ,Bq27x00_battery.c和i2c-s3c2410.c


   7. mini2440的板文件mach-mini2440.c


   8. 参考资料:《linux设备驱动开发详解(第2版)》 by 宋宝华


本文的结构:


第一部分:At24C08驱动


   1. mini2440中at24c08的电气连接


   2. Linux中I2C驱动框架分析


   3. I2C总线驱动代码分析


   4. at24c08驱动代码分析


第二部分:Bq27200驱动


   1. Bq27200的典型应用电路


   2. 主要分析一下ba27x00的代码,对比at24c08来加深理解。


---------------------我是分割线----------------------


第一部分


1. mini2440中at24c08的电气连接及其板文件


如下图。

                                

24C08的I2C接口是与2440的IICSCL/IICSDA直接相连的。在2440内部集成了一个I2C控制器,可以通过寄存器来控制它。先来和这四个寄存器混个脸熟吧,后面分析时还会经常用到这四个寄存器。

在mini2440的板文件中可以找到关于at24c08的内容,如下:


/*

 * I2C devices

 */

static struct at24_platform_data at24c08 = {

.byte_len = SZ_8K / 8,

.page_size = 16,

};


static struct i2c_board_info mini2440_i2c_devs[] __initdata = {

{

I2C_BOARD_INFO("24c08", 0x50),

.platform_data = &at24c08,

},

};


static void __init mini2440_init(void)

{

   ... ...

   i2c_register_board_info(0, mini2440_i2c_devs,

ARRAY_SIZE(mini2440_i2c_devs));

   ... ...

}


可以看出,在mini2440的init函数中注册了一个i2c的设备,这个设备我们使用了一个结构体i2c_board_info来描述。这个结构体定义在i2c.h文件中。如下:


struct i2c_board_info {

char type[I2C_NAME_SIZE];

unsigned short flags;

unsigned short addr;

void *platform_data;

struct dev_archdata *archdata;

int irq;

};


其中的platform_data又指向一个at24_platform_data结构体。


以上只是at24c08的部分,在板文件中还可以看到关于2440内部i2c控制器的部分,如下:


static struct platform_device *mini2440_devices[] __initdata = {

 ... ...

 &s3c_device_i2c0,

 ... ...

};


static void __init mini2440_init(void)

{

 ... ...

 platform_add_devices(mini2440_devices, ARRAY_SIZE(mini2440_devices));

 ... ...

}


其中s2c_device_i2c0定义在arch/arm/plat-s3c/Dev-i2c0.c中(在同一目录下还可以看到很多Dev-开头的c文件,都是2440内部集成的各种设备),仔细看下面的代码再对比2440的datasheet就可以很清楚的知道:


   * 控制器的IO起始地址为S3C_PA_IIC =0x54000000,大小是4K,中断号是43 = IRQ_IIC      S3C2410_IRQ(27)


* 控制器名是"s3c2410-i2c"



2. Linux中I2C驱动框架分析


这部分是本文的重点部分。根据上面的电气连接关系我们可以看出,我们要想操作24c08,必须要做两方面的驱动。


第一方面: 2440中I2C控制器的驱动,有了这部分驱动,我们才可以操作控制器来产生I2C的时序信号,来发送数据和接收数据。


第二方面: 24C08的驱动,有了这部分驱动,才能使用控制器正确操作芯片,来读取和存放数据。


在Linux系统中,对上边第一方面的实现叫做I2C总线驱动,对第二方面的实现叫做I2C设备驱动。一般来说,如果CPU中集成了I2C控制器并且Linux内核支持这个CPU,那么总线驱动方面就不用我们操心了,内核已经做好了。但如果CPU中没有I2C控制器,而是外接的话,那么就要我们自己实现总线驱动了。对于设备驱动来说,一般常用的驱动也都包含在内核中了,如果我们用了一个内核中没有的芯片,那么就要自己来写了。


Linux中I2C体系结构如下图所示(图片来源于网络)。图中用分割线分成了三个层次:用户空间(也就是应用程序),内核(也就是驱动部分)和硬件(也就是实际物理设备,这里就是2440中的i2c控制器和at24c08)。这个够清晰了吧?我们现在就是要研究中间那一层。

 由上图我们还可以看出哪些信息呢?


1). 可以看到几个重要的组成部分,它们是:Driver,Client,i2c-dev,i2c-core,Algorithm,Adapter。这几个部分在内核中都有相应的数据结构,定义在i2c.h文件中,尽量避免粘贴打断代码来凑数,就不贴出来了。简要概括一下每个结构体的意义。


Driver --> struct i2c_driver 


这个结构体对应了驱动方法,重要成员函数有probe,remove,suspend,resume。


还包括一个重要的数据结构: struct i2c_device_id *id_table; 如果驱动可以支持好几个设备,那么这里面就要包含这些设备的ID


Client --> struct i2c_client


应用程序是选择性失明的,它只能看到抽象的设备文件,其他部分都是看不见的。图中只有Client与应用程序有联系,所以我们可以大胆得出结论:这个Client是对应于真实的物理设备,在本文就是at24c08。 所以很显然这个结构体中的内容应该是描述设备的。包含了芯片地址,设备名称,设备使用的中断号,设备所依附的控制器,设备所依附的驱动等内容。


Algorithm -->struct i2c_algorithm


Algorithm就是算法的意思。在这个结构体中定义了一套控制器使用的通信方法。其中关键函数是master_xfer()。我们实际工作中的重要一点就是要实现这个函数。


int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs, int num);

Adapter --> struct i2c_adapter


这个结构体对应一个控制器。其中包含了控制器名称,algorithm数据,控制器设备等。


 2). 可以看出,i2c-core起到了关键的承上启下的作用。事实上也是这样,我们将从这里展开来分析。源代码位于drivers/i2c/i2c-core.c中。在这个文件中可以看到几个重要的函数。


*增加/删除i2c控制器的函数

int i2c_add_adapter(struct i2c_adapter *adapter)

int i2c_del_adapter(struct i2c_adapter *adap)


*增加/删除设备驱动的函数

int i2c_register_driver(struct module *owner, struct i2c_driver *driver)

void i2c_del_driver(struct i2c_driver *driver)


 *增加/删除i2c设备的函数

struct i2c_client *

i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info);

void i2c_unregister_device(struct i2c_client *client)


注:在2.6.30版本之前使用的是i2c_attach_client()和i2c_detach_client()函数。之后attach被merge到了i2c_new_device中,而detach直接被unresister取代。实际上这两个函数内部都是调用了device_register()和device_unregister()。源码如下:


 *I2C传输、发送和接收函数

int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num);


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);


其中send和receive分别都调用了transfer函数,而transfer也不是直接和硬件交互,而是调用algorithm中的master_xfer()函数,所以我们要想进行数据传输,必须自己来实现这个master_xfer()函数,这是总线驱动开发的重点之一。下面以read()系统调用的流程来简单梳理一下:

关键字:Linux  I2C  驱动 引用地址:Linux I2C驱动完全分析(一)

上一篇:mini2440-i2c驱动分析
下一篇:mini2440 linuxi2c驱动

推荐阅读最新更新时间:2024-11-06 12:07

基于LabVIEW的普通数据采集卡驱动研究
引 言 虚拟仪器技术是20世纪90年代发展起来的一种新技术,融计算机和总线技术、微电子技术、测量技术于一身,它是对传统仪器的重大突破,是计算机技术与仪器技术相结合的产物。它利用计算机系统的强大功能,结合相应的硬件,大大突破传统仪器在数据处理、显示和存储以及系统维护和扩展等方面的限制。虚拟仪器的众多优点使其得到了广泛的应用,然而当今著名的虚拟仪器开发软件(如LabVIEW等)只能支持NI公司生产的数据采集卡,而不能直接驱动普通的数据采集卡。本文重点研究了在虚拟仪器开发软件平台LabVIEW下使用普通数据采集卡的方法。 1 虚拟仪器开发平台LabVIEW与数据采集卡的连接 LabVIEW是美国NI公司推出的一种基于G语言(G
[测试测量]
基于LabVIEW的普通数据采集卡<font color='red'>驱动</font>研究
linux 2.6.32 在arm9(s3c2440)平台的移植2 -- Kconfig和Makefile(2)linux
Linux内核源码树的每个目录下都有一个Kconfig和Makefile, 分布到各目录的Kconfig构成了一个分布式的内核配置数据库, 每个Kconfig分别描述了所属目录源文档相关的内核配置菜单. 在linux目录执行内核配置make menuconfig时, 从Kconfig中读出菜单, 用户选择后保存到linux-2.6/.config的内核配置文档中. 在内核编译时, 主Makefile调用这个.config, Kconfig就是对应着内核的每级配置菜单. 添加新的驱动时需要修改有两种( 1如果添加的只是文件, 则只需修改当前层Kconfig和Makefile文件; 2如果添加的是目录, 则需修改当前层和新添目
[单片机]
LED 驱动器减少低成本灯具的电磁干扰
Diodes公司推出AL8807降压型开关模式LED驱动器。其开关频率高达1MHz,具有严格控制的开关上升和下降时间,专为减少低成本MR16 LED灯具的电磁干扰 (EMI) 而设计。为达到高输出功率的要求,该驱动器可以在6V至30V的输入电压间运行,为多达八个串联的LED提供高达1A的恒定电流。 该驱动器的电流精度为±5%,能满足大多数低电压照明应用的要求,并能改善3W LED照明系统中灯与灯之间的亮度搭配。该驱动器的效率高达96%,其输出电流的设置简单,只需使用一个外置电阻器。 通过改变驱动器控制输入端上的直流电压或脉冲宽度调制 (PWM) 信号,用户就能调节LED亮度,也能避免LED过流。AL8807的P
[电源管理]
Linux刷机总结(S5PV210)
刷机前要破环iNand,才能从SD卡启动 u-boot下破坏iNand movi write u-boot 0x30000000 SD卡下载.bin失败: 管理员身份运行 WIN10_Andriod驱动安装失败: https://blog.csdn.net/qq_24046029/article/details/95728564 LinuxQT刷Andriod总结: 开机,使用SecuteCRT链接开发板 进入linux系统控制台,执行如下指令: busybox dd if=/dev/zero of=/dev/mmcblk0 bs=512 seek=1 count=1 conv=sync 刷卡工具去制作启动SD卡,
[单片机]
<font color='red'>Linux</font>刷机总结(S5PV210)
LTM8042/LTM8042-1 开关稳压器LED驱动
凌力尔特公司 (Linear Technology Corporation) 推出 DC/DC 微型模块 (uModule®) LED 驱动器 LTM8042 和 LTM8042-1,这两款器件面向由多达 8 个白光 LED 或 9 个红光 LED 组成的 LED 串。每款器件都能在 3V 至 30V 的输入电压范围内以升压、降压或降压-升压模式工作,采用 9mm x 15mm x 2.8mm LGA (盘网格阵列) 封装,是完整的 LED 驱动器解决方案。LTM8042 和 LTM8042-1 拥有 0.5% 的输出电流电压调节准确度,并能承受高达 40V 的输入瞬态电压。LTM8042 提供高达 1A 的电流,而 LTM80
[电源管理]
采用混合信号高电压单片机实现LED降压-升压驱动电路
   LED背景知识   近年来,LED逐渐成为一种可行的新兴光源,它们已经不再仅仅用作电子设备的“状态指示灯”。技术进步使得LED的发光效率通常可达白炽灯的三倍多,此外,LED还非常耐用,寿命超过上万小时。   针对照明应用的大功率LED要采用恒流源驱动,一些标准驱动电流常常用在不同LED生产商的产品中,其中,350mA和700 mA最为常见。根据串联结的类型和数量,LED两端的正向压降可能不同。许多生产厂商的大功率LED产品都在单个模块中集成了多个结。   驱动LED的一种简单方法是采用串联电阻来限制电流。线性稳压器或运算放大器也可连接成恒流配置。然而,此类线性方法无法在所需要的功率水平下提供足够的效率。   开关电源(
[电源管理]
采用混合信号高电压单片机实现LED降压-升压<font color='red'>驱动</font>电路
LED驱动器与升压转换器的编程设计实现
  升压DC-DC开关转换器的工作频率是650 kHz/1300 kHz。分别采用1.8 V至5.5 V单电源或2.5 V至5.5 V单电源供电时, 升压转换器ADP1612和ADP1613能够以高达20 V的电压供应超过150 mA的电流。通过将一个1.4 A/2.0 A、0.13 Ω 功率开关与一个电流模式脉宽调制调节器集成在一起,其输出随输入电压、负载电流和温度变化仅改变不到 1%。工作频率可通过引脚选择,并通过优化实现高效率或最小外部元件尺寸:650kHz 时,其效率可达到 90%;1.3 MHz 时,其电路能够以最小空间实现,因而非常适合便携式设备和液晶显示器中的空间受限环境。   可调软启动电路防止发生浪涌电流——确
[电源管理]
黑客将任天堂Switch变成了一个Linux平板电脑
    许多人都可以想到一个主要原因来破解一台游戏主机。很明显,你可以在破解主机上玩盗版游戏。   这就是为什么现代的游戏机很难被破解的原因,因为盗版游戏涉及到成千上万的收入。   但是有些人只是想在他们自己的硬件上运行他们选择的软件。对于那些人来说,Linux on the Switch是一个巨大的成就。   几个星期前,fail0verflow黑客集体在Twitter上推出了一个启动Linux的Nintendo Switch的提议。   现在终于有了结果,fail0verflow有一个完整版的Linux发行版的视频,它运行在被黑的Switch上,支持触摸屏,一个完全可操作的web浏览器,甚至还有一个GPU驱动的演示应用程序。
[手机便携]
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved