1# I2C
2
3## 概述
4
5### 功能简介
6
7I2C(Inter Integrated Circuit)总线是由Philips公司开发的一种简单、双向二线制同步串行总线。由于其硬件连接简单、成本低廉,因此被广泛应用于各种短距离通信的场景。
8
9### 运作机制
10
11I2C以主从方式工作,通常有一个主设备和一个或者多个从设备,主从设备通过SDA(SerialData)串行数据线以及SCL(SerialClock)串行时钟线两根线相连(如图1)。
12
13I2C数据的传输必须以一个起始信号作为开始条件,以一个结束信号作为传输的停止条件。数据传输以字节为单位,高位在前,逐个bit进行传输。
14
15I2C总线上的每一个设备都可以作为主设备或者从设备,而且每一个设备都会对应一个唯一的地址,当主设备需要和某一个从设备通信时,通过广播的方式,将从设备地址写到总线上,如果某个从设备符合此地址,将会发出应答信号,建立传输。
16
17I2C接口定义了完成I2C传输的通用方法集合,包括:
18
19- I2C控制器管理:打开或关闭I2C控制器
20
21- I2C消息传输:通过消息传输结构体数组进行自定义传输
22
23**图 1** I2C物理连线示意图
24
25![I2C物理连线示意图](figures/I2C物理连线示意图.png)
26
27## 使用指导
28
29### 场景介绍
30
31I2C通常用于与各类支持I2C协议的传感器、执行器或输入输出设备进行通信。
32
33### 接口说明
34
35I2C模块提供的主要接口如表1所示,具体API详见//drivers/hdf_core/framework/include/platform/i2c_if.h36
37**表 1** I2C驱动API接口功能介绍
38
39| 接口名 | 接口描述 |
40| -------- | -------- |
41| DevHandle I2cOpen(int16_t number) | 打开I2C控制器 |
42| void I2cClose(DevHandle handle) | 关闭I2C控制器 |
43| int32_t I2cTransfer(DevHandle handle, struct I2cMsg \*msgs, int16_t count) | 自定义传输 |
44
45### 使用流程
46
47使用I2C设备的一般流程如下图所示。
48
49**图 2** I2C设备使用流程图
50
51![I2C设备使用流程图](figures/I2C设备使用流程图.png)
52
53
54#### 打开I2C控制器
55
56在进行I2C通信前,首先要调用I2cOpen打开I2C控制器。
57
58```c
59DevHandle I2cOpen(int16_t number);
60```
61
62**表 2** I2cOpen参数和返回值描述
63
64| **参数** | **参数描述** |
65| -------- | -------- |
66| number | int16_t类型,I2C控制器号 |
67| **返回值** | **返回值描述** |
68| NULL | 打开I2C控制器失败 |
69| 设备句柄 | 打开的I2C控制器设备句柄 |
70
71假设系统中存在8个I2C控制器,编号从0到7,以下代码示例为获取3号控制器:
72
73```c
74DevHandle i2cHandle = NULL;  // I2C控制器句柄
75
76// 打开I2C控制器
77i2cHandle = I2cOpen(3);
78if (i2cHandle == NULL) {
79    HDF_LOGE("I2cOpen: i2c open fail.\n");
80    return NULL;
81}
82```
83
84#### 进行I2C通信
85
86消息传输
87
88```c
89int32_t I2cTransfer(DevHandle handle, struct I2cMsg *msgs, int16_t count);
90```
91
92**表 3** I2cTransfer参数和返回值描述
93
94| **参数** | **参数描述** |
95| -------- | -------- |
96| handle | DevHandle类型,I2C控制器设备句柄 |
97| msgs | 结构体指针,待传输数据的消息结构体数组 |
98| count | int16_t类型,消息数组长度 |
99| **返回值** | **返回值描述** |
100| 正整数 | 成功传输的消息结构体数目 |
101| 负数 | 执行失败 |
102
103I2C传输消息类型为I2cMsg,每个传输消息结构体表示一次读或写,通过一个消息数组,可以执行若干次的读写组合操作。组合读写示例:
104
105```c
106int32_t ret;
107uint8_t wbuff[2] = { 0x12, 0x13 };
108uint8_t rbuff[2] = { 0 };
109struct I2cMsg msgs[2];        // 自定义传输的消息结构体数组
110msgs[0].buf = wbuff;          // 写入的数据
111msgs[0].len = 2;              // 写入数据长度为2
112msgs[0].addr = 0x5A;          // 写入设备地址为0x5A
113msgs[0].flags = 0;            // 传输标记为0,默认为写
114msgs[1].buf = rbuff;          // 要读取的数据
115msgs[1].len = 2;              // 读取数据长度为2
116msgs[1].addr = 0x5A;          // 读取设备地址为0x5A
117msgs[1].flags = I2C_FLAG_READ // I2C_FLAG_READ置位
118// 进行一次自定义传输,传输的消息个数为2
119ret = I2cTransfer(i2cHandle, msgs, 2);
120if (ret != 2) {
121    HDF_LOGE("I2cTransfer: i2c transfer fail, ret:%d\n", ret);
122    return HDF_FAILURE;
123}
124```
125
126> ![icon-caution.gif](public_sys-resources/icon-caution.gif) **注意:**
127> - I2cMsg结构体中的设备地址不包含读写标志位,读写信息由flags成员变量的读写控制位传递。
128>
129> - 本函数不对消息结构体个数count做限制,其最大个数度由具体I2C控制器决定。
130>
131> - 本函数也不对每个消息结构体中的数据长度做限制,同样由具体I2C控制器决定。
132>
133> - 本函数可能会引起系统休眠,不允许在中断上下文调用
134
135
136#### 关闭I2C控制器
137
138I2C通信完成之后,需要关闭I2C控制器,关闭函数如下所述:
139
140```c
141void I2cClose(DevHandle handle);
142```
143
144**表 4** I2cClose参数和返回值描述
145
146| 参数 | 参数描述 |
147| -------- | -------- |
148| handle | DevHandle类型,I2C控制器设备句柄 |
149
150关闭I2C控制器示例:
151
152```c
153I2cClose(i2cHandle); // 关闭I2C控制器
154```
155
156### 使用示例
157
158本例程以操作开发板上的I2C设备为例,详细展示I2C接口的完整使用流程。
159
160本例拟对Hi3516DV300开发板上TouchPad设备进行简单的寄存器读写访问,基本硬件信息如下:
161
162- SOC:hi3516dv300。
163
164- Touch IC:I2C地址为0x38,IC内部寄存器位宽为1字节。
165
166- 硬件连接:TouchPad设备挂接在3号I2C控制器下;IC的复位管脚为3号GPIO。
167
168本例程首先对Touch IC进行复位操作(开发板上电默认会给TouchIC供电,本例程不考虑供电),然后对其内部寄存器进行随机读写,测试I2C通路是否正常。
169
170> ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**<br>
171> 本示例重点在于展示I2C设备访问流程,并验证I2C通路,所以对于设备寄存器读写值不做关注,读写寄存器导致的行为由设备自身决定。
172
173示例如下:
174
175```c
176#include "i2c_if.h"          /* I2C标准接口头文件 */
177#include "gpio_if.h"         /* GPIO标准接口头文件 */
178#include "hdf_log.h"         /* 标准日志打印头文件 */
179#include "osal_io.h"         /* 标准IO读写接口头文件 */
180#include "osal_time.h"       /* 标准延迟&睡眠接口头文件 */
181
182/* 定义一个表示TP设备的结构体,存储i2c及gpio相关硬件信息 */
183struct TpI2cDevice {
184    uint16_t rstGpio;             /* 复位管脚 */
185    uint16_t busId;               /* I2C总线号 */
186    uint16_t addr;                /* I2C设备地址 */
187    uint16_t regLen;              /* 寄存器字节宽度 */
188    DevHandle i2cHandle;  /* I2C控制器句柄 */
189};
190
191/* I2C管脚io配置,需要查阅SOC寄存器手册  */
192#define I2C3_DATA_REG_ADDR 0x112f008c /* 3号I2C控制器SDA管脚配置寄存器地址 */
193#define I2C3_CLK_REG_ADDR 0x112f0090  /* 3号I2C控制器SCL管脚配置寄存器地址 */
194#define I2C_REG_CFG 0x5f1             /* 3号I2C控制器SDA及SCL管脚配置值 */
195
196static void TpSocIoCfg(void)
197{
198    /* 将3号I2C控制器对应两个管脚的IO功能设置为I2C */
199    OSAL_WRITEL(I2C_REG_CFG, IO_DEVICE_ADDR(I2C3_DATA_REG_ADDR));
200    OSAL_WRITEL(I2C_REG_CFG, IO_DEVICE_ADDR(I2C3_CLK_REG_ADDR));
201}
202
203/* 对TP的复位管脚进行初始化, 拉高维持20ms, 再拉底维持50ms,最后再拉高维持20ms, 完成复位动作 */
204static int32_t TestCaseGpioInit(struct TpI2cDevice *tpDevice)
205{
206    int32_t ret;
207
208    /* 设置复位管脚方向为输出 */
209    ret = GpioSetDir(tpDevice->rstGpio, GPIO_DIR_OUT);
210    if (ret != HDF_SUCCESS) {
211        HDF_LOGE("%s: set rst dir fail!:%d", __func__, ret);
212        return ret;
213    }
214
215    ret = GpioWrite(tpDevice->rstGpio, GPIO_VAL_HIGH);
216    if (ret != HDF_SUCCESS) {
217        HDF_LOGE("%s: set rst hight fail!:%d", __func__, ret);
218        return ret;
219    }
220    OsalMSleep(20);
221
222    ret = GpioWrite(tpDevice->rstGpio, GPIO_VAL_LOW);
223    if (ret != HDF_SUCCESS) {
224        HDF_LOGE("%s: set rst low fail!:%d", __func__, ret);
225        return ret;
226    }
227    OsalMSleep(50);
228
229    ret = GpioWrite(tpDevice->rstGpio, GPIO_VAL_HIGH);
230    if (ret != HDF_SUCCESS) {
231        HDF_LOGE("%s: set rst high fail!:%d", __func__, ret);
232        return ret;
233    }
234    OsalMSleep(20);
235
236    return HDF_SUCCESS;
237}
238
239/* 基于I2cTransfer方法封装一个寄存器读写的辅助函数, 通过flag表示读或写 */
240static int TpI2cReadWrite(struct TpI2cDevice *tpDevice, unsigned int regAddr,
241    unsigned char *regData, unsigned int dataLen, uint8_t flag)
242{
243    int index = 0;
244    unsigned char regBuf[4] = {0};
245    struct I2cMsg msgs[2] = {0};
246
247    /* 单双字节寄存器长度适配 */
248    if (tpDevice->regLen == 1) {
249        regBuf[index++] = regAddr & 0xFF;
250    } else {
251        regBuf[index++] = (regAddr >> 8) & 0xFF;
252        regBuf[index++] = regAddr & 0xFF;
253    }
254
255    /* 填充I2cMsg消息结构 */
256    msgs[0].addr = tpDevice->addr;
257    msgs[0].flags = 0; /* 标记为0,表示写入 */
258    msgs[0].len = tpDevice->regLen;
259    msgs[0].buf = regBuf;
260
261    msgs[1].addr = tpDevice->addr;
262    msgs[1].flags = (flag == 1) ? I2C_FLAG_READ : 0; /* 添加读标记位,表示读取 */
263    msgs[1].len = dataLen;
264    msgs[1].buf = regData;
265
266    if (I2cTransfer(tpDevice->i2cHandle, msgs, 2) != 2) {
267        HDF_LOGE("%s: i2c read err", __func__);
268        return HDF_FAILURE;
269    }
270    return HDF_SUCCESS;
271}
272
273/* TP寄存器读函数 */
274static inline int TpI2cReadReg(struct TpI2cDevice *tpDevice, unsigned int regAddr,
275    unsigned char *regData, unsigned int dataLen)
276{
277    return TpI2cReadWrite(tpDevice, regAddr, regData, dataLen, 1);
278}
279
280/* TP寄存器写函数 */
281static inline int TpI2cWriteReg(struct TpI2cDevice *tpDevice, unsigned int regAddr,
282    unsigned char *regData, unsigned int dataLen)
283{
284    return TpI2cReadWrite(tpDevice, regAddr, regData, dataLen, 0);
285}
286
287/* I2C例程总入口 */
288static int32_t TestCaseI2c(void)
289{
290    int32_t i;
291    int32_t ret;
292    unsigned char bufWrite[7] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xA, 0xB, 0xC };
293    unsigned char bufRead[7] = {0};
294    static struct TpI2cDevice tpDevice;
295
296    /* IO管脚功能配置 */
297    TpSocIoCfg();
298
299    /* TP设备信息初始化 */
300    tpDevice.rstGpio = 3;
301    tpDevice.busId = 3;
302    tpDevice.addr = 0x38;
303    tpDevice.regLen = 1;
304    tpDevice.i2cHandle = NULL;
305
306    /* GPIO管脚初始化 */
307    ret = TestCaseGpioInit(&tpDevice);
308    if (ret != HDF_SUCCESS) {
309        HDF_LOGE("%s: gpio init fail!:%d", __func__, ret);
310        return ret;
311    }
312
313    /* 打开I2C控制器 */
314    tpDevice.i2cHandle = I2cOpen(tpDevice.busId);
315    if (tpDevice.i2cHandle == NULL) {
316        HDF_LOGE("%s: Open I2c:%u fail!", __func__, tpDevice.busId);
317        return -1;
318    }
319
320    /* 向TP-IC的0xD5寄存器连续写7字节数据 */
321    ret = TpI2cWriteReg(&tpDevice, 0xD5, bufWrite, 7);
322    if (ret != HDF_SUCCESS) {
323        HDF_LOGE("%s: tp i2c write reg fail!:%d", __func__, ret);
324        I2cClose(tpDevice.i2cHandle);
325        return -1;
326    }
327    OsalMSleep(10);
328
329    /* 从TP-IC的0xD5寄存器连续读7字节数据 */
330    ret = TpI2cReadReg(&tpDevice, 0xD5, bufRead, 7);
331    if (ret != HDF_SUCCESS) {
332        HDF_LOGE("%s: tp i2c read reg fail!:%d", __func__, ret);
333        I2cClose(tpDevice.i2cHandle);
334        return -1;
335    }
336
337    HDF_LOGE("%s: tp i2c write&read reg success!", __func__);
338    for (i = 0; i < 7; i++) {
339        HDF_LOGE("%s: bufRead[%d] = 0x%x", __func__, i, bufRead[i]);
340    }
341
342    /* 访问完毕关闭I2C控制器 */
343    I2cClose(tpDevice.i2cHandle);
344    return ret;
345}
346```
347