linux内核的i2c-gpio是使用GPIO模拟I2C协议的驱动,只需要配置2根GPIO即可使用。Linux的I2C子系统比较复杂,笔者暂时还没有研究。本着“实用”的目的,介绍一下如何使用这个驱动及一些注意事项。

一、概述

Linux内核很多驱动都使用到I2C子系统。如EEPROM、RTC等。

GPIO模拟I2C协议的驱动位于drivers/i2c/busses目录。驱动名称为“i2c-gpio”,驱动文件为drivers/i2c/busses/i2c-gpio.c。

二、内核配置

本文基于linux 3.17.1版本内核进行分析。
内核配置(make menuconfig)信息如下:
Device Drivers->I2C support  --->I2C Hardware Bus support  ---><*> GPIO-based bitbanging I2C 

从配置中看到将驱动整合到内核中,而不是module形式。这样能保证在其它I2C板级信息注册之前,已经存在了i2c总线。另外,还需要GPIO库支持:

[*] GPIO Support  --->

否则无法不会出现选项“GPIO-based bitbanging I2C”。

三、设备注册及使用

3.1 I2C相关结构体

本文不是深入I2C子系统的,所以抛开原理方面的描述。看一下i2c平台数据结构i2c_gpio_platform_data的声明:

/*** struct i2c_gpio_platform_data - Platform-dependent data for i2c-gpio* @sda_pin: GPIO pin ID to use for SDA* @scl_pin: GPIO pin ID to use for SCL* @udelay: signal toggle delay. SCL frequency is (500 / udelay) kHz* @timeout: clock stretching timeout in jiffies. If the slave keeps*    SCL low for longer than this, the transfer will time out.* @sda_is_open_drain: SDA is configured as open drain, i.e. the pin*    isn't actively driven high when setting the output value high.*    gpio_get_value() must return the actual pin state even if the*    pin is configured as an output.* @scl_is_open_drain: SCL is set up as open drain. Same requirements*    as for sda_is_open_drain apply.* @scl_is_output_only: SCL output drivers cannot be turned off.*/
struct i2c_gpio_platform_data {unsigned int    sda_pin;unsigned int    scl_pin;int        udelay;int        timeout;unsigned int    sda_is_open_drain:1;unsigned int    scl_is_open_drain:1;unsigned int    scl_is_output_only:1;
};

重要的是sda_pin和scl_pin,分别表示I2C的SDA、SCL信号引脚。udelay可控制SCL频率,计算公式为:500/udelay kHZ。timeout为超时时间,单位为jiffies。sda_is_open_drain和scl_is_open_drain分别表示SDA和SCL是否为开漏电路,对此方面研究不深,不再描述。

一个实例如下:

static struct i2c_gpio_platform_data i2c_gpio_data = {.sda_pin = 68,.scl_pin = 88,.timeout = 100,.udelay  = 2,
};

例子中使用的引脚分别为68和88,是由主板硬件确定的。

3.2 平台设备

i2c-gpio驱动定义入口代码如下(drivers/i2c/busses/i2c-gpio.c):
static struct platform_driver i2c_gpio_driver = {.driver        = {.name    = "i2c-gpio",.owner    = THIS_MODULE,.of_match_table    = of_match_ptr(i2c_gpio_dt_ids),},.probe        = i2c_gpio_probe,.remove        = i2c_gpio_remove,
};static int __init i2c_gpio_init(void)
{int ret;ret = platform_driver_register(&i2c_gpio_driver);if (ret)printk(KERN_ERR "i2c-gpio: probe failed: %d\n", ret);return ret;
}
subsys_initcall(i2c_gpio_init);

从代码分析知,这里将GPIO模拟I2C总线当作平台设备处理。而从i2c_gpio_driver结构体中可以看到驱动名称为i2c-gpio。因此要使用这个驱动,必须另外定义一个platform设备,并调用函数platform_device_register注册。本文实例如下

static struct platform_device i2c_gpio_device = {.name       = "i2c-gpio",.id         = 0, // first bus for "i2c-gpio", so --> 0.dev        = {.platform_data = &i2c_gpio_data,.release = platformdev_release,},
};

其中name表示设备名称,这里必须为“leds-gpio”,platform_data即为前面定义的i2c_gpio_data。id表示i2c-gpio的第几条I2C总线。驱动正常工作后,将生成/sys/bus/platform/devices/i2c-gpio.id目录,里面有挂载在此总线上的设备地址。

最后,注册设备——建议在板子的GPIO正常工作之后再进行注册。

platform_device_register(&i2c_gpio_device);

四、I2C板级信息注册

i2c设备的板级信息由i2c_board_info结构体描述,其声明如下:
struct i2c_board_info {char        type[I2C_NAME_SIZE];unsigned short    flags;unsigned short    addr;void        *platform_data;struct dev_archdata    *archdata;struct device_node *of_node;struct acpi_dev_node acpi_node;int        irq;
};

该结构体包括了I2C设备名称、标志、地址等等信息。

比如主板上有2个I2C设备:LM75和EEPROM,地址分别为0x48、0x50——地址根据datasheet说明及硬件接线来确定。下面定义板级信息:
static struct at24_platform_data at24_eeprom = {.byte_len        = 2 * 1024 / 8,.page_size        = 16,.flags            = 0,
};static struct i2c_board_info my_i2c_boardinfo[] = {{I2C_BOARD_INFO("lm75", 0x48),},{I2C_BOARD_INFO("24c02", 0x50), // 24c02 == at24 driver.platform_data    = &at24_eeprom,},
};
2C_BOARD_INFO宏定义如下:
#define I2C_BOARD_INFO(dev_type, dev_addr) \.type = dev_type, .addr = (dev_addr)

常见注册方法有分静态注册i2c_register_board_info和动态注册i2c_new_device。大部分ARM框架都使用静态注册,使用很简单,如下:

i2c_register_board_info(0, i2c_info, ARRAY_SIZE(i2c_info));

不过如果以modules形式编译,则会提示i2c_register_board_info未定义:

WARNING: "i2c_register_board_info" [drivers/gpio/gpio-misc.ko] undefined!

这里使用第二种动态注册的方式。先调用i2c_get_adapter获取适配器(参数为i2c总线,根据前面i2c_gpio_device定义的id确定),然后调用i2c_new_device添加到该适配器中。代码片段如下:

struct i2c_adapter* adap = NULL;
struct i2c_client* client = NULL;adap = i2c_get_adapter(i2c_gpio_device.id);
if (adap)
{for (i = 0; i < ARRAY_SIZE(my_i2c_boardinfo); i++){client = i2c_new_device(adap, &my_i2c_boardinfo[i]);pr_info("Add %s to adapter %s %s.\n", my_i2c_boardinfo[i].type, adap->name, client?"ok":"failed");}
}
else
{pr_info("i2c bus %d found no adapter...\n", i2c_gpio_device.id);
}    

五、用户空间

驱动正常工作后,会生成/sys/bus/platform/devices/i2c-gpio.0目录(以本文为例)。里面存放着对应驱动映射的文件。比如/sys/bus/platform/devices/i2c-gpio.0/i2c-0/0-0050/eeprom即为eeprom内容。该文件属性为可读写,因此修改此文件内容,即为修改EEPROM内容(此方法有一定危险性,就不直接写出来了)。
而查看主板温度,则可以用如下命令:
cat /sys/bus/platform/devices/i2c-gpio.0/i2c-0/0-0048/hwmon/hwmon0/temp1_input

注:目录出现的0-0050表示该I2C设备在第0条总线上的0x50地址。

六、总结

使用GPIO模拟I2C驱动前,最好保证系统的GPIO已能正常工作。

在驱动中可以注册多条i2c-gpio总线,驱动名称均为i2c-gpio,但根据id值来区别不的同总线。比如主板上有3条GPIO模拟总线,则可分别命名为0、1、2。

参考资源:

1、内核源码官网:https://www.kernel.org

2、内核源码查询:http://lxr.free-electrons.com/source/?v=3.17

李迟 2016.12.9 周五 晚

我的内核学习笔记12:linux i2c-gpio驱动应用实例相关推荐

  1. 我的内核学习笔记10:Intel GPIO驱动源码分析

    本文对Intel e3800的GPIO驱动源码进行分析. 一.概述 1.1 内核配置 Intel e3800的GPIO在Linux内核中使用的驱动名为gpio_ich(为了行文方便,将对应的设备称为& ...

  2. 我的内核学习笔记7:Intel LPC驱动lpc_ich分析

    接触这么久的内核代码,还没有真正分析一个完整的驱动源码,都是零零散散写只言片字.本文就作一个尝试,写一写Linux内核源码分析层面的文章. 本文介绍基于Intel baytrail系列的e3800系列 ...

  3. Linux学习笔记12——配置ftp、squid、Tomcat、Samba、MySQL主从

    Linux学习笔记12 Linux学习笔记12 配置FTP服务 配置pure-ftpd 开机启动 上传下载文件 配置vsftpd CentOS 70安装配置Vsftp服务器 搭好vsftp之后出现55 ...

  4. 操作系统进程学习(Linux 内核学习笔记)

    操作系统进程学习(Linux 内核学习笔记) 进程优先级 并非所有进程都具有相同的重要性.除了大多数我们所熟悉的进程优先级之外,进程还有不同的关键度类别,以满足不同需求.首先进程比较粗糙的划分,进程可 ...

  5. Linux学习笔记:Linux常用命令总结

    文章目录 前言 Linux学习笔记:Linux常用命令总结 1. 目录命令 1.1 Linux目录结构 1.2 cd命令 切换工作目录 1.3 ls命令 显示目录下文件 1.4 mkdir命令 创建目 ...

  6. Windows x64内核学习笔记(四)—— 9-9-9-9-12分页

    Windows x64内核学习笔记(四)-- 9-9-9-9-12分页 前言 9-9-9-9-12分页 实验一:线性地址转物理地址 页表基址 定位基址 PTE to PXE 实验二:通过页表基址定位各 ...

  7. 我的内核学习笔记5:proc目录文件创建及读写

    上一篇内核学习笔记<我的内核学习笔记4:sysfs学习>是2013年写的,彼时至今,随着工作的展开和安排,内核方面的知识可谓突飞猛进,当然,其它方面亦是如此.关于内核方面,积累的笔记大大小 ...

  8. linux系统管理学习笔记之八---linux文件与目录的管理及权限

    linux系统管理学习笔记之八---linux文件与目录的管理及权限 2010-01-05 09:00:49 标签:权限 管理 文件目录 linx [推送到技术圈] 版权声明:原创作品,允许转载,转载 ...

  9. Linux中常用的文件目录,Linux学习笔记2——Linux中常用文件目录操作命令

    ls 显示文件和目录列表 -l 列出文件的详细信息 -a 列出当前目录所有文件,包含隐藏文件 mkdir 创建目录 -p 父目录不存在情况下先生成父目录 cd 切换目录 touch 生成一个空文件 e ...

最新文章

  1. Qt中如何改变三角形图形项的包围盒
  2. Django 模板HTML转义和CSRF4.3
  3. Matlab学习笔记——find()函数
  4. 神策数据受邀参加全国 APP 个人信息保护监管会
  5. 数据库系统(五)——数据库设计
  6. php rmdir 返回值,php通过rmdir删除目录的简单用法
  7. 665. 非递减数列 golang 切片越界问题的探讨(二)
  8. ansible软件部署
  9. linux获取进程io,linux查看哪个进程占用磁盘IO
  10. 【UVA11795】 Mega Man's Mission
  11. [转]unresolved external symbol _main解决办法
  12. IOUtils工具类的依赖maven
  13. 【ElementUI样式优化】el-input带自定义查询删除图标 ==> 图标点击可实现对应功能 ==> 一个input实现查询重置功能
  14. Zookeeper分布式锁解决羊群效应的方案
  15. 闭关六个月整理出来的微机原理知识点(特别适用河北专接本)
  16. PowerPoint演示文档大瘦身
  17. 微信公众号里放XLS链接教程
  18. lua与c#交互篇 | 合理用好lua+unity,更省性能的方案整理
  19. WAF学习之一——Nginx与反向代理
  20. 怎样使用word模板?两分钟教你搞定!

热门文章

  1. 佳恩半导体完成数千万A轮融资
  2. 抖音测试快递服务“音尊达” 已接入中通、圆通等,可送货上门
  3. 杀入“果链”!闻泰科技取代台厂成苹果MacBook组装供应商
  4. 又上热搜!鸿星尔克悄悄给山西捐赠2000万物资!这些大厂也出手了...
  5. 滴!你的“十三香”已发货,iPhone 13系列今日正式发售
  6. 研究机构预计芯片短缺将导致全球轻型汽车今年减产502万辆
  7. 年轻人开始“反算法”
  8. 法院才是最童叟无欺的一元店
  9. keep公众号就“借鉴”原创文章致歉:将停更一周
  10. 疑似全新华为Mate X2折叠屏手机入网:麒麟9000重磅加持