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

I2C通讯协议详解:SDA/SCL时序与数据传输机制

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

I2C通讯协议详解:SDA/SCL时序与数据传输机制

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

I2C(Inter-Integrated Circuit)总线是一种两线式串行通信总线,广泛应用于微控制器(MCU)与外围设备之间的数据传输。它采用一主多从的总线结构,通过SDA(串行数据线)和SCL(串行时钟线)实现数据的发送和接收。本文将详细介绍I2C总线的基本概念、时序规则以及具体实现方式。

一、基础介绍

I2C总线的主要功能是实现主机和从机之间的数据传输,其核心机制包括起始信号、终止信号、应答信号、读字节、写字节、数据读取和数据写入等。I2C总线的基本架构如下:

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

二、I2C通信协议详解

1. 空闲状态

SDA和SCL通过上拉电阻保持高电平,表示总线处于空闲状态。

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的每个时钟脉冲期间传输一个数据位。数据线SDA上的数据必须在SCL为高电平时保持稳定,只有在SCL为低电平时,SDA的状态才能发生变化。从机地址发送完成后,可能会发送一些指令,然后开始传输数据,每个数据为8位,数据的字节数没有限制。

5. 应答信号

  • 当SDA是低电平表示有效应答(ACK),表示接收成功。
  • 当SDA是高电平表示无效应答(NACK),表示接收失败。

(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的控制函数实现通信,无需手动控制SCL、SDA的电平变化。

2. 模拟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)
  • 写地址:10100000(A0)
  • 读地址:10100001(A1)

具体流程:

  1. 发送开始条件
  2. 发送从机地址和读写控制位
  3. 传输数据及数据传输结束时
  4. 发送停止条件

4. 应用示例

(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号