问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

I2C通讯协议详解:SDA/SCL的工作原理与应用

创作时间:
作者:
@小白创作中心

I2C通讯协议详解:SDA/SCL的工作原理与应用

引用
CSDN
1.
https://blog.csdn.net/weixin_44006573/article/details/105674761

I2C(Inter-Integrated Circuit)总线是一种两线式串行总线,主要用于微控制器(MCU)和外围设备(从设备)之间的通信。它采用一主多从的总线结构,每个设备都有一个特定的设备地址,以区分同一I2C总线上的其他设备。连接到I2C总线上的设备既可以用作主设备,也可以用作从设备。物理I2C接口包含两根双向线:串行时钟线(SCL)和双向串行数据线(SDA),用于发送和接收数据。通信由主设备发起,从设备被动响应,实现数据的传输。SDA负责在设备间传输串行数据,SCL负责产生同步时钟脉冲。

一、基础介绍

调用I2C时序,主要在调用数据写入write_I2C和数据读取read_I2C的机制。I2C主要实现数据的传输,使主机和从机的相互响应。它有一种数据传送机制。总结起来为:起始信号,终止信号、应答信号,读字节,写字节,数据读取和数据写入。

I2C基本架构:

Start_I2C
Stop_I2C
readack  读取应答信号
sendack and sendnack  输出应答或非应答
sendbyte
readbyte
write_I2C  
read_I2C

二、I2C通信协议详解

SDA和SCL变化情况:

所有数据传输均起始于一个start,终于一个stop.
Start_I2C定义:SCL为高时,SDA从高到低转换;
Stop_I2C定义:SCL为高时,SDA从低到高转换;
数据有效性:SCL为高时,SDA的数据必须稳定;
只有SCL变低时,SDA的状态才能跳变。

1. 空闲状态

SCL和SDA接上拉电阻,默认高电平,表示总线是空闲状态

2. 主从设备

主设备负责控制通信,通过对数据传输进行初始化/终止化,来发送数据并产生所需的同步时钟脉冲。
从设备则是等待来自主设备的命令,并响应命令的接收。且同步时钟信号只能由主设备产生。

3. 起始信号和结束信号

(1) I2C的起始位

void I2C_sendStart() //开始位
{
    SDA=1; /*发送起始条件的数据信号*/
    SCL=1;
    SDA=0; /*发送起始信号*/
    Delay_us(1);
    SCL=0; 
}

(2)I2C的结束位

Void sendstop()
{
    SCL=0;
    SDA=0; /*发送结束条件的数据信号*/
    SCL=1;
    while(SCL!=1) { };
    Delay_us(1);
    SDA=1; Delay_us(1);
}

4. 数据有效性

I2C总线进行数据传送时,在SCL的每个时钟脉冲期间传输一个数据位,时钟信号SCL为高电平期间,数据线SDA上的数据必须保持稳定,只有在时钟线SCL上的信号为低电平期间,数据线SDA上的高电平或低电平状态才允许变化,因为当SCL是高电平时,数据线SDA的变化被规定为控制命令(START或STOP,也就是前面的起始信号和停止信号)。

从机地址发送完后可能会发送一些指令,依从机而定,然后开始传输数据,由主机或者从机发送,每个数据为8位,数据的字节数没有限制。在开始信号之后,SDA和SCL先都处于低电平,当要传输数据时SDA先为高,之后SCL再跳变为高,才可进行数据的传输:

5. 应答信号

当SDA是低电平为有效应答(ACK),表示对方接收成功;
当SDA是高电平为无效应答(NACK),表示对方没有接收成功。

接收端收到有效数据后向对方响应的信号,发送端每发送一个字节(8位)数据,在第9个时钟周期释放数据线去接收对方的应答。

(1)、接收数据需向发送方发送应答:

void IIC_ack(u8 ack)
{
    // 数据线设置为输出
    
    SCL = 0;
    delay_us(5);
    
    if(ack)
        SDA = 1; // 无效应答
    else
        SDA = 0; // 有效应答      
    delay_us(5);
    SCL = 1;
    // 保持数据稳定
    delay_us(5);
    // 拉低SCL开始传输数据
    SCL = 0;
} 

(2)、发送数据需等待接收方的应答:

// 等待ACK   1-无效    0-有效
u8 IIC_wait_ack(void)
{
    u8 ack = 0;
    
    // 数据线设置为输入
    
    // 拉高时钟线
    SCL = 1;
    delay_us(5);
    // 获取数据线的电平
    if(SDA)
    {   // 无效应答
        ack = 1;
        IIC_stop();
    }
    else
    {   // 有效应答
        ack = 0;
        // 拉低SCL开始传输数据
        SCL = 0;
        delay_us(5);
    }
    
    return ack;
}

三、I2C通信实现方式

1. 硬件I2C

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

2. 模拟I2C

通过使用任意IO口去模拟实现I2C通信协议,手动写代码去控制IO口的电平变化,模拟I2C协议的时序,实现I2C信号和数据传输。

3. 数据读取和写入的示例:

Write_I2C:start->slave address+0+ACK+数据包(byte+ack+…+byten+Nack)+stop
Read_I2C:start->slave address+1+ACK+数据包(byte+ack+…+byten+Nack)+stop

注意:关于slave address是7位的一个字节,write是0位,read是1位;

示例:
slave address为50H(1010000)
则写地址在其后加一位0,即变成(10100000),为A0
读地址在其后加一位1,即变成(10100001),为A1;
具体为:
当总线空闲时,SDA和SCL都处于高电平状态,
当主机要和某个从机通讯时:
❶发送一个开始条件,
❷发送从机地址和读写控制位,
❸传输数据及数据传输结束时,
❹主机会发送停止条件。

write_I2C  
{
    Start_I2C;
    sendbyte();//传送地址与写入标记
    readack;
    sendbyte();//传送字节
    readack;
    Stop_I2C;
}

read_I2C  
{
    Start_I2C;
    sendbyte();//传送地址与读取标记
    readack;
    readbyte();//读取字节
    sendack and sendnack
    Stop_I2C;
}

4. 应用示例

Define address:根据芯片规格书定义读写地址
设置端口:SDA/SCL/INT
缓存buffer: 主控端的写入和读取资料缓存

(1)读取I2C的应答标志位

Unsigned char readACK() //读取应答信号
{
        SCL=0;
        SDA=1; /*此处为释放SDA 总线,由从从机发出低电平应答*/
        _nop_();
        SCL=1;
        _nop_();
        if(SDA)
        return 1; //no ACK
        else
        return 0; //ACK
}

(2)主控端送出应答信号

void sendACK() //输出应答信号
{
        SCL=0;
        SDA=0;
        _nop_();
        SCL=1;
        }
        void sendNOACK() //输出无应答信号
        {
        SCL=0;
        SDA=1;
        _nop_();
        SCL=1;
}

(3)主控端写入一个字节到从机

void sendByte(uchar dat) //写一个字节
{
        uchar i;
        for(i=0;i<8;i++)
        {
        SCL=0; /*钳住I2C 总线,准备发送数据 */
        if(dat&0x80)
        SDA=1;
        else
        SDA=0;
        _nop_(); 
        _nop_();
        SCL=1; 
        dat<<=1;
        }
}

(4)主控端对从机读取一个字节

uchar readByte() //读一个字节
{
        uchar i, dat=0;
        for(i=0;i<8;i++)
        {
        SCL=0;
        SDA=1;
        _nop_(); 
        dat<<=1;
        SCL=1; 
        if(SDA==1)
        dat|=0x01;
        }
        return dat;
}

(5)主控端数据写入

bit writeIIC(uchar addrW, uchar *writeData, uchar length)
{
        uchar i;
        bit ACK;
        sendStart();
        sendByte(addrW); //传送地址与写入标记
        ACK = readACK();
        if (ACK)
        {
            sendStop(); //地址不正确或装置未连接,送出停止信号
            return ACK;
        }
        for(i = 0; i<length; i++)
        {
            sendByte(writeData[i]);
            ACK = readACK();
            if (ACK)
            {
            sendStop(); //未接收到ACK,送出停止信号
            return ACK;
            }
        }
        sendStop(); //资料写入完成,送出停止信号
        return ACK;
}

(6)主控端对从机数据读取

bit readIIC(uchar addrR, uchar *readData, uchar length)
{
        uchar i;
        bit ACK;
        sendStart();
        sendByte(addrR); //传送地址与读取标记
        ACK = readACK();
        if (ACK)
        {
        sendStop(); //地址不正确或装置未连接,送出停止信号
        return ACK;
        }
        for(i = 0; i<length; i++)
        {
        readData[i] = readByte();
        if(i<length-1)
        sendACK();
        else
        sendNOACK(); //读取最后一笔资料,送出No ACK
        }
        sendStop(); //资料读取完成,送出停止信号
        return ACK;
}

(7)调用数据写入和读取

writeIIC(address_W, &Write_Buffer,4);
readIIC(address_R, &Read_Buffer, 5);
© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号