0
点赞
收藏
分享

微信扫一扫

#导入Word文档图片# Linux下I2C驱动架构全面分析​

  • 物理接线I2C总线在物理连接上非常简单,分别由SDA(串行数据线)和SCL(串行时钟线)及上拉电阻组成。通信原理是通过对SCL和SDA线高低电平时序的控制,来产生I2C总线协议所需要的信号进行数据的传递。在总线空闲状态时,这两根线一般被上面所接的上拉电阻拉高,保持着高电平。
  • I2C总线特征I2C总线上的每一个设备都可以作为主设备或者从设备,而且每一个设备都会对应一个唯一的地址(可以从I2C器件的数据手册得知),主从设备之间就通过这个地址来确定与哪个器件进行通信,在通常的应用中,
    我们把CPU带I2C总线接口的模块作为主设备,把挂接在总线上的其他设备都作为从设备。I2C总线上可挂接的设备数量受总线的最大电容400pF限制,如果所挂接的是相同型号的器件,则还受器件地位的限制。I2C总线数据传输速率在标准模式下可达 100kbit/s,快速模式下可达 400kbit/s,高速模式下可达3.4Mbit/s。一般通过 I2C总线接口可编程时钟来实现传输速率的调整。I2C总线上的主设备与从设备之间以字节(8位)为单位进行双向的数据传。
  • I2C总线协议1.I2C总线协议基本时序信号空闲状态:SCL和SDA都保持着高电平。起始条件:总线在空闲状态时,SCL和SDA都保持着高电平,当SCL为高电平而SDA由高到低的跳变,表示产生一个起始条件。在起始条件产生后,总线处于忙状态,由本次数据传输的主从设备独占,其他2C器件无法访问总线。
    ① 停止条件:当SCL为高而SDA由低到高的跳变,表示产生一个停止条件。
    ② 应答信号: 每个字节传输完成后的下一个时钟信号,在SCL高电平期间,SDA为低,则 表示一个应答信号。
    ③ 非应答信号:每个字节传输完成后的下一个时钟信号,在SCL高电平期间,SDA为高,则表示一个应答信号。
    注意:起始和结束信号总是由主设备产生。

17.1 Linux下的驱动思路

在linux系统下编写I2C驱动,目前主要有两种方法

1)把I2C设备当作一个普通的字符设备来处理

2)利用linux下I2C驱动体系结构(子系统)来完成

下面比较下这两种方法:

第一种方法

优点:思路比较直接,不需要花很多时间去了解linux中复杂的I2C子系统的操作方法。

缺点

要求工程师不仅要对I2C设备的操作熟悉,而且要熟悉I2C的适配器(I2C控制器)操作。

要求工程师对I2C的设备器及I2C的设备操作方法都比较熟悉,最重要的是写出的程序可移植性差。

对内核的资源无法直接使用,因为内核提供的所有I2C设备器以及设备驱动都是基于I2C子系统的格式。

第一种方法的优点就是第二种方法的缺点

第一种方法的缺点就是第二种方法的优点。

17.2 I2C架构概述

上图完整的描述了linux i2c驱动架构,虽然I2C硬件体系结构比较简单,但是i2c体系结构在linux中的实现却相当复杂。

I2C子系统由上到下分成3层:

层名

描述

I2C设备驱动层

真正实现具体设备的时序的代码。使用核心层提供API接口写,有特定编写框架。

I2C核心层

提供了I2C总线驱动设备驱动的注册注销方法I2C通信方法(”algorithm”),与具体适配器无关的代码以及探测设备,检测设备地址的上层代码等。

提供了设备驱动层和适配器驱动层需要API接口,以及实现收发数据管理功能。起到一个连接上下两的作用。

I2C适配器驱动层

对I2C硬件体系结构中适配器端的实现

适配器可由CPU控制,甚至可以直接集成在CPU内部。

通俗说就是直接操作硬件上IIC控制的的驱动代码。真正的实现IIC数据收发。

上面三层,设备驱动层需要自己写,核心层不变,由内核提供,I2C适配器驱动层一般由芯片厂商提供。

17.3 Linux下I2C体系文件构架

在Linux内核源代码中的driver目录下包含一个i2c目录

文件

功能描述

i2c-core.c

这个文件实现了I2C核心的功能以及/proc/bus/i2c*接口。

i2c-dev.c

实现了I2C适配器设备文件的功能,每一个I2C适配器都被分配一个设备。通过适配器访设备时的主设备号都为89,次设备号为0-255。

I2c-dev.c并没有针对特定的设备而设计,只是提供了通用的read(),write(),和ioctl()等接口,应用层可以借用这些接口访问挂接在适配器上的I2C设备的存储空间或寄存器,并控制I2C设备的工作方式。

busses文件夹

这个文件中包含了一些I2C总线的驱动,如针对S3C2410,S3C2440,S3C6410等处理器的I2C控制器驱动为i2c-s3c2410.c

algos文件夹

实现了一些I2C总线适配器的algorithm

[root@WBYQ /]# ls /dev/i2c-* -l

crw-rw---- 1 root root 89, 0 Jul 12 01:30 /dev/i2c-0

crw-rw---- 1 root root 89, 1 Jul 12 01:30 /dev/i2c-1

crw-rw---- 1 root root 89, 2 Jul 12 01:30 /dev/i2c-2

crw-rw---- 1 root root 89, 3 Jul 12 01:30 /dev/i2c-3

crw-rw---- 1 root root 89, 7 Jul 12 01:30 /dev/i2c-7

crw-rw---- 1 root root 89, 8 Jul 12 01:30 /dev/i2c-8

[root@WBYQ /]#

核心层:i2c-core.c i2c-boardinfo.c i2c-smbus.c i2c-mux.c

I2C适配器驱动层:

busses文件夹下的一个C文件对应于一个物理的I2C适配器驱动程序。

比如:EXYNOS4412 的I2C 适配器驱动i2c-s3c2410.c。

现在这里的两层我们都实现不了,linux系统和芯片厂家提供。

我们主要实现设备驱动层,下面主要讲解它了

17.4 设备驱动层(重点)

17.4.0 设备驱动层结构

由设备层代码 + 驱动层代码构成,可以类比平台设备驱动模型。

17.4.1 驱动层核心结构

该核心结构在I2c.h(include\Linux )下。内核使用 struct i2c_driver 结构描述一个I2C设备驱动,这个结构必须实现的是:probe,remove。

struct i2c_driver {

unsigned int class;


/* Notifies the driver that a new bus has appeared or is about to be

* removed. You should avoid using this, it will be removed in a

老接口,可能会消失,建议不要使用.

*/

int (*attach_adapter)(struct i2c_adapter *) __deprecated;

int (*detach_adapter)(struct i2c_adapter *) __deprecated;


/* 新的接口,用来替代上面两个接口,必须实现,功能类型平台模型的probe,remove*/

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

int (*remove)(struct i2c_client *);


/* driver model interfaces that don't relate to enumeration */

void (*shutdown)(struct i2c_client *);

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

int (*resume)(struct i2c_client *);


/* Alert callback, for example for the SMBus alert protocol.

* The format and meaning of the data value depends on the protocol.

* For the SMBus alert protocol, there is a single bit of data passed

* as the alert response's low bit ("event flag").

*/

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


/* a ioctl like command that can be used to perform specific functions

* with the device.

*/

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


struct device_driver driver;

const struct i2c_device_id *id_table;


/* Device detection callback for automatic device creation */

int (*detect)(struct i2c_client *, struct i2c_board_info *);

const unsigned short *address_list;

struct list_head clients;

};


17.4.2 设备层核心结构

内核使用 struct i2c_client 来描述一个设备信息。

比如,器件地址,标志(器件地址位数:10位,7位等),所依赖的总线等。

内核定义的结构

struct i2c_client {

标志,比如说设备地址10位还是7位

unsigned short addr; 低7位为芯片地址

char name[I2C_NAME_SIZE];设备名称,随便,但是要和驱动层id_table相同

struct i2c_adapter *adapter;依附的 i2c_adapter,它表示一个IIC控制器

依附的i2c_driver

设备结构体

设备所使用的中断号

链表头

};

最小的情况下实现flags,addr,name,adapter

17.5 API函数

17.5.1 注册iic驱动

int i2c_add_driver(struct i2c_driver *driver)

注册I2C设备驱动,driver 是已经填充好的struct i2c_driver结构指针

一般写在模块的初始化代码。

17.5.2 注销iic驱动

void i2c_del_driver(struct i2c_driver *driver)

注销I2C设备驱动,

一般写在模块的出口处。

17.5.3 标准的发送数据函数

以下关于内核I2C核心层提供的标准发收函数:

int i2c_master_send(struct i2c_client *client,

const char *buf ,

发送函数

功能:发送数据给真正的硬件设备。

参数:

:指针I2C设备结构的指针。

:发送的数据指针

发送的字符数量

返回发送的字节数,失败返回-1。

注意:此函数只是实现标准IIC的写协议不代表具体器件写协议

如:要写数据给AT24C02 ,从内部地址10开始写,应该怎么写。

方法1:把内部地址当数据写在第一个缓冲中,后面是真正的数据。

buf[0]=subaddr; //内部地址

buf[1]=? //数据

……

buf[9]=?;

i2c_master_send(client,buf ,10) ;

方法2:先单独发器件地址,再发送要写在内部地址的数据。

subaddr=subaddr; //内部地址

i2c_master_send(client,&subaddr ,1) ;

?; //数据

buf[1]=?

……

buf[9]=?;

i2c_master_send(client,buf ,10) ;

17.5.4 标准的读取数据函数

int i2c_master_recv(struct i2c_client *client, char *buf ,int count)

功能:从硬件中读取数据

参数:

:指针I2C设备结构的指针。

:存放数据指针

要读的字节数量

注意:此函数只是实现标准IIC的读协议,不代表具体器件读协议。

比如,对24c02进行读操作,先使用 i2c_master_send发送内部地址,然后调用 i2c_master_recv 函数读数据。

17.5.5 收发一体函数

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

功能:这个函数是I2C传输函数,收发一体的函数。

参数:

adap:指针I2C适配器结构的指针,这个指针是使用 i2c_client 中的 adapter 指针。

:存放数据指针

要传输的 struct i2c_msg 数量。

内核使用struct i2c_msg 结构来描述一则消息,包含了目标地址操作方式(读写)数据存放位置源位置

struct i2c_msg {

__u16 addr;/* 这条消息是发送给谁*/

__u16 flags; /* 消息额外的标志,可选择的值有以下宏*/

#define I2C_M_TEN0x0010/* 表示目标器件地址是10位的 */

#define I2C_M_RD0x0001/* 在从设备中读取数据 */

/* 没有专门定义一个写的标志,默认是写,*/

//以下标志使用不到

#define I2C_M_NOSTART0x4000/* if I2C_FUNC_NOSTART */

#define I2C_M_REV_DIR_ADDR0x2000/* if I2C_FUNC_PROTOCOL_MANGLING */

#define I2C_M_IGNORE_NAK0x1000/* if I2C_FUNC_PROTOCOL_MANGLING */

#define I2C_M_NO_RD_ACK0x0800/* if I2C_FUNC_PROTOCOL_MANGLING */

#define I2C_M_RECV_LEN0x0400/* length will be first received byte */

__u16 len;/* 传输的数据字节数量*/

__u8 *buf;/* 接收/发送缓冲区*/

};

一则消息不是传递一个字节,可以传递多个字节,最大65536字节。

比如:要写数据给AT24C02 ,从内部地址10开始写,写2个,应该怎么写。

方法1:把内部地址当数据写在第一个缓冲中,后面是真正的数据。

char subaddr = 10; //内部地址

char data[100]={1,2,3};//等待发送的数据

struct i2c_msg msgs[2]={

[0]={

.addr = EEPROM_DEVICE_ADDR,//EEPROM_DEVICE_ADDR器件地址

.flags= 0 , //默认是写

.len = 1,

.buf =&subaddr

},

[1]={

器件地址

.flags= 0 ,

.len = 2,

.buf = data

},

};

i2c_transfer(client->adapter,msgs,2);

注意:24c02连续进行页写操作不能跨页写,这个要用户自己保证。

17.5.6 注册IIC适配器

static int i2c_register_adapter(struct i2c_adapter *adap) //注册IIC适配器,该函数在下面两个函数里已经调用


int i2c_add_adapter(struct i2c_adapter *adapter) //声明并注册i2c适配器,使用动态总线编号

int i2c_add_numbered_adapter(struct i2c_adapter *adap) //声明并注册i2c适配器,使用静态总线编号

  • 适配器数据结构:

struct i2c_adapter {

struct module *owner;

unsigned int class;允许探测的类 */

const struct i2c_algorithm *algo; /*访问总线的函数接口*/

void *algo_data;


/* data fields that are valid for all devices*/

struct rt_mutex bus_lock;


int timeout;/*时间节拍*/

int retries; /*重试*/

struct device dev;/* the adapter device */


int nr; /*总线编号*/

char name[48]; /*总线名称*/

struct completion dev_released;


struct mutex userspace_clients_lock;

struct list


17.5.9 Exynos4412开发板内核IIC适配器注册分析​

  1. 开发板底层的IIC驱动文件: i2c-s3c2410.c
  2. i2c-s3c2410.cprobe函数里完成了IIC适配器注册:
  3. #导入Word文档图片# Linux下I2C驱动架构全面分析​_I2C


  4. 在适配器注册之前,对适配器的底层函数指针进行了赋值
  5. #导入Word文档图片# Linux下I2C驱动架构全面分析​_I2C_02


  6. 最终IIC的数据通过CPU本身的IIC控制器写入硬件是依靠&s3c24xx_i2c_algorithm结构体赋值的函数完成,这是最底层操作IIC硬件的函数。

#导入Word文档图片# Linux下I2C驱动架构全面分析​_I2C_03


IIC适配器注册示例:

#include <linux/init.h>

#include <linux/module.h>

#include <linux/platform_device.h>

#include <linux/i2c.h>

#include <linux/gpio.h>

#include <mach/gpio.h>

#include <plat/gpio-cfg.h>


static struct i2c_adapter adapter;


/* 声明我们的i2c功能 */

static u32 i2c_support(struct i2c_adapter *adap)

{

return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_NOSTART |

I2C_FUNC_PROTOCOL_MANGLING;

}


static int i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,int num)

{

printk("设备地址:0x%X\n",msgs[0].addr);

if(num==1) //写模式

{

printk("写模式:\n");

printk("寄存器起始位置:%d\n",msgs[0].buf[0]);

printk("写入的数据长度:%d\n",msgs[0].len-1);

int i;

printk("数据如下:");

for(i=1;i<=msgs[0].len-1;i++)

{

printk("%d ",msgs[0].buf[i]);

}

printk("\n");

}

else if(num==2)//读模式

{

printk("读模式:\n");

printk("寄存器起始位置:%d\n",msgs[0].buf[0]);

printk("读取的数据长度:%d\n",msgs[1].len);

int i;

//给数据赋值,模拟数据读取

for(i=0;i<msgs[1].len;i++)

{

msgs[1].buf[i]=i+66;

}

printk("\n");

}

return 0;

}


static const struct i2c_algorithm my_i2c_algorithm = {

.master_xfer= i2c_master_xfer, //底层调用函数

.functionality= i2c_support, //支持的功能声明

};



static int __init i2c_adapter_init(void)

{

strlcpy(adapter.name, "my_exynos_i2c", sizeof(adapter.name));

adapter.owner = THIS_MODULE;

adapter.algo = &my_i2c_algorithm; /*适配器对应的功能函数*/

adapter.retries = 2;

adapter.class = I2C_CLASS_HWMON | I2C_CLASS_SPD;

if(i2c_add_adapter(&adapter)!=0)

{

printk("IIC适配器注册失败!\n");

return 0;

}

printk("适配器注册的总线编号:%d\n",adapter.nr);

printk("i2c_adapter_init!!\n");

return 0;

}


static void __exit i2c_adapter_exit(void)

{

i2c_del_adapter(&adapter);

printk(" i2c_adapter_exit!\n");

}


module_init(i2c_adapter_init);

module_exit(i2c_adapter_exit);

MODULE_LICENSE("GPL");


17.5.7 注销IIC适配器​

int i2c_del_adapter(struct i2c_adapter *adap)

17.5.8 获取适配器​

struct i2c_adapter *i2c_get_adapter(int nr)

功能: 根据注册时绑定的总线编号,获取IIC适配器结构体

参数: nr 总线编号

返回值: IIC适配器结构

17.6 Smbus总线操作函数​

以下是 smbus 总线的操作函数,smbus是系统管理总线,可以看成是IIC总线的一个子集,它的规范大部分都是基于I2C标准。所以,大部分的IIC器件也可以使用 smbus 总线来操作。非常合适操作那些有内部地址的器件。

17.6.1 单字节读函数​

s32 i2c_smbus_read_byte_data(const struct i2c_client *client,u8 command):

功能:指定地址的单字节读

参数:

client结构指针。

command:内部地址。

返回值:读到的数据,失败返回负数。

17.6.2 单字节写函数​

s32 i2c_smbus_write_byte_data(const struct i2c_client *client,

u8 command, u8 value)

功能:指定地址的单字节写入数据

参数:

client结构指针。

command:内部地址

value:要写入的数据

返回值:写如的数据,失败返回负数。

17.6.3 读指定长函数​

如果器件存在页写问题,建议使用这个函数,循环操作。

s32 i2c_smbus_read_i2c_block_data(const struct i2c_client *client,

u8 command,

u8 length,

u8 *values)

功能:从指定地址开始读取指定长度的数据。

参数:

client结构指针

command:内部地址

length:要读的数据长度

value:存放数据的指针

返回值:读到的数据,失败返回负数。

17.6.4 写指定长度函数​

s32 i2c_smbus_write_i2c_block_data(const struct i2c_client *client,

u8 command,

u8 length,

u8 *values)

功能:从指定地址开始读取指定长度的数据

参数:

client结构指针

command:内部地址

length:要写的数据长度

value:源数据指针

返回值:写入的数据,失败返回负数。

17.7 编程模板​

像平台设备一样,编写两层代码:设备层,驱动层。

17.7.1 驱动层代码模板​

驱动层是否需要写文件操作方法根据自己需要。

//编写器件ID列表

static const struct i2c_device_id ft5x0x_id_table[] = {

{ "ft5x0x", 0 },

{},

};

MODULE_DEVICE_TABLE(i2c, ft5x0x_id_table);

struct i2c_client *tsclient;


/* 编写probe函数,针对单点 */

static int __devinit ft5x0x_probe(struct i2c_client *client,

const struct i2c_device_id *id)

{

s32 val;


设备层传入的client结构

给用户提供访问接口

……

return 0;

}

/* 编写反探测函数 */

static int __devexit ft5x0x_remove(struct i2c_client *client)

{


return 0;

}


//1-驱动层核心核心结构

static struct i2c_driver ft5x0x_driver = {

.driver = {

可以在 sys/bus/i2c/drivers查看到

.owner = THIS_MODULE,

},

.probe = ft5x0x_probe,

.remove = __devexit_p(ft5x0x_remove),

.id_table = ft5x0x_id_table,

};


//模块初始换函数

static __init int ft5x0x_drv_init(void)

{

注册一个IIC驱动 */

i2c_add_driver(&ft5x0x_driver);

return 0;

}

//模块卸载函数

static __exit void ft5x0x_drv_exit(void)

{

i2c_del_driver(&ft5x0x_driver);

}


module_init(ft5x0x_drv_init);

module_exit(ft5x0x_drv_exit);

MODULE_LICENSE("GPL");


17.7.2 设备层模板​

1)获取 i2c_adapter 的内存地址

struct i2c_adapter *i2c_get_adapter(int nr)

作用:1)获取 i2c_adapter 结构地址

)增加 i2c_adapter 结构的引用计数,防止使用过程中被移除。

nr:就是适配器的总线编号:

就是0

就是1

返回:指针适配器结构的首地址,失败返回NULL。

2)减少i2c_adapter引用计数

使用 i2c_get_adapter 后都需要使用这个函数。

i2c_put_adapter(struct i2c_adapter *adap)


3)注册I2C设备

struct i2c_client *i2c_new_probed_device(struct i2c_adapter *adap,

struct i2c_board_info *info,

unsigned short const *addr_list,

int (*probe)(struct i2c_adapter *, unsigned short addr))

功能:向内核注册一个I2C设备

参数:

adap :i2c_client 所依赖的适配器指针,adap则通过i2c_get_adapter 函数获取

:i2c设备的描述信息

addr_list:i2c设备可能出现的地址表,是一个数组。

probe:探测到i2c设备时候会执行回调函数,一般是NULL。

4)struct i2c_board_info结构

内核使用 struct i2c_board_info 描述一个i2c设备基本信息,通过这些可以创建一个 i2c_client 结构。

/**

* struct i2c_board_info - template for device creation

用来初始化 i2c_client.name

用来初始化 i2c_client.flags

用来初始化 i2c_client.addr

用来初始化 i2c_client.dev.platform_data

* @archdata: copied into i2c_client.dev.archdata

* @of_node: pointer to OpenFirmware device node

用来初始化 i2c_client.irq

*/

struct i2c_board_info {

chartype[I2C_NAME_SIZE];

unsigned shortflags;

unsigned shortaddr;

void*platform_data;

struct dev_archdata*archdata;

struct device_node *of_node;

intirq;

};

5)取消一个I2C设备注册

void i2c_unregister_device(struct i2c_client*client)

6)设备层中地址表定义

就是传递给 i2c_new_probed_device函数的参数 addr_list,一定要以宏 I2C_CLIENT_END 结尾。

示例:

static const unsigned short ft5x0x_i2c[] = {

0x38, I2C_CLIENT_END

};//地址为0x38

关于平台数据:

如果i2c_driver中需要使用到平台数据,可以这样取出client->dev.platform_data,这个就是struct i2c_board_info的.platform_data成员

关注I2CI2c子系统的器件地址是纯地址,就裸机的地址表示方法 >> 1 得到。

比如:0xA0 在Linux系统中是 0xA0>>1 得到 0X50。

I2C子系统机制和平台设备不一样,虽然是通过id_table,但是还有一个条件,就是i2c器件是真实存在的。

17.8 I2C注册层方式​

1)动态注册:可以自己后期安装模块注册

2)静态注册:把信息写板级文件中。

注意:测试时要去除内核自带驱动。

Symbol: TOUCHSCREEN_FT5X0X [=n]

Type : tristate

Prompt: FocalTech ft5x0x TouchScreen driver

Defined at drivers/input/touchscreen/Kconfig:312

Depends on: !S390 && !UML && INPUT [=y] && INPUT_TOUCHSCREEN [=y] && MACH_TINY4412 [=y]

Location:

-> Device Drivers

-> Input device support

-> Generic input layer (needed for keyboard, mouse, ...) (INPUT [=y])

-> Touchscreens (INPUT_TOUCHSCREEN [=y])

17.9 IIC设备驱动存在两种注册​

17.9.1 动态注册​

I2C设备的信息也是系统启动后通过安装模块形式。其法。

#include <linux/i2c.h>

#include <linux/slab.h>

#include <linux/export.h>

#include <linux/module.h>

static struct i2c_client *ft5x0x_client;

/* 传入iic的设备地址和name给iic驱动端 */

static const unsigned short ft5x0x_i2c[] = {

0x38, I2C_CLIENT_END

};


static __init int ft5x0x_dev_init(void)

{

struct i2c_adapter *i2c_adap;

struct i2c_board_info ft5x0x_info;

memset(&ft5x0x_info, 0, sizeof(struct i2c_board_info));

strlcpy(ft5x0x_info.type, "ft5x0x", I2C_NAME_SIZE);


i2c_adap = i2c_get_adapter(1); //根据总线号取得适配器指针


注册iic设备 */

ft5x0x_client = i2c_new_probed_device(i2c_adap, &ft5x0x_info,

ft5x0x_i2c, NULL);

i2c_put_adapter(i2c_adap);

return 0;

}


static __exit void ft5x0x_dev_exit(void)

{

i2c_unregister_device(ft5x0x_client);

}


module_init(ft5x0x_dev_init);

module_exit(ft5x0x_dev_exit);

MODULE_LICENSE("GPL");


17.9.2 静态注册比较常见。​

直接mach-xxxxx.c 板级文件中的机器初始化函数通过传递struct i2c_board_info 结构,使用

int __initi2c_register_board_info(int busnum,

struct i2c_board_info const *info,

unsigned len)

busnum :I2C设备挂接的总线号示例,填入0,

info已经填充好的 struct i2c_board_info 结构指针

len:代表info指针多少个 struct i2c_board_info 元素。(条总线挂了多少个设备)

只需要调用这个函数就可以创建一个i2c_client 结构,并且注册。

示例

static struct i2c_board_info ft5x06_info[] = {

//,实际上是填充了typeaddr成员

I2C_BOARD_INFO("ft5x0x_ts", (0x70 >> 1)),

.platform_data = &ft5x0x_pdata, //平台数据可有可无,根据驱动需要传递。

}


#define I2C_BOARD_INFO(dev_type, dev_addr) \

.type = dev_type, .addr = (dev_addr)


然后在机器初始化函数中调用

i2c_register_board_info(1, ft5x06_info,1);

样内核会根据总线号和struct i2c_board_info信息构建一个i2c_client结构并且注册。该结构和总线编号对应的适配绑定起来

注意:静态注册方式必须保证先于适配器驱动注册。一般情况下只要注册代码写在板级文件机器初始化函数中,基本上不会出问题。

以下截图,是开发板底层静态初始化IIC信息的代码:

#导入Word文档图片# Linux下I2C驱动架构全面分析​_I2C_04


17.10 IIC接口ft5x06触摸屏驱动代码示例​

FT5x06系列ICs是单芯片电容式触摸屏控制器IC,带有一个内置的8位微控制器单元(MCU)。采用互电容的方法,在配合的相互的电容式触摸面板,它支持真正的多点触摸功能。FT5x06具有用户友好的输入的功能,这可以应用在许多便携式设备,例如蜂窝式电话,移动互联网设备,上网本和笔记本个人电脑。FT5x06系列IC包括FT5206/FT5306/FT5406。

如果内核有自带的驱动,需要将内核自带的驱动先卸载掉!

[root@XiaoLong linux-3.5]# make menuconfig

Device Drivers --->

Input device support --->

[*] Touchscreens --->

<> FocalTech ft5x0x TouchScreen driver

开发板触摸屏芯片驱动-将*号去掉

17.10.1 FT5x06设备端代码​

#include <linux/init.h>

#include <linux/module.h>

#include <linux/platform_device.h>

#include <linux/i2c.h>

#include <linux/irq.h>

#include <linux/gpio.h>

#include <mach/gpio.h>

#include <plat/gpio-cfg.h>

static struct i2c_client *i2cClient = NULL;

static unsigned short const i2c_addr_list[] =

{

0x38, I2C_CLIENT_END

};//地址队列



static int __init i2c_dev_init(void)

{

struct i2c_adapter *i2c_adap;//获取到的总线存放在这个结构体

struct i2c_board_info i2c_info;//设备描述结构体,里面存放着欲设备的名字还有地址


/*获取IIC控制器*/

i2c_adap = i2c_get_adapter(1);


/*清空结构体*/

memset(&i2c_info,0,sizeof(struct i2c_board_info));


/*名称的赋值*/

strlcpy(i2c_info.type,"FT5X06",I2C_NAME_SIZE);


/*获取中断号*/

i2c_info.irq=gpio_to_irq(EXYNOS4_GPX1(6));


/*创建IIC设备--客户端*/

i2cClient = i2c_new_probed_device(i2c_adap,&i2c_info,i2c_addr_list,NULL);


/*设置模块信息 */

i2c_put_adapter(i2c_adap);


printk("i2c_dev_init!!\n");

return 0;

}



static void __exit i2c_dev_exit(void)//平台设备端的出口函数

{

printk(" i2c_dev_exit ok!!\n");


/*注销设备*/

i2c_unregister_device(i2cClient);


/*释放*/

i2c_release_client(i2cClient);


//增加模块计数

try_module_get(i2c_adap->owner);

}


module_init(i2c_dev_init);

module_exit(i2c_dev_exit);

MODULE_LICENSE("GPL");

17.10.2 FT5X06驱动端代码​

以下的驱动代码单纯读取了触摸屏的坐标信息,没有加入输入子系统上报,下一章节讲了输入子系统再对触摸屏代码进行修改。

#include <linux/init.h>

#include <linux/module.h>

#include <linux/platform_device.h>

#include <linux/i2c.h>

#include <linux/interrupt.h>

#include <linux/irq.h>

#include <linux/workqueue.h>


static struct work_struct work;

static struct i2c_client *client_drv;

static const struct i2c_device_id i2c_id[] =

{

{"FT5X06",0},//设备端的名字为"myiic",0表示不需要私有数据

{}

};


//工作队列处理函数

static void work_func(struct work_struct *work)

{

s32 x=0,y=0,num=0;


/*1. 读出按下的点数量*/

num=i2c_smbus_read_byte_data(client_drv,0x02)&0x0f;


/*2. 读取X坐标*/

x=(i2c_smbus_read_byte_data(client_drv,0x03)&0x0f)<<8; //高位

x|=i2c_smbus_read_byte_data(client_drv,0x04); //低位


/*3. 读取Y坐标*/

y=(i2c_smbus_read_byte_data(client_drv,0x05)&0x0f)<<8; //高位

y|=i2c_smbus_read_byte_data(client_drv,0x06); //低位


printk("num=%d,x=%d,y=%d\n",num,x,y);

}


//中断服务函数

static irqreturn_t irq_handler(int irq, void *dev)

{

schedule_work(&work);

return IRQ_NONE;

}

static int i2c_probe(struct i2c_client *client, const struct i2c_device_id *device_id)//匹配成功时调用

{

client_drv=client;

printk("i2c_probe!!!\n");

printk("驱动端IIC匹配的地址=0x%x\n",client->addr);


注册触摸屏的中断*/

if(request_irq(client->irq,irq_handler,IRQ_TYPE_EDGE_BOTH,client->name,NULL)!=0)

{

中断注册失败!\n");

}


/*初始化工作*/

INIT_WORK(&work,work_func);

return 0;

}

static int i2c_remove(struct i2c_client *client)

{

printk("i2c_remove!!!\n");

//注销中断

free_irq(client->irq,NULL);

return 0;

}


struct i2c_driver i2c_drv =

{

.driver = //这个不添加会报错,实际上没作用

{

.name = "XL",

.owner = THIS_MODULE,

},

.probe = i2c_probe, //探测函数

.remove = i2c_remove, //资源卸载

.id_table = i2c_id, //里面有一个名字的参数用来匹配设备端名字

};

/*iic驱动端*/

static int __init i2c_drv_init(void)

{

i2c_add_driver(&i2c_drv);//向iic总线注册一个驱动

return 0;

}


static void __exit i2c_drv_exit(void)//平台设备端的出口函数

{

i2c_del_driver(&i2c_drv);

}


module_init(i2c_drv_init);

module_exit(i2c_drv_exit);

MODULE_LICENSE("GPL");

17.11 IIC接口IT7260触摸屏驱动代码示例​

17.11.1 IT7260设备端代码​

#include <linux/init.h>

#include <linux/module.h>

#include <linux/platform_device.h>

#include <linux/i2c.h>


static struct i2c_client *i2cClient = NULL;

static unsigned short i2c_addr_list[0xFF]={0x46,I2C_CLIENT_END};

static int __init i2c_dev_init(void)

{

int i;

struct i2c_adapter *i2c_adap;//获取到的总线存放在这个结构体

struct i2c_board_info i2c_info;//设备描述结构体,里面存放着欲设备的名字还有地址

/*获取IIC控制器*/

i2c_adap = i2c_get_adapter(1);

/*清空结构体*/

memset(&i2c_info,0,sizeof(struct i2c_board_info));

/*名称的赋值*/

strlcpy(i2c_info.type,"IT7260_touch",I2C_NAME_SIZE);


/*创建IIC设备--客户端*/

i2cClient = i2c_new_probed_device(i2c_adap,&i2c_info,i2c_addr_list,NULL);

if(i2cClient==NULL)

{

printk("地址不存在\r\n");

}

else

{

printk("地址存在0x%x\r\n",i-1);

}


/*设置模块信息 */

i2c_put_adapter(i2c_adap);

printk("i2c_dev_init!!\n");

return 0;

}


static void __exit i2c_dev_exit(void)//平台设备端的出口函数

{

printk(" i2c_dev_exit ok!!\n");

/*注销设备*/

i2c_unregister_device(i2cClient);

/*释放*/

i2c_release_client(i2cClient);

}

module_init(i2c_dev_init);

module_exit(i2c_dev_exit);

MODULE_LICENSE("GPL");

17.11.2 IT7260驱动端代码​

#include <linux/module.h>

#include <linux/kernel.h>

#include <linux/input.h>

#include <linux/interrupt.h>

#include <linux/pm.h>

#include <linux/slab.h>

#include <asm/io.h>

#include <linux/i2c.h>

#include <linux/timer.h>

#include <linux/gpio.h>

#include <linux/irq.h>

#include <plat/ctouch.h>


static int it7260_irq; //中断号

static struct i2c_client *it7260_client; //IIC客户端

static struct work_struct work; 工作队列


static void it7260_ts_poscheck(struct work_struct *work)

{

unsigned char buf[14];

unsigned short xpos, ypos;

unsigned char event=0;

memset(buf, 0, sizeof(buf));


/*读取触摸屏寄存器数据*/

i2c_smbus_read_i2c_block_data(it7260_client,0xE0,14,buf);


/*触摸按键*/

if (buf[0] == 0x41)

{

if (buf[1] == 0x04)printk("stat=%d,KEY_MENU=%d\n",buf[1],!!buf[2]);

else if (buf[1] == 0x03)printk("stat=%d,KEY_HOMEPAGE=%d\n",buf[1],!!buf[2]);

else if (buf[1] == 0x02)printk("stat=%d,KEY_BACK=%d\n",buf[1],!!buf[2]);

else if (buf[1] == 0x01)printk("stat=%d,KEY_SEARCH=%d\n",buf[1],!!buf[2]);

}


/* 第一个点*/

if (buf[0] & 0x01)

{

xpos = ((buf[3] & 0x0F) << 8) | buf[2];

ypos = ((buf[3] & 0xF0) << 4) | buf[4];

event = buf[5] & 0x0F;

printk("x=%d,y=%d,ev=%d\n",xpos,ypos,event);

}

}


static irqreturn_t it7260_ts_isr(int irq, void *dev_id)

{

schedule_work(&work);

return IRQ_HANDLED;

}


/*

资源匹配函数

*/

static int it7260_ts_probe(struct i2c_client *client,const struct i2c_device_id *idp)

{

it7260_client = client;

INIT_WORK(&work, it7260_ts_poscheck);

it7260_irq = gpio_to_irq(EXYNOS4_GPX1(6));

request_irq(it7260_irq, it7260_ts_isr,IRQ_TYPE_EDGE_BOTH,client->name, NULL);

return 0;

}


/*

资源卸载函数

*/

static int __devexit it7260_ts_remove(struct i2c_client *client)

{

free_irq(it7260_irq, NULL);

return 0;

}


/*IIC驱动名称匹配结构*/

static const struct i2c_device_id it7260_ts_id[] = {

{"IT7260_touch", 0},

{}/* should not omitted */

};


/*底层结构*/

static struct i2c_driver it7260_ts_driver = {

.driver={

.name = "IT7260",

},

.probe = it7260_ts_probe, /*资源探测*/

.remove = it7260_ts_remove, /*资源卸载*/

.id_table = it7260_ts_id, /*ID匹配*/

};


static int __init it7260_ts_init(void)

{

return i2c_add_driver(&it7260_ts_driver);

}


static void __exit it7260_ts_exit(void)

{

i2c_del_driver(&it7260_ts_driver);

}


module_init(it7260_ts_init);

module_exit(it7260_ts_exit);


MODULE_LICENSE("GPL");

MODULE_DESCRIPTION("it7260 touchscreen driver");

17.12 IIC接口AT24C02驱动代码示例​

17.12.1 应用层代码​

#include <stdio.h>

#include <sys/types.h>

#include <sys/stat.h>

#include <fcntl.h>


/*

AT24C02 应用层测试代码

*/

int main(int argc,char **argv)

{

int fp;

char buff_write[]="AT24C02读写测试!123456789ABCDEFG";

char buff_read[100];

fp=open("/dev/Tiny4412_AT24C02",O_RDWR);

if(fp<0) /*判断文件是否打开成功*/

{

printf("AT24C02 driver open error!\n");

return -1;

}

printf("写入的数据:%s\n",buff_write);

write(fp,buff_write,strlen(buff_write)+1); /*调用写函数->向驱动层传递数据*/

read(fp,buff_read,strlen(buff_write)+1); /*调用读函数->获取驱动层的数据*/

printf("读出的数据:%s\n",buff_read);

close(fp);

return 0;

}

17.12.2 设备端代码​

#include <linux/init.h>

#include <linux/module.h>

#include <linux/platform_device.h>

#include <linux/i2c.h>


/*获取总线*/

struct i2c_adapter *i2c_adap; //获取到的总线存放在这个结构体

static struct i2c_client *i2cClient = NULL;


//AT24C02固定地址 b1010

//AT24C02硬件地址 b000

//组合:b1010000 = 0x50

//注意:IIC标准地址是7位

static unsigned short const i2c_addr_list[] =

{

0x50, I2C_CLIENT_END

};//地址队列



static int __init i2c_dev_init(void)

{

struct i2c_board_info i2c_info;//设备描述结构体,里面存放着欲设备的名字还有地址

i2c_adap = i2c_get_adapter(0); //获取0号总线

if(i2c_adap==NULL)

{

printk("AT24C02--II总线0 获取失败!!\n");

}


memset(&i2c_info,0,sizeof(struct i2c_board_info));//把设备描述结构体清空结构体清空

strlcpy(i2c_info.type,"Tiny4412_AT24C02",I2C_NAME_SIZE);//把设备的名字赋值给i2c_info


i2cClient = i2c_new_probed_device(i2c_adap,&i2c_info,i2c_addr_list,NULL);

if(i2cClient==NULL)

{

printk("AT24C02 0x%x:地址不可用!!\n",i2c_addr_list[0]);

}

i2c_put_adapter(i2c_adap);

printk("AT24C02_dev_init初始化成功!!\n");

return 0;

}



static void __exit i2c_dev_exit(void)//平台设备端的出口函数

{

/*注销设备*/

i2c_unregister_device(i2cClient);

i2c_release_client(i2cClient);

printk("AT24C02_dev_exit ok!!\n");

}


module_init(i2c_dev_init);

module_exit(i2c_dev_exit);

MODULE_LICENSE("GPL");

17.12.3 驱动端代码​

#include <linux/init.h>

#include <linux/module.h>

#include <linux/platform_device.h>

#include <linux/i2c.h>

#include <linux/interrupt.h> /*注册中断相关*/

#include <linux/irq.h> 中断边沿类型定义*/

#include <linux/gpio.h> 中断IO口定义*/

#include <linux/workqueue.h> /*工作队列相关*/

#include <linux/mutex.h> /*互斥信号量头文件*/

#include <linux/delay.h>

#include <linux/miscdevice.h> /*杂项设备相关结构体*/

#include <linux/fs.h> /*文件操作集合头文件*/

#include <linux/uaccess.h> /*使用copy_to_user和copy_from_user*/


static struct i2c_client *at24cxx_client;


/*互斥锁相关*/

/*互斥锁*/

static DEFINE_MUTEX(my_mutexname);


/*相关的函数声明*/

static u8 i2c_read_data(u8 addr,u8 len,u8* str);

static ssize_t at24cxx_read(struct file *my_file, char __user *buf, size_t my_size, loff_t * my_loff);

static int at24cxx_release(struct inode *my_inode, struct file *my_file);

static ssize_t at24cxx_write(struct file *my_file, const char __user *buf, size_t my_size, loff_t *my_loff);

static int at24cxx_open(struct inode *my_inode, struct file *my_file);



/*定义一个文件操作集合结构体*/

static struct file_operations ops_at24cxx={

.owner = THIS_MODULE,

读函数-被应用层read函数调用*/

写函数-被应用层write函数调用*/

打开函数-被应用层open函数调用*/

释放函数*/

};


/*定义一个杂项设备结构体*/

static struct miscdevice misce_at24cxx={

.minor =MISC_DYNAMIC_MINOR, /*自动分配次设备号*/

.name = "Tiny4412_AT24C02", /*名称 在dev/目录下边可以找到*/

.fops = &ops_at24cxx, /*文件操作集合*/

};


/*

IIC驱动端

*/

static const struct i2c_device_id i2c_id[] =

{

{"Tiny4412_AT24C02",0},//设备端的名字为"my_at24c02",后面的表示需要私有数据

{}

};



/*----------------------------------

写数据函数

参数

addr:地址

len :长度

str :数据首地址

------------------------------------*/

static u8 i2c_write_data(u8 addr,u8 len,u8* str)

{

u8 i,flag=0;

for(i=0;i<len;i++)

{

/*循环写入数据*/

flag=i2c_smbus_write_byte_data(at24cxx_client,addr+i,str[i]);

if(flag<0)return -1;

写延时*/

}

return flag;

}


/*-----------------------------------------

AT24C02测试

写入一个字节再读出来比较

------------------------------------------*/

static s32 at24cxx_test(void)

{

s32 data;

/*写入数据*/

data=i2c_smbus_write_byte_data(at24cxx_client,0,0xAA);

if(data<0)printk("at24cxx write data error!\n");


msleep(2); /*写延时*/


/*读出数据*/

data=i2c_smbus_read_byte_data(at24cxx_client,0);

if(data<0)printk("at24cxx read data error!\n");


if(data==0xAA)

{

return 0;

}

else

{

return -1;

}

}



/*----------------------------------

读数据函数

参数

addr:地址

len :长度

str :存放数据首地址

------------------------------------*/

static u8 i2c_read_data(u8 addr,u8 len,u8* str)

{

u8 i,flag=0;

for(i=0;i<len;i++)

{

/*循环读出数据*/

flag=i2c_smbus_read_byte_data(at24cxx_client,addr+i);

if(flag<0)return -1;

str[i]=flag;

}

return flag;

}


static int at24cxx_open(struct inode *my_inode, struct file *my_file)

{

return 0;

}


static u8 at24cxx_addr=0;

static ssize_t at24cxx_read(struct file *my_file, char __user *buf, size_t my_len, loff_t * my_loff)

{

int error;

u8 read_buff[100];

i2c_read_data(at24cxx_addr,my_len,read_buff);

error=copy_to_user(buf,read_buff,my_len);

return error;

}


static ssize_t at24cxx_write(struct file *my_file, const char __user *buf, size_t my_len, loff_t *my_loff)

{

int error;

u8 write_buff[100];

unsigned char key=0;

error=copy_from_user(write_buff,buf,my_len); //接收应用层的数据

error=i2c_write_data(at24cxx_addr,my_len,write_buff);

return error;

}


static int at24cxx_release(struct inode *my_inode, struct file *my_file)

{

return 0;

}



static int i2c_probe(struct i2c_client *client, const struct i2c_device_id *device_id)//匹配成功时调用

{

at24cxx_client=client;

s32 data=0;

printk("<1>""AT24C02驱动端资源匹配成功!\n");

printk("<1>""驱动端IIC匹配的地址=0x%x\n",client->addr);

mutex_lock(&my_mutexname); /*获取互斥锁*/

/* 检测适配器是否支持smbus字节读写函数 */

if(i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))

{

printk("适配器支持smbus字节读写函数\n");

}


if(at24cxx_test()<0) /*测试AT24C02是否OK*/

{

printk("AT24C02 device read/write error!\n");

}

else

{

printk("AT24C02 device read/write ok!\n");

}


if(misc_register(&misce_at24cxx)==0)/*注册*/

{

printk("misc AT24C02 init ok!\n");

}

else

{

printk("misc AT24C02 init error!\n");

}


mutex_unlock(&my_mutexname); /*释放互斥锁*/

return 0;

}



static int i2c_remove(struct i2c_client *client)

{

if(misc_deregister(&misce_at24cxx)==0)/*注销*/

{

printk("at24cxx remove exit ok!\n");

}

printk("i2c_remove!!!\n");

printk("i2c_驱动端卸载成功!!!\n");

return 0;

}



struct i2c_driver i2c_drv =

{

.driver = //这个不添加会报错,实际上没作用

{

.name = "AT24C02", /*sys/bus/i2c/drivers*/

.owner = THIS_MODULE,

},

.probe = i2c_probe,//探测函数

.remove = i2c_remove,

.id_table = i2c_id,//里面有一个名字的参数用来匹配设备端名字

};


/*iic驱动端*/

static int __init i2c_drv_init(void)

{

i2c_add_driver(&i2c_drv);//向iic总线注册一个驱动

return 0;

}


static void __exit i2c_drv_exit(void)//平台设备端的出口函数

{

i2c_del_driver(&i2c_drv);

}

module_init(i2c_drv_init);

module_exit(i2c_drv_exit);

MODULE_LICENSE("GPL");

17.13 IIC接口PCF8591ADC芯片驱动​

PCF8591是一种具有I2C总线接口的A/D转换芯片。在与CPU的信息传输过程中仅靠时钟线SCL和数据线SDA就可以实现。

#导入Word文档图片# Linux下I2C驱动架构全面分析​_I2C_05


17.13.1 应用层代码​

#include <stdio.h>

#include <sys/types.h>

#include <sys/stat.h>

#include <fcntl.h>

/*

PCF8591 应用层测试代码

*/

int main(int argc,char **argv)

{

unsigned char data=0;

int fp;

float tmp; // tmp=5.34v 0.34

int a;

int b;

fp=open("/dev/Tiny4412_PCF8591",O_RDWR);

if(fp<0) /*判断文件是否打开成功*/

{

printf("PCF8591 driver open error!\n");

return -1;

}

while(1)

{

read(fp,&data,1);

write(fp,&data,1);

printf("ADC1=%d\n",data);

tmp=(float)data*(5.0/255); //电压= 采集的数字量*(参考电压/分辨率);

a=tmp; //a=5 tmp=5.3

b=(int)((tmp-a)*1000); //b=0.34

printf("ADC1=%d.%dV\r\n",(int)a,(int)b);

sleep(1);

}

close(fp);

return 0;

}

17.13.2 设备端代码​

#include <linux/init.h>

#include <linux/module.h>

#include <linux/platform_device.h>

#include <linux/i2c.h>


/*获取总线*/

struct i2c_adapter *i2c_adap; //获取到的总线存放在这个结构体

static struct i2c_client *i2cClient = NULL;


//PCF8591固定地址 b1001

//PCF8591硬件地址 b000

//组合:b1001000 = 0x48

//注意:IIC标准地址是7位

static unsigned short const i2c_addr_list[] =

{

0x48, I2C_CLIENT_END

};//地址队列



static int __init i2c_dev_init(void)

{

struct i2c_board_info i2c_info;//设备描述结构体,里面存放着欲设备的名字还有地址

i2c_adap = i2c_get_adapter(0); //获取0号总线

if(i2c_adap==NULL)

{

printk("PCF8591--II总线0 获取失败!!\n");

}


memset(&i2c_info,0,sizeof(struct i2c_board_info));//把设备描述结构体清空结构体清空

strlcpy(i2c_info.type,"Tiny4412_PCF8591",I2C_NAME_SIZE);//把设备的名字赋值给i2c_info


i2cClient = i2c_new_probed_device(i2c_adap,&i2c_info,i2c_addr_list,NULL);

if(i2cClient==NULL)

{

printk("PCF8591 0x%x:地址不可用!!\n",i2c_addr_list[0]);

}

i2c_put_adapter(i2c_adap);

printk("PCF8591_dev_init初始化成功!!\n");

return 0;

}



static void __exit i2c_dev_exit(void)//平台设备端的出口函数

{

/*注销设备*/

i2c_unregister_device(i2cClient);

i2c_release_client(i2cClient);

printk("PCF8591_dev_exit ok!!\n");

}


module_init(i2c_dev_init);

module_exit(i2c_dev_exit);

MODULE_LICENSE("GPL");

17.13.3 驱动端代码​

#include <linux/init.h>

#include <linux/module.h>

#include <linux/platform_device.h>

#include <linux/i2c.h>

#include <linux/interrupt.h> /*注册中断相关*/

#include <linux/irq.h> 中断边沿类型定义*/

#include <linux/gpio.h> 中断IO口定义*/

#include <linux/workqueue.h> /*工作队列相关*/

#include <linux/mutex.h> /*互斥信号量头文件*/

#include <linux/delay.h>

#include <linux/miscdevice.h> /*杂项设备相关结构体*/

#include <linux/fs.h> /*文件操作集合头文件*/

#include <linux/uaccess.h> /*使用copy_to_user和copy_from_user*/


#define AIN0 0x40

#define AIN1 0x41

#define AIN2 0x42

#define AIN3 0x43


static struct i2c_client *PCF8591_client; /*IIC设备总线*/


/*读取PCF8591 ADC数据*/

unsigned char PCF8591_ReadADC(unsigned char ch)

{

return i2c_smbus_read_byte_data(PCF8591_client,ch);

}



static int PCF8591_open(struct inode *my_inode, struct file *my_file)

{

return 0;

}


static ssize_t PCF8591_read(struct file *my_file, char __user *buf, size_t my_len, loff_t * my_loff)

{

unsigned char data=PCF8591_ReadADC(AIN0);

copy_to_user(buf,&data,1);


data=PCF8591_ReadADC(AIN1);

printk("1:%d\r\n",data);

data=PCF8591_ReadADC(AIN2);

printk("2:%d\r\n",data);

data=PCF8591_ReadADC(AIN3);

printk("3:%d\r\n",data);

return 0;

}



static ssize_t PCF8591_write(struct file *my_file, const char __user *buf, size_t my_len, loff_t *my_loff)

{

//DAC输出

i2c_smbus_write_byte_data(PCF8591_client,0x40,100);

return 0;

}



static int PCF8591_release(struct inode *my_inode, struct file *my_file)

{

return 0;

}



/*定义一个文件操作集合结构体*/

static struct file_operations ops_PCF8591={

.owner = THIS_MODULE,

读函数-被应用层read函数调用*/

写函数-被应用层write函数调用*/

打开函数-被应用层open函数调用*/

释放函数*/

};


/*定义一个杂项设备结构体*/

static struct miscdevice misce_PCF8591={

.minor =MISC_DYNAMIC_MINOR, /*自动分配次设备号*/

.name = "Tiny4412_PCF8591", /*名称 在dev/目录下边可以找到*/

.fops = &ops_PCF8591, /*文件操作集合*/

};


static int i2c_probe(struct i2c_client *client, const struct i2c_device_id *device_id)//匹配成功时调用

{

PCF8591_client=client;

printk("<1>""驱动端IIC匹配的地址=0x%x\n",client->addr);


/* 检测适配器是否支持smbus字节读写函数 */

if(i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))

{

printk("适配器支持smbus字节读写函数\n");

}


/*注册*/

misc_register(&misce_PCF8591);

return 0;

}



static int i2c_remove(struct i2c_client *client)

{

misc_deregister(&misce_PCF8591);/*注销*/

printk("i2c_驱动端卸载成功!!!\n");

return 0;

}


/*

IIC驱动端

*/

static const struct i2c_device_id i2c_id[] =

{

{"Tiny4412_PCF8591",0},//设备端的名字为"my_PCF8591",后面的表示需要私有数据

{}

};


struct i2c_driver i2c_drv =

{

.driver=

{

.name = "PCF8591",

.owner = THIS_MODULE,

},

.probe = i2c_probe,

.remove = i2c_remove,

.id_table = i2c_id,

};



static int __init i2c_drv_init(void)

{

i2c_add_driver(&i2c_drv);//向iic总线注册一个驱动

return 0;

}


static void __exit i2c_drv_exit(void)//平台设备端的出口函数

{

i2c_del_driver(&i2c_drv);

}


module_init(i2c_drv_init);

module_exit(i2c_drv_exit);

MODULE_LICENSE("GPL");


举报

相关推荐

0 条评论