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.h。 35 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