集成电路总线 ( Inter-Integrated Circuit ,缩写 I2C ),详细说明参见:I2C(维基百科)
I2C 总线介绍
- I2C 总线由数据线 SDA 和 时钟线 SCL 两条线构成通信线路,即可发送数据,也可接收数据。在 I2C 通信中,支持
一主多从 或多主多从 ,即可以有多个 I2C 设备同时并联到 I2C 总线上。 - 在 I2C 通信过程中,CPU 发出的控制信号分为
地址码 和数据码 两部分。
地址码 用来选址,即接通需要控制的电路。每个 I2C 设备都有唯一的地址,在信息传输过程中,通过不同的设备地址来识别通信对象。这样各 I2C 设备虽然挂在同一条总线上,却彼此独立。数据码 是通信的内容。
地址码(7 bit):
- 以 24C02 (EEPROM)芯片为例,高 4 bit 是固定的 1010,而低 3 bit 的地址取决于具体的硬件电路设计,由芯片的 A2、A1、A0 这 3 个管脚的实际电平决定。
如图,A2、A1、A0 这 3 个管脚都接 GND,即地址码的低 3 bit 都为 0 ,规则为 :1010(A0)(A1)(A2)(R/W),R/W 位我们要设置为0 (写操作)。因此 24C02 (EEPROM)芯片的地址码为:(0b)1010000 , 即 0x50 。
I2C 接口的优点:
I2C 通信格式
I2C 通信格式包含起始信号 、数据传输 和停止信号 。
起始信号 :SCL处于高电平,SDA 由高到低。 I2C 通信的起始信号的定义是在 SCL 为高电平器件,SDA 由高电平向低电平变化产生一个下降沿,表示起始信号,如上图 Start 部分所示。数据传输 :SDA 上的数据必须在 SCL 高电平周期时保持稳定,数据的高低电平翻转变化发生在 SCL 低电平时期。 I2C 时序要求当 SCL 在低电平的时候,SDA 运行变化,即发送方必须先保持 SCL 是低电平,才可以改变数据线 SDA,输出要发送的当前数据的一位;而 SCL 在高电平的时候,SDA 绝对不可以变化,因为此时接收方要来读取当前 SDA 的电平信号是 0 还是 1 ,所以需要保证 SDA 的稳定。如图中的每一位数据的变化,都是在 SCL 为低电平的位置。数据位后面跟着的是1 bit 应答位(ACK)停止信号 :SCL 处于高电平,SDA 由低到高。 I2C 通信停止信号的定义是 SCL 为高电平期间,SDA 由低电平向高电平变化产生一个上升沿,表示结束信号,如图中的 Stop 部分所示。
I2C 数据传输有 3 种情形:写传输 ,读传输 ,混合传输 。对于一次完整的数据传输,由多个字节传输组成。
如上图所示。在主机发出 START 之后的第 1 次字节传输中,主机发送 7bit 从机地址,1bit 读指示(1)。被寻址的从机在收到第 1 次字节传输后回复 ACK,并接管数据线向主机传递数据,主机每收到一字节数据则向从机回复一次 ACK。最后一次字节传输时,主机回复 NACK 并且发出 STOP 告知从机读传输完成。
说明:
- SDA 数据线和 SCL 时钟线在空闲状态下均保持高电平,连到总线上的任一器件输出的低电平都将使总线的信号变低,即各 I2C 器件的 SDA 与 SCL 都是 “与” 关系。
- ACK(应答信号):当传输完 8 位数据以后,在第 9 个 SCL 时钟周期。主机释放 SDA 控制权交给从机,由于上拉电阻的作用,此时该电平为高,但是如果从机正确接受了数据,就会将 SDA 拉低
- NACK(非应答信号):如果第 9 个 SCL 时钟周期,SDA 保持高电平,则代表非应该信号
ESP32 I2C 接口
参考资料说明:
1 总体描述
ESP32 有 2 个 I2C 总线接口,根据用户的配置,总线接口可以用作 I2C 主机 或从机 模式。I2C 接口支持:
- 标准模式 (100 Kbit/s)
- 高速模式 (400 Kbit/s)
- 速度最高可达
5 MHz ,但受制于 SDA 上拉强度 - 支持多主机多从机通信
- 支持
7-bit 以及 10-bit 寻址模式 - 双寻址模式
- 支持关闭 SCL 时钟实现连续数据传输
- 支持可编程数字噪声滤波功能
- 用户可以配置指令寄存器来控制 I2C 接口,从而实现更多灵活的应用。
2 管脚说明
ESP32 的 I2C 管脚可使用任意 GPIO 管脚映射,请参见《ESP32 技术规格书》中 “4.2 外设管脚分配” 说明。
3 硬件设计
- I2C 仅使用两个双向开漏线,
串行数据线 (SDA)和串行时钟线 (SCL),以及上拉电阻。使用的典型电压是 +5 V 或 +3.3 V (虽然其他电压系统也是允许的)。 - 通常来说,所选择的频率越高,需要的上拉电阻越小 (但是不要小于 1K 欧姆)。这是因为高电阻会减小电流,这会延长上升时间从而使频率变慢。通常我们推荐的上拉阻值范围为 2K 欧姆到 5K 欧姆,但是用户可能也需要根据他们的实际情况做出一些调整。
- 将 SCL 的 pad 配置成开漏方式时,SCL 从低电平转向高电平的时间会变长,这个转变时间由 SCL 线上的上拉电阻以及电容共同决定。开漏模式下,SCL 的输出频率会偏低。
- Deep-sleep 模式下,仅 ULP 协处理器处于工作状态时,可以操作如下
RTC_GPIO 作为低功耗 I2C 管脚, 可参见《ESP32 技术参考手册》中关于 “4.11 RTC_MUX 管脚清单” 说明。
4 ESP32 I2C 常见的应用
- 为了保存用户的设置而访问 NVRAM 芯片
- 访问低速的数字模拟转换器(DAC)和模拟数字转换器(ADC)
- 改变监控器的对比度、色调及色彩平衡设置(VESA显示数据频道)
- 控制小型液晶或 OLED 屏幕
- 调节音量大小
- 获取硬件监控及诊断资料,例如中央处理器的温度及风扇转速
- 读取实时时钟(Real-time clock)
- 在系统设备中用来开启或关闭电源供应
4.1 I2C 配置说明
建议阅读 ESP32 的 I2C 驱动程序 编程指南
- I2C 时钟速度由 I2C Master(主机)设置
- I2C 的 SDA 和 SCL 管脚必须上拉(软件上拉或硬件上拉)
4.2 配置示例:
int i2c_master_port = 0;
i2c_config_t conf = {
.mode = I2C_MODE_MASTER,
.sda_io_num = I2C_MASTER_SDA_IO,
.sda_pullup_en = GPIO_PULLUP_ENABLE,
.scl_io_num = I2C_MASTER_SCL_IO,
.scl_pullup_en = GPIO_PULLUP_ENABLE,
.master.clk_speed = I2C_MASTER_FREQ_HZ,
};
当 I2C 控制器工作于 Master 模式,SCL 为输出信号。
int i2c_slave_port = I2C_SLAVE_NUM;
i2c_config_t conf_slave = {
.sda_io_num = I2C_SLAVE_SDA_IO,
.sda_pullup_en = GPIO_PULLUP_ENABLE,
.scl_io_num = I2C_SLAVE_SCL_IO,
.scl_pullup_en = GPIO_PULLUP_ENABLE,
.mode = I2C_MODE_SLAVE,
.slave.addr_10bit_en = 0,
.slave.slave_addr = ESP_SLAVE_ADDR,
};
当 I2C 控制器工作于 Slave 模式,SCL 为输入信号。
ESP32 设备作为 I2C 从机时,支持 7 bit 和 10 bit 双地址寻址,且地址码可自定义为任何 7 bit 或 10 bit 的值。 以 I2C 例程为例,默认使用的是 7 bit 地址码。若使用 10 bit (读)地址码,可以设置类似 0x329 (0011 0010 1001)的值
在此阶段,i2c_param_config() 还将其他 I2C 配置参数设置为 I2C 总线协议规范中定义的默认值。有关默认值及修改默认值的详细信息,请参考 用户自定义配置。
4.3 参考例程:
注意: I2C 外接传感器的应用说明可参见:传感器集 指南
5 ESP32 I2C 的时钟源特性
可参见 ESP32 I2C 源时钟配置 说明
注意:在主机模式下,SCL 的时钟频率不应大于上表中提到的 SCL 的最大频率。
typedef enum {
I2C_SCLK_DEFAULT = 0,
#if SOC_I2C_SUPPORT_APB
I2C_SCLK_APB,
#endif
#if SOC_I2C_SUPPORT_XTAL
I2C_SCLK_XTAL,
#endif
#if SOC_I2C_SUPPORT_RTC
I2C_SCLK_RTC,
#endif
#if SOC_I2C_SUPPORT_REF_TICK
I2C_SCLK_REF_TICK,
#endif
I2C_SCLK_MAX,
} i2c_sclk_t;
6 ESP32 I2C 中断处理
6.1 I2C 中断触发条件
参见《ESP32 技术参考手册》中 “ 11.3.7 中断 ” 说明
- I2C_TX_SEND_EMPTY_INT: 每当 I2C Master 或 Slave 发送
nonfifo_tx_thres 个数据,即触发该中断。 - I2C_RX_REC_FULL_INT: 每当 I2C Master 或 Slave 接收
nonfifo_rx_thres 个数据,即触发该中断。 - I2C_ACK_ERR_INT: 当 I2C 配置为 Master 时,接收到的 ACK 与命令中期望的
ACK 值不一致时,即触发该中断;当 I2C 配置为 Slave 时,接收到的 ACK 值为 1 时即触发该中断。 - I2C_TRANS_START_INT: 当 I2C Master 或 Slave 发送一个
START 位时,即触发该中断。 - I2C_TIME_OUT_INT: 在传输过程中,当
I2C SCL 保持为高或为低电平的时间超过 I2C_TIME_OUT 个时钟后,即触发该中断。 - I2C_TRANS_COMPLETE_INT: 当 I2C Master 或 Slave 检测到
STOP 位时,即触发该中断。 - I2C_MASTER_TRAN_COMP_INT: 当 I2C Master 发送或接收一个字节,即触发该中断。
- I2C_ARBITRATION_LOST_INT: 当 I2C Master 的
SCL 为高电平,SDA 输出值与输入值不相等时,即触发该中断。 - I2C_END_DETECT_INT: 当
I2C Master 处理 END 命令时,即触发该中断。
- 大多数 I2C 驱动程序的函数在成功完成时会返回
ESP_OK ,或在失败时会返回特定的错误代码。实时检查返回的值并进行错误处理是一种好习惯。驱动程序也会打印日志消息,其中包含错误说明,例如检查输入配置的正确性。有关详细信息,请参考文件 driver/i2c.c 并用后缀 _ERR_STR 查找定义。
#define I2C_DRIVER_ERR_STR "i2c driver install error"
#define I2C_DRIVER_MALLOC_ERR_STR "i2c driver malloc error"
#define I2C_NUM_ERROR_STR "i2c number error"
#define I2C_TIMING_VAL_ERR_STR "i2c timing value error"
#define I2C_ADDR_ERROR_STR "i2c null address error"
#define I2C_DRIVER_NOT_INSTALL_ERR_STR "i2c driver not installed"
#define I2C_SLAVE_BUFFER_LEN_ERR_STR "i2c buffer size too small for slave mode"
#define I2C_EVT_QUEUE_ERR_STR "i2c evt queue error"
#define I2C_SEM_ERR_STR "i2c semaphore error"
#define I2C_BUF_ERR_STR "i2c ringbuffer error"
#define I2C_MASTER_MODE_ERR_STR "Only allowed in master mode"
#define I2C_MODE_SLAVE_ERR_STR "Only allowed in slave mode"
#define I2C_CMD_MALLOC_ERR_STR "i2c command link malloc error"
#define I2C_CMD_USER_ALLOC_ERR_STR "i2c command link allocation error: the buffer provided is too small."
#define I2C_TRANS_MODE_ERR_STR "i2c trans mode error"
#define I2C_MODE_ERR_STR "i2c mode error"
#define I2C_SDA_IO_ERR_STR "sda gpio number error"
#define I2C_SCL_IO_ERR_STR "scl gpio number error"
#define I2C_SCL_SDA_EQUAL_ERR_STR "scl and sda gpio numbers are the same"
#define I2C_CMD_LINK_INIT_ERR_STR "i2c command link error"
#define I2C_GPIO_PULLUP_ERR_STR "this i2c pin does not support internal pull-up"
#define I2C_ACK_TYPE_ERR_STR "i2c ack type error"
#define I2C_DATA_LEN_ERR_STR "i2c data read length error"
#define I2C_PSRAM_BUFFER_WARN_STR "Using buffer allocated from psram"
#define I2C_LOCK_ERR_STR "Power lock creation error"
#define I2C_CLK_FLAG_ERR_STR "i2c clock choice is invalid, please check flag and frequency"
需要注意 ESP32 I2C 总线上的几种特殊情况:
- I2C Master 在发送 STOP 位时,若因为 SDA 被其他设备拉低导致发送不了 STOP 位,则需要复位 I2C Master。
- I2C Master 在发送 START 位时,若因为 SDA 或者 SCL 被其他设备拉低导致发送不了 START 位,则需要复位 I2C Master。建议软件使用超时时间来执行重置。
- 数据传输过程中,SDA 被 I2C Slave 拉为低电平,此时 I2C Master 只需给 Slave 至多九个 SCL 时钟即可释放 SDA 线。
8 ESP32 I2C FAQ
- 使用的 ESP32-WROOM32-32E 模块,想用 GPIO0、GPIO4 作为 I2C 信号接口,但是 GPIO0 是 PROG 的烧写口,GPIO0 这样并联状态会出现冲突吗?
GPIO0 做 I2C 时需要加上拉,固件下载的时候只要保证 GPIO0 在上电时能拉低,然后就可以释放了,不需要一直拉低,而且只有下载的时候需要拉低。
- 使用 ESP32 在程序初始化时报一个错误,请问如何解决?
I2C_BUS: C:/Users/hpc/Desktop/esp-adf/components/esp_peripherals/driver/i2c_bus/i2c_bus.c:100 (i2c_bus_write_bytes):I2C Bus WriteReg Error
报错 I2C Bus WriteReg Error,是 I2C 写寄存器出错了。WriteReg 与 Write 类似,但 WriteReg 是写入寄存器的。
ESP32 I2C 的传输速率理论支持 5MHZ ,当前最大支持 1MHz 。
- 中断方式或调高 I2C 时钟的话,是否可以优化按键处理延时?
ESP32 支持 1 MHZ 的 I2C ,I2C 时钟 越大,传输速率越快,这样延时就越小了。
|