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 26 27## 使用指导 28 29### 场景介绍 30 31I2C通常用于与各类支持I2C协议的传感器、执行器或输入输出设备进行通信。 32 33### 接口说明 34 35I2C模块提供的主要接口如表1所示,具体API详见//drivers/hdf_core/framework/include/platform/i2c_if.h。 36 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 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>  **注意:** 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>  **说明:**<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