1.线与特性

“线与” 并非传统数字电路中 “多个输出端直接连接” 的物理与逻辑(需特定门电路),而是 IIC 通过总线外部上拉电阻 + 设备开漏输出(Open-Drain) 实现的逻辑效果:

  • 所有设备的 SDA(串行数据线)/SCL(串行时钟线)引脚均处于高电平(释放总线) 时,总线电平由上拉电阻拉至 VDD(高电平);
  • 只要任意一个设备将 SDA/SCL 引脚拉低(输出低电平),总线电平就会被拉至低电平;
  • 最终总线电平的逻辑规则:只有所有设备都不拉低总线时,总线才为高;只要有一个设备拉低,总线就为低—— 这与 “逻辑与(AND)” 的真值表完全一致,因此称为 “线与”。

解决 “多主竞争” 的总线仲裁

当多个主设备同时尝试占用总线时(多主模式),IIC 通过 “总线仲裁” 避免数据冲突,而仲裁的核心就是线与特性:

  • 多个主设备同时向 SDA 发送电平(高或低);
  • 若某主设备发送 “高电平”,但通过线与特性检测到总线实际为 “低电平”(说明其他主设备发送了低电平),则该主设备会立即停止发送,主动放弃总线控制权;
  • 最终只有 “始终发送与总线电平一致信号” 的主设备能继续占用总线,实现无冲突的仲裁,且仲裁过程不破坏已发送的有效数据。

2.相关概念

IIC(Inter-Integrated Circuit,集成电路间总线)是由飞利浦(现恩智浦 / NXP)于 1982 年推出的短距离、同步串行通信总线,核心特点是 “双线制、多主多从、共享总线”,广泛用于芯片间的低速数据交互(如传感器、存储器、显示器与 MCU/CPU 的通信)。

3.IIC通信时序

1.start信号

在 IIC 通信时序中,Start(起始)信号Stop(停止)信号是由主设备主动生成的 “总线控制信号”,核心作用是界定一次完整 IIC 通信的 “开始” 与 “结束”,同时向总线上所有从设备传递 “总线即将被占用” 或 “总线已释放” 的状态,是 IIC 时序逻辑的 “总开关”。

Start 信号的唯一判定条件:在 SCL 保持高电平期间,SDA 从高电平跳变到低电平

2.stop信号

Stop 信号的唯一判定条件:在 SCL 保持高电平期间,SDA 从低电平跳变到高电平

3.通信时序

1. 总线空闲状态

  • 当 SCL(时钟线)和 SDA(数据线)均为高电平时(由上拉电阻维持),总线处于空闲状态,所有设备未进行通信。

2. 起始信号(Start)

  • 触发条件:SCL 保持高电平时,SDA 从高电平跳变为低电平(高→低)。
  • 作用:标志一次通信的开始,主设备通过此信号通知所有从设备 “总线即将被占用”,从设备进入监听状态。

3. 数据传输时序

  • 位传输规则:每个数据位(0 或 1)在 SCL 高电平期间保持稳定,从设备在 SCL 高电平时采样 SDA 电平(读取数据);SDA 的电平变化只能在 SCL 低电平时进行(避免数据错乱)。
  • 字节传输:每次传输 1 字节(8 位),按 “高位在前(MSB)” 的顺序发送。

4. 应答信号(ACK/NACK)

  • 每传输 1 字节后,紧跟 1 个应答位:
    • ACK(应答):接收方在应答位期间(SCL 高电平)拉低 SDA,表示 “已正确接收”。
    • NACK(非应答):接收方释放 SDA(由上拉电阻维持高电平),表示 “未接收” 或 “结束传输”。
  • 应答位由接收方(从设备或主设备,取决于传输方向)控制。

5. 重复起始信号(Repeated Start)

  • 若主设备需要在一次通信中切换从设备或传输方向(如先写后读),无需发送 Stop 信号,直接再次发送 Start 信号(时序与首次 Start 相同)。
  • 作用:避免总线被其他主设备抢占,实现连续通信。

6. 停止信号(Stop)

  • 触发条件:SCL 保持高电平时,SDA 从低电平跳变为高电平(低→高)。
  • 作用:标志一次通信的结束,主设备释放总线,从设备回到空闲状态。

4.相关配置

先配置IIC的初始化

注意设置波特率之前最好先将I2Cn_I2CR的IEN清0,设置好之后再置1

分频:由于时钟是irq_clk_root为66MHz,需要配置为100KHz,故分频系数为660,查阅手册知,与660最相近的为0x15

示例

void i2c1_init(void)
{
    IOMUXC_SetPinMux(IOMUXC_UART4_RX_DATA_I2C1_SDA, 1);    //sion位置1表示检测引脚电平状态
    IOMUXC_SetPinMux(IOMUXC_UART4_TX_DATA_I2C1_SCL, 1);

    //设置引脚电器特性
    IOMUXC_SetPinConfig(IOMUXC_UART4_RX_DATA_I2C1_SDA, 0xF0B0);
    IOMUXC_SetPinConfig(IOMUXC_UART4_TX_DATA_I2C1_SCL, 0xF0B0);

    //失能I2C1(会进行复位)
    I2C1->I2CR &= ~I2CR_IEN;
    //修改分频值
    I2C1->IFDR = 0x15;
    //使能I2C1
    I2C1->I2CR |= I2CR_IEN;
}

配置中断相关

  1. IIF 位的状态定义

    • 当 IIF 位为 1 时:表示 IIC 模块已产生中断事件(如数据发送完成、数据接收完成、地址匹配、总线错误等,具体触发源取决于芯片设计)。
    • 当 IIF 位为 0 时:表示 IIC 模块未产生中断,或中断已被清除。
  2. 触发场景
    IIF 位的置位(变为 1)通常与 IIC 模块的具体操作相关,常见触发条件包括:

    • 主设备发送完 1 字节数据;
    • 从设备接收到 1 字节数据;
    • 从设备检测到自身地址被主设备匹配(地址应答);
    • 总线出现错误(如仲裁失败、无应答等)。
  3. 软件操作逻辑

    • 当 IIF 位为 1 时,会触发 CPU 的中断请求(需先使能 IIC 中断),程序会进入中断服务函数处理相应事件。
    • 中断处理完成后,软件需通过特定指令清除 IIF 位(通常是向该位写 0,或读取状态寄存器后写特定值,具体依赖芯片手册),否则会持续触发中断。

I2CR 的 TXAK 位:控制 “本设备发送的应答信号”

  • 全称:通常为 “Transmit Acknowledge”(发送应答)位,位于控制寄存器(I2CR)中,用于配置本设备在接收数据后,向发送方返回的应答类型
  • 功能
    • 当 TXAK=0 时:本设备在接收完 1 字节数据后,会主动拉低 SDA 线(在应答位周期内),向发送方返回ACK(应答信号),表示 “已正确接收数据”。
    • 当 TXAK=1 时:本设备在接收完 1 字节数据后,会释放 SDA 线(由上拉电阻维持高电平),向发送方返回NACK(非应答信号),表示 “未接收数据” 或 “要求发送方停止传输”。
  • 适用场景
    • 若本设备是从设备:接收主设备发送的数据后,通过 TXAK 配置是否返回 ACK(默认通常为 0,即返回 ACK)。
    • 若本设备是主设备:接收从设备返回的数据后,通过 TXAK 配置是否返回 ACK(例如,主设备读取最后 1 字节数据时,可设置 TXAK=1 返回 NACK,告知从设备 “数据已读完”)。
  • 本质主动控制信号,由本设备软件配置,决定向对方发送 ACK 还是 NACK。

2. I2SR 的 RXAK 位:指示 “本设备收到的应答信号”

  • 全称:通常为 “Receive Acknowledge”(接收应答)位,位于状态寄存器(I2SR)中,用于指示本设备作为发送方时,是否收到了接收方返回的应答信号
  • 功能
    • 当 RXAK=0 时:表示本设备发送完 1 字节数据后,接收到了接收方返回的ACK 信号(SDA 被拉低),即 “接收方已正确接收数据”。
    • 当 RXAK=1 时:表示本设备发送完 1 字节数据后,接收到的是NACK 信号(SDA 为高电平),即 “接收方未接收数据” 或 “通信异常”(如从设备地址不存在、数据溢出等)。
  • 适用场景
    • 若本设备是主设备:发送地址或数据后,通过 RXAK 判断从设备是否应答(例如,RXAK=1 可能意味着从设备未连接或地址错误)。
    • 若本设备是从设备:发送数据给主设备后,通过 RXAK 判断主设备是否接收(例如,主设备返回 NACK 表示 “无需继续发送”)。
  • 本质被动状态指示,由硬件自动更新(根据总线上的应答信号),供软件查询通信是否正常。

每次发送数据后从机都会回应ACK还是NACK故需要写一个函数来判断

int i2c_wait_iif(I2C_Type *base)
{
    while((base->I2SR & I2SR_IIF) == 0)
    {
        //超时机制
    }
    //清除中断(写0清除中断)
    base->I2SR &= ~(I2SR_IIF);

    if ((base->I2SR & I2SR_RXAK) != 0)
    {
        return -1;         //NACK
    }

    return 0;              //ACK

}

在此用的是RXAK,来读取从机回应的是什么

配置主机向从机写

示例

void i2c_write(I2C_Type *base, unsigned char dev_addr, unsigned char reg_addr, unsigned char *data, int len)
{
    int statu;
    int i = 0;

    //清除仲裁丢失标志IAL,清除中断标志IIF, 切换收发模式到发送
    base->I2SR &= ~(I2SR_IAL | I2SR_IIF);
    base->I2CR |= (I2CR_MTX);

    //产生start信号
    base->I2CR |= (I2CR_MSTA);

    //发送从机地址
    base->I2DR = ((dev_addr << 1) | 0);      //表示7个从机地址位和一个数据流向位
    statu = i2c_wait_iif(base);
    if (statu == -1) goto stop;

    //发送从机要写的寄存器的地址
    base->I2DR = reg_addr;      
    statu = i2c_wait_iif(base);
    if (statu == -1) goto stop;

    //循环发要传送的数据
    for (; i < len; i++)
    {
        base->I2DR = data[i];
        statu = i2c_wait_iif(base);
        if (statu == -1) goto stop;
    }

stop:
    //结束发送stop信号
    base->I2CR &= ~I2CR_MSTA;
    //检测总线知道空闲
    while ((base->I2SR & I2SR_IBB) != 0)
    {
        printf("i2c is busy\n");
    }
    delay_ms(5);

}

配置主机 从 从机中读取数据

注意 在读取到最后一个字节的时候,注意必须回复NACK,否则可能会导致从机通信异常

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐