iic驱动

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   = &reg,
        },
        {
            .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,&reg,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;  
}

  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值