1. i2c_client 结构体
该结构体定义了挂载在I2C总线下的slave设备,一个结构体对象代表一个slave设备
struct i2c_client {
unsigned int flags; /* 设备标志 */
unsigned int addr; /* slave地址,7-bit or 10-bit */
struct i2c_adapter *adapter; /* 适配器 */
struct i2c_driver *driver; /* 该设备驱动 */
int usage_count; /* 访问设备总数 */
struct device dev; /* 设备信息 */
struct list_head list; /* 挂载总线下的设备链表 */
char name[I2C_NAME_SIZE]; /* 设备名称 */
struct completion released;
};
2. i2c_driver 结构体
该结构体与i2c_client是一对多关系,即一个驱动可以对上多个设备.根据name进行匹配.
struct i2c_driver {
struct module *owner; /* 模块拥有这,基本上设置成.owner = THIS MODULE */
char name[32]; /* 驱动名称,用于与设备名称进行匹配 */
int id; /* 与设备匹配也可以使用ID,但好像用的不多 */
unsigned int class;
unsigned int flags; /* */
int (*attach_adapter)(struct i2c_adapter *); /* 通知总线上出现一个新的驱动程序 */
int (*detach_adapter)(struct i2c_adapter *); /* 总线上移除了一个驱动程序 */
int (*detach_client)(struct i2c_client *); /* 总线上移除了一个从设备,如果这个设备是动态创建的,这里就必须被释放掉 */
/* 可以对设备进行命令控制 */
int (*command)(struct i2c_client *client,unsigned int cmd, void *arg);
struct device_driver driver; /* 设备驱动信息 */
struct list_head list;
};
3. i2c_adapter 结构体
该结构体定义了一个物理层次的I2C总线
struct i2c_adapter {
struct module *owner;
unsigned int id; /* == is algo->id | hwdep.struct->id, */
unsigned int class;
struct i2c_algorithm *algo; /* 总线通信的算法 */
void *algo_data;
/* 设备注册和取消 */
int (*client_register)(struct i2c_client *);
int (*client_unregister)(struct i2c_client *);
/* data fields that are valid for all devices */
struct semaphore bus_lock;
struct semaphore clist_lock;
int timeout; /* 定义超时时间 */
int retries; /* 重试次数 */
struct device dev; /* adapter 设备 */
struct class_device class_dev; /* class 设备 */
#ifdef CONFIG_PROC_FS
/* 如果初始化adapter,就不需要设置 */
int inode;
#endif /* def CONFIG_PROC_FS */
int nr; /* 总线号 */
struct list_head clients;
struct list_head list;
char name[I2C_NAME_SIZE]; /* adapter名称 */
struct completion dev_released;
struct completion class_dev_released;
};
4. i2c_algorithm 结构体
该I2C主设备传输数据的算法. 其中提供了关键的函数master_xfer()用于产生i2c访问周期需要的信号,以i2c_msg为单位
struct i2c_algorithm {
char name[32]; /* 算法描述,以文本形式 */
unsigned int id;
/*
*一般都使用 master_xfer() 算法
*/
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);
/* 从设备发送/接收数据 */
int (*slave_send)(struct i2c_adapter *,char*,int);
int (*slave_recv)(struct i2c_adapter *,char*,int);
/* 设置参数方法 */
int (*algo_control)(struct i2c_adapter *, unsigned int, unsigned long);
/* */
u32 (*functionality) (struct i2c_adapter *);
};
5. i2c_msg 结构体
struct i2c_msg {
__u16 addr; /* 从设备地址 */
__u16 flags; /* 设备标示 */
#define I2C_M_TEN 0x10 /* 这个使用10-bit地址 */
#define I2C_M_RD 0x01
#define I2C_M_NOSTART 0x4000
#define I2C_M_REV_DIR_ADDR 0x2000
#define I2C_M_IGNORE_NAK 0x1000
#define I2C_M_NO_RD_ACK 0x0800
__u16 len; /* msg 长度 */
__u8 *buf; /* msg数据指针 */
};
I2C_M_RD:
表示这是一个读操作,默认是把相应的位置1
I2C_M_REV_DIR_ADDR:
表示把读写标志位反转,也就是读是把相应位置0
看一个读函数实例
i2c的读数据操作分为:“写地址” 和 “读数据” 两步操作。
因此可以定义两个 i2c_msg 结构体变量,分别通过对结构体成员变量flag的赋值来确定传输方向,
该函数本身没啥问题,读数据也正常,只需要注意length是以字节为单位即可。函数编写如下:
/* 读取多个寄存器数据
* 入口参数: @client: 从机地址
* @reg : 寄存器地址
* @buffer: 保存读取数据
* @length: reg/buffer的长度
*/
static int i2c_read_regs(struct i2c_client *client, u8 reg, u8 *buffer, int length)
{
int err = 0;
/* msg[0]是发送要读取的寄存器首地址 */
struct i2c_msg msg[] = {
{
.addr = client->addr,
.flags = 0,
.len = 1, //表示寄存器地址字节长度,是以byte为单位
.buf = ®,
},
{
.addr = client->addr,
.flags = I2C_M_RD,
.len = length, //表示期望读到数据的字节长度(寄存器长度),是以byte为单位
.buf = buffer, //将读取到的数据保存在buffer中
},
};
err = i2c_transfer(client->adapter, msg, 2);
if(err != 2)C
{
err = -EINVAL;
printk("read regs from i2c has been failed\n\r");
}
return err;
}
-
再来看一个写函数实例
写数据操作也分为:“写地址” 和 “写数据” 两步操作,根据写时序图来看,没有什么问题。
static int i2c_write_regs(struct i2c_client *client,u8 reg, u8 *buffer, int length)
{
int err = 0;
u8 data[256];
struct i2c_msg msg;
data[0] = reg;
memcpy(&data[1], buffer, length);
msg.addr = client->addr,
msg.flags = 0;
msg.len = length + 1; /* +1是因为还有一个data[0]所存储的寄存器 */
msg.buf = data;
err = i2c_transfer(client->adapter, &msg, 1);
if(err != 1)
{
err = -EINVAL;
printk("write data to i2c has been failed\n\r");
}
return err;
}
对于写I2C寄存器,我们需要做的就是给 i2c_master_send 函数传入两个字节的数据即可,i2c_master_send 接口的三个参数:client 为此次与主机通信的从机,buf 为发送的数据指针,count 为发送数据的字节数。
示例如下:
static int xxxx_i2c_write( struct i2c_client* client,uint8_t reg,uint8_t data)
{
unsigned char buffer[2];
buffer[0] = reg;
buffer[1] = data;
if( 2!= i2c_master_send(client,buffer,2) ) {
printk( KERN_ERR " xxxx_i2c_write fail! \n" );
return -1;
}
return 0;
}
读时序需要做的操作是,先向I2C总线上写入需要读的寄存器地址,然后读I2C总线上的值。i2c_master_recv 接口的三个参数:client 为此次与主机通信的从机,buf 为接收的数据指针,count 为接收数据的字节数。
示例如下:
static int xxxx_i2c_read( struct i2c_client* client,uint8_t reg,uint8_t *data)
{
// write reg addr
if( 1!= i2c_master_send(client,®,1) ) {
printk( KERN_ERR " xxxx_i2c_read fail! \n" );
return -1;
}
// wait
msleep(10);
// read
if( 1!= i2c_master_recv(client,data,1) ) {
printk( KERN_ERR " xxxx_i2c_read fail! \n" );
return -1;
}
return 0;
}