1、概述

IIC(Inter-IntegratedCircuit)总线是一种由PHILIPS公司开发的两线式串行总线,用于连接微控制器以及其外围设备,IIC也被称为I2C,其实两者是完全相同的。它是由数据线SDA和时钟线SCL构成的串行总线,可发送和接收数据。两根线定义如下:

  • 数据线SDA:数据线用来传输数据
  • 时钟线SCL:时钟线用来同步数据收发

2、信号分解

时钟图绘制由https://wavedrom.com/editor.html提供,在线绘制非常方便!

IIC信号总共可以分为五种信号:

  • 开始信号:当SCL为高电平的时候,SDA线上由高到低的跳变被定义为起始条件
    start.png
  • 结束信号:当SCL为高电平的时候,SDA线上由低到高的跳变被定义为停止条件
    stop.png
  • 应答信号:每当主机向从机发送完一个字节的数据,主机总是需要等待从机给出一个应答信号,以确认从机是否成功接收到了数据,从机应答主机所需要的时钟仍是主机提供的,应答出现在每一次主机完成8个数据位传输后紧跟着的时钟周期,低电平0表示应答,1表示非应答

    • 应答
      ack_0.png
    • 非应答
      ack_1.png
  • 发送/接受一个字节:在发送一个字节时,在SCL拉底时,SDA上的数据可以发生变化,在SCL拉高时,SDA上的数据需要保持不变,在读取数据时,SCL时钟线仍然需要主机提供,SDA设定为输入读取从机的数据
    write.png

3、编码实现

  • 开始信号

    void drv_iic_start(void)
    {
      _iic_sad_out();
      _iic_sda_high();
      _iic_scl_high();
      _iic_delay0_1us(3);
      _iic_sda_low();
      _iic_delay0_1us(6);
      _iic_scl_low();
      _iic_delay0_1us(3);
    }
  • 结束信号

    void drv_iic_stop(void)
    {
      _iic_sad_out();
      _iic_sda_low();
      _iic_scl_high();
      _iic_delay0_1us(6);
      _iic_sda_high();
      _iic_delay0_1us(3);
    }
  • 等待应答信号

    uint8_t drv_iic_wait_ack(void)
    {
      uint8_t ret = 0;
      _iic_sad_in();
      _iic_scl_high();
      _iic_delay0_1us(5);
      ret = (_iic_sda_read()) ? NAK : ACK;
      _iic_delay0_1us(5);
      _iic_scl_low();
      _iic_delay0_1us(2);
      return ret;
    }
  • 发送应答 1 信号

    void drv_iic_send_ack(void)
    {
      _iic_sad_out();
      _iic_sda_low();
      _iic_delay0_1us(3);
      _iic_scl_high();
      _iic_delay0_1us(7);
      _iic_scl_low();
      _iic_delay0_1us(3);
    }
  • 写一个字节

    void drv_iic_send_byte(uint8_t data)
    {
      uint8_t i = 0;
      _iic_sad_out();
      _iic_delay0_1us(3);
      for (i = 0; i < 8; i++)
      {
          _iic_scl_low();
          _iic_delay0_1us(3);
          (data & 0x80) ? _iic_sda_high() : _iic_sda_low();
          _iic_delay0_1us(13);
          _iic_scl_high();
          data <<= 1;
          _iic_delay0_1us(7);
      }
      _iic_scl_low();
      _iic_delay0_1us(2);
    }
  • 读取一个字节

    uint8_t drv_iic_recv_byte(void)
    {
      uint8_t data = 0;
      uint8_t i = 0;
      _iic_sad_in();
      for (i = 0; i < 8; i++)
      {
          _iic_scl_low();
          data <<= 1;
          _iic_delay0_1us(13);
          _iic_scl_high();
          _iic_delay0_1us(5);
          if (_iic_sda_read())
          {
              data |= 0x01;
          }
          _iic_delay0_1us(2);
      }
      _iic_scl_low();
      return data;
    }

    4、实战练习

    随便找一个具有IIC协议的芯片手册,通过查阅手册,得到总线通信图

  • 写总线时序图
    iic_write.png
    编码实现:

    void drv_write_reg(uint8_t reg,uint8_t data)
    {
      drv_iic_start();
      drv_iic_send_byte(HW_I2C_ADDR & 0xFE);
      drv_iic_wait_ack();
      drv_iic_send_byte(reg);
      drv_iic_wait_ack();
      drv_iic_send_byte(data);
      drv_iic_wait_ack();
      drv_iic_stop();
    }
  • 读总线时序图
    iic_read.png

    uint8_t drv_is3729_read_reg(uint8_t reg)
    {
      uint8_t rec = 0x00;
      drv_iic_start();
      drv_iic_send_byte(HW_IS3730_I2C_ADDR & 0xFE);
      drv_iic_wait_ack();
      drv_iic_send_byte(reg);
      drv_iic_wait_ack();
      drv_iic_stop();
      drv_iic_start();
      drv_iic_send_byte(HW_IS3730_I2C_ADDR | 0x01);
      drv_iic_wait_ack();
      rec = drv_iic_recv_byte();
      drv_iic_send_nack();
      drv_iic_stop();
      return rec;
    }

    5、其他注意

  • IIC协议速度较慢,因此通常只用来传输较少的数据
  • 软件IIC可以通过调整间隙延时来提高速度,但是至少要满足手册所规定的最短时间
文章目录