I2C

简介

I2C (Inter-Integrated Circuit) 总线是由PHILIPS公司开发的两线式串行总线,用于连接微控制器及其外围设备。是微电子通信控制领域广泛采用的一种总线标准。它是同步通信的一种特殊形式,具有接口线少控制方式简单器件封装形式小通信速率较高传输距离短等特性。

  • 7位或10位的地址空间
  • 普通I2C总线速度为100kbit/s的标准模式和10kbit/s的低速模式,任意时钟频率

结构

两条接口线

  • 串行数据线(SDA)
  • 串行时钟线(SCL)

时序

I2C时序

  • 主机信号时序: start + address-read/write + ACK + data + ACK + … + stop

  • 主机信号时序: start + address-read + ACK + data + ACK + … + restart + adress-write + stop

I2C协议

总线协议分析

参考设计为使用串行数据线(SDA)和串行时钟线(SCL)、拥有7bit寻址空间的总线。

  • 主机 - 产生时钟并发起与从机的通信
  • 从机 - 接收时钟并响应主机的寻址
主机发送-从机接收
  1. 主机发送start信号,发送7bit从机地址和写控制位位(0)
  2. 从机响应start信号,匹配地址。如果匹配判断读/写标志位,从机发送ACK校验;如果地址不匹配,不发送ACK
  3. 主机发送1byte数据
  4. 从机接收数据,发送ACK校验;否则不发送ACK校验
  5. 重复3、4
  6. 主机发送stop信号,终止本次通信
master12345678
信号startaddresswriteACKdataACK重复5、6stop
bit71181
主机接收-从机发送
  1. 主机发送start信号,发送7bit从机地址和写控制位(0)
  2. 从机相应start信号,匹配地址。如果匹配判断读写标志位,从机发送ACK校验;如果地址不匹配,不发送ACK
  3. 主机发送1byte数据
  4. 从机接收数据,并发送ACK校验;否则不发送ACK校验
  5. 重复3、4
  6. 主机发送restart信号,发送7bit从机地址和读控制位(1)
  7. 从机相应restart信号,并判断地址是否匹配。如果匹配判断读/写标志位,发送ACK校验信号。否则,不发送ACK校验
  8. 从机发送1byte数据
  9. 主机接收数据,并发送ACK校验
  10. 主机接收最后一位数据,发送NACK检验
  11. 主机发送stop信号,本次通信结束
master1234567891011121314
信号startaddresswriteACKrestartaddressreadACKdataACK重复9、10last dataNACKstop
bit7117118181

#注:主机读取从机特定地址上的数据

I2C协议代码分析

开发环境

  • 飞思卡尔FRDM-K64F开发板
  • KDS3.2集成开发环境
  • 飞思卡尔官方KSDK2.0开发库
  • ubuntu16.04操作系统

代码实例

master主机
/* 产生start信号,发送地址数据,读/写操作 */
result = I2C_MasterStart(I2C_MASTER, I2C_SLAVE_ADDR, kI2C_Write);
/* 等待slave回复ACK信号 */
while (I2C_MASTER->S & kI2C_ReceiveNakFlag);
/* 发送数据 */
result = I2C_MasterWriteBlocking(I2C_MASTER, masterTxBuffer, I2C_DATA_LEN);
/* restart信号 */
result = I2C_MasterRepeatedStart(I2C_MASTER, I2C_SLAVE_ADDR, kI2C_Read);
/* 等待slave回复ACK信号 */
while (I2C_MASTER->S & kI2C_ReceiveNakFlag);
/* 读取数据 */
I2C_MasterReadBlocking(I2C_MASTER, masterRxBuffer, 2 * sizeof(uint8_t));
slave从机
/* check stop flag */
if (status & kI2C_StopDetectFlag) {
    I2C_SLAVE->S = kI2C_IntPendingFlag;
    I2C_SlaveClearStatusFlags(I2C_SLAVE, kI2C_StopDetectFlag);
    isStop = true;
    return ;
}

/* check start flag */
if (status & kI2C_StartDetectFlag) {
    isStart = true;
    I2C_SLAVE->S = kI2C_IntPendingFlag;
    I2C_SlaveClearStatusFlags(I2C_SLAVE, kI2C_StartDetectFlag);
    if (!(status & kI2C_AddressMatchFlag)) {
        return;
    }
}

/* clear pending flag */
I2C_SLAVE->S = kI2C_IntPendingFlag;

/* check NAK */
if (status & kI2C_ReceiveNakFlag) {
    /* nothing to do */
    /* Receive mode */
    I2C_SLAVE->C1 &= ~(I2C_C1_TX_MASK | I2C_C1_TXAK_MASK);
    I2C_SLAVE->D;
    PRINTF("NAK\r\n");
}
/* Slave address match */
else if (status & kI2C_AddressMatchFlag) {
    if (status & kI2C_TransferDirectionFlag) {
        /* Slave transfer mode */
        I2C_SLAVE->C1 |= I2C_C1_TX_MASK;
        isTransfer = true;
    } else {
        /* Slave recive mode */
        I2C_SLAVE->C1 &= ~(I2C_C1_TX_MASK | I2C_C1_TXAK_MASK);
        I2C_SLAVE->D;
        isRecive = true;
        return;
    }
}
/* Transfer data */
if (status & kI2C_TransferCompleteFlag) {
    /* Transmit data to master */
    if (status & kI2C_TransferDirectionFlag) {
        I2C_SLAVE->D = slaveRxBuffer[1];
    }
    else {
        /* Receive data from master */
        slaveRxBuffer[receive_data_len] = I2C_SLAVE->D;
        receive_data_len++;
    }
}

问题分析

I2C从机读取数据时,读取的第一字节数据总是为0x00。在KSDK2.0代码库中,I2C主机在读取数据之前,会先读取1byte的无效数据(Do dummy read),然后读取有效数据,KSDK库函数代码如下:

status_t I2C_MasterReadBlocking(I2C_Type *base, uint8_t *rxBuff, size_t rxSize)
{
    status_t result = kStatus_Success;
    volatile uint8_t dummy = 0;

    /* Add this to avoid build warning. */
    dummy++;

    /* Wait until the data register is ready for transmit. */
    while (!(base->S & kI2C_TransferCompleteFlag))
    {
    }

    /* Clear the IICIF flag. */
    base->S = kI2C_IntPendingFlag;

    /* Setup the I2C peripheral to receive data. */
    base->C1 &= ~(I2C_C1_TX_MASK | I2C_C1_TXAK_MASK);

    /* If rxSize equals 1, configure to send NAK. */
    if (rxSize == 1)
    {
        /* Issue NACK on read. */
        base->C1 |= I2C_C1_TXAK_MASK;
    }

    /* Do dummy read. */
    dummy = base->D;

    while ((rxSize--))
    {
        /* Wait until data transfer complete. */
        while (!(base->S & kI2C_IntPendingFlag))
        {
        }

        /* Clear the IICIF flag. */
        base->S = kI2C_IntPendingFlag;

        /* Single byte use case. */
        if (rxSize == 0)
        {
            /* Read the final byte. */
            result = I2C_MasterStop(base);
        }

        if (rxSize == 1)
        {
            /* Issue NACK on read. */
            base->C1 |= I2C_C1_TXAK_MASK;
        }

        /* Read from the data register. */
        *rxBuff++ = base->D;
    }

    return result;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值