I2C 简介
I2C(Inter Integrated Circuit)总线是 PHILIPS 公司开发的一种半双工、双向二线制同步串行总线。I2C 总线传输数据时只需两根信号线,一根是双向数据线 SDA(serial data),另一根是双向时钟线 SCL(serial clock)。SPI 总线有两根线分别用于主从设备之间接收数据和发送数据,而 I2C 总线只使用一根线进行数据收发。
I2C 和 SPI 一样以主从的方式工作,不同于 SPI 一主多从的结构,它允许同时有多个主设备存在,每个连接到总线上的器件都有唯一的地址,主设备启动数据传输并产生时钟信号,从设备被主设备寻址,同一时刻只允许有一个主设备。如下图所示:
I2C 总线主从设备连接方式
如下图所示为 I2C 总线主要的数据传输格式:
I2C 总线数据传输格式
当总线空闲时,SDA 和 SCL 都处于高电平状态,当主机要和某个从机通讯时,会先发送一个开始信号,然后发送从机地址和读写控制位,接下来传输数据(主机发送或者接收数据),数据传输结束时主机会发送结束信号。传输的每个字节为8位,高位在前,低位在后。数据传输过程中的不同名词详解如下所示:
开始条件:SCL 为高电平时,主机将 SDA 拉低,表示数据传输即将开始。
从机地址:主机发送的第一个字节为从机地址,高 7 位为地址,最低位为 R/W 读写控制位,1 表示读操作,0 表示写操作。一般从机地址有 7 位地址模式和 10 位地址模式两种,如果是 10 位地址模式,第一个字节的头 7 位 是 11110XX 的组合,其中最后两位(XX)是 10 位地址的两个最高位,第二个字节为 10 位从机地址的剩下8位,如下图所示:
7 位地址和 10 位地址格式
应答信号:每传输完成一个字节的数据,接收方就需要回复一个 ACK(acknowledge)。写数据时由从机发送 ACK,读数据时由主机发送 ACK。当主机读到最后一个字节数据时,可发送 NACK(Negative Acknowledgement)然后跟停止条件。
数据:地址帧发送完后可能会发送一些指令,依从机而定,然后开始传输数据,由主机或者从机发送,每个数据为 8 位,数据的字节数没有限制。
重复开始条件:在一次通信过程中,主机可能需要和不同的从机传输数据或者需要切换读写操作时,主机可以再发送一个开始条件。
停止条件:在 SDA 为低电平时,主机将 SCL 拉高并保持高电平,然后在将 SDA 拉高,表示传输结束。
访问 I2C 总线设备
一般情况下 MCU 的 I2C 器件都是作为主机和从机通讯,在 RT-Thread 中将 I2C 主机虚拟为 I2C总线设备,I2C 从机通过 I2C 设备接口和 I2C 总线通讯,相关接口如下所示:
查找 I2C 总线设备
在使用 I2C 总线设备前需要根据 I2C 总线设备名称获取设备句柄,进而才可以操作 I2C 总线设备,查找设备函数如下所示,
1rt_device_t rt_device_find(const char* name);
1#define AHT10_I2C_BUS_NAME "i2c1" /* 传感器连接的I2C总线设备名称 */2struct rt_i2c_bus_device *i2c_bus; /* I2C总线设备句柄 */34/* 查找I2C总线设备,获取I2C总线设备句柄 */5i2c_bus = (struct rt_i2c_bus_device *)rt_device_find(name);一般情况下,注册到系统的 I2C 设备名称为 i2c0 ,i2c1等,使用示例如下所示:
数据传输
获取到 I2C 总线设备句柄就可以使用 rt_i2c_transfer() 进行数据传输。函数原型如下所示:
1rt_size_t rt_i2c_transfer(struct rt_i2c_bus_device *bus,2 struct rt_i2c_msg msgs[],3 rt_uint32_t num);
和 SPI 总线的自定义传输接口一样,I2C 总线的自定义传输接口传输的数据也是以一个消息为单位。参数 msgs[] 指向待传输的消息数组,用户可以自定义每条消息的内容,实现 I2C 总线所支持的 2 种不同的数据传输模式。如果主设备需要发送重复开始信号,则需要发送 2 个消息。
I2C 消息数据结构原型如下:
1struct rt_i2c_msg2{3 rt_uint16_t addr; /* 从机地址 */4 rt_uint16_t flags; /* 读、写标志等 */5 rt_uint16_t len; /* 读写数据字节数 */6 rt_uint8_t *buf; /* 读写数据缓冲区指针 */7}
从机地址 addr:支持 7 位和 10 位二进制地址,需查看不同设备的数据手册 。RT-Thread I2C 设备接口使用的从机地址均为不包含读写位的地址,读写位控制需修改标志 flags。
标志 flags 可取值为以下宏定义,根据需要可以与其他宏使用位运算 “|” 组合起来使用。
1#define RT_I2C_WR 0x0000 /* 写标志 */2#define RT_I2C_RD (1u << 0) /* 读标志 */3#define RT_I2C_ADDR_10BIT (1u << 2) /* 10 位地址模式 */4#define RT_I2C_NO_START (1u << 4) /* 无开始条件 */5#define RT_I2C_IGNORE_NACK (1u << 5) /* 忽视 NACK */6#define RT_I2C_NO_READ_ACK (1u << 6) /* 读的时候不发送 ACK */
使用示例如下所示:
1#define AHT10_I2C_BUS_NAME "i2c1" /* 传感器连接的I2C总线设备名称 */ 2#define AHT10_ADDR 0x38 /* 从机地址 */ 3struct rt_i2c_bus_device *i2c_bus; /* I2C总线设备句柄 */ 4 5/* 查找I2C总线设备,获取I2C总线设备句柄 */ 6i2c_bus = (struct rt_i2c_bus_device *)rt_device_find(name); 7 8/* 读传感器寄存器数据 */ 9static rt_err_t read_regs(struct rt_i2c_bus_device *bus, rt_uint8_t len, rt_uint8_t *buf)10{11 struct rt_i2c_msg msgs;1213 msgs.addr = AHT10_ADDR; /* 从机地址 */14 msgs.flags = RT_I2C_RD; /* 读标志 */15 msgs.buf = buf; /* 读写数据缓冲区指针 */16 msgs.len = len; /* 读写数据字节数 */1718 /* 调用I2C设备接口传输数据 */19 if (rt_i2c_transfer(bus, &msgs, 1) == 1)20 {21 return RT_EOK;22 }23 else24 {25 return -RT_ERROR;26 }27}
I2C 总线设备使用示例
I2C 设备的具体使用方式可以参考如下示例代码,示例代码的主要步骤如下:
首先根据 I2C 设备名称查找 I2C 名称,获取设备句柄,然后初始化 aht10 传感器。
控制传感器的 2 的函数为写传感器寄存器 write_reg() 和读传感器寄存器 read_regs(),这两个函数分别调用了 rt_i2c_transfer() 传输数据。读取温湿度信息的函数 read_temp_humi() 则是调用这两个函数完成功能。
1/* 2 * 程序清单:这是一个 I2C 设备使用例程 3 * 例程导出了 i2c_aht10_sample 命令到控制终端 4 * 命令调用格式:i2c_aht10_sample i2c1 5 * 命令解释:命令第二个参数是要使用的I2C总线设备名称,为空则使用默认的I2C总线设备 6 * 程序功能:通过 I2C 设备读取温湿度传感器 aht10 的温湿度数据并打印 7*/ 8 9#include
全部0条评论
快来发表一下你的评论吧 !