I2C总线
I2C总线是由Philips公司开发的一种简单、双向二线制同步串行总线。它只需要两根线即可在连接于总线上的器件之间传送信息。
I2C子系统
I2C子系统 类似中断子系统, i2c子系统中也使用一个对象来描述一个物理实体, 设备对象与驱动分离, 驱动结合设备对象对硬件设备的描述才可以驱动一个具体的物理设备, 体现了分离的设计思想, 实现了代码的复用
I2C子系统层次结构:
I2C总线层:完成总线通讯,时序协议
I2C核心层:提供的是设备驱动和总线层的注册注销相关函数(i2c-core.c)I2C通信方法(algorithm)上层的、与具体适配器无关的代码以及探测设备、检测设备地址的上层代码等。
I2C设备驱动层:驱动硬件设备
I2C的相关函数与数据结构
1、i2c_adapter对象实现了一组通过一个i2c控制器发送消息的所有信息, 包括时序, 地址等等, 即封装了i2c控制器的"控制信息"。它被i2c主机驱动创建, 通过clien域和i2c_client和i2c_driver相连, 这样设备端驱动就可以通过其中的方法以及i2c物理控制器来和一个i2c总线的物理设备进行交互
struct i2c_adapter {
struct module *owner;
unsigned int class; /*classes to allow probing for */
const struct i2c_algorithm*algo; /* 总线上数据传输的算法*/
void *algo_data; /* algorithm 数据 */
int timeout; /* injiffies */
int retries; /* 重试次数 */
struct device dev; /* the adapter device */
int nr;
char name[48]; /* 适配器名字 */
struct completion dev_released; /* 用于同步 */
};
2、i2c_algorithm描述一个i2c主机的发送时序的信息,该类的对象algo是i2c_adapter的一个域,其中的master_xfer()注册的函数最终被设备驱动端的i2c_transfer()回调。
struct i2c_algorithm {
int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs, intnum);
int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr, \
unsigned short flags, charread_write, \
u8 command, int size, unioni2c_smbus_data *data);
u32 (*functionality) (structi2c_adapter *);
};
3、i2c_client描述一个挂接在硬件i2c总线上的设备的设备信息,即i2c设备的设备对象,与i2c_driver对象匹配成功后通过detected和i2c_driver以及i2c_adapter相连,在控制器驱动与控制器设备匹配成功后被控制器驱动通过i2c_new_device()创建。
struct i2c_client {
unsigned short flags;
unsignedshort addr; //从机地址
charname[I2C_NAME_SIZE]; //名字
struct i2c_adapter *adapter; /* 依附的i2c_adapter 适配器结构体指针*/
struct i2c_driver *driver; /* 依附的i2c_driver*/
struct device dev; /* the devicestructure */
intirq; /* irq issuedby device */
structlist_head detected;
};
i2c_driver描述一个挂接在硬件i2c总线上的设备的驱动方法,即i2c设备的驱动对象,通
过i2c_bus_type和设备信息i2c_client匹配,匹配成功后通过clients和i2c_client对象以及i2c_adapter对象相连
struct i2c_driver {
unsignedint class;
int(*attach_adapter)(struct i2c_adapter *) __deprecated; /*依附i2c适配器函数指针*/
int(*detach_adapter)(struct i2c_adapter *) __deprecated;/*脱离i2c适配器函数指针*/
int (*probe)(struct i2c_client*, const struct i2c_device_id *);
int (*remove)(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表 */
/*Device detection callback for automatic device creation */
int(*detect)(struct i2c_client *, struct i2c_board_info *);
constunsigned short *address_list;
structlist_head clients;
};
i2c_msg描述一个在设备端和主机端之间进行流动的数据, 在设备驱动中打包并通过
i2c_transfer()发送。相当于skbuf之于网络设备,urb之于USB设备
struct i2c_msg {
__u16 addr; /* 从机地址 */
__u16 flags;
/******************************/
#define I2C_M_RD 0x0001 /* read data, from slave to master */
/*写数据,flags对应的是0*/
/******************************/
__u16 len; /* msg length */
__u8 *buf; /* pointer to msg data */
};
驱动端:
#define i2c_add_driver(driver) \
i2c_register_driver(THIS_MODULE, driver)
功能:i2c驱动注册
参数:
@driver struct i2c_driver 结构体指针
设备端:
kernel-3.4.39/arch/arm/plat-s5p6818/x6818/device.c
内核启动过程中,为x6818开发平台外设提供设备信息
struct i2c_board_info {
char type[I2C_NAME_SIZE]; //名字,用于和驱动进行匹配
unsigned short addr; //从机地址
};
int i2c_register_board_info(int busnum, struct i2c_board_info const *info,unsigned n);
功能:注册I2C板级信息(设备端信息注册)
参数:
@busnum 总线编号(I2C适配器)
@info i2c_board_info结构体指针
@n 个数
通信
int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs,int num);
功能:完成i2C总线通讯
参数:
@adap i2c_adapter结构体指针(从i2c_client结构体中获取i2c_adapter结构体指针)
@msgs 消息结构体指针
@num 个数
实例
基于s5p6818平台的mma8653实现
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <linux/input.h>
#include <linux/i2c.h>
#include <linux/miscdevice.h>
#define Debug_log(level) \
printk(level "---%s---%s---%d---\n",__FILE__,__func__,__LINE__)
int mma8653_read_reg(struct i2c_client *client,u8 reg)
{
int ret = 0;
u8 mma_buf[] = {reg};
u8 r_buf[2];
struct i2c_msg msgs[] = {
[0] = {
.addr = client -> addr,
.flags= 0,
.buf = mma_buf,
.len = ARRAY_SIZE(mma_buf),
},
[1] = {
.addr = client -> addr,
.flags= I2C_M_RD,
.buf = r_buf,
.len = 1,
},
};
ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
if(ret < 0){
printk(KERN_ERR "i2c_transfer failed...\n");
return ret;
}
return (int)r_buf[0];
}
int mma8653_write_reg(struct i2c_client *client,u8 reg,u8 val)
{
int ret = 0;
u8 mma_buf[] = {reg,val};
struct i2c_msg msgs[] = {
[0] = {
.addr = client -> addr,
.flags= 0,
.buf = mma_buf,
.len = ARRAY_SIZE(mma_buf),
},
};
ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
if(ret < 0){
printk(KERN_ERR "i2c_transfer failed...\n");
return ret;
}
return 0;
}
int mma_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
int dev_id = 0,val;
int x,y,z;
dev_id = mma8653_read_reg(client,0xD);
Debug_log(KERN_INFO);
val = mma8653_read_reg(client,0x2A);
mma8653_write_reg(client, 0x2A,(u8)val|(0x1<<0));
x = (((u8)mma8653_read_reg(client,0x1)<<8)|(u8)mma8653_read_reg(client,0x2))>>6;
y = (((u8)mma8653_read_reg(client,0x3)<<8)|(u8)mma8653_read_reg(client,0x4))>>6;
z = (((u8)mma8653_read_reg(client,0x5)<<8)|(u8)mma8653_read_reg(client,0x6))>>6;
printk(KERN_INFO "---x:%d,y:%d,z:%d---\n",x,y,z);
printk(KERN_INFO "----client_addr:0x%x----\n",client->addr);
printk(KERN_INFO "----client_id:0x%x----\n",dev_id);
return 0;
}
int mma_remove(struct i2c_client *client)
{
Debug_log(KERN_INFO);
return 0;
}
struct i2c_device_id mma_idts[] = {
[0] = {
.name = "mma8653",
},
[1] = {/*NULL*/}
};
MODULE_DEVICE_TABLE(i2c,mma_idts);
struct i2c_driver mma8653_drv = {
.probe = mma_probe,
.remove = mma_remove,
.driver = {
.name = "mma8653",
.owner = THIS_MODULE,
},
.id_table = mma_idts,
};
static int __init demo_init(void)
{
Debug_log(KERN_INFO);
return i2c_add_driver(&mma8653_drv);
}
static void __exit demo_exit(void)
{
i2c_del_driver(&mma8653_drv);
Debug_log(KERN_INFO);
}
module_init(demo_init);
module_exit(demo_exit);
MODULE_LICENSE("GPL");