IIC协议总结

1.基本理解

        iic通信协议:双线制串行通信协议,由时钟线SCL和数据线SDA构成.

        通信方式:主从模式,主设备发起通信,从设备响应通信

2.通信的基本步骤


    a.主设备发送一个开始信号,表示开始通信,即启动I2C  

        条件:SCL=1,SDA出现下降沿 
        IIC启动之后,SCL=1时,SDA的电平不允许有变化
                              SCL=0时,数据发送方才能在SDA上改变发送电平
    b.主设备发送一个从设备的地址和(读写位) 一般地址的长度是7bit,最后一个bit是读写指令,
        1是读(主机接收从机数据),0是写(主机发送数据到从机)
    C.仲裁机制和应答,从机只有收到自己的地址信息才会被唤醒,这个过程是以一个低电平的SDA脉冲应答,
        主机检测这个应答位,0是 1不是
    d.数据传输 确认目标后,主设备发送或者接收数据,数据传输在每个时钟周期的上升沿或者下降沿进行
    e.停止:SCL = 1,SDA出现上升沿
        注意:I2C外部需根据传输速率匹配上拉电阻,速率越高,上拉电阻越小,否则会影响时序;
                    I2C引脚作为输出时需是开漏输出,作为输入时需是浮空输入,不能匹配内部上拉或下拉电阻;
              

    3.软件模拟


    1.结构体定义总线的SDA和SCL两个引脚:用于控制两个引脚的输入输出

typedef struct{
            uint32_t SDA_RCC_APB2Periph;//SDA脚的时钟
            GPIO_TypeDef* SDA_Port;//SDA脚Port
            uint16_t SDA_Pin;//SDA脚Pin
            
            uint32_t SCL_RCC_APB2Periph;//SCL脚的时钟
            GPIO_TypeDef* SCL_Port;//SCL脚Port
            uint16_t SCL_Pin;//SCL脚Pin
        }sw_i2c_gpio_t;


     2.宏定义
      

#define I2C_USE_7BIT_ADDR //如果使用的从机地址是7Bit模式,则打开这个宏,否则注释掉这个宏
      #define I2C_DELAY                50 // I2C每个Bit之间的延时时间,延时越小I2C的速率越高

      #define SW_I2C_SCL_LOW          GPIO_ResetBits(gpio->SCL_Port,gpio->SCL_Pin) // I2C SCL脚输出0 地电平
    #define SW_I2C_SCL_HIGH         GPIO_SetBits(gpio->SCL_Port,gpio->SCL_Pin) // I2C SCL脚输出1 高电平
    #define SW_I2C_SDA_LOW          GPIO_ResetBits(gpio->SDA_Port,gpio->SDA_Pin) // I2C SDA脚输出0 低电平
    #define SW_I2C_SDA_HIGH         GPIO_SetBits(gpio->SDA_Port,gpio->SDA_Pin) // I2C SDA脚输出1 高电平
    #define SW_I2C_SDA_INPUT        sw_i2c_set_sda_input(gpio) // 将SDA脚方向设置为输入 
    #define SW_I2C_SDA_OUTPUT        sw_i2c_set_sda_output(gpio) // 将SDA脚方向设置为输出
    #define SW_I2C_SDA_STATUS        sw_i2c_sda_status(gpio) // 获取SDA脚输入电平状态  
      
    #define i2c_delay_us(a)            SystemDelayUs(a) // 延时


   3,其他准备函数
  

 SDA输入输出及状态
    /**************************************************************************
***                          读取SDA脚的状态                             ***
***************************************************************************/
static uint8_t sw_i2c_sda_status(sw_i2c_gpio_t *gpio)
{
    uint8_t sda_status;
    
    sda_status = GPIO_ReadInputDataBit(gpio->SDA_Port,gpio->SDA_Pin);    
    return sda_status?1:0;
}
/**************************************************************************
***                          设置SDA脚为输入                             ***
***************************************************************************/
static void sw_i2c_set_sda_input(sw_i2c_gpio_t *gpio)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    
    GPIO_InitStructure.GPIO_Pin = gpio->SDA_Pin;    
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入模式
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init (gpio->SDA_Port, & GPIO_InitStructure );
}
/**************************************************************************
***                          设置SDA脚为输出                             ***
***************************************************************************/
static void sw_i2c_set_sda_output(sw_i2c_gpio_t *gpio)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    
    GPIO_InitStructure.GPIO_Pin = gpio->SDA_Pin;    
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;   //开漏输出模式
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init (gpio->SDA_Port, & GPIO_InitStructure );
}
    I2C启动
    static void sw_i2c_start(sw_i2c_gpio_t *gpio)
{
    // I2C 开始时序:SCL=1时,SDA由1变成0.
    SW_I2C_SDA_HIGH;         
    i2c_delay_us(I2C_DELAY);
    SW_I2C_SCL_HIGH;           
    i2c_delay_us(I2C_DELAY);
    SW_I2C_SDA_LOW;
    i2c_delay_us(I2C_DELAY);
    SW_I2C_SCL_LOW;
    i2c_delay_us(I2C_DELAY);
}
I2C停止
static void sw_i2c_stop(sw_i2c_gpio_t *gpio)
{
    // I2C 开始时序:SCL=1时,SDA由0变成1.
    SW_I2C_SDA_LOW;
    i2c_delay_us(I2C_DELAY);
    SW_I2C_SCL_HIGH;
    i2c_delay_us(I2C_DELAY);
    SW_I2C_SDA_HIGH;
}

等待数据接收方反馈
static uint8_t sw_i2c_wait_ack(sw_i2c_gpio_t *gpio)
{
    uint8_t sda_status;
    uint8_t wait_time=0;
    uint8_t ack_nack = 1;
    
    //先设置SDA脚为输入
    SW_I2C_SDA_INPUT;
    //等待SDA脚被从机拉低
    while(SW_I2C_SDA_STATUS)
    {
        wait_time++;
        //如果等待时间过长,则退出等待
        if (wait_time>=200)
        {
            ack_nack = 0;
            break;
        }
    }
    // SCL由0变为1,读入ACK状态
    // 如果此时SDA=0,则是ACK
    // 如果此时SDA=1,则是NACK
    i2c_delay_us(I2C_DELAY);
    SW_I2C_SCL_HIGH;
    i2c_delay_us(I2C_DELAY);
    
    //再次将SCL=0,并且将SDA脚设置为输出
    SW_I2C_SCL_LOW;
    i2c_delay_us(I2C_DELAY);
    SW_I2C_SDA_OUTPUT;
    i2c_delay_us(I2C_DELAY);
    return ack_nack;
}

发送ACK给数据发送方  应答信号
static void sw_i2c_send_ack(sw_i2c_gpio_t *gpio)
{
    // 发送ACK就是在SDA=0时,SCL由0变成1
    SW_I2C_SDA_LOW;
    i2c_delay_us(I2C_DELAY);
    SW_I2C_SCL_HIGH;
    i2c_delay_us(I2C_DELAY);
    SW_I2C_SCL_LOW;
    i2c_delay_us(I2C_DELAY);
}

发送NACK给数据发生方 非应答信号,表示接受没有成功
static void sw_i2c_send_nack(sw_i2c_gpio_t *gpio)
{
    // 发送NACK就是在SDA=1时,SCL由0变成1
    SW_I2C_SDA_HIGH;
    i2c_delay_us(I2C_DELAY);
    SW_I2C_SCL_HIGH;
    i2c_delay_us(I2C_DELAY);
    SW_I2C_SCL_LOW;
    i2c_delay_us(I2C_DELAY);
}

4.主设备接受和发送字节 

static void sw_i2c_write_byte(sw_i2c_gpio_t *gpio,uint8_t aByte)
{
    uint8_t i;
    for (i=0;i<8;i++)
    {
        //先将SCL拉低;
        SW_I2C_SCL_LOW;
        i2c_delay_us(I2C_DELAY);
        //然后在SDA输出数据
        if(aByte&0x80)
        {
            SW_I2C_SDA_HIGH;
        }
        else
        {
            SW_I2C_SDA_LOW;
        }
        i2c_delay_us(I2C_DELAY);
        //最后将SCL拉高,在SCL上升沿写入数据
        SW_I2C_SCL_HIGH;
        i2c_delay_us(I2C_DELAY);
        
        aByte = aByte<<1;//数据位移
    }
    //写完一个字节只后要将SCL拉低
    SW_I2C_SCL_LOW;
    i2c_delay_us(I2C_DELAY);
}

主设备从从设备读取字节
static uint8_t sw_i2c_read_byte(sw_i2c_gpio_t *gpio)
{
    uint8_t i,aByte;
    
    //先将SDA脚设置为输入
    SW_I2C_SDA_INPUT;
    for (i=0;i<8;i++)
    {
        //数据位移
        aByte = aByte << 1;
        //延时等待SDA数据稳定
        i2c_delay_us(I2C_DELAY);
        //SCL=1,锁定SDA数据
        SW_I2C_SCL_HIGH;
        i2c_delay_us(I2C_DELAY);
        //读取SDA状态
        if(SW_I2C_SDA_STATUS)
        {
            aByte |= 0x01;
        }
        //SCL=0,解除锁定
        SW_I2C_SCL_LOW;
    }
    //读完一个字节,将SDA重新设置为输出
    SW_I2C_SDA_OUTPUT;
    return aByte;
}

  • 11
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值