前言
本篇文章来讲解I2C系统的重要结构体,了解这些结构体对于编写I2C驱动来说是至关重要的,所以要想编写好一个I2C驱动程序那么就必须先了解这些结构体。
一、I2C硬件框架
这里使用百问网的一张图片来讲解:
一个芯片中可以有多个I2C控制器,并且一个I2C控制器可以控制多个设备。那么在对应的驱动中I2C控制器和这些设备还有传输的消息都是用什么来表示的呢?
在Linux内核中:
使用i2c_adapter这个结构体来代表一个I2C控制器。
传输的消息使用i2c_msg结构体表示。
设备使用i2c_client结构体表示。
二、i2c_adapter
i2c_adapter结构体:
struct i2c_adapter { struct module *owner; unsigned int class; /* classes to allow probing for */ const struct i2c_algorithm *algo; /* the algorithm to access the bus */ void *algo_data; /* data fields that are valid for all devices */ const struct i2c_lock_operations *lock_ops; struct rt_mutex bus_lock; struct rt_mutex mux_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_bus_recovery_info *bus_recovery_info; const struct i2c_adapter_quirks *quirks; };
在Linux内核中我们能找到这个结构体,下面我们来讲解一下其中比较重要的成员。
nr成员:
这个成员变量代表的是第几个I2C控制器,因为在一个芯片中I2C控制器的数量肯定是不止一个的,所以需要有一个变量来标识具体是第几个I2C控制器。
const struct i2c_algorithm *algo
const struct i2c_algorithm *algo 是指向 I2C 访问算法的结构体指针,该指针指向一个常量类型的 struct i2c_algorithm 结构体,其中定义了 I2C 总线上的访问操作函数指针,包括了 master_xfer、smbus_xfer 以及 functionality 等。通过这些访问函数,可以实现对 I2C 总线上的传感器、EEPROM 和其他设备的读写操作。
在实现 I2C 设备驱动程序时,通常需要根据具体的 I2C 总线访问算法进行编程。而在适配器结构体 struct i2c_adapter 中,通过 algo 指针可以方便地获取到对应的 I2C 总线访问算法信息,从而实现 I2C 设备的访问。
algo结构体:
struct i2c_algorithm { /* If an adapter algorithm can't do I2C-level access, set master_xfer to NULL. If an adapter algorithm can do SMBus access, set smbus_xfer. If set to NULL, the SMBus protocol is simulated using common I2C messages */ /* master_xfer should return the number of messages successfully processed, or a negative value on error */ 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); /* To determine what the adapter supports */ u32 (*functionality) (struct i2c_adapter *); #if IS_ENABLED(CONFIG_I2C_SLAVE) int (*reg_slave)(struct i2c_client *client); int (*unreg_slave)(struct i2c_client *client); #endif };
三、i2c_client
i2c_client结构体:
struct i2c_client { unsigned short flags; /* div., see below */ unsigned short addr; /* chip address - NOTE: 7bit */ /* addresses are stored in the */ /* _LOWER_ 7 bits */ char name[I2C_NAME_SIZE]; struct i2c_adapter *adapter; /* the adapter we sit on */ struct device dev; /* the device structure */ int irq; /* irq issued by device */ struct list_head detected; #if IS_ENABLED(CONFIG_I2C_SLAVE) i2c_slave_cb_t slave_cb; /* callback for slave mode */ #endif };
在 Linux 内核中,i2c_client 是 I2C 从设备的设备结构体,表示连接到 I2C 总线上的一个 I2C 从设备。它包含了该从设备的一些信息,例如设备地址、从设备驱动程序、从设备所在的 I2C 适配器等。
重要的成员:
unsigned short addr:I2C 从设备的地址,存储在 7 位中。注意,这个地址不包括 I2C 地址的读写位,即 7 位地址用于标识从设备的身份。
struct i2c_adapter *adapter:指向从设备所连接的 I2C 适配器的指针。有了适配器信息,可以通过适配器的接口访问连接的从设备。
四、i2c_msg
i2c_msg结构体:
struct i2c_msg { __u16 addr; /* slave address */ __u16 flags; #define I2C_M_RD 0x0001 /* read data, from slave to master */ /* I2C_M_RD is guaranteed to be 0x0001! */ #define I2C_M_TEN 0x0010 /* this is a ten bit chip address */ #define I2C_M_RECV_LEN 0x0400 /* length will be first received byte */ #define I2C_M_NO_RD_ACK 0x0800 /* if I2C_FUNC_PROTOCOL_MANGLING */ #define I2C_M_IGNORE_NAK 0x1000 /* if I2C_FUNC_PROTOCOL_MANGLING */ #define I2C_M_REV_DIR_ADDR 0x2000 /* if I2C_FUNC_PROTOCOL_MANGLING */ #define I2C_M_NOSTART 0x4000 /* if I2C_FUNC_NOSTART */ #define I2C_M_STOP 0x8000 /* if I2C_FUNC_PROTOCOL_MANGLING */ __u16 len; /* msg length */ __u8 *buf; /* pointer to msg data */ };
struct i2c_msg 是 Linux 内核中 I2C/SMBus 消息的结构体,用于描述 I2C 总线上的一条消息。
重要的成员:
__u16 addr:I2C 从设备的地址,用于指定消息要发送到哪个从设备上。
__u16 flags:消息的标志参数,包括多种不同的位掩码:
通过这些参数,struct i2c_msg 结构体可以描述出 I2C 总线上的一条具体的消息,并通过设备驱动程序对该消息进行操作。
总结
有了这些重要结构体的知识我们就可以开始编写驱动程序了。