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