1# I2C
2
3
4## Overview
5
6### Function
7
8The Inter-Integrated Circuit (I2C) is a simple, bidirectional, and synchronous serial bus that uses merely two wires. It is widely used in short-distance communication due to simple connection and low cost.
9
10### Working Principles
11
12In I2C communication, one controller communicates with one or more devices through the serial data line (SDA) and serial clock line (SCL), as shown in the figure below.
13
14I2C data transfer must begin with a **START** condition and end with a **STOP** condition. Data is transmitted byte-by-byte from the most significant bit to the least significant bit.
15
16Each I2C node is recognized by a unique address and can serve as either a controller or a device. When the controller needs to communicate with a device, it writes the device address to the bus through broadcast. A device matching this address sends a response to set up a data transfer channel.
17
18The I2C module provides a set of APIs for I2C data transfer, including:
19- Opening or closing an I2C controller
20- Performing custom transfer via a message array
21
22    **Figure 1** I2C physical connection
23
24    ![](figures/physical-connection-diagram-for-i2c.png "physical-connection-diagram-for-i2c")
25
26## Usage Guidelines
27
28### When to Use
29
30The I2C is used in communication with the sensors, executors, and input/output devices that support the I2C protocol.
31
32### Available APIs
33
34The following table describes the APIs provided by the I2C module. For more information about the APIs, see **//drivers/hdf_core/framework/include/platform/i2c_if.h**.
35
36**Table 1** I2C driver APIs
37
38|  API | Description|
39| -------- | -------- |
40| DevHandle I2cOpen(int16_t number) | Opens an I2C controller.|
41| void I2cClose(DevHandle handle) | Closes an I2C controller.|
42| int32_t I2cTransfer(DevHandle handle, struct I2cMsg \*msgs, int16_t count) | Transfers data.|
43
44### How to Use
45
46The following figure illustrates how to use I2C APIs.
47
48**Figure 2** Process of using I2C APIs
49
50![](figures/using-I2C-process.png "process-of-using-an-i2c-device")
51
52
53#### Opening an I2C Controller
54
55Call **I2cOpen()** to open an I2C controller.
56
57```c
58DevHandle I2cOpen(int16_t number);
59```
60
61  **Table 2** Description of I2cOpen
62
63| **Parameter**| **Description**|
64| -------- | -------- |
65| number | I2C controller number.|
66| Return Value| **Description**|
67| NULL | The operation fails.|
68| Device handle| The operation is successful. The handle of the I2C controller opened is returned.|
69
70Example: Open controller 3 of the eight I2C controllers (numbered 0 and 7) in the system.
71
72```c
73DevHandle i2cHandle = NULL; /* I2C controller handle. */
74
75/* Open I2C controller 3. */
76i2cHandle = I2cOpen(3);
77if (i2cHandle == NULL) {
78    HDF_LOGE("I2cOpen: failed\n");
79    return;
80}
81```
82
83
84#### Performing I2C Communication
85
86Call **I2cTransfer()** to transfer data.
87
88```c
89int32_t I2cTransfer(DevHandle handle, struct I2cMsg \*msgs, int16_t count);
90```
91
92  **Table 3** Description of I2cTransfer
93
94| **Parameter**| **Description**|
95| -------- | -------- |
96| handle | Handle of the I2C controller.|
97| msgs | Pointer to the message array to transfer.|
98| count | Number of messages in the message array to transfer.|
99| Return Value| **Description**|
100| Positive integer| The operation is successful. The number of messages that are successfully transferred is returned.|
101| Negative value| The operation fails.|
102
103The I2C message type is defined by **I2cMsg**. Each message structure indicates a read or write operation. A message array specifies multiple read and write operations to perform.
104
105Example of read and write operations:
106
107
108```c
109int32_t ret;
110uint8_t wbuff[2] = { 0x12, 0x13 };
111uint8_t rbuff[2] = { 0 };
112struct I2cMsg msgs[2];        /* Custom message array to transfer. */
113msgs[0].buf = wbuff;          /* Data to write. */
114msgs[0].len = 2;              /* The length of the data to write is 2. */
115msgs[0].addr = 0x5A;          /* The address of the device to write the data is 0x5A. */
116msgs[0].flags = 0;            /* The flag 0 indicates a write operation. */
117msgs[1].buf = rbuff;          /* Data to read. */
118msgs[1].len = 2;              /* The length of the data to read is 2. */
119msgs[1].addr = 0x5A;          /* The address of the device to read is 0x5A. */
120msgs[1].flags = I2C_FLAG_READ /* I2C_FLAG_READ is set. */
121/* Transfer two messages. */
122ret = I2cTransfer(i2cHandle, msgs, 2);
123if (ret != 2) {
124    HDF_LOGE("I2cTransfer: failed, ret %d\n", ret);
125    return;
126}
127```
128
129> ![icon-caution.gif](../public_sys-resources/icon-caution.gif) **CAUTION**<br/>
130> - The device address in the **I2cMsg** structure does not contain the read/write flag bit. The read/write information is passed by the read/write control bit in **flags**.
131>
132> - The I2C controller determines the maximum number of messages to be transferred at a time and the maximum length of each message to transfer.
133>
134> - The **I2cTransfer** function may cause the system to sleep and therefore cannot be called in the interrupt context.
135
136
137#### Closing an I2C Controller
138
139Call **I2cClose()** to close the I2C controller after the communication is complete.
140
141```c
142void I2cClose(DevHandle handle);
143```
144
145  **Table 4** Description of I2cClose
146
147| Parameter| Description|
148| -------- | -------- |
149| handle | Handle of the I2C controller to close.|
150
151Example:
152
153```c
154I2cClose(i2cHandle); /* Close an I2C controller. */
155```
156
157
158### Example
159
160The following example describes how to use I2C APIs to implement simple read/write operations on TouchPad from a Hi3516D V300 development board.
161
162The basic hardware information is as follows:
163
164- SoC: Hi3516D V300
165
166- Touch IC: The I2C address is 0x38, and the bit width of touch IC internal register is 1 byte.
167
168- Hardware connection: The TouchPad is connected to I2C controller 3. The reset pin of the touch IC is GPIO 3.
169
170In this example, reset the touch IC (the development board supplies power to the touch IC by default after being powered on) and perform read/write operations on the internal register to test whether the I2C channel is functioning.
171
172> ![icon-note.gif](public_sys-resources/icon-note.gif) **NOTE**<br>
173> This example focuses on access to the I2C device and verifies the I2C channel, rather than the specific data read from or written to the device register and the result caused by the read and write operations on the register.
174
175The sample code is as follows:
176
177```c
178#include "i2c_if.h"          /* Header file of I2C APIs. */
179#include "gpio_if.h"         /* Header file of GPIO APIs. */
180#include "hdf_log.h"         /* Header file of log APIs. */
181#include "osal_io.h"         /* Header file of I/O read and write APIs. */
182#include "osal_time.h"       /* Header file of delay and sleep APIs. */
183
184/* Define a TP device structure to store I2C and GPIO hardware information. */
185struct TpI2cDevice {
186     uint16_t rstGpio;            /* Reset pin. */
187    uint16_t busId;               /* I2C bus number. */
188    uint16_t addr;                /* I2C device address. */
189    uint16_t regLen;              /* Register bit width. */
190    DevHandle i2cHandle;          /* I2C controller handle. */
191};
192
193/* I2C pin I/O configuration. For details, see the SoC register manual. */
194#define I2C3_DATA_REG_ADDR 0x112f008c  /* Address of the SDA pin configuration register of I2C controller 3. */
195#define I2C3_CLK_REG_ADDR 0x112f0090   /* Address of the SCL pin configuration register of I2C controller 3. */
196#define I2C_REG_CFG 0x5f1              /* Configuration values of SDA and SCL pins of I2C controller 3. */
197
198static void TpSocIoCfg(void)
199{
200    /* Set the I/O function of the two pins corresponding to I2C controller 3 to 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/* Initialize the reset pin of the TP. Pull up the pin for 20 ms, pull down the pin for 50 ms, and then pull up the pin for 20 ms to complete the reset. */
206static int32_t TestCaseGpioInit(struct TpI2cDevice *tpDevice)
207{
208    int32_t ret;
209
210    /* Set the output direction for the reset pin. */
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/* Use I2cTransfer to encapsulate a register read/write auxiliary function. Use flag to indicate the read or write operation. */
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    /* Perform length adaptation for the single- or dual-byte register. */
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    /* Fill in the I2cMsg message structure. */
258    msgs[0].addr = tpDevice->addr;
259    msgs[0].flags = 0; /* The flag 0 indicates a write operation. */
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; /* Add the read flag. */
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 register read function. */
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 register write function. */
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/* Main entry of 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    /* Configuration of I/O pin functions. */
299    TpSocIoCfg();
300
301    /* Initialize TP device information. */
302    tpDevice.rstGpio = 3;
303    tpDevice.busId = 3;
304    tpDevice.addr = 0x38;
305    tpDevice.regLen = 1;
306    tpDevice.i2cHandle = NULL;
307
308     /* Initialize the GPIO pin. */
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    /* Open an I2C controller. */
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    /* Continuously write 7-byte data to register 0xD5 of TP-IC. */
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    /* Continuously read 7-byte data from register 0xD5 of TP-IC. */
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    /* Close the I2C controller. */
345    I2cClose(tpDevice.i2cHandle);
346    return ret;
347}
348```
349