0

0

0

修罗

站点介绍

只有了解事实才能获得真正的自由

I2C总线

修罗 2025-08-04 292 0条评论 51单片机

首页 / 正文

I2C总线

1.1 概述

I2C(Inter-Integrated Circuit),通常简称为IIC,是一种用在集成电路(IC)之间的串行通信总线。它是由Philips(现在的NXP半导体)在上世纪80年代开发的,并在之后广泛应用于各种电子设备和嵌入式系统中。

1.2 信号线

I2C同步串行通信,使用两根线路进行通信,分别是数据线(SDA)和时钟线(SCL),SDA线用于数据传输,SCL线用于数据传输的同步。SCL的每个时钟周期,SDA传输一位数据。

I2C规定,数据接收方会在每个时钟周期的高电平期间读取数据,具体来讲就是在SCL处于高电平时,读取SDA上的数据,如下图所示:

image-20250804111632421.png

​ 因此,SDA必须在SCL低电平期准备好要发送的下一位数据,然后在SCL高电平期间保持稳定。

1.3 主从架构

I2C采用主从架构,一个主设备可连接多个从设备。主设备负责发起通信和控制总线,而从设备负责响应主设备的请求。如下图所示:

image-20250804112032444.png

I2C总线中的每个设备都有一个唯一的地址(用7位二进制数字表示),用于在总线上标识自己。主设备可以根据地址选择性的与特定的从设备进行通信。

​ 需要注意的是,SCL信号线上的时钟信号始终由主设备产生,而SDA信号线上的数据信号既可由主设备产生,也可由从设备产生。当主设备向从设备发送数据时,SDA信号由主设备产生,从设备接收信号;当主设备从从设备读取数据时,SDA信号由从设备产生,主设备接收信号。

1.4 通信协议

1.4.1 空闲状态

I2C协议规定,当SDASCL均为高电平时,总线为空闲状态。

1.4.2 起始和结束信号

​ 主设备和从设备间的每次通信,都需要以一个起始信号开始,以一个结束信号终止。起始信号和结束信号的定义如下:

起始信号:当SCL处于高电平时,SDA由高变低。

停止信号:当SCL处于高电平时,SDA由低变高。

​ 如下图:

image-20250804140156266.png

起始信号和结束信号,都只能由主设备产生。

1.4.3 确认信号

I2C协议规定,发送方每发送一个字节(8位)的数据,接收方都要向发送方回复一个1位的确认信号,如下图所示。

image-20250804140427806.png

​ 如果该确认信号为0表示接收方已成功接收到该字节,发送方可继续发送下一字节,这个信号在I2C协议中称为ACK(Acknowledge);如果该信号为1,则表示接收方未能成功接收到该字节,或者不希望接收更多数据,该信号在I2C协议中称为NACK(Not Acknowledge)。

1.4.4 从机地址和读写标识

​ 由于一个I2C总线上可能有多个从设备,所以开始通信前,主设备需要先与目标设备取得联系,然后再进行数据传输,除此之外,主机还需要向目标设备明确本次通信的操作是写数据还是读数据。

​ 上述操作的实现思路如下:

  1. 当主设备发送起始信号之后,会向所有设备发送一个字节的数据,这一个字节中,前7位为目标设备地址第8位为读/写标识(1表示读,0表示写),如下图所示。

    image-20250804140717676.png

  1. 当各从设备收到这个字节的数据后,会将7位地址与自身进行对比,相同则会向主设备回复确认信号,不相同则不做任何回应。
  2. 当主设备收到目标设备的确认信号后,便会开始与该设备进行通信。

1.4.5 完整通信流程

​ 综上所述,当主设备想要与某个从设备进行通信时,需要经历如下流程。

image-20250804140929542.png

  1. 发送起始信号;
  2. 发送目标从设备地址+读写标识位;
  3. 接收从设备回复的确认信号;
  4. 与从设备进行数据传输(发送/接收);
  5. 发送终止信号;

1.5 基础驱动编写

1.5.1功能分析

​ 为方便后序使用I2C协议通信,此处先编写一个通用的基础的驱动,该驱动包含的具体函数如下图所示:

image-20250804141207297.png

1.5.2 发送起始信号

  1. 确保在空闲状态:当SDASCL均为高电平时,总线为空闲状态

image-20250804141408318.png

SCL = 1;
SDA = 1;
  1. 拉低SDA,发送起始信号

image-20250804141608389.png

SDA = 0;
  1. 拉低SCL,准备后面的数据位:

image-20250804141646941.png

SCL = 0;

代码:

void Dri_IIC_Start(){
    SCL = 1;
    SDA = 1;
    SDA = 0;
    SCL = 0;
}

1.5.3 发送一个字节

  1. SCL低电平期间准备数据位,例如:
SDA = 1

image-20250804142318161.png

  1. 拉高SCL,发送数据:

image-20250804142427045.png

SCL = 1
  1. 拉低SCL,准备下一位:

image-20250804142526157.png

SCL = 0
  1. 准备下一位数据:

image-20250804142605341.png

SDA = 0
  1. 拉高SCL,发送数据:

image-20250804142632516.png

SCL = 1

image-20250804142706638.png

SCL = 0

至此,发送了两个数据位,不难发现,发送每个数据位的操作是相同的,因此,想要发送一个字节,只需将发送一个数据位的操作循环执行8次即可:

image-20250804142746405.png

需要注意的是I2C协议规定,发送一个字节的数据时,要从高位开始,具体代码如下:

void Dri_IIC_SendByte(u8 byte)
{
    u8 i;
    for (i = 0; i < 8; i++) {
        // SDA = (byte & (0x80 >> i)) == 0 ? 0 : 1;
        // 0x80: 1000 0000
        if((byte & 0x80) == 0x00) {
            SDA = 0;
        }else {
            SDA = 1;
        }
        byte <<= 1;
        SCL = 1;
        SCL = 0;
    }
}

1.5.4 接收确认信号

主设备释放SDA

image-20250804144158095.png

如何释放SDA?

  • I2C协议规定,所有设备的SCLSDA引脚都需要以开漏(Open-Drain)模式接入总线,如下图所示:

image-20250804144635747.png

  • 该模式的工作原理是:设备输出低电平时,MOS管导通,信号线接地为低电平; 设备输出高电平时,MOS管关闭,信号线被上拉电阻拉至高电平。
  • 因此主设备在接受从设备数据时,需要将SDA输出置为1,此时主设备的SDA输出和SDA信号线呈断开状态,从设备便可控制SDA信号线向主设备发送确认信号了。

释放步骤:

SDA = 1;

image-20250804145236398.png

  1. 设置SDA = 1 后,从设备会在SCL电平期间准备好确认信号

image-20250804145411604.png

  1. 拉高SCL, 读取确认信号:
SCL = 1;
ack = SDA;

image-20250804145559165.png

  1. 拉低SCL,准备下一位数据
SCL = 0;

image-20250804145750668.png

代码:

bit Dri_IIC_ReceiveAck()
{
    bit ack;
    SDA = 1;
    SCL = 1;
    ack = SDA;
    SCL = 0;
    return ack;
}

1.5.5 接收一个字节

  1. 主设备释放SDA,以允许从设备驱动SDA
SDA = 1

image-20250804151454725.png

image-20250804151518265.png

  1. 拉高SCL,并在SCL高电平期间读取SDA的值
SCL = 1;
current_bit = SDA;

image-20250804151640569.png

  1. 拉低SCL,以便从设备准备下一位数据:

image-20250804151725207.png

SCL = 0;

image-20250804151800025.png

image-20250804151818464.png

SCL = 1;
current_bit = SDA;

完整代码:

u8 Dri_IIC_ReceiveByte()
{
    u8 byte = 0;
    u8 i;
    SDA = 1;
    for (i = 0; i < 8; i++) {
        SCL = 1;
        byte = (byte << 1) | SDA;
        SCL = 0;
    }
    return byte;
}

1.5.6 发送确认信号

  1. image-20250804152024384.png
SDA = 0

image-20250804152057648.png

SCL = 1;

image-20250804152143433.png

SCL = 0;

代码:

void Dri_IIC_SendAck(bit ack)
{
    SDA = ack;
    SCL = 1;
    SCL = 0;
}

1.5.7 发送停止信号

image-20250804152515526.png

SDA = 0

image-20250804152635120.png

SCL = 1;

image-20250804152741840.png

SDA = 1

代码:

void Dri_IIC_Stop()
{
    SDA = 0;
    SCL = 1;
    SDA = 1;
}

1.5.8 完整代码

Dri_IIC.h

#ifndef __DRI_IIC_H__
#define __DRI_IIC_H__
#include <STC89C5xRC.H>
#include "Util.h"
#define SCL P17
#define SDA P16
void Dri_IIC_Start();
void Dri_IIC_Stop();

void Dri_IIC_SendByte(u8 byte);
u8 Dri_IIC_ReceiveByte();

void Dri_IIC_SendAck(bit ack);
bit Dri_IIC_ReceiveAck();

#endif /* __DRI_IIC_H__ */

Dri_IIC.c

#include "Dri_IIC.h"

void Dri_IIC_Start()
{
    SCL = 1;
    SDA = 1;
    SDA = 0;
    SCL = 0;
}

void Dri_IIC_Stop()
{
    SDA = 0;
    SCL = 1;
    SDA = 1;
}

void Dri_IIC_SendByte(u8 byte)
{
    u8 i;
    for (i = 0; i < 8; i++) {
        // SDA = (byte & (0x80 >> i)) == 0 ? 0 : 1;
        // 0x80: 1000 0000
        if((byte & 0x80) == 0x00) {
            SDA = 0;
        }else {
            SDA = 1;
        }
        byte <<= 1;
        SCL = 1;
        SCL = 0;
    }
}

u8 Dri_IIC_ReceiveByte()
{
    u8 byte = 0;
    u8 i;
    SDA = 1;
    for (i = 0; i < 8; i++) {
        SCL  = 1;
        byte = (byte << 1) | SDA;
        SCL  = 0;
    }
    return byte;
}

void Dri_IIC_SendAck(bit ack)
{
    SDA = ack;
    SCL = 1;
    SCL = 0;
}

bit Dri_IIC_ReceiveAck()
{
    bit ack;
    SDA = 1;
    SCL = 1;
    ack = SDA;
    SCL = 0;
    return ack;
}

评论(0)


最新评论

  • https://www.studyglobus.com/author/carroll34a5552/

    Ценники на вывоз мусора сейчас космос, любые лайфхаки по экономии в тему.

  • Richardgrold

    1xbet onlayn kazino platforması ilə vaxtınızı daha səmərəli və maraqlı keçirin. Saytın canlı kazino bölməsi istifadəçilərə fərqli bir dünya təklif edir. Burada canlı dilerli kazino oyunlarının hər bir növünü sınaqdan keçirə bilərsiniz. Krupyelərin peşəkarlığı oyunun dinamikasını və marağını artırır. Müntəzəm olaraq yenilənən oyun siyahısı cansıxıcılığa yol vermir. Hesabınızı artırmaq üçün onlarla fərqli və etibarlı üsul mövcuddur. Qazanılan pullar cəmi bir neçə dəqiqə ərzində şəxsi hesabınıza köçürülür. Aktiv fəaliyyətinizə görə fərdi bonuslar və hədiyyələr qazana bilərsiniz. Dəstək xidməti həm çat, həm də e-poçt vasitəsilə kömək göstərir. 1xbet ailəsinə qoşulun və böyük uduşlar üçün ilk addımı atın. onlayn kazino

  • 砂岩白丘

    我一直梦想, 看到你们相册那样的地方。真棒。

  • 城市地標

    我喜欢, 这里分享真实经验。你的项目 就是 属于这里的。请继续。

  • 宗教地標

    我热爱这样的想法, 去那么多国家。真的很鼓舞。

  • 觀景高地

    能感受到热爱。继续保持 感受。

  • 最佳季節

    我关注这样的资源, 写得很实在。你的网站 就是 属于这里的。加油。

  • 1

    1

  • 1

    1

  • -1' OR 2+158-158-1=0+0+0+1 or 'TKCTZnRa'='

    1

日历

2026年07月

   1234
567891011
12131415161718
19202122232425
262728293031 

文章目录

推荐关键字: Linux webpack js 算法 MongoDB laravel JAVA jquery javase redis