linux IIC系统学习
IIC硬件框图
一般soc有多个iic 控制器,每个IIC总线有SCL SDA两个线完成,每条线上有一个3.3V的上拉电阻,设备挂在总线上。
驱动程序分为IIC总线控制程序、控制器驱动程序和设备驱动程序
IIC软件框架
软件分层结构如下:
APP :知道需要读写什么数据
IIC Device driver :知道怎么读写数据
IIC controller driver :负责传输数据
linux iic驱动分层如下:
内核支持使用控制器访问设备,也支持GPIO模拟IIC访问设备。
内核中的关键数据结构:
- i2c_adapter
i2c_adapter 主要是抽象iic控制器,里面比较关键的成员有nr:表示控制器的编号,algo里面封装了一系列的iic操作函数。比如master_xfer
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 */
unsigned long locked_flags; /* owned by the I2C core */
#define I2C_ALF_IS_SUSPENDED 0
#define I2C_ALF_SUSPEND_REPORTED 1
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;
struct irq_domain *host_notify_domain;
};
- i2c_client
i2c_client里面主要的内容包括addr:设备地址, i2c_adapter:指向设备挂载的iic控制器下
struct i2c_client {
unsigned short flags; /* div., see below */
#define I2C_CLIENT_PEC 0x04 /* Use Packet Error Checking */
#define I2C_CLIENT_TEN 0x10 /* we have a ten bit chip address */
/* Must equal I2C_M_TEN below */
#define I2C_CLIENT_SLAVE 0x20 /* we are the slave */
#define I2C_CLIENT_HOST_NOTIFY 0x40 /* We want to use I2C host notify */
#define I2C_CLIENT_WAKE 0x80 /* for board_info; true iff can wake */
#define I2C_CLIENT_SCCB 0x9000 /* Use Omnivision SCCB protocol */
/* Must match I2C_M_STOP|IGNORE_NAK */
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 init_irq; /* irq set at initialization */
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
};
- i2c_msg
i2c_msg 里面最主要的部分有msg的内容(buf),然后是设备地址(addr),读写标志(flag),数据的长度(len)
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_DMA_SAFE 0x0200 /* the buffer of this message is DMA safe */
/* makes only sense in kernelspace */
/* userspace buffers are copied anyway */
#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 */
};
驱动中最长使用i2c_transfer API来发送数据,可以一次发送多条msg,比如需要先发送寄存器地址再获取数据等就可以封装msg调用一次接口即可。
IIC Tools
app通过IIC Controler与device通信,使用iic tools也需要指定:
- 哪个iic控制器
- 设备地址
- 消息(读/写)
查看当前系统的iic总线:
i2cdetect -l
查看IIC 0总线下的设备挂载情况,UU代表当前内核已经有相关驱动,如果出现明确地址表示探测到IIC设备存在但是当前内核没有相关驱动。当前IIC0下面挂载了2个IIC设备,设备地址分别为0x22 0x3c
i2cdetect -y 0
读写寄存器:
向0号总线0x1e设备写入2个字节0 0x4
i2ctransfer -f -y 0 w2@0x1e 0 0x4
从0号总线0x1e设备0xc地址读取2个字节
i2ctransfer -f -y 0 w1@0x1e 0xc r2