IIC驱动框架:
在Linux平台外设中有不少的IIC外设,例如基于Linux系统的Android触摸屏获取触摸坐标、内部陀螺仪等等外设都是在IIC总线上工作,如果需要添加自己的IIC外设那必不可少需要了解IIC框架的运用。
开发环境:
PC :VMworkstation 12.0 Ubuntu 12 32bit
开发版:Tiny 4412 (三星猎户CPU4412,Cortex-A9)
Linux内核版本:Linux 3.5
PC内核阅读器:SourceInsight
一、Linux下内核IIC体系文件架构:
文件路径:/linux-3.5/drivers/i2c
i2c-core.c:核心功能API函数文件。
busses 文件夹:这个文件中包含了一些 I2C 总线的驱动。
algos 文件夹:实现了一些 I2C 总线适配器的 algorithm。
此外,内核中的 i2c.h 这个头文件对 i2c_driver、i2c_client、i2c_adapter 和i2c_algorithm 这 4 个数据结构进行了定义。
在i2c-core.c文件里面包括了所有的IIC的API函数,以下是常用的API:
1、注册/注销函数:i2c_add_driver/i2c_del_driver
2、收发一体函数:i2c_transfer
3、smbus指定长度读取/发送函数:i2c_smbus_write/read_i2c_block_data
4、获取i2c_adapter 内存地址:i2c_get_adapter
5、注册/注销IIC设备:i2c_new_probed_device/i2c_unregister_device
6、添加IIC适配器:i2c_add_adapter
7、添加IIC设备:i2c_add_driver
二、驱动框架结构:
主要分成三个层次:适配器层、设备层和驱动层;适配器层用户或获取(i2c_add_adapter)总线号并且里面提供最底层的IIC时序读写函数直接用于控制硬件IO收发;设备层主要是通过获取适配器注册成功的总线使用权(i2c_get_adapter),并且创建一个IIC设备(i2c_new_probed_device),主要是在平台设备框架的基础上将设备端的匹配名字与器件的地址发送到驱动端可以和驱动层合在一起,为了移植性,采用平台设备;驱动层主要注册创建的IIC设备,当与设备层匹配成功后可以注册字符设备,通过提供的API收发函数来将用户层的数据发送到IIC设备与读取IIC设备数据信息到用户层。
1、适配器:int i2c_add_adapter(struct i2c_adapter *adapter);
根据函数的传参,先定义i2c_adapter 结构,先了解下i2c_adapter 这个结构内容:
struct i2c_adapter
{
struct module *owner;/*所属模块一般为: THIS_MODULE*/
unsigned int class; /* classes to allow probing for 一般为: I2C_CLASS_HWMON | I2C_CLASS_SPD */
const struct i2c_algorithm *algo;/*总线通信方法结构体指针 ,实现底层通信*/
void *algo_data;/* algorithm 数据 */
/* data fields that are valid for all devices */
struct rt_mutex bus_lock;
int timeout; /* in jiffies*/
int retries; /* 超时重试次数*/
struct device dev; /* the adapter device 适配器设备结构*/
int nr; /* 总线编号*/
char name[48];/*适配器名称*/
struct completion dev_released;
struct mutex userspace_clients_lock;
struct list_head userspace_clients;
};
以上加粗的是需要赋值的,里面有一个struct i2c_algorithm结构,显然还需要再定义这个结构体赋值于i2c_adapter结构体内部,这个结构:
struct i2c_algorithm
{
int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs, int num);
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 *);
};
master_xfer:这个接口但是底层收发的函数,num的值为1表示写,为2表示读,原因将在后面分析内核函数时说明。并且这个函数我们暂时还不能实现,因为传参msg还没有知道是什么。将在后面介绍。
也就是说,首先得知道是发送还是接收,其次要发送完数据首先得知道发送地址,发送长度,发送的缓冲区这三点或者是读 取时的读取地址,读取长度,读取缓冲区。
functionality:调用时返回I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_NOSTART |I2C_FUNC_PROTOCOL_MANGLING
适配器初始化代码:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/delay.h>
#include <linux/io.h>
/*
具体的硬件时序代码
*/
static u32 IIC_ADAPTER_i2c_func(struct i2c_adapter *adap)
{
return I2C_FUNC_I2C |I2C_FUNC_SMBUS_EMUL|I2C_FUNC_NOSTART|I2C_FUNC_PROTOCOL_MANGLING;
}
static int IIC_ADAPTER_i2c_xfer(struct i2c_adapter *adap,struct i2c_msg *msgs, int num)
{
/*调用具体的发送与接收函数,在后面分析*/
return 0;
}
static const struct i2c_algorithm IIC_ADAPTER_i2c_algorithm = {
.master_xfer = IIC_ADAPTER_i2c_xfer,
.functionality = IIC_ADAPTER_i2c_func,
};
static int __init i2c_adapter_init(void)
{
/*
GPIO硬件初始化
*/
adapter.owner = THIS_MODULE;
adapter.algo = &IIC_ADAPTER_i2c_algorithm;
adapter.retries = 2;
adapter.class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
strlcpy(adapter.name,"IIC_ADAPTER_i2c",sizeof(adapter.name));
i2c_add_adapter(&adapter);
printk("适配器分配成功的总线编号: %d\n",adapter.nr);
return 0;
}
static void __exit i2c_adapter_exit(void)
{
iounmap(GPD1CON);
iounmap(GPD1DAT);
i2c_del_adapter(&adapter);
printk(" IIC适配器注销成功!\n");
}
module_init(i2c_adapter_init);
module_exit(i2c_adapter_exit);
MODULE_LICENSE("GPL");
2、设备层:
获取总线使用权:struct i2c_adapter *i2c_get_adapter(int nr) /*要使用总线,总线编号是通过适配器注册并且打印出来的*/
创建一个IIC设备: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));
故在调用之前还需要传入两个结构:struct i2c_board_info 与 unsigned short const *addr_list,其中struct i2c_board_info为一个结构体类型用于存放板级的信息:
struct i2c_board_info {
char type[I2C_NAME_SIZE];/* i2c设备端名称,匹配用,名称指定20 */
unsigned short flags;
unsigned short addr; /* i2c设备地址*/
void *platform_data; /* 平台设备私有数据 */
struct dev_archdata *archdata;
struct device_node *of_node;
int irq; /* 中断号 */
};
要与驱动端匹配,起码一点至少要将type赋值,这是能否匹配的到的必要条件。i2c_new_probed_device函数到底干了些什么,下面分析:
前期主要是对地址的检查与核实,将addr_list器件地址保存到板级信息结构体里面,其中addr_list结构如下:
static unsigned short i2c_addr_list[0xFF]=
{
0x50, /*实际的外设地址,低七位有效 AT24C02:厂商地址:1010 硬件连接:000 ---->01010000*/
I2C_CLIENT_END /*I2C_CLIENT_END为结束符,通过这个来判断地址已经结束*/
};
最后还是调用最后一个函数:i2c_new_device这个函数又执行了什么:
除去一些错误的保护机制,主要将i2c_adapter与i2c_board_info进行进一步的打包,将所有的数据都保存在最终的结构体里面,最终将数据匹配成功后发送到驱动层。最后再调用i2c_put_adapter设置模块信息记录模块的使用计数。
设备层代码:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/i2c.h>
static struct i2c_client *i2cClient = NULL;
static struct i2c_adapter *i2c_adap;
static struct i2c_board_info i2c_info;
static unsigned short i2c_addr_list[0xFF]=
{
0x50,I2C_CLIENT_END
};
static int __init i2c_dev_init(void)
{
i2c_adap = i2c_get_adapter(9); /*要使用总线*/
if(i2c_adap==NULL)
{
printk("适配器获取失败!\n");
return 0;
}
memset(&i2c_info,0,sizeof(struct i2c_board_info));//清空数组信息
strlcpy(i2c_info.type,"touch_name",I2C_NAME_SIZE);//数组成员赋值
i2cClient = i2c_new_probed_device(i2c_adap,&i2c_info,i2c_addr_list,NULL);
if(i2cClient==NULL)
{
printk("地址检查错误!\n");
return 0;
}
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");
if(i2c_adap!=NULL)
{
i2c_unregister_device(i2cClient);
i2c_release_client(i2cClient);
}
}
module_init(i2c_dev_init);
module_exit(i2c_dev_exit);
MODULE_LICENSE("GPL");
3、驱动层:
注册一个IIC设备:i2c_add_driver(struct i2c_driver *driver);
实际在内核里面上面的函数只是一个宏:#define i2c_add_driver(driver) i2c_register_driver(THIS_MODULE, driver),实质是调用i2c_register_driver来注册的。注册之前要定义一个i2c_driver 结构体:
struct i2c_driver
{
unsigned int class;
int (*attach_adapter)(struct i2c_adapter *) __deprecated;
int (*detach_adapter)(struct i2c_adapter *) __deprecated;
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;
int (*detect)(struct i2c_client *, struct i2c_board_info *);
const unsigned short *address_list;
struct list_head clients;
};
struct device_driver driver:结构体主要对 .name和 .owner (固定THIS_MODULE),其中.name用于与设备端匹配的i2c_board_info里面的.type成员一致,如果i2c_device_id 定义了就以id_table为准。当匹配成功了,就可以调用内核的收发函数来发送数据。
驱动层代码:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/i2c.h>
#include <linux/delay.h>
static const struct i2c_device_id i2c_id[] =
{
{"touch_name",0},//设备端的名字为"myiic",0表示不需要私有数据
{}
};
static int i2c_probe(struct i2c_client *client, const struct i2c_device_id *device_id)//匹配成功时调用
{
printk("驱动端IIC匹配的地址=0x%x\n",client->addr);
u8 buff_read[10];
u8 buff_write[10];
strcpy(buff_write,"houjunzui");
i2c_smbus_write_i2c_block_data(client,1,10,buff_write);//写数据
mdelay(10);
i2c_smbus_read_i2c_block_data(client,1,10,buff_read);//读数据
printk("read: %s \n",buff_read);
return 0;
}
static int i2c_remove(struct i2c_client *client)
{
printk("i2c_remove!!!\n");
return 0;
}
struct i2c_driver i2c_drv =
{
.driver = //这个不添加会报错,实际上没作用
{
.name = "iic_drv",
.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");
三、发送/接收函数分析:
先介绍发送函数:i2c_smbus_read_i2c_block_data
传入参数:第一个为来自probe函数的传参,第二个为可以传递到底层的一个寄存器地址,比如使用AT24C02可以为读取写入数据寄存器的地址,第三个参数是读取数据的长度,最后一个是读取的数据存放的缓冲区。很显然,这是个上层函数,对所有的功能已经完成了一定的封装。用于通过IIC设备从某一块地址读取某一长度的数据到缓冲区。上面的函数结构可以看出,主要只是对length变量进行了一次判断,并没有实际读取功能,最终将所有传入的参数传参给函数:i2c_smbus_xfer,当这个函数返回后进行内存拷贝,将data.block里面的数据拷贝到读取缓冲区,可以看出,i2c_smbus_xfer函数将读取的数据放在data.block里面然后再拷贝到读取缓冲区,并且可以看出有效数据是从data.block[1]开始,data.block[0]存放的是读取的长度。之后便要分析i2c_smbus_xfer函数:
很显然,我们在适配器里面并没有实现smbus_xfer函数,if后面的命题为假,执行else后面的代码,又将所有的传入参数传递到下一个函数:i2c_smbus_xfer_emulated,由于这个函数有点长,我们简单起来分析。先了解传入的参数传递关系:
以下是对i2c_smbus_xfer_emulated进行简写,将无用的代码删掉后:
unsigned char msgbuf0[I2C_SMBUS_BLOCK_MAX+3];//定义一个写缓冲区,用于IIC设备读取的缓冲区
unsigned char msgbuf1[I2C_SMBUS_BLOCK_MAX+2];//定义一个读缓冲区,用于IIC设备读取的缓冲区
int num = read_write == I2C_SMBUS_READ ? 2 : 1;//读调用时为2,写为1.此参数用于返回给适配器的注册时i2c_algorithm结构体的.master_xfer变量的第三个参数:static int IIC_ADAPTER_i2c_xfer(struct i2c_adapter *adap,struct i2c_msg *msgs, int num),用于区分操作是读还是写。由此可知IIC_ADAPTER_i2c_xfer里面的简单模板:
static int IIC_ADAPTER_i2c_xfer(struct i2c_adapter *adap,struct i2c_msg *msgs, int num)
{
if(num==1)
{
//底层写函数,在适配器层
}
else if(num==2)
{
//底层读函数,在适配器层
}
return 0;
}
现在知道了是读操作,剩下读取的长度与读取到的缓冲区位置和读取地址。
struct i2c_msg msg[2] = { { addr, flags, 1, msgbuf0 },
{ addr, flags | I2C_M_RD, 0, msgbuf1 } };//用于存放读取发送缓冲信息,里面的参数: addr:器件地址;flags:消息额 外的标志位;len:传输数据的字节数;buf:接收/发送缓冲区
msgbuf0[0] = command;/*寄存器地址,i2c_smbus_read_i2c_block_data函数的第2个参数,用于传递给硬件的读取写入地址*/
switch (size) //调用i2c_smbus_read_i2c_block_data或者i2c_smbus_write_i2c_block_data时,size都是等于I2C_SMBUS_BLOCK_DATA
{
case I2C_SMBUS_BLOCK_DATA:
if (read_write == I2C_SMBUS_READ) /*读时事件,对msg[1]操作*/
{
msg[1].flags |= I2C_M_RECV_LEN;
msg[1].len = 1; /* block length will be added by the underlying bus driver */
}
else/*写事件*/
{
/*写的长度赋值,在前面的函数里存储长度的变量是: data->block[0],现在给msg[0].len,只有写操作这个变量 有效。读操作msg[1].len并非是读取的长度而是1,见上面对 msg[1].len的复制。读操作控制长度由data->block[0]决定,后面分析 */
msg[0].len = data->block[0] + 2;
if (msg[0].len > I2C_SMBUS_BLOCK_MAX + 2)
{
dev_err(&adapter->dev,"Invalid block write size %d\n",data->block[0]);
return -EINVAL;
}
for (i = 1; i < msg[0].len; i++)
msgbuf0[i] = data->block[i-1];
/*这里为啥要将data->block[0]的数据存放在写缓冲区,原因是i2c_smbus_write_i2c_block_data函数将要写的数据保存在 data->block[0],然后再将要写入的数据存放在msgbuf0[1]开始,通过i2c_transfer函数发送*/
}
break;
}
i2c_transfer(adapter, msg, num);/*收发一体函数,num=2表示发送,为1表示读取消息*/
if(read_write == I2C_SMBUS_READ)//用于提取msg里面读取的信息,只有读取消息函数有效。
{
switch (size)
{
case I2C_SMBUS_I2C_BLOCK_DATA:
for (i = 0; i < data->block[0]; i++)
data->block[i+1] = msgbuf1[i];
/*读完的数据存放在msgbuf1里面,block[0]被占用了从block[1]开始赋值, i2c_smbus_read_i2c_block_data调用i2c_smbus_xfer再调用i2c_smbus_xfer_emulated再间接调用i2c_transfer将数据读取在msgbuf后在这里将数据存放在block[1]开始的内存 后返回,通过memcpy将block[1]开始的数据复制到读取数据缓冲区里面 */
break;
}
}
总体概述:
i2c_smbus_read_i2c_block_data和i2c_smbus_write_i2c_block_data对长度判断后将所有的数据转发给了另一个函数:i2c_smbus_xfer,然后i2c_smbus_xfer又做了一个中转,将所有的参数传递给i2c_smbus_xfer_emulated,由上面的函数体可以知道,i2c_smbus_xfer_emulated函数主要负责对msg结构的封装以及对发送数据与接收数据的装载,通过发送函数与接收函数来改变read_write这个函数传参的宏来判断到底是读操作还是写操作,当前是读操作时num的值为1,data->block[0]存放的是读取的长度,i2c_transfer函数将msg与num传入,这个函数通过IIC读取设备数据,将读取的数据保存在msgbuf1[0]开始,通过将msgbuf1数据复制到data->block[1] 开始的内存里面,复制的长度是由data->block[0] 来决定,读取的数据还有一个command参数,赋值在msg[0].buf[0]变量里面,可以用作传递给底层硬件的命令或者设备寄存器的地址等(读写位置),当函数i2c_smbus_xfer_emulated返回键读取的数据拷贝到上层读取缓冲区。同理,当当前的操作时写时num的值为2,data->block[0]存放的是读取的长度,但是在i2c_smbus_xfer_emulated里面将长度值转存在msg[0].len,这是于读取操作有区别的地方,然后将要写的数据从data->block[1]开始复制到msg[0].len长度到msg[0].buf[1]开始的内存空间,因为msg[0].buf[0]存储的是上层的command。
以上阐述了msg消息的结构存放位置,下面看看收发一体函数:i2c_transfer:
主要是调用我们在适配器里面填充的结构体里面实现的函数指针,经过上面的分析,我们可以知道具体怎么填充适配器数据获取函数的msg与num的作用了,实际使用适配器注册的总线编号的设备在设备端获取的并且注册后,使用的是适配器里面的函数,这个接口的调用关系由上述的总结概括可以了解到驱动层使用i2c_smbus_read_i2c_block_data和i2c_smbus_write_i2c_block_data函数最终是将读取长度,读取位置,读取到哪或者写的长度,写的位置,写的数据存放在msg结构里面然后传递到适配器层,也就是说真正处理的地方是适配器,设备层以及驱动层对底层已经屏蔽起来了,这样我们只要在是适配器里面添加对设备初始化以及读取写入函数便可以操作IIC。
为了提供例子,我们采用AT24C02的读写函数来作为例子,其读写函数的函数定义为:
void IIC_ADAPTER_Write_Byte(u8 addr,u8 len,u8 *buff);
void IIC_ADAPTER_Write_Byte(u8 addr,u8 len,u8 *buff);
函数参数:addr表示读写在EEPROM里面的存储地址
len表示读取的长度
buff读写缓冲区
可知上面的参数都在msg里面,addr并非msg结构里面成员的addr,这里指的是AT24C02的存储地址,不是器件地址。addr通过驱动的command参数来传递,位于msg[0].buf[0],len在msg结构的成员的len里面,buff对应msg结构的buf,记住读取时是从msg[1].buf[0]开始读取,将数据读取的数据复制到msg[1].buf[0]开始的地址,而写入的数据从msg[0].buf[1]地址开始,因为msg[0].buf[0]被command占用。通过这里我们知道具体的IIC_ADAPTER_i2c_xfer函数的填参数:
static int IIC_ADAPTER_i2c_xfer(struct i2c_adapter *adap,struct i2c_msg *msgs, int num)
{
//不管是读操作还是写操作mags都有两个结构体,msgs[0]与msgs[1]。
if(num==1)
{
IIC_ADAPTER_Write_Byte(msgs[0].buf[0],msgs[0].len,msgs[0].buf);
//这里虽然传入的是msgs[0].buf,这个函数在里面处理了,从msgs[0].buf[1]开始存放
printk("adapter write data\n");
}
else if(num==2)
{
//读取到的数据存放在msgs[1].buf里面
IIC_ADAPTER_Read_Byte(msgs[0].buf[0],msgs[1].len,msgs[1].buf);
printk("adapter read data\n");
}
return 0;
}
通过上面的分析附上整个框架的代码:
适配器:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/delay.h>
#include <linux/io.h>
#define GPD1CON_addr (0x11400000+0x00C0)
#define GPD1DAT_addr (0x11400000+0x00C4)
//八位,前四位为厂商自带的地址1010 第一位到第三位为硬件地址,均连接0,最后一位为读写为,0:读,1:写
#define DEV_write_addr 0xA0 //write
#define DEV_read_addr 0xA1 //read
//SDA:GPD1_0
//SCL:GPD1_1
static volatile unsigned int *GPD1CON ;
static volatile unsigned int *GPD1DAT ;
#define SDA(x) if(x){*GPD1DAT |=1<<0;}else {*GPD1DAT&=~(1<<0);}
#define SCL(x) if(x){*GPD1DAT |=1<<1;}else {*GPD1DAT&=~(1<<1);}
#define SDA_STATE (*GPD1DAT&1<<0)
#define SDA_INPUT_MODE() {*GPD1CON&=~(0xF<<0*4);}//SDA输入模式
#define SDA_OUTPUT_MODE() {*GPD1CON&=~(0xF<<0*4);*GPD1CON|=0x1<<0*4;}//SDA输出模式
static struct i2c_adapter adapter;
static void IIC_Init(void)
{
*GPD1CON&=~(0xF<<0*4);//SDA初始化
*GPD1CON|=0x1<<0*4;
*GPD1CON&=~(0xF<<1*4);//SCL初始化
*GPD1CON|=0x1<<1*4;
SCL(1);
SDA(1);
}
static void START_condition()
{
SDA_OUTPUT_MODE();
SCL(1);
SDA(1);//start 条件之前
udelay(3);
SDA(0);
udelay(3);
SCL(0);
}
static void STOP_condition()
{
SDA_OUTPUT_MODE();
SCL(1);
SDA(0);//stop 条件之前
udelay(3);
SDA(1);
}
static u8 IIC_ADAPTER_BYTE_READ()
{
int i;
u8 data=0;
SDA_INPUT_MODE();
SCL(0);
for(i=0;i<8;i++)
{
SCL(1);
udelay(3);
data<<=1;
if(SDA_STATE)
{
data|=1;
}
SCL(0);
udelay(3);
}
SCL(0);//可不要 确保不是进入stop状态
return data;
}
static void IIC_ADAPTER_BYTE_WRITE(char data)
{
SDA_OUTPUT_MODE();
int i;
for(i=0;i<8;i++)
{
SCL(0);
if(data&0x80)
{
SDA(1);
}
else
{
SDA(0);
}
udelay(3);
SCL(1);
udelay(3);
data<<=1;
}
SCL(0);
}
static int RECIVE_ACK()//主机发送数据后接收来自从机的ack
{
SDA_INPUT_MODE();
SDA(1);//******上拉
int i=0;
SCL(0);
udelay(3);
SCL(1);
while(SDA_STATE)
{
i++;
udelay(1);
if(i>=100)
{
printk("超时\n");
return 1;
}
}
SCL(0);
return 0;
}
static void SEND_ACK(int statu)//主机接收数据后发送给从机的ack
{
SDA_OUTPUT_MODE();
SCL(0);
if(statu==0)//ack
{
SDA(0);
udelay(3);
}
else
if(statu==1)// no ack
{
SDA(1);
udelay(3);
}
SCL(1);//告诉从机读
udelay(3);
SCL(0);
}
void IIC_ADAPTER_Read_Byte(u8 addr,u8 len,u8 *buff)
{
int i=0;
START_condition();
IIC_ADAPTER_BYTE_WRITE(DEV_write_addr);//读操作,但是告诉主机从机要执行的是读操作,先要写入写命令
RECIVE_ACK();
IIC_ADAPTER_BYTE_WRITE(addr);//然后写入要读取的地址
RECIVE_ACK();
START_condition();
IIC_ADAPTER_BYTE_WRITE(DEV_read_addr);//告诉从机开始读数据了
RECIVE_ACK();
for(i=0;i<len;i++)
{
buff[i]=IIC_ADAPTER_BYTE_READ();
SEND_ACK(0);
}
SEND_ACK(1);
STOP_condition();//
}
void IIC_ADAPTER_Write_Byte(u8 addr,u8 len,u8 *buff)
{
int i=0;
START_condition();
IIC_ADAPTER_BYTE_WRITE(DEV_write_addr);
RECIVE_ACK();
IIC_ADAPTER_BYTE_WRITE(addr);
RECIVE_ACK();
for(i=1;i<=len;i++)//从msgs[0].buf[1]开始写,msgs[0].buf[0]存放的是地址
{
IIC_ADAPTER_BYTE_WRITE(buff[i]);
RECIVE_ACK();
}
STOP_condition();
}
static u32 IIC_ADAPTER_i2c_func(struct i2c_adapter *adap)
{
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_NOSTART |I2C_FUNC_PROTOCOL_MANGLING;
}
static int IIC_ADAPTER_i2c_xfer(struct i2c_adapter *adap,struct i2c_msg *msgs, int num)
{
//不管是读操作还是写操作mags都有两个结构体,msgs[0]与msgs[1]。
if(num==1)
{
IIC_ADAPTER_Write_Byte(msgs[0].buf[0],msgs[0].len,msgs[0].buf);
printk("adapter write data\n");
}
else if(num==2)
{
//读取到的数据存放在msgs[1].buf里面
IIC_ADAPTER_Read_Byte(msgs[0].buf[0],msgs[1].len,msgs[1].buf);
printk("adapter read data\n");
}
return 0;
}
static const struct i2c_algorithm IIC_ADAPTER_i2c_algorithm = {
.master_xfer = IIC_ADAPTER_i2c_xfer,
.functionality = IIC_ADAPTER_i2c_func,
};
static int __init i2c_adapter_init(void)
{
GPD1CON=ioremap(GPD1CON_addr,4);
GPD1DAT=ioremap(GPD1DAT_addr,4);
IIC_Init();
adapter.owner = THIS_MODULE;
adapter.algo = &IIC_ADAPTER_i2c_algorithm;
adapter.retries = 2;
adapter.class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
strlcpy(adapter.name,"IIC_ADAPTER_i2c",sizeof(adapter.name));
i2c_add_adapter(&adapter);
printk("适配器分配成功的总线编号: %d\n",adapter.nr);
return 0;
}
static void __exit i2c_adapter_exit(void)
{
iounmap(GPD1CON);
iounmap(GPD1DAT);
i2c_del_adapter(&adapter);
printk(" IIC适配器注销成功!\n");
}
module_init(i2c_adapter_init);
module_exit(i2c_adapter_exit);
MODULE_LICENSE("GPL");
设备层:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/i2c.h>
static struct i2c_client *i2cClient = NULL;
static struct i2c_adapter *i2c_adap;
static struct i2c_board_info i2c_info;
static unsigned short i2c_addr_list[0xFF]=
{
0x50,I2C_CLIENT_END
};
static int __init i2c_dev_init(void)
{
i2c_adap = i2c_get_adapter(9); /*要使用总线*/
if(i2c_adap==NULL)
{
printk("适配器获取失败!\n");
return 0;
}
memset(&i2c_info,0,sizeof(struct i2c_board_info));
strlcpy(i2c_info.type,"touch_name",I2C_NAME_SIZE);
i2cClient = i2c_new_probed_device(i2c_adap,&i2c_info,i2c_addr_list,NULL);
if(i2cClient==NULL)
{
printk("地址检查错误!\n");
return 0;
}
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");
if(i2c_adap!=NULL)
{
i2c_unregister_device(i2cClient);
i2c_release_client(i2cClient);
}
}
module_init(i2c_dev_init);
module_exit(i2c_dev_exit);
MODULE_LICENSE("GPL");
驱动层:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/i2c.h>
#include <linux/delay.h>
static const struct i2c_device_id i2c_id[] =
{
{"touch_name",0},//设备端的名字为"myiic",0表示不需要私有数据
{}
};
static int i2c_probe(struct i2c_client *client, const struct i2c_device_id *device_id)//匹配成功时调用
{
printk("驱动端IIC匹配的地址=0x%x\n",client->addr);
u8 buff_read[10];
u8 buff_write[10];
strcpy(buff_write,"houjunzui");
i2c_smbus_write_i2c_block_data(client,1,10,buff_write);
mdelay(10);
i2c_smbus_read_i2c_block_data(client,1,10,buff_read);
printk("read: %s \n",buff_read);
return 0;
}
static int i2c_remove(struct i2c_client *client)
{
printk("i2c_remove!!!\n");
return 0;
}
struct i2c_driver i2c_drv =
{
.driver = //这个不添加会报错,实际上没作用
{
.name = "iic_drv",
.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");