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和detach_client字段为函数指针,这两个函数在I2C设备注册的时候会自动调用,需要自己实现这两个函数,后面将详细讲述。

2.   i2c_client 结构体对象

上面定义的i2c_driver对象,抽象为一个i2c的驱动模型,提供对i2C设备的探测和注销方法,而i2c_client结构体则是代表着一个具体的i2c设备,该结构体有一个data指针,可以指向任何私有的设备数据,在复杂点的驱动中可能会用到。示例如下:

  1. struct tvp5158_obj{
  2. struct i2c_client client;
  3. int users; // how many users using the driver
  4. };
  5. struct tvp5158_obj* g_tvp5158_obj;

其中,users为示例,用户可以自己在tvp5158_obj这个结构体里面添加感兴趣的字段,但是i2c_client字段不可少。具体用法后面再详细讲。

3.   设备注册及探测功能

这一步很关键,按照标准的要求来写,则Linux系统会自动调用相关的代码去探测你的I2C设备,并且添加到系统的I2C设备列表中以供后面访问。

我们知道,每一个I2C设备芯片,都通过硬件连接设定好了该设备的I2C设备地址。因此,I2C设备的探测一般是靠设备地址来完成的。那么,首先要在驱动代码中声明你要探测的I2C设备地址列表,以及一个宏。示例如下:

  1. static unsigned short normal_i2c[] = {
  2. 0xbc >> 1,
  3. 0xbe >> 1,
  4. I2C_CLIENT_END
  5. };
  6. I2C_CLIENT_INSMOD;

normal_i2c 数组包含了你需要探测的I2C设备地址列表,并且必须以I2C_CLIENT_END作为结尾,注意,上述代码中的0xbc和0xbe是我在硬件上为我的tvp5158分配的地址,硬件上我支持通过跳线将该地址设置为 0xbc 或者 0xbe,所以把这两个地址均写入到探测列表中,让系统进行探测。如果你的I2C设备的地址是固定的,那么,这里可以只写你自己的I2C设备地址,注意必须向右移位1。

宏 I2C_CLIENT_INSMOD 的作用网上有许多文章进行了详细的讲解,这里我就不详细描述了,记得加上就行,我们重点关注实现。

下一步就应该编写第1步中的两个回调函数,一个用于注册设备,一个用于注销设备。探测函数示例如下:

  1. static int tvp5158_attach_adapter(struct i2c_adapter *adapter)
  2. {
  3. return i2c_probe(adapter, &addr_data, &tvp5158_detect_client);
  4. }

这个回调函数系统会自动调用,我们只需要按照上述代码形式写好就行,这里调用了系统的I2C设备探测函数,i2c_probe(),第三个参数为具体的设备探测回调函数,系统会在探测设备的时候调用这个函数,需要自己实现。示例如下:

  1. static int tvp5158_detect_client(struct i2c_adapter *adapter,int address,int kind)
  2. {
  3. struct tvp5158_obj *pObj;
  4. int err = 0;
  5. printk(KERN_INFO "I2C: tvp5158_detect_client at address %x ...\n", address);
  6. if( g_tvp5158_obj != NULL  ) {
  7. //already allocated,inc user count, and return the allocated handle
  8. g_tvp5158_obj->users++;
  9. return 0;
  10. }
  11. /* alloc obj */
  12. pObj = kmalloc(sizeof(struct tvp5158_obj), GFP_KERNEL);
  13. if (pObj==0){
  14. return -ENOMEM;
  15. }
  16. memset(pObj, 0, sizeof(struct tvp5158_obj));
  17. pObj->client.addr    = address;
  18. pObj->client.adapter = adapter;
  19. pObj->client.driver  = &tvp5158_i2c_driver;
  20. pObj->client.flags   = I2C_CLIENT_ALLOW_USE;
  21. pObj->users++;
  22. /* attach i2c client to sys i2c clients list */
  23. if((err = i2c_attach_client(&pObj->client))){
  24. printk( KERN_ERR "I2C: ERROR: i2c_attach_client fail! address=%x\n",address);
  25. return err;
  26. }
  27. // store the pObj
  28. g_tvp5158_obj = pObj;
  29. printk( KERN_ERR "I2C: i2c_attach_client ok! address=%x\n",address);
  30. return 0;
  31. }

到此为止,探测并且注册设备的代码已经完成,以后对该  I2C 设备的访问均可以通过 g_tvp5158_obj 这个全局的指针进行了。

4.    注销I2C设备 

同理,设备注销的回调函数也会自动被系统调用,只需要按照模板写好设备注销代码,示例如下:

  1. static int tvp5158_detach_client(struct i2c_client *client)
  2. {
  3. int err;
  4. if( ! client->adapter ){
  5. return -ENODEV;
  6. }
  7. if( (err = i2c_detach_client(client)) ) {
  8. printk( KERN_ERR "Client deregistration failed (address=%x), client not detached.\n", client->addr);
  9. return err;
  10. }
  11. client->adapter = NULL;
  12. if( g_tvp5158_obj ){
  13. kfree(g_tvp5158_obj);
  14. }
  15. return 0;
  16. }

到此为止,设备的注册和注销代码已经全部完成,下面要做的就是提供读写I2C设备的方法。

 5.   I2C设备的读写      

对I2C设备的读写,Linux系统提供了多种接口,可以在内核的 i2c.h 中找到,这里简单介绍其中的两种接口。

【接口一】:

  1. extern int i2c_master_send(struct i2c_client *,const char* ,int);
  2. extern int i2c_master_recv(struct i2c_client *,char* ,int);

第一个参数是 i2c_client 对象指针,第二个参数是要传输的数据buffer指针,第三个参数为buffer的大小。

【接口二】:

  1. extern int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msg, int num);

这个接口支持一次向I2C设备发送多个消息,每一个消息可以是读也可以是写,读或者写以及读写的目标地址(寄存器地址)均包含在msg消息参数里面。

这些接口仅仅是最底层的读写方法,关于具体怎么与I2C设备交互,比如具体怎么读芯片的某个特定寄存器的值,这需要看具体的芯片手册,每个I2C芯片都会有具体的I2C寄存器读写时序图。因此,为了在驱动中提供更好的访问接口,还需要根据具体的时序要求对这些读写函数进行进一步封装,这些内容将在后面的文章中讲述。

6.  模块初始化及其他

下一步就是整个模块的初始化代码和逆初始化代码,以及模块声明了。

  1. static int __init tvp5158_i2c_init(void)
  2. {
  3. g_tvp5158_obj = NULL;
  4. return i2c_add_driver(&tvp5158_i2c_driver);
  5. }
  6. static void __exit tvp5158_i2c_exit(void)
  7. {
  8. i2c_del_driver(&tvp5158_i2c_driver);
  9. }
  10. module_init(tvp5158_i2c_init);
  11. module_exit(tvp5158_i2c_exit);
  12. MODULE_DESCRIPTION("TVP5158 i2c driver");
  13. MODULE_AUTHOR("Lujun @hust");
  14. MODULE_LICENSE("GPL");

在初始化的代码里面,添加本模块的 i2c driver 对象,在逆初始化代码里面,删除本模块的 i2c driver 对象。

7.   总结

到此为止,算是从应用的角度把编写一个I2C的设备驱动代码讲完了,很多原理性的东西我都没有具体分析(其实我也了解的不深),以后会慢慢更深入地学习和了解,文中有什么讲述不正确的地方,欢迎留言或者来信lujun-hust@gmail.com交流。

读到最后,大家可能还有一个疑问,这个驱动写完了怎么在用户空间(应用层)去使用它呢?由于本文不想把代码弄得太多太复杂,怕提高理解的难度,所以就没有讲,其实要想在用户空间使用该I2C设备驱动,则还需要借助字符设备驱动来完成,即为这个I2C设备驱动封装一层字符设备驱动,这样,用户空间就可以通过对字符设备驱动的访问来访问I2C设备,这个方法我会在后面的文章中讲述。

手把手教你写Linux I2C设备驱动 tvp5158相关推荐

  1. 手把手教你写Linux I2C设备驱动

    手把手教你写Linux I2C设备驱动 标签:Linux 设备 驱动 详解 i2c 原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责任.http:/ ...

  2. linux探测i2c设备连接状态,手把手教你写Linux I2C设备驱动

    Linux I2C驱动是嵌入式Linux驱动开发人员经常需要编写的一种驱动,因为凡是系统中使用到的I2C设备,几乎都需要编写相应的I2C驱动去配置和控制它,例如 RTC实时时钟芯片.音视频采集芯片.音 ...

  3. linux i2c detect函数,手把手教你写Linux I2C设备驱动

    Linux I2C驱动是嵌入式Linux驱动开发人员经常需要编写的一种驱动,因为凡是系统中使用到的I2C设备,几乎都需要编写相应的I2C驱动去配置和控制它,例如 RTC实时时钟芯片.音视频采集芯片.音 ...

  4. Linux添加一个i2c设备,手把手教你写Linux I2C设备驱动

    Linux I2C驱动是嵌入式Linux驱动开发人员经常需要编写的一种驱动,因为凡是系统中使用到的I2C设备,几乎都需要编写相应的I2C驱动去配置和控制它,例如 RTC实时时钟芯片.音视频采集芯片.音 ...

  5. linux下i2c设备驱动程序,Linux I2C 设备驱动

    I2C 设备驱动要使用 i2c_driver 和 i2c_client 数据结构并填充其中的成员函数.i2c_client 一般被包含在设备的私有信息结构体yyy_data 中,而 i2c_drive ...

  6. linux字符设备驱动在哪里设置,从点一个灯开始学写Linux字符设备驱动!

    原标题:从点一个灯开始学写Linux字符设备驱动! [导读] 前一篇文章,介绍了如何将一个hello word模块编译进内核或者编译为动态加载内核模块,本篇来介绍一下如何利用Linux驱动模型来完成一 ...

  7. Linux I2C设备驱动编写(二)

    I2C对外API I2C client的注册 i2c_register_board_info具体实现 i2c_new_device I2C driver 关于I2C设备驱动的小总结 I2C adapt ...

  8. LINUX I2C设备驱动模型分析之二 总线部分分析

    上一章我们对I2C模块做了总体框架的分析,本章我们主要分析下I2C模块的总线部分,主要涉 及总线初始化.总线相关属性.总线相关接口函数处理等几部分 I2c bus的定义 I2c bus的定义如下,主要 ...

  9. 迅为IMX6ULL开发板Linux I2C设备驱动编写流程-信息描述

    1 .不使用设备树文件 当开始编写 I2C 设备驱动时,首先要添加设备信息.先来看一下在不使用设备树,使用平台文件时, 如何在平台文件中添加 I2C 设备信息. 在平台文件中通过 i2c_board_ ...

最新文章

  1. 深入理解 Java 虚拟机(第一弹) - Java 内存区域透彻分析
  2. xutils使用手册(二)——数据库
  3. 【内网穿透】生壳SSH映射 for Linux 使用教程
  4. python绘制三维散点图-Python散点图与三维图
  5. mysql索引检测_mysql检测重复索引
  6. java 调整数组顺序使奇数位于偶数前面
  7. Android应用程序窗口(Activity)的窗口对象(Window)的创建过程分析
  8. log4j(六)——log4j.properties简单配置样例说明
  9. Cause: com.ibatis.common.xml.NodeletException: Error parsing XML. Cause: jav
  10. kettlejava脚本的api_[翻译]KETTLE JAVA API :编程定制自己的Kettle转换(transformation)...
  11. JAVA在线小说电子书阅读系统毕业设计 开题报告
  12. git push reject 解决方案
  13. 520情人节告白❤HTML+CSS+JavaScript实现抖音流动爱心
  14. 爬虫--Item Pipeline 介绍(21)
  15. 游戏:杀戮尖塔(Slay the spire)mod--拉格朗·月
  16. 支付宝生活号开发中所遇到的困难及解决记录
  17. VF2, VF3算法
  18. mysql的双井号_C语言中单井号(#)和双井号(##)在宏语句中的应用
  19. C++ 内存管理 —— 第一講:C++ 內存構件
  20. 操作系统笔试面试笔记总结

热门文章

  1. 《终身成长》学习总结
  2. 天创速盈:拼多多商家提升投产比有什么技巧?
  3. 如何取消隐藏文件扩展名
  4. ruby的require, 和in clude有什么区别
  5. mad和php的区别,良心解析kakaKUC-MAD好用吗?怎么样呢?体验揭秘分析
  6. opencv无法打开摄像头
  7. iOS m3u8本地缓存播放(控制下载并发、暂停恢复)
  8. java横线_java 字体 加横线
  9. 计算机做word的表格,怎么用word做表格,电脑word表格详细制作教程
  10. Python中随机数种子的作用及使用