【车载开发系列】IIC总线协议时序图

【车载开发系列】IIC总线协议时序图

【车载开发系列】IIC总线协议时序图

  • 【车载开发系列】IIC总线协议时序图
    • 一、前言
    • 二、IIC硬件软件实现
      • 1)使用I2C控制器实现
      • 2)使用GPIO通过软件模拟实现
    • 三、I2C协议标准代码
      • 1)起始信号
      • 2)停止信号
      • 3)发送一个字节
      • 4)读取一个字节
      • 5)ACK应答
      • 6)NACK应答
      • 7)ACK应答信号
    • 二、向IIC总线写数据
    • 三、向IIC总线读数据
    • 四、总结

一、前言

  • IIC协议是一种具有自动寻址、高低速设备同步和仲裁等功能的高性能串行总线,它是一个真正的多主机总线,支持一对多(一主多从)、多对多传输(多主多从)。
  • 它是各种总线协议中使用信号线最少的,只需要两根线便可以实现功能。
  • 连在IIC总线上的每个器件都有一个唯一的地址识别。其中高四位A7-A4是从机器件的固定编址,出厂时就已给定;A3-A1是从机器件的引脚地址,通过接地接电源来形成地址。
术语描述
发送器发送数据到总线的器件
接收器从总线接收数据的器件
主机初始化总线的数据,传输并产生允许传输的时钟信号和起始终止发送的器件
从机被主机寻址的器件
多主机同时有多于一个主机尝试控制总线,但不破坏报文
仲裁是一个在有多个主机同时尝试控制总线,但只允许其中一个控制总线并使报文不被破坏
同步两个或多个器件同步时钟信号的过程

二、IIC硬件软件实现

  • I2C有两种实现方式:一种是GPIO软件模拟,另一种是直接使用I2C硬件
  • 时钟线完全由主机控制。而从机只有对SDA的(短暂)控制权。通常使用I2C通信,SCL和SDA线上都会接一个上拉电阻,要么是在外设的内部,要么是在外面可以直接通过外设的硬件接线图看到。
  • 硬件连接图如下
    在这里插入图片描述

1)使用I2C控制器实现

就是使用芯片上的I2C外设,也就是硬件I2C,它有相应的I2C驱动电路,有专用的IIC引脚,效率更高,写代码会相对简单,只要调用I2C的控制函数即可,不需要用代码去控制SCL、SDA的各种高低电平变化来实现I2C协议,只需要将I2C协议中的可变部分(如:从设备地址、传输数据等等)通过函数传参给控制器,控制器自动按照I2C协议实现传输,但是如果出现问题,就只能通过示波器看波形找问题。

2)使用GPIO通过软件模拟实现

软件模拟I2C比较重要,因为软件模拟的整个流程比较清晰,哪里出来bug,很快能找到问题,模拟一遍会对I2C通信协议更加熟悉。
如果芯片上没有IIC控制器,或者控制接口不够用了,通过使用任意IO口去模拟实现IIC通信协议,手动写代码去控制IO口的电平变化,模拟IIC协议的时序,实现IIC的信号和数据传输。

三、I2C协议标准代码

1)起始信号

起始信号:当 SCL 线是高电平时,SDA线从高电平向低电平切换。
在这里插入图片描述

void I2C_Start(void){
    // 总线空闲, SCL和SDA输出高
    I2C_SDA_High();     //SDA=1
    I2C_SCL_High();     //SCL=1
    I2C_Delay();
    I2C_SDA_Low();     // SDA由高变低
    I2C_Delay();
    I2C_SCL_Low();     // 拉低SCL开始传输数据
    I2C_Delay();       
}

2)停止信号

终止信号:SCL为高电平时,SDA由低向高跳变。
在这里插入图片描述

 void I2C_Stop(void){
    I2C_SDA_Low();     //SDA=0 把数据线设置为输出模式
    I2C_SCL_High();    // 拉高时钟线
    I2C_Delay();
    I2C_SDA_High();     // SDA由低变高
    I2C_Delay();
}

3)发送一个字节

CPU向I2C总线设备发送一个字节(8bit)数据

uint8 I2C_SendByte(uint8_t Byte){
    uint8_t i;
    /* 先发送高位字节 */
    for(i = 0 ; i < 8 ; i++){
        if(Byte & 0x80){
            I2C_SDA_High();
        }
        else{
            I2C_SDA_Low();
        }
        I2C_Delay();
        I2C_SCL_High();
        I2C_Delay();
        I2C_SCL_Low();
        I2C_Delay();
        if(i == 7){
            I2C_SDA_High();/* 释放SDA总线 */
        }
        Byte <<= 1;/* 左移一位 */
        I2C_Delay();
    }
} 

4)读取一个字节

CPU从I2C总线设备上读取一个字节(8bit数据)

uint8_t I2C_ReadByte(void)
{
    uint8_t i;
    uint8_t value;
 
    /* 先读取最高位即bit7 */
    value = 0;
    for(i = 0 ; i < 8 ; i++)
    {
        value <<= 1;
        I2C_SCL_High();
        I2C_Delay();
        if(I2C_SDA_READ())
        {
            value++;
        }
        I2C_SCL_Low();
        I2C_Delay();
    }
 
    return value;
}

5)ACK应答

CPU产生一个ACK信号
当SDA是低电平为有效应答(ACK),表示对方接收成功;

void I2C_Ack(void)
{
    I2C_SDA_Low();  //SDA=0 当SDA是低电平为有效应答(ACK),表示对方接收成功;
    I2C_Delay();
    I2C_SCL_High();
    I2C_Delay();
    I2C_SCL_Low(); // 拉低SCL开始传输数据
    I2C_Delay(); 
    I2C_SDA_High();  //SCL_Low之后,SDA=0->1
}

6)NACK应答

CPU产生一个非ACK信号
当SDA是高电平为无效应答(NACK),表示对方没有接收成功。

void I2C_NoAck(void)
{
    I2C_SDA_High();  //SDA=1 当SDA是高电平为无效应答(NACK),表示对方没有接收成功。
    I2C_Delay();
    I2C_SCL_High();
    I2C_Delay();
    I2C_SCL_Low();// 拉低SCL开始传输数据
    I2C_Delay();
}

7)ACK应答信号

CPU产生一个时钟,并读取器件的ACK应答信号
发送数据需要等待接收方的应答:

// 等待ACK   1-无效    0-有效
uint8_t I2C_WaitToAck(void)
{
    uint8_t redata;
 
    I2C_SDA_High();// 拉高数据线(设置为输入)
    I2C_Delay();
    I2C_SCL_High();// 拉高时钟线
    I2C_Delay();
 
    if(I2C_SDA_READ())
    {
        redata = 1; //ACK应答无效
    }
    else
    {
        redata = 0; //ACK应答有效
    }
    I2C_SCL_Low(); // 拉低SCL开始传输数据
    I2C_Delay();
 
    return redata;
} 

二、向IIC总线写数据

首先我们先来看一下写数据的时序图,如下图所示:
在这里插入图片描述
将上图中的写数据时序图进行分解,经分解后如下图所示:
在这里插入图片描述

  • 第一步,发送一个起始信号。
  • 第二步,发送7bit从机地址。此处需要注意,发送数据时,无法发送7bit数据,此处发送了7bit地址+1bit读写选择位,即发送7bit+r/w。最低位为1表示读,为0表示写,这里是写。
  • 第三步,产生一个ACK应答信号,此应答信号为从机器件产生的应答。
  • 第四步,发送寄存器地址,8bit数据。
  • 第五步,产生一个ACK应答信号,此应答信号为从机器件产生的应答。
  • 第六步,发送一个数据,8bit数据。
  • 第七步,产生一个ACK应答信号,此应答信号为从机器件产生的应答信号。
  • 第八步,发送一个CRC校验码,此CRC校验值为2、4、6步数据产生的校验码。
  • 第九步,既可以发送一个应答信号,也可以发送一个无应答信号,均有从机器件产生。
  • 第十步,发送一个停止信号。
    在这里插入图片描述
uint8_t I2C_WriteBytes(void)
{
    I2C_Start();                    //1
    I2C_SendByte(Slaver_Addr | 0);  //2
    I2C_WaitToAck();                //3
    I2C_SendByte(Reg_Addr);         //4
    I2C_WaitToAck();                //5
    I2C_SendByte(data);             //6
    I2C_WaitToAck();                //7
    I2C_SendByte(crc);              //8
    I2C_WaitToAck();                //9
    I2C_Stop();                     //10
}

三、向IIC总线读数据

读数据的时序图如下图所示:
在这里插入图片描述
读数据的时序图经分解后如下图所示:
在这里插入图片描述

  • 第一步,发送一个起始信号。
  • 第二步,发送7bit从机地址,此处需要注意,发送数据时,无法发送7bit数据,此处发送了7bit地址+1bit读写选择位,即发送7bit+r/w。最低位为1表示读,为0表示写。
  • 第三步,产生一个ACK应答信号,此应答信号为从机器件产生的应答。
  • 第四步,发送寄存器地址。
  • 第五步,产生一个ACK应答信号,此应答信号为从机器件产生的应答。
  • 第六步,再次发送一个起始信号。
  • 第七步,发送7bit从机地址。此处需要注意,发送数据时,无法发送7bit数据,此处发送了7bit地址+1bit读写选择位,即发送7bit+r/w。最低位为1表示读,为0表示写。
  • 第八步,产生一个ACK应答信号,此应答信号为从机器件产生的应答。
  • 第九步,读取一个字节(8bit)的数据。
  • 第十步,产生一个ACK应答信号,此应答信号为CPU产生。
  • 第十一步,读取一个CRC校验码。
  • 第十二步,产生一个NACK信号。此无应答信号由CPU产生。
  • 第十三步,产生一个停止信号。
    在这里插入图片描述
uint8_t I2C_ReadBytes(void)
{
    uint8_t data;
    uint8_t crc;
 
    I2C_Start();                    //1
    I2C_SendByte(Slaver_Addr | 0);  //2
    I2C_WaitToAck();                //3
    I2C_SendByte(Reg_Addr);         //4
    I2C_WaitToAck();                //5
    I2C_Start();                   //6
    I2C_SendByte(Slaver_Addr | 1);  //7 1-读
    I2C_WaitToAck();                //8
    data=I2C_ReadByte();            //9
    I2C_Ack();                      //10
    crc=I2C_ReadByte();             //11
    I2C_NoAck();                    //12
    I2C_Stop();                     //13
}

四、总结

  • 每次向SDA发送一位数据,都需要在SCL高电平时保持,所以SDA上所传输每一位数据都会占用一个时钟周期。

  • 主机接收到从机的应答信号之后,

    1. 如果第8位读写位是0,即主机向从机写数据,接下来继续由主机占用SDA,向从机传输数据,期间主机每传送8位数据,从机就会产生一个应答信号ACK,由主机接收。
    2. 如果第8位读写位是1 ,即从机向主机写数据,接下来继续由从机占用SDA,向主机传输数据,期间从机每传送8位数据,主机就会产生一个应答信号ACK,由从机接收。
  • 每传输8位数据,都会产生ACK信号,除了以下三种情况外;

    1. 从机不能响应主机发送的从机地址
      例如从机正忙而无法响应IIC总线的操作,或者这个地址没有对应的从机,那么在第9个SCL周期内SDA线就没有被拉低,即没有ACK信号。
      这时,由主机发送一个停止信号终止传输,或者重新发送一个START信号开始新的传输。
    2. 从机无法接收更多数据
      如果从机无法接收更多的数据,即主机发送的数据超过从机的数据接收能力时,从机不会发出ACK信号,这时,由主机发出一个停止信号终止传输或者重新发送一个START信号开始新的传输。
    3. 主机接收最后一个字节
      主机接收器接收到最后一个字节后,也不会发出ACK信号。于是从机发送器释放SDA线,以允许主机发出停止信号结束传输。

在这里插入图片描述

  • 16
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: 首先,你需要明确I2C总线协议的功能要求,并确定技术细节。其次,使用C语言编写I2C总线协议,可以使用标准的I2C库函数,以及特定于处理器的I2C驱动程序。最后,使用模拟或调试工具来验证I2C总线协议的正确性。 ### 回答2: I²C(Inter-Integrated Circuit)总线协议是一种用于在集成电路之间进行通信的串行总线协议。下面是使用C语言编写I²C总线协议的步骤: 首先,我们需要定义I²C总线的相关参数,包括时钟频率、数据位数和I²C地址等。可以使用宏定义或者全局变量来定义这些参数。 接下来,我们需要实现I²C总线的初始化函数,用于初始化I²C控制器和相关硬件。在初始化函数中,我们需要配置相关引脚的输入输出模式、上下拉电阻等。 然后,我们需要实现发送数据的函数。发送数据的过程包括发送起始位、I²C地址、数据、应答等步骤。通常情况下,我们可以使用位运算来控制每个步骤的时序和数据传输。 接着,我们需要实现接收数据的函数。接收数据的过程与发送类似,只是在发送结束后需要切换为接收模式,并且在接收数据时需要发送应答或非应答信号。 最后,我们可以实现其他辅助函数,例如读写寄存器、发送和接收多个字节的数据等。 在编写以上函数时,我们需要参考相关的I²C总线协议规范和硬件手册,以确保代码的正确性和可靠性。另外,为了提高代码的可读性和可维护性,可以使用适当的注释和模块化设计。 需要注意的是,具体的代码实现可能因硬件平台和具体需求而有所不同。以上只是基本的编写步骤和思路,实际编写过程中还需根据实际情况进行相应的优化和调整。 编写I²C总线协议的代码是一项复杂而精细的任务,需要对硬件和通信协议有深入的理解。因此,在实际应用中建议使用已经存在的I²C库或者驱动程序进行开发,以减少开发时间和复杂度。 ### 回答3: I2C(Inter-Integrated Circuit)总线协议是一种用于连接低速外设的串行通信协议,通过两根传输线SCL(时钟线)和SDA(数据线)进行通信。以下是使用C语言编写I2C总线协议的示例代码。 ```c #include <stdio.h> #include <stdint.h> #define I2C_ACK 0 #define I2C_NACK 1 // 初始化I2C总线 void i2c_init() { // TODO: 设置SCL和SDA引脚和配置相关寄存器 } // 发送一个起始信号 void i2c_start() { // TODO: 设置SDA和SCL引脚状态,产生起始信号 } // 发送一个停止信号 void i2c_stop() { // TODO: 设置SDA和SCL引脚状态,产生停止信号 } // 等待应答信号 int i2c_wait_ack() { // TODO: 读取SDA引脚状态,判断是否接收到应答信号 // 如果接收到应答信号,返回 I2C_ACK // 如果没有接收到应答信号,返回 I2C_NACK } // 发送一个字节 void i2c_write_byte(uint8_t data) { // TODO: 依次发送数据的每一位,同时检测应答信号 } // 读取一个字节 uint8_t i2c_read_byte() { // TODO: 依次读取数据的每一位,同时发送应答信号 // 返回读取的字节数据 } int main() { i2c_init(); // 初始化I2C总线 // 示例: 使用I2C总线向某个设备写入数据 i2c_start(); // 发送起始信号 i2c_write_byte(0xA0); // 发送设备地址和写入标志 i2c_wait_ack(); // 等待应答信号 i2c_write_byte(0x01); // 发送数据 i2c_wait_ack(); // 等待应答信号 i2c_write_byte(0x23); // 发送数据 i2c_wait_ack(); // 等待应答信号 i2c_stop(); // 发送停止信号 // 示例: 使用I2C总线从某个设备读取数据 i2c_start(); // 发送起始信号 i2c_write_byte(0xA1); // 发送设备地址和读取标志 i2c_wait_ack(); // 等待应答信号 uint8_t data = i2c_read_byte(); // 读取数据 i2c_send_ack(I2C_NACK); // 发送非应答信号 i2c_stop(); // 发送停止信号 printf("读取的数据是 %d\n", data); return 0; } ``` 以上示例是一个简单的I2C总线协议的实现。具体的操作需要根据硬件平台和具体的I2C总线驱动进行相应的设置和配置。可以根据实际需求对上述代码进行修改和扩展。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

进击的横打

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值