Android 驱动(二) IIC简介

一、 I2C简介

I2C(Inter-Integrated Circuit)总线是一种由 Philips 公司开发的两线式串行总线,用于连接微控制器及其外围设备。I2C 总线最主要的优点就是简单性和有效性,简单体现在接线简单,只有两根线数据线(SCL)和时钟线(SDA),而且 控制简单。所以一些封装较小的器件多使用I2C总线,常见的使用I2C总线的设备有EEPROM、RTC及一些传感器。这里我们介绍下基于linux的I2C设备驱动的编写。

  • I2C设备驱动的编写有多种方式

一种是直接操作CPU的I2C控制器,正对于某一个设备写一个字符驱动,这种驱动相对来说比较直接,不需要太依赖于内核相关配置,但是这类设备驱动依赖CPU,可移植性较差。

一种是基于linux内核I2C子系统完成设备驱动的编写,一般内核会继承相关CPU的控制器驱动即使没有也可以通过技术支持可以获得,所以我们只需要使用linux下I2C子系统提供的相关接口来构建我们的设备驱动就行了。这样我们的设备驱动并不依赖于某一个特定的CPU,可移植性较好。

IIC驱动主要分为Master和Slave,Master就是主机控制器,像A10内部的IIC控制器就是一个Master, Slave就是IIC从机设备,它要被挂接到Master上才能工作

智能手机和平板电脑上用的sensor几乎都是IIC设备,最常用的IIC设备就是电容触摸屏和摄像头,接下来,我们将针对触摸屏、EEPROM、摄像头等驱动来分析。

  • I2C总线工作原理

I2C 总线是由数据线 SDA 和时钟 SCL 构成的串行总线,各种被控制器件均

并联在这条总线上,每个器件都有一个唯一的地址识别,可以作为总线上的一个

发送器件或接收器件(具体由器件的功能决定)。I2C 总线的接口电路结构如图 1所示

  • I2C 总线的几种信号状态

1. 空闲状态:SDA 和 SCL 都为高电平。

2. 开始条件(S):SCL 为高电平时,SDA 由高电平向低电平跳变,开始传送数据。

3. 结束条件(P):SCL 为高电平时,SDA 由低电平向高电平跳变,结束传送数据。

4. 数据有效:在 SCL 的高电平期间,SDA 保持稳定,数据有效。SDA 的改变只能发生在 SCL 的低电平期间。

5. ACK 信号:数据传输的过程中,接收器件每接收一个字节数据要产生一个 ACK 信号,向发送器件发出特定的低电平脉冲,表示已经收到数据。

  • I2C 总线基本操作

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

数据传输中,首先由主器件产生开始条件,随后是器件的控制字节(前七位是从器件的地址,最后一位为读写位)。接下来是读写操作的数据,以及 ACK响应信号。数据传输结束时,主器件产生停止条件。具体的过程如图 2 所示。

二、Linux 系统 I2C 驱动程序

  • I2C驱动层次结构

Linux 系统对 I2C 设备具有很好的支持,Linux 系统下的 I2C 驱动程序从逻 辑上可以分为 3 个部分:

1. I2C 核心(I2C core):实现对 I2C 总线、I2C adapter 及 I2C driver 的管理。

2. I2C 控制器驱动 I2C adapter :针对不同类型的 I2C 控制器 ,实现对 I2C 总线访问的具体方法。

3. I2C 设备驱动 I2C driver:针对特定的 I2C 设备,实现具体的功能,包括read,write 以及 ioctl 等对用户层操作的接口。

这三个部分的层次关系如图 3 和图 4 所示。

  • I2C 核心(I2C core)

I2C core 是 Linux 内核用来维护和管理 I2C 的核心部分,其中维护了两个静 态的 List,分别记录系统中的 I2C driver 结构和 I2C adapter 结构。I2C core 提供 接口函数,允许一个 I2C adapter,I2C driver 和 I2C client 初始化时在I2C core 中 进行注册,以及退出时进行注销。同时还提供了 I2C 总线读写访问的一般接口(具 体的实现在与 I2C 控制器相关的
I2C adapter 中实现),主要应用在 I2C 设备驱动中。

  • I2C 控制器驱动(I2C adapter)

I2C adapter 是针对不同类型 I2C 控制器硬件,实现比较底层的对 I2C 总线访 问的具体方法。I2C adapter 构造一个对 I2C core 层接口的数据结构,并通过接口函数向 I2C core 注册一个控制器。

I2C adapter 主要实现对 I2C 总线访问的算法,master_xfer()函数就是 I2C adapter 底层对 I2C 总线读写方法的实现。同时 I2C adapter 中还实现了对 I2C 控制器中断的处理函数。

  • I2C 设备驱动(I2C driver)

I2C driver 是对 I2C 从设备的软件实现。I2C driver 中提供了一个通用的 I2C 设备的驱动程序,实现了字符类型设备的访问接口,对设备的具体访问是通过 I2C adapter 来实现的。I2C driver 构造一个对 I2C core 层接口的数据结构,通过 接口函数向 I2C Core 注册一个 I2C 设备驱动。同时 I2C driver 构造一个对用户层接口的数据结构,并通过接口函数向内核注册为一个主设备号为
89 的字符类型设备。I2C driver 实现用户层对 I2C 设备的访问,包括 open,read,write,ioctl,release 等常规文件操作,可以通过 open 函数打开 I2C 设备文件,通过 ioctl 函数设定要访问 I2C 设备的地址,然后就可以通过 read 和 write 函数完成对 I2C 设备的读写操作。通过 I2C driver 提供的通用方法可以访问任何一个 I2C 的设备,但是其中实 现的 read,write 及 ioctl 等功能完全是基于一般设备的实现,所有的操作数据都是基于字节流,没有明确的格式和意义。为了更方便和有效地使用
I2C 设备,可 以为一个具体的 I2C 设备开发特定的 I2C 设备驱动程序,在驱动中完成对特定的数据格式的解释以及实现一些专用的功能。

三、 基于 SUNXI 平台的 I2C 控制器驱动

位于drivers/i2c/busses目录下的文件i2c-sunxi.c,是基于sunxi平台实现的I2C 总线控制器驱动。它的职责是为系统中 3 条 I2C 总线实现相应的读写方法,但是控制器驱动本身并不会进行任何的通讯,而是等待设备驱动调用其函数。

图 5 是基于 SUNXI 平台的 I2C 驱动层次架构图,图中有 3 块 I2C adapter,分别对应 SUNXI 平台上的 3 块 I2C 控制器

系统开机时,I2C 控制器驱动首先被装载,I2C 控制器驱动用于支持 I2C 总 线 的读 写。i2c_sunxi_algorithm 结构体中定 义了 I2C 总线通信方 法函数i2c_sunxi_xfer(),该函数实现了对 I2C 总线访问的具体方法,设备驱动通过调用

这个函数,实现对 I2C 总线的访问;而在函数 i2c_sunxi_probe()中完成了对 I2C adapter 的初始化。

  • I2C 驱动源码结构

在 drivers/i2c/目录下,包含有几个重要文件和目录,如下:

1. 文件 i2c-core.c:I2C 子系统核心功能的实现;

2. 文件 i2c-dev.c:通用的从设备驱动实现;

3. 目录 busses:里面包括基于不同平台实现的 I2C 总线控制器驱动;

4. 目录 algos:里面实现了一些 I2C 总线控制器的 algorithm。

四、I2C 设备驱动程序的开发

  • I2C 设备驱动一般结构

一个具体的 I2C 设备驱动需要实现两个方面的接口,一方面是对 I2C core 层的接口,用以挂接 I2C adapter 来实现对 I2C 总线及 I2C 设备具体的访问方法, 包括要实现 probe,remove,detect 等接口函数;另一方面是对用户应用层的接 口,提供用户程序访问 I2C 设备的接口,包括实现 open,release,read,write 以及最重要的 ioctl 等标准文件操作的接口函数。

对 I2C core 层的接口函数的具体功能解释如下:

probe:I2C driver 进行设备绑定的回调函数。

remove:I2C driver 解除设备绑定的回调函数。

detect:I2C 设备探测回调函数,它会识别所支持的设备(返回 0 表示支持, 否则返回-ENODEV);此外,需要定义一个供探测的地址列表(address_list)和 一个设备类型(class),这样使那些仅匹配设备类型的 i2c 总线被探测到。例如, 对 于 一 个 自 动 监 测 硬 件 芯 片 的 驱 动 将 会 设 置 它 的 class 域 为 I2C_CLASS_HWMON,只有那些 class 域为 I2C_CLASS_HWMON
的控制器能 够被驱动探测。

  • 常用数据结构解析

i2c_adapter

struct i2c_adapter {

struct module *owner; /* 所属模块 */

unsigned int id; /* algorithm 的类型,定义于 i2c-id.h,以 I2C_ALGO_开始 */

unsigned int class;

const struct i2c_algorithm *algo; /* 总线通信方法结构体指针 */

void *algo_data;  /* algorithm 数据 */

struct rt_mutex bus_lock;

int timeout;  /* 超时时间,以 jiffies 为单位 */

int retries;  /* 重试次数 */

struct device dev;  /* 控制器设备 */

int nr;

char name[48]; /* 控制器名称 */

struct completion dev_released; /* 用于同步 */

struct mutex userspace_clients_lock;

struct list_head userspace_clients;

};

i2c_adapter 对应于物理上的一个控制器。一个 I2C 控制器需要 i2c_algorithm 中提供的通信函数来控制控制器上产生特定的访问周期。

i2c_algorithm

struct i2c_algorithm {

/* I2C 传输函数指针 */

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

/* smbus 传输函数指针 */

int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr, unsigned short flags, char read_write,  u8 command, int size, union i2c_smbus_data *data);

/* 返回控制器支持的功能 */

u32 (*functionality) (struct i2c_adapter *);

};

i2c_algorithm中的关键函数master_xfer()用于产生I2C访问周期需要的信号, 以 i2c_msg(即 I2C 消息)为单位。

i2c_msg

struct i2c_msg {

__u16 addr; /* 从设备地址 */

__u16 flags; /* 消息类型 */

__u16 len; /* 消息长度 */

__u8 *buf; /* 消息数据 */

};

i2c_msg 是 I2C 传输的基本单位,它包含了从设备的具体地址,消息的类型 以及要传输的具体数据信息。每个 I2C 消息传输前,都会产生一个开始位,紧接 着传送从设备地址,然后开始数据的发送或接收,对最后的消息还需产生一个停止位。

i2c_client

struct i2c_client {

unsigned short flags; /* 标志 */

unsigned short addr; /* 低 7 位的芯片地址 */

char name[I2C_NAME_SIZE];  /* 设备名称 */

struct i2c_adapter *adapter; /* 依附的 i2c_adapter */

struct i2c_driver *driver; /* 依附的 i2c_driver */

struct device dev;

int irq;  /* 设备使用的中断号 */

struct list_head detected;

};

i2c_client 对应于真实的物理设备,每个 I2C 设备都需要一个 i2c_client 来描

述。

i2c_driver

struct i2c_driver {

unsigned int class;

int (*attach_adapter)(struct i2c_adapter *); /* 依附 i2c_adapter 函数指针 */

int (*detach_adapter)(struct i2c_adapter *); /* 脱离 i2c_adapter 函数指针 */

int (*probe)(struct i2c_client *, const struct i2c_device_id *);

int (*remove)(struct i2c_client *);

void (*shutdown)(struct i2c_client *);

int (*suspend)(struct i2c_client *, pm_message_t mesg);

int (*resume)(struct i2c_client *);

void (*alert)(struct i2c_client *, unsigned int data);

int (*command)(struct i2c_client *client, unsigned int cmd, void *arg);

struct device_driver driver;

const struct i2c_device_id *id_table;  /* 该驱动所支持的设备 ID 表 */

int (*detect)(struct i2c_client *, struct i2c_board_info *); /* 设备探测函数 */

const unsigned short *address_list; /* 驱动支持的设备地址 */

struct list_head clients; /* 挂接探测到的支持的设备 */

};

i2c_driver 对应一套驱动方法,其主要成员函数是 probe()、remove()、 suspend()、resume()等,另外id_table是该驱动所支持的I2C设备的ID表。i2c_driver 与 i2c_client
的关系是一对多,一个 i2c_driver 上可以支持多个同等类型的 i2c_client。

Android 驱动(二) IIC简介

时间: 2024-08-27 04:17:37

Android 驱动(二) IIC简介的相关文章

Android 驱动 (一) GPIO

前面的博文对Lichee做了系列分析,其实就是对在<七年之痒>中所说的,Android BSP具备的一项基本素质-SHELL脚本,所以我们Lichee系列的文章着重分析了SHELL脚本和Lichee的基本结构,当然作为一名合格的Android BSP工程师来说,掌握Linux的驱动程序的移植,也是一项基本技能.所以从本文开始,将对sun4i的一些驱动程序做深入分析.当然了,驱动程序涉及的面很广,比如摄像头的驱动涉及到sensor的移植和内核队列等数据结构相关内容,SD卡驱动又涉及到DMA的基本

GitHub Android Libraries Top 100 简介

GitHub Android Libraries Top 100 简介 排名完全是根据 GitHub 搜索 Java 语言选择 (Best Match) 得到的结果, 然后过滤了跟 Android 不相关的项目, 所以排名并不具备任何官方效力, 仅供参考学习, 方便初学者快速了解当前一些流行的 Android 开源库. 项目名称 项目简介 1. react-native 这个是 Facebook 在 React.js Conf 2015 大会上推出的基于 JavaScript 的开源框架 Rea

Android Studio使用心得 - 简介与环境配置

关于Android Studio 在2013 Google IO大会上发布的全新IDE Android Studio相信各位猿们并不陌生,截止2014的Google IO大会,虽然依然木有发布正式版,但是根据我这几周的使用情况来说,BUG已经很少了,完全不影响正常使用.而且Android Studio是基于IntelliJ的,无论从运行速度上还是编程的快捷性上,都比Eclipse的体验要好,开发效率妥妥的提高了. Android Studio vs Eclipse ADT 目前Android S

MT6582 Android 驱动设置问题

MT6582 Android 驱动设置问题 MT6577是MTK公司的双核处理器的3G手机芯片,下面是整理的MT6577 Android驱动设置: 1. LCD 1.1怎样新建一个LCD驱动 LCD模组主要包括LCD显示屏和驱动IC.比如LF040DNYB16a模组的驱动IC型号为NT35510.要在MT577平台上新建这个lcd的驱动,步骤如下: A.      新建文件夹nt35510: \mediatek\custom\common\kernel\lcm\nt35510 \mediatek

【视频】嵌入式Linux/Android驱动开发揭秘(1)触摸屏驱动开发

嵌入式Linux/Android驱动开发揭秘(1)触摸屏驱动开发 专题简介:自1971年,美国人SamHurst发明了世界上第一个触摸传感器以来,触摸屏技术不断革新,给了程序设计师和UI工程师无限的想象空间,它极大改善了终端用户对各种设备的操作方便程度,现在我们的日常生活如手机.平板等,已经很大程度上依赖于和习惯于使用和操作触摸屏.做为工程师,我们很有必要掌握触摸屏的工作原理和软件驱动方法,如果您对一窥如何在嵌入式中操控和使用触摸屏这一司空见惯却又神奇的技术感兴趣,敬请关注! 1.LINUX驱动

Android学习5&mdash;布局简介

Android界面的布局主要有四种,分别为RelativeLayout.LinearLayout.TableLayout.FrameLayout,接下来分别介绍这些布局如何使用(为了简单起见,接下来的介绍工作中,我分别附上布局文件的代码以及效果图,供大家参考) 一:RelativeLayout <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="htt

Android安卓书籍推荐《Android驱动开发与移植实战详解》下载

百度云下载地址:点我 Android凭借其开源性.优异的用户体验和极为方便的开发方式,赢得了广大用户和开发者的青睐,目前已经发展成为市场占有率很高的智能手机操作系统. <Android驱动开发与移植实战详解>分为18章,依次讲解了Android系统的基本知识, Linux内核的基本知识,分析了Android系统的源码,深入分析HAL层的基本知识,GoldFish下的驱动.MSM内核和驱动.OMAP内核和驱动.显示系统驱动.输入系统驱动.振动器系统驱动.音频系统驱动和视频输出系统驱动,多媒体框架

android蓝牙(二)——接收数据

在蓝牙开发中,我们有这样的一个需求:我们的android客户端要始终保持和蓝牙的连接,当蓝牙有数据返回的时候,android客户端就要及时的收取数据,当蓝牙没有数据返回的时候我们就要保持android客户端和蓝牙之间的连接.这个时候我们就要采取socket来实现和蓝牙之间的连接.做项目使用过http轮询去获取数据,但是发现那样总是有一定的弊端.于是就才用了socket方式去获取数据. 实现步骤:1.启动一个service去监听是否有数据返回.一旦有数据返回就启动一个线程去处理数据 2.处理完数据

第6章 Android驱动编程

第6章  Android驱动编程 通过介绍本章设备驱动.字符设备驱动编程.GPIO驱动程序实例和4*4扫描键盘驱动等内容,熟练掌握了Android驱动编程.Android内核内核模块编程中包括设备驱动和内核模块.模块相关命令.Android内核内核模块编程和内核模块实例程序.Android内核中采用可加载的模块化设计,一般情况下编译的Android内核是支持可插入式模块的,也就是将最基本的核心代码编译在内核中.模块相关命令中lsmod列出了当前系统中加载的模块,rmmood用于当前模块卸载,in