Skip to content

https://blog.csdn.net/u010249597/article/details/135607546

您的代码是实现软件模拟IIC通信的基本操作,这些函数的实现是基于IIC协议的通信时序标准,包括起始条件、数据传输、应答信号和停止条件。我们来详细分析每个函数的逻辑和其与IIC时序的关系,并给出发送两个字节数据的例子。


IIC通信时序基础

IIC协议的通信时序包括以下几个关键点:

  1. 起始条件(START Condition):

    • 主设备将 SDA 从高电平拉低,同时保持 SCL 为高电平。
    • 起始条件标志通信的开始。
  2. 数据传输(Data Transfer):

    • 每个字节(8位)按位从高位到低位发送。
    • 在每一位传输时,SDA 数据线在 SCL 时钟线为低电平时稳定,SCL 上升沿采样数据。
  3. 应答信号(ACK/NACK):

    • 接收方在收到完整的一个字节后,在第9个时钟周期拉低 SDA,表示收到数据(ACK)。
    • 如果接收方不拉低 SDA,则表示 NACK(通常用于结束通信)。
  4. 停止条件(STOP Condition):

    • 主设备将 SDA 从低电平拉高,同时保持 SCL 为高电平。
    • 停止条件标志通信结束。

函数逻辑分析

1. IIC_Start()

c
void IIC_Start(void) {
    IIC_SDA_H();  // 确保 SDA 处于高电平
    IIC_SCL_H();  // 确保 SCL 处于高电平
    HAL_Delay(1); // 短暂延时,确保稳定
    IIC_SDA_L();  // SDA 从高电平拉低,产生起始条件
    HAL_Delay(1); // 短暂延时,确保起始信号被识别
    IIC_SCL_L();  // 将 SCL 拉低,准备发送数据
}

逻辑分析:

  • 起始条件:当 SCL 为高电平时,SDA 从高电平拉低,表示通信开始。
  • 确保了 SCL 和 SDA 的状态符合 IIC 时序要求。

2. IIC_Stop()

c
void IIC_Stop(void) {
    IIC_SDA_L();  // 确保 SDA 处于低电平
    IIC_SCL_H();  // 将 SCL 拉高
    HAL_Delay(1); // 短暂延时,确保信号稳定
    IIC_SDA_H();  // SDA 从低电平拉高,产生停止条件
    HAL_Delay(1); // 短暂延时,确保停止信号被识别
}

逻辑分析:

  • 停止条件:当 SCL 为高电平时,SDA 从低电平拉高,表示通信结束。

3. IIC_SendByte(uint8_t data)

c
void IIC_SendByte(uint8_t data) {
    for (int i = 0; i < 8; i++) { // 循环发送8位数据
        if (data & 0x80) {       // 判断最高位是1还是0
            IIC_SDA_H();         // 如果是1,设置 SDA 为高电平
        } else {
            IIC_SDA_L();         // 如果是0,设置 SDA 为低电平
        }
        data <<= 1;              // 左移1位,准备发送下一位
        HAL_Delay(1);            // 短暂延时,确保 SDA 稳定
        IIC_SCL_H();             // 拉高 SCL,产生上升沿(数据被接收方采样)
        HAL_Delay(1);            // 短暂延时,确保接收方完成采样
        IIC_SCL_L();             // 拉低 SCL,准备发送下一位
    }
}

逻辑分析:

  • 每一位数据在 SCL 为低电平时设置 SDA。
  • SCL 上升沿时,接收方采样 SDA 上的数据。

4. IIC_WaitAck()

c
uint8_t IIC_WaitAck(void) {
    uint8_t ack;

    IIC_SDA_H();  // 释放 SDA,接收方控制 SDA
    HAL_Delay(1);
    IIC_SCL_H();  // 拉高 SCL,接收方在此时钟周期返回 ACK
    HAL_Delay(1);
    ack = IIC_SDA_READ(); // 读取 SDA 电平
    IIC_SCL_L();          // 拉低 SCL,准备下一步
    return ack == 0 ? 1 : 0;  // 返回1表示收到 ACK,0表示 NACK
}

逻辑分析:

  • 主机释放 SDA(设置为高阻态),等待从机控制 SDA。
  • 在第9个时钟周期读取从机返回的 SDA 状态,判断是否收到 ACK。

发送两个字节数据的例子

假设需要发送两个字节数据 0xA50x5A,目标从机地址为 0x3C(7位地址)

代码实现

c
void IIC_SendData(uint8_t deviceAddr, uint8_t data1, uint8_t data2) {
    IIC_Start();                               // 1. 发送起始信号

    IIC_SendByte(deviceAddr << 1);             // 2. 发送从机地址(左移1位 + 写模式)
    if (!IIC_WaitAck()) {                      // 3. 等待从机返回 ACK
        IIC_Stop();                            // 如果没有收到 ACK,则停止通信
        return;
    }

    IIC_SendByte(data1);                       // 4. 发送第一个字节数据
    if (!IIC_WaitAck()) {                      // 5. 等待从机返回 ACK
        IIC_Stop();                            // 如果没有收到 ACK,则停止通信
        return;
    }

    IIC_SendByte(data2);                       // 6. 发送第二个字节数据
    if (!IIC_WaitAck()) {                      // 7. 等待从机返回 ACK
        IIC_Stop();                            // 如果没有收到 ACK,则停止通信
        return;
    }

    IIC_Stop();                                // 8. 发送停止信号
}

执行流程

  1. 起始信号:调用IIC_Start(),在总线上产生起始条件。
  2. 发送从机地址:通过IIC_SendByte()发送从机地址(如0x3C << 1表示写操作)。
  3. 等待ACK:从机返回ACK信号表示地址匹配。
  4. 发送第一个字节数据:调用IIC_SendByte()发送0xA5
  5. 等待ACK:从机返回ACK信号,表示数据接收成功。
  6. 发送第二个字节数据:调用IIC_SendByte()发送0x5A
  7. 等待ACK:从机返回ACK信号。
  8. 停止信号:调用IIC_Stop(),在总线上产生停止条件。

总结

  1. 与时序的关系

    • 起始信号、数据传输、ACK、停止信号均严格按照IIC时序标准实现。
    • SCL 上升沿用于数据采样,SCL 低电平时设置 SDA。
  2. 逻辑清晰性

    • IIC_StartIIC_Stop控制通信的开始和结束。
    • IIC_SendByte负责逐位发送数据。
    • IIC_WaitAck确保接收方正常应答。
  3. 发送两个字节的流程

    • 起始信号 -> 发送设备地址 -> 等待ACK -> 发送数据 -> 等待ACK -> 停止信号。

如果还有任何疑问,欢迎继续交流!