文章目录
I2C协议
I2C使用两条线在主控制器和从控制器之间进行数据通信。一条是SCL(串行时钟线),另一种是SDA(串行数据线),这两条数据线需要接上拉电阻,总线空闲时SCL和SDA处于高电平。
I2C是支持多从机的,就是说一个I2C控制器下可以挂载多个I2C从设备,这些不同的I2C从设备有不同的器件地址,这样I2C控制器就可以通过I2C设备的器件地址访问指定的I2C设备了。
上图中SDA和SCL这两根线必须要接一个上拉电阻,一般是4.7K,其余的I2C从器件都挂载到SDA和SCL这两根线上,这样就可以通过SDA和SCL这两根线来访问多个I2C设备了。
起始位
I2C起始位就是I2C通信起始标志,通过这个起始位就可以告诉I2C从机,"我"要开始进行I2C通信了。在SCL为高电平的时候,SDA出现下降沿就表示为起始位。
停止位
停止位就是停止I2C通信的标志位,和起始位的功能相反。在SCL为高电平时,SDA出现上升沿就表示为停止位。
数据传输
I2C总线在数据传输的时候要保证在SCL高电平期间,SDA上的数据稳定,因此SDA上的数据只能在SCL为低电平期间发生。
应答信号
当I2C主机发送完8位数据以后会将SDA设置为输入状态,等待I2C从机应答,也就是等到I2C从机告诉主机他接收到数据了。应带信号是由从机发出的,主机需要提供应答信号所需的时钟,主机发送完8位数据以后会紧跟着的一个时钟信号就是给应答信号使用的。从机通过SDA拉低来表示发出应答信号,表示通信成功,否则失败。
下图是我抓的IIC波形。
I2C写时序
- 开始信号
- 发送I2C设备地址,每个I2C器件都有一个设备地址,通过发送具体的设备地址来决定要访问哪个I2C器件。
- I2C器件地址后面跟着一个读写位,0表示写操作,1表示读操作。
- 从机发送的ACK应答信号。
- 发送要写入的寄存器地址
- 从机发送的ACK应答信号
- 发送要写入的寄存器数据
- 从机发送的ACK应答信号
- 停止信号
I2C读时序
- 主机发送起始信号
- 主机发送读取的I2C从设备地址
- 读写控制位,因为是向I2C从设备发送数据,因此是写信号
- 从机发送的ACK应答信号
- 主机发送要读取的寄存器地址
- 从机发送ACK信号
- 重新发送start信号
- 重新发送要读取的I2C从设备地址
- 读写控制位,这里是读信号,表示接下来是从I2C从设备里面读数据。
- 从机发送的ACK应答信号
- 从I2C器件中读取到的数据
- 主机发出NO ACK信号,表示读取完成,不需要从机再发送ACK信号了。
- 主机发出STOP信号,停止I2C通信。
linux I2C驱动框架简介
Linux I2C驱动主要分为I2C总线驱动和I2C设备驱动。
对于I2C总线驱动一旦编写完成就不需要再做修改了,其他的I2C设备直接调用主机驱动提供的API函数完成读写操作即可。这个正好符合Linux的驱动分离与分层的思想,因此Linux内核也将I2C驱动分为两部分:
I2C总线驱动:I2C总线驱动就是SOC的I2C控制器驱动,也叫做I2C适配器驱动/I2C主机驱动。
I2C设备驱动:I2C设备驱动就是针对具体的I2C设备二编写的驱动。
不同的平台(比如:IMX,瑞星微等)I2C主机驱动是不一样的,Linux针对每一种平台,都有其特定的I2C控制器驱动,I2C主机驱动是由芯片厂家编写好的,而I2C设备驱动一般是由该I2C设备厂家编写的。我们使用I2C外设时,一般只需要去移植该I2C器件的驱动就可以了。
I2C总线驱动
我们可以发现,I2C器件是符合平台总线驱动模型这种框架的。
事实上,I2C驱动框架是platform设备驱动框架的一种,platform可以用于没有总线概念的设备,从而虚拟出来一种总线——platform总线,从而构成平台总线模型,但是I2C是有自己实际的I2C总线的,就不需要虚拟了。
重要数据结构
i2c_adapter:Linux内核将SOC的I2C适配器(控制器)抽象成i2c_adapter
结构体。
struct i2c_adapter
{
499 struct module *owner;
500 unsigned int class; /* classes to allow probing for */
501 const struct i2c_algorithm *algo; /* 总线访问算法 */
502 void *algo_data;
503
504 /* data fields that are valid for all devices */
505 struct rt_mutex bus_lock;
506
507 int timeout; /* in jiffies */
508 int retries;
509 struct device dev; /* the adapter device */
510
511 int nr;
512 char name[48];
513 struct completion dev_released;
514
515 struct mutex userspace_clients_lock;
516 struct list_head userspace_clients;
517
518 struct i2c_bus_recovery_info *bus_recovery_info;
519 const struct i2c_adapter_quirks *quirks;
};
i2c_algorithm:上面的第501行,i2c_algorithm
类型的指针变量algo,对于一个I2C适配器,重要的功能就是提供读写API函数给I2C设备驱动去完成读写操作的。i2c_algorithm
就是I2C适配器与I2C设备进行通信的方法。
struct i2c_algorithm
{
......
398 int (*master_xfer)(struct i2c_adapter *adap,struct i2c_msg *msgs,int num);
400 int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,
401 unsigned short flags, char read_write,
402 u8 command, int size, union i2c_smbus_data *data);
403
404 /* To determine what the adapter supports */
405 u32 (*functionality) (struct i2c_adapter *);
......
};
master_xfer :第 398 行,master_xfer
就是 I2C 适配器的传输函数,可以通过此函数来完成与 IIC 设备之间的通信。
smbus_xfer :第 400 行,smbus_xfer
就是 SMBUS 总线的传输函数。
向内核添加一个I2C适配器
综上所述:I2C总线驱动的主要工作就是初始化i2c_adapter
结构体变量,然后设置i2c_algorithm
中的master_xfer
函数。完成以后通过i2c_add_numbered_adapter
或者i2c_add_adapter
这两个函数向系统注册设置好i2c_adapter
,这两个函数原型如下:
int i2c_add_adapter(struct i2c_adapter *adapter) //使用动态总线号
int i2c_add_numbered_adapter(struct i2c_adapter *adap) //使用静态总线号
参数 | 描述 |
---|---|
adapter 或 adap | 要添加到 Linux 内核中的 i2c_adapter,也就是 I2C 适配器。 |
返回值 | 0,成功;负值,失败。 |
从内核删除一个I2C适配器
void i2c_del_adapter(struct i2c_adapter * adap)
参数 | 描述 |
---|---|
adap | 要删除的 I2C 适配器。 |
返回值 | 无。 |
I2C设备驱动
重要数据结构
I2C 设备驱动重点关注两个数据结构:i2c_client
和 i2c_driver
,根据总线、设备和驱动模型,I2C 总线上一小节已经讲了。还剩下设备和驱动,i2c_client
就是描述设备信息的,i2c_driver
描述驱动内容,类似于 platform_driver
。
i2c_client 结构体:一个设备对应一个i2c_client
,每检测到一个i2c设备就会给这个I2C设备分配一个i2c_client
.
struct i2c_client
{
218 unsigned short flags; /* 标志 */
219 unsigned short addr; /* 芯片地址,7 位,存在低 7 位*/
......
222 char name[I2C_NAME_SIZE]; /* 名字 */
223 struct i2c_adapter *adapter; /* 对应的 I2C 适配器 */
224 struct device dev; /* 设备结构体 */
225 int irq; /* 中断 */
226 struct list_head detected;
......
};
i2c_driver 结构体:i2c_driver
类似 platform_driver
,是我们编写 I2C 设备驱动重点要处理的内容。
struct i2c_driver
{
162 unsigned int class;
163
164 /* Notifies the driver that a new bus has appeared. You should
165 * avoid using this, it will be removed in a near future.
166 */
167 int (*attach_adapter)(struct i2c_adapter *) __deprecated;
168
169 /* Standard driver model interfaces */
170 int (*probe)(struct i2c_client *, const struct i2c_device_id *);
171 int (*remove)(struct i2c_client *);
172
173 /* driver model interfaces that don't relate to enumeration */
174 void (*shutdown)(struct i2c_client *);
175
176 /* Alert callback, for example for the SMBus alert protocol.
177 * The format and meaning of the data value depends on the
178 * protocol.For the SMBus alert protocol, there is a single bit
179 * of data passed as the alert response's low bit ("event
180 flag"). */
181 void (*alert)(struct i2c_client *, unsigned int data);
182
183 /* a ioctl like command that can be used to perform specific
184 * functions with the device.
185 */
186 int (*command)(struct i2c_client *client, unsigned int cmd,
void *arg);
187
188 struct device_driver driver;
189 const struct i2c_device_id *id_table;
190
191 /* Device detection callback for automatic device creation */
192 int (*detect)(struct i2c_client *, struct i2c_board_info *);
193 const unsigned short *address_list;
194 struct list_head clients;
};
向内核注册i2c_driver
对于我们 I2C 设备驱动编写人来说,重点工作就是构建 i2c_driver,构建完成以后需要向Linux 内核注册这个 i2c_driver
。i2c_driver
注册函数为i2c_register_driver
,此函数原型如下:
int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
参数 | 描述 |
---|---|
owner | 一般为 THIS_MODULE。 |
driver | 要注册的 i2c_driver。 |
返回值 | 0,成功;负值,失败。 |
另外 i2c_add_driver
也常常用于注册 i2c_driver
,i2c_add_driver
是一个宏。i2c_add_driver 就是对 i2c_register_driver 做了一个简单的封装,只有一个参数,就是要注册的 i2c_driver
。
587 #define i2c_add_driver(driver) \
588 i2c_register_driver(THIS_MODULE, driver)
注销i2c_driver
void i2c_del_driver(struct i2c_driver *driver)
参数 | 描述 |
---|---|
driver | 要注销的 i2c_driver。 |
返回值 | 无。 |
I2C设备和驱动匹配的过程
I2C设备和驱动匹配的过程是由I2C核心来完成的,driver/i2c/i2c-core.c
就是I2C的核心部分,I2C核心提供了一些与具体硬件无关的API函数。
1、i2c_adapter 注册/注销函数
int i2c_add_adapter(struct i2c_adapter *adapter)
int i2c_add_numbered_adapter(struct i2c_adapter *adap)
void i2c_del_adapter(struct i2c_adapter * adap)
2、i2c_driver 注册/注销函数
int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
int i2c_add_driver (struct i2c_driver *driver)
void i2c_del_driver(struct i2c_driver *driver)
I2C总线
设备和驱动的匹配是由I2C总线完成的,I2C总线的数据结构为i2c_bus_type
,定义在driver/i2c/i2c-core.c
文件。
736 struct bus_type i2c_bus_type = {
737 .name = "i2c",
738 .match = i2c_device_match,
739 .probe = i2c_device_probe,
740 .remove = i2c_device_remove,
741 .shutdown = i2c_device_shutdown,
742 };
.match
就是I2C总线的设备和驱动匹配函数,在这里就是i2c_device_match
这个函数,此函数内容如下:
static int i2c_device_match(struct device *dev, struct device_driver *drv)
{
459 struct i2c_client *client = i2c_verify_client(dev);
460 struct i2c_driver *driver;
461
462 if (!client)
463 return 0;
464
465 /* Attempt an OF style match */
466 if (of_driver_match_device(dev, drv))
467 return 1;
468
469 /* Then ACPI style match */
470 if (acpi_driver_match_device(dev, drv))
471 return 1;
472
473 driver = to_i2c_driver(drv);
474 /* match on an id table if there is one */
475 if (driver->id_table)
476 return i2c_match_id(driver->id_table, client) != NULL;
477
478 return 0;
}
驱动和设备的匹配根据compatable
属性是否一致进行匹配的。
如果采用设备树的方式来描述设备信息:
匹配成功之后probe
函数就会调用,然后则probe
函数中去获取I2C设备信息。
984 irq = platform_get_irq(pdev, 0);//数获取中断号
......
990 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);//从设备树中获取 I2C1 控制器寄存器物理基地址
991 base = (&pdev->dev, res);//映射为虚拟地址
......
1008 i2c_imx->adapter.owner = THIS_MODULE;
1009 i2c_imx->adapter.algo = &i2c_imx_algo; //通信方法
1010 i2c_imx->adapter.dev.parent = &pdev->dev;
1011 i2c_imx->adapter.nr = pdev->id;
1012 i2c_imx->adapter.dev.of_node = pdev->dev.of_node;
1013 i2c_imx->base = base;
......
1028 ret = devm_request_irq(&pdev->dev, irq, i2c_imx_isr,
1029 IRQF_NO_SUSPEND, pdev->name, i2c_imx); //注册I2C控制器中断,中断服务函数为i2c_imx_isr
1054 ret = i2c_add_numbered_adapter(&i2c_imx->adapter); //向内核注册I2C控制器驱动,
主要看一下i2c_imx_xfer
函数,因为最终就是通过此函数来完成与I2C设备通信的。
static int i2c_imx_xfer(struct i2c_adapter *adapter,struct i2c_msg *msgs, int num)
{
891 unsigned int i, temp;
892 int result;
893 bool is_lastmsg = false;
894 struct imx_i2c_struct *i2c_imx = i2c_get_adapdata(adapter);
895
896 dev_dbg(&i2c_imx->adapter.dev, "<%s>\n", __func__);
897
898 /* Start I2C transfer */
899 result = i2c_imx_start(i2c_imx);
900 if (result)
901 goto fail0;
902
903 /* read/write data */
904 for (i = 0; i < num; i++) {
905 if (i == num - 1)
906 is_lastmsg = true;
907
908 if (i) {
909 dev_dbg(&i2c_imx->adapter.dev,
910 "<%s> repeated start\n", __func__);
911 temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);
912 temp |= I2CR_RSTA;
913 imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
914 result = i2c_imx_bus_busy(i2c_imx, 1);
915 if (result)
916 goto fail0;
917 }
918 dev_dbg(&i2c_imx->adapter.dev,
919 "<%s> transfer message: %d\n", __func__, i);
920 /* write/read data */
......
938 if (msgs[i].flags & I2C_M_RD)
939 result = i2c_imx_read(i2c_imx, &msgs[i], is_lastmsg);
940 else {
941 if (i2c_imx->dma && msgs[i].len >= DMA_THRESHOLD)
942 result = i2c_imx_dma_write(i2c_imx, &msgs[i]);
943 else
944 result = i2c_imx_write(i2c_imx, &msgs[i]);
945 }
946 if (result)
947 goto fail0;
948 }
949
950 fail0:
951 /* Stop I2C transfer */
952 i2c_imx_stop(i2c_imx);
953
954 dev_dbg(&i2c_imx->adapter.dev, "<%s> exit with: %s: %d\n",
__func__,
955 (result < 0) ? "error" : "success msg",
956 (result < 0) ? result : num);
957 return (result < 0) ? result : num;
}
i2c_imx_start:第 899 行,调用 i2c_imx_start 函数开启 I2C 通信。
i2c_imx_read:第 939 行,如果是从 I2C 设备读数据的话就调用 i2c_imx_read 函数。
i2c_imx_dma_write/i2c_imx_write第 941~945 行,向 I2C 设备写数据,如果要用 DMA 的话就使用 i2c_imx_dma_write
函数来完成写数据。如果不使用 DMA 的话就使用 i2c_imx_write
函数完成写数据。
i2c_imx_stop :第 952 行,I2C 通信完成以后调用 i2c_imx_stop
函数停止 I2C 通信。
i2c_imx_start
、i2c_imx_read
、i2c_imx_write
和 i2c_imx_stop
这些函数就是 I2C 寄存器的具体操作函数。
设备数据收发API
i2c_transfer
i2c_transfer:i2c_transfer
函数最终会调用 I2C 适配器中 i2c_algorithm
里面的 master_xfer 函数。
int i2c_transfer(struct i2c_adapter *adap,struct i2c_msg *msgs,int num)
参数 | 描述 |
---|---|
adap | 所使用的 I2C 适配器,i2c_client 会保存其对应的 i2c_adapter。 |
msgs | I2C 要发送的一个或多个消息。 |
num | 消息数量,也就是 msgs 的数量。 |
返回值 | 负值,失败,其他非负值,发送的 msgs 数量。 |
我们重点来看一下,msgs这个参数,这是一个i2c_msg
类型的指针参数,Linux内核使用i2c_msg
结构体来描述一个消息。
68 struct i2c_msg
{
69 __u16 addr; /* 从机地址 */
70 __u16 flags; /* 标志 */
71 #define I2C_M_TEN 0x0010
72 #define I2C_M_RD 0x0001
73 #define I2C_M_STOP 0x8000
74 #define I2C_M_NOSTART 0x4000
75 #define I2C_M_REV_DIR_ADDR 0x2000
76 #define I2C_M_IGNORE_NAK 0x1000
77 #define I2C_M_NO_RD_ACK 0x0800
78 #define I2C_M_RECV_LEN 0x0400
79 __u16 len; /* 消息(本 msg)长度 */
80 __u8 *buf; /* 消息数据 */
};
使用i2c_transfer函数发送之前要构建好I2C_msg,使用i2c_tranfer
进行I2C数据收发的代码如下:
1 /* 设备结构体 */
2 struct xxx_dev {
4 void *private_data; /* 私有数据,一般会设置为 i2c_client */
5 };
6
7 /*
8 * @description : 读取 I2C 设备多个寄存器数据
9 * @param – dev : I2C 设备
10 * @param – reg : 要读取的寄存器首地址
11 * @param – val : 读取到的数据
12 * @param – len : 要读取的数据长度
13 * @return : 操作结果
14 */
static int xxx_read_regs(struct xxx_dev *dev, u8 reg, void *val,int len)
{
17 int ret;
18 struct i2c_msg msg[2];
19 struct i2c_client *client = (struct i2c_client *)
dev->private_data;
20
21 /* msg[0],第一条写消息,发送要读取的寄存器首地址 */
22 msg[0].addr = client->addr; /* I2C 器件地址 */
23 msg[0].flags = 0; /* 标记为发送数据 */
24 msg[0].buf = ® /* 读取的首地址 */
25 msg[0].len = 1; /* reg 长度 */
26
27 /* msg[1],第二条读消息,读取寄存器数据 */
28 msg[1].addr = client->addr; /* I2C 器件地址 */
29 msg[1].flags = I2C_M_RD; /* 标记为读取数据 */
30 msg[1].buf = val; /* 读取数据缓冲区 */
31 msg[1].len = len; /* 要读取的数据长度 */
32
33 ret = i2c_transfer(client->adapter, msg, 2);
34 if(ret == 2) {
35 ret = 0;
36 } else {
37 ret = -EREMOTEIO;
38 }
39 return ret;
}
42 /*
43 * @description : 向 I2C 设备多个寄存器写入数据
44 * @param – dev : 要写入的设备结构体
45 * @param – reg : 要写入的寄存器首地址
46 * @param – buf : 要写入的数据缓冲区
47 * @param – len : 要写入的数据长度
48 * @return : 操作结果
49 */
static s32 xxx_write_regs(struct xxx_dev *dev, u8 reg, u8 *buf,u8 len)
{
52 u8 b[256];
53 struct i2c_msg msg;
54 struct i2c_client *client = (struct i2c_client *)
dev->private_data;
56 b[0] = reg; /* 寄存器首地址 */
57 memcpy(&b[1],buf,len); /* 将要发送的数据拷贝到数组 b 里面 */
59 msg.addr = client->addr; /* I2C 器件地址 */
60 msg.flags = 0; /* 标记为写数据 */
62 msg.buf = b; /* 要发送的数据缓冲区 */
63 msg.len = len + 1; /* 要发送的数据长度 */
65 return i2c_transfer(client->adapter, &msg, 1);
}
第 15~40 行,xxx_read_regs
函数用于读取 I2C 设备多个寄存器数据。第 18 行定义了一个i2c_msg 数组,2 个数组元素,因为 I2C 读取数据的时候要先发送要读取的寄存器地址,然后再读取数据,所以需要准备两个 i2c_msg
。一个用于发送寄存器地址
,一个用于读取寄存器值
。对于 msg[0],将 flags 设置为 0,表示写数据。msg[0]的 addr 是 I2C 设备的器件地址,msg[0]的 buf成员变量就是要读取的寄存器地址。对于 msg[1],将 flags 设置为 I2C_M_RD
,表示读取数据。msg[1]的 buf 成员变量用于保存读取到的数据,len 成员变量就是要读取的数据长度。调用i2c_transfer
函数完成 I2C 数据读操作。
第 50~66 行,xxx_write_regs
函数用于向 I2C 设备多个寄存器写数据,I2C 写操作要比读操作简单一点,因此一个 i2c_msg 即可。数组 b 用于存放寄存器首地址和要发送的数据,第 59 行设置 msg 的 addr 为 I2C 器件地址。第 60 行设置 msg 的 flags 为 0,也就是写数据。第 62 行设置要发送的数据,也就是数组 b。第 63 行设置 msg 的 len 为 len+1,因为要加上一个字节的寄存器地址。最后通过 i2c_transfer
函数完成向 I2C 设备的写操作。
i2c_master_send和i2c_master_recv
这两个API函数分别用于I2C数据的收发操作,这两个函数最终都会调用i2c_transfer
。
发送函数 i2c_master_send
int i2c_master_send(const struct i2c_client *client, const char *buf,int count)
参数 | 描述 |
---|---|
client | I2C 设备对应的 i2c_client。 |
buf | 要发送的数据。 |
count | 要发送的数据字节数,要小于 64KB,以为 i2c_msg 的 len 成员变量是一个 u16(无符号 16 位)类型的数据。 |
返回值 | 负值,失败,其他非负值,发送的字节数。 |
int i2c_master_recv(const struct i2c_client *client, char *buf,int count)
参数 | 描述 |
---|---|
client | I2C 设备对应的 i2c_client。 |
buf | 要接收的数据。 |
count | 要接收的数据字节数,要小于 64KB,以为 i2c_msg 的 len 成员变量是一个 u16(无符号 16 位)类型的数据。 |
返回值 | 负值,失败,其他非负值,发送的字节数。 |