62、IIC通信解析

IIC通信解析

一、IIC总线核心认知

1. 总线结构与特性

IIC总线仅包含两根线:

  • SDA(Serial Data):双向数据线,用于传输数据;
  • SCL(Serial Clock):时钟线,由主机提供同步时钟。

其核心特性是多主多从:同一总线上可连接多个主机和从机,每个设备有唯一的7/10位地址,通过地址区分通信对象。

2. 线与特性:总线电平的核心规则

IIC总线采用线与逻辑(多个设备共享总线,实际电平由所有设备的输出共同决定),用户笔记中的表格清晰展示了线与逻辑:

chipA输出 chipB输出 实际电平
0 0 0
1 0 0
1 1 1
0 1 0

关键结论:只要有一个设备输出低电平,总线电平就是低;只有所有设备都输出高电平时,总线才是高。

3. 硬件驱动模式:开漏输出+上拉电阻

IIC设备的SDA/SCL引脚必须配置为开漏输出模式(不能用推挽模式),并外接上拉电阻(通常4.7k~10kΩ),原因如下:

  • 开漏模式下,设备只能"拉低总线",无法"主动输出高电平";高电平由上拉电阻提供,天然契合线与逻辑;
  • 推挽模式下,若两个设备同时输出高低电平,会导致总线短路(用户笔记中"不允许mos1和mos2同时导通"即为此理)。

用户笔记中MOS管控制的开漏模式表:

mos1 mos2 pad电平
0 1 0
1 0 1(上拉)
1 0 高阻/悬空态
1 1 不允许(短路)

二、IIC通信核心时序

IIC的通信由起始信号(Start)、地址/数据传输、应答(ACK/NACK)、停止信号(Stop) 四个核心环节组成,用户笔记中的时序图清晰展示了这些环节。

1. 起始信号(Start)

  • 触发条件:SCL为高电平时,SDA由高变低
  • 作用:告诉总线上的设备"通信即将开始",所有设备准备接收地址。

2. 数据传输规则

IIC以字节(8位) 为单位传输数据,传输时需遵循:

  • SCL低电平时:发送方可以修改SDA电平(准备数据);
  • SCL高电平时:SDA电平必须稳定,接收方采样数据(不能修改);
  • 数据传输遵循MSB优先(高位先传)。

3. 应答机制(ACK/NACK)

每传输1字节数据后,接收方需要回复应答信号:

  • ACK(应答):接收方拉低SDA(SCL高时),表示"数据接收成功";
  • NACK(非应答):接收方不拉低SDA,或释放总线(高电平),表示"数据接收失败"或"通信结束"。

4. 停止信号(Stop)

  • 触发条件:SCL为高电平时,SDA由低变高
  • 作用:告诉总线上的设备"本次通信结束",总线恢复空闲状态。

三、IIC写操作完整流程(结合用户流程图)

以"主机向从机指定寄存器写数据"为例,流程如下(对应用户的写操作流程图):

  1. 初始化准备:清除仲裁标志、中断标志,设置主机发送模式;
  2. 发Start信号:主机置位"主模式位",触发Start;
  3. 发从机地址(写方向):从机地址左移1位,最低位设0(表示写);
  4. 等从机应答:若收到ACK,继续;否则终止;
  5. 发寄存器地址:告诉从机"要写哪个寄存器";
  6. 等从机应答:确认从机收到寄存器地址;
  7. 发数据:依次发送要写入的数据,每发1字节等一次应答;
  8. 发Stop信号:清除"主模式位",触发Stop,等待总线空闲。

四、IMX6ULL IIC代码实战解析

用户提供了IMX6ULL的IIC初始化与写操作代码,以下是关键步骤的原理对应:

1. 头文件与寄存器宏定义

先定义IIC控制器的核心寄存器位(对应IMX6ULL的I2CR/I2SR寄存器):

c 复制代码
// I2CR(控制寄存器)关键位
#define I2CR_IEN    (1 << 7)  // IIC使能位
#define I2CR_MSTA   (1 << 5)  // 主模式位(置1=主机,触发Start)
#define I2CR_MTX    (1 << 4)  // 发送模式位(置1=发送,0=接收)
#define I2CR_RSTA   (1 << 2)  // 重发起始位

// I2SR(状态寄存器)关键位
#define I2SR_IIF    (1 << 1)  // 中断标志(传输1字节后置1)
#define I2SR_RXAK   (1 << 0)  // 接收的应答类型(0=ACK,1=NACK)

2. IIC初始化函数:硬件层配置

初始化分为引脚配置IIC控制器配置两部分:

c 复制代码
void i2c_init(I2C_Type *base)
{
    // 1. 引脚复用:将UART4的引脚配置为IIC1的SDA/SCL
    IOMUXC_SetPinMux(IOMUXC_UART4_RX_DATA_I2C1_SDA, 1);
    IOMUXC_SetPinMux(IOMUXC_UART4_TX_DATA_I2C1_SCL, 1);
    
    // 2. 电气特性:配置开漏、上拉等(0x10B0为IMX6ULL的IIC标准配置)
    IOMUXC_SetPinConfig(IOMUXC_UART4_RX_DATA_I2C1_SDA, 0x10B0);
    IOMUXC_SetPinConfig(IOMUXC_UART4_TX_DATA_I2C1_SCL, 0x10B0);

    // 3. IIC控制器配置
    base->I2CR &= ~I2CR_IEN;  // 先失能IIC,复位控制器
    base->IFDR = 0x15;        // 设置时钟分频,对应IIC时钟频率(如100kHz)
    base->I2CR |= I2CR_IEN;   // 使能IIC控制器
}
  • 引脚复用:IMX6ULL的引脚是多功能的,需通过IOMUXC将引脚切换为IIC功能;
  • 时钟分频IFDR寄存器决定IIC的通信速率(如0x15对应100kHz,需匹配从机支持的速率)。

3. 应答等待函数:确保数据传输成功

传输1字节后,需等待"中断标志(IIF)"置1,并检查应答类型:

c 复制代码
int wait_i2c_iif(I2C_Type *base)
{
    while ((base->I2SR & I2SR_IIF) == 0);  // 等待传输完成(IIF置1)
    base->I2SR &= ~I2SR_IIF;               // 清除中断标志
    if ((base->I2SR & I2SR_RXAK) != 0)     // 检查应答:非0则NACK
    {
        return -1;
    }
    return 0;  // 收到ACK,传输成功
}

4. IIC写操作函数:对应通信流程

代码完全遵循"写操作流程",每一步对应时序环节:

c 复制代码
void i2c_write(I2C_Type *base, unsigned char dev_addr, unsigned char reg_addr, unsigned char *data, unsigned int len)
{
    int stat = 0;
    // 初始化:清除仲裁、中断标志,设为发送模式
    base->I2SR &= ~(I2SR_IAL | I2SR_IIF);
    base->I2CR |= I2CR_MTX;

    // 1. 发Start信号:置位主模式位(MSTA)
    base->I2CR |= I2CR_MSTA;

    // 2. 发从机地址(写方向:最低位0)
    base->I2DR = ((dev_addr << 1) | 0);
    stat = wait_i2c_iif(base);  // 等应答
    if (stat != 0) goto stop;

    // 3. 发寄存器地址
    base->I2DR = reg_addr;
    stat = wait_i2c_iif(base);
    if (stat != 0) goto stop;

    // 4. 发数据(循环发送len字节)
    int i = 0;
    for (; i < len; i++)
    {
        base->I2DR = data[i];
        stat = wait_i2c_iif(base);
        if (stat == -1) goto stop;
    }

stop:
    // 5. 发Stop信号:清除主模式位
    base->I2CR &= ~I2CR_MSTA;
    while ((base->I2SR & I2SR_IBB) != 0);  // 等待总线空闲
    delay_ms(5);
}
  • base->I2CR |= I2CR_MSTA:置位主模式位,触发Start信号;
  • ((dev_addr << 1) | 0):从机地址左移1位,最低位0表示"写操作";
  • goto stop:若某一步收到NACK,直接跳转到Stop环节终止通信。

五、总结

IIC通信的核心是"线与逻辑+时序规则":

  • 硬件层依赖开漏输出+上拉电阻实现线与,避免总线短路;
  • 通信层通过Start/Stop信号 界定通信范围,SCL高低电平 同步数据,ACK/NACK确认传输;
  • 代码层需将寄存器操作与时序流程一一对应,确保每一步符合IIC协议。
相关推荐
2501_927773072 小时前
嵌入式——I.MX6ULL裸机环境配置
c语言·嵌入式硬件
海星船长丶2 小时前
预编译与sql注入,正则回溯绕过,mysql常见绕过,报错注入7大常用函数
服务器·数据库·sql·mysql·网络安全
yuan199972 小时前
STM32F103CBT6驱动AW9523B实现呼吸灯实例
stm32·单片机·嵌入式硬件
三伏5222 小时前
Cortex-M3权威指南Cn第八章——笔记
笔记·单片机·嵌入式硬件·cortex-m3
学工科的皮皮志^_^2 小时前
以太网PHY芯片学习RTF8211
经验分享·嵌入式硬件·学习·以太网·phy
沐欣工作室_lvyiyi2 小时前
基于单片机的直流伺服电机控制器设计与仿真(论文+源码)
单片机·嵌入式硬件·毕业设计·直流伺服电机
清风6666662 小时前
基于单片机的智能传送带自动计数与数据管理系统设计
单片机·嵌入式硬件·毕业设计·课程设计·期末大作业
AlphaFinance2 小时前
Windows 服务器自动部署方案
运维·服务器
麒qiqi2 小时前
嵌入式定时器核心解析:51 单片机 / IMX6ULL (EPIT/GPT) 原理与实战
单片机·嵌入式硬件·gpt