[嵌入式]I2C协议指东

最近闲来无聊,入了一块MPU6050,手头本来就有一块原子的STM32 MINI开发板,凑活着学习了一下IIC,特此总结。

IIC,是集成电路总线【Inter-Intergrated Circuit】的缩写,属于飞利浦公司的原创。

主要用两根线:数据线SDA和时钟线SCL。

关于时序方面本文就不截图了,网上一大堆。

下面就具体说IIC的传输过程中,比较重要的几个方法,下文的代码均是在STM32中实现,是一种模拟IIC。

SCL为输出模式的PC(12),SDA则根据情况切换输入和输出模式,为PC(11)。

1、开始信号

开始信号定义为:SCL高电平时,SDA的下降沿

//开始信号
void IIC_Start(void)
{
    SDA_OUT();     //SDA输出模式
    IIC_SDA=1;
    IIC_SCL=1;
    delay_us(IIC_DELAY);
    IIC_SDA=0;//SCL高电平时SDA的下降沿
    delay_us(IIC_DELAY);
}

2、结束信号

结束信号定义为:SCL高电平时,SDA的上升沿

//结束信号
void IIC_Stop(void)
{
    SDA_OUT();
    IIC_SDA=0;    
    IIC_SCL=1;
    delay_us(IIC_DELAY);
    IIC_SDA=1;//SCL高电平时SDA的上升沿
    delay_us(IIC_DELAY);
}

其中的SDA_OUT()是STM32的IO口模式设置,其他MCU可忽略或更改。IIC_DELAY是定义的宏,可以控制延迟时间从而控制IIC速率。

3、IIC写一个字节

这里的写一个字节是说,控制了IIC总线的主机往总线上写数据。

void IIC_Send_Byte(u8 data)
{
    u8 i;
    SDA_OUT();//输出模式    
    for(i=0;i<8;i++)
    {
        IIC_SCL=0;//拉低时钟 占据总线
        delay_us(IIC_DELAY);
        IIC_SDA=(data&0x80)>>7;//每次1位,先高位
        data<<=1;
        delay_us(IIC_DELAY);
        IIC_SCL=1;
        delay_us(IIC_DELAY);        
    }
    IIC_SCL=0;    
}

这里默认是先MSB后LSB,IIC_SDA根据数据位依次置1或0,传输数据时,SCL必须拉低,以此告诉其他器件“传输进行中”,在传输结束后,还需要再次拉高SCL总线。在送完一个字节后,拉低SCL,等待应答。

4、IIC读一个字节

//IIC读一个BYTE
u8 IIC_Read_Byte(void)
{
    u8 i,receive=0;

    SDA_IN();//输入模式
    READ_SDA=1;
    for(i=0;i<8;i++)
    {
        receive<<=1;//先接收的是高位
        IIC_SCL=0;
        delay_us(IIC_DELAY);
        IIC_SCL=1;
        delay_us(IIC_DELAY);
        receive|=READ_SDA;         
    }
    IIC_SCL=0;
    return receive;
}

 这里同样的默认是先高位后低位,使用receive|=READ_SDA;来组成数据,接收数据位时,需要先拉低SCL再拉高SCL,然后再读取SDA的数据。这里的READ_SDA和IIC_SDA都是PC(11),只不过是不同的模式。

5、应答

在IIC中,应答不是必须的,所以对于应答的检测其实也不是必须的

下面是应答和不应答的代码。

//产生ACK应答
void IIC_Ack(void)
{
    SDA_OUT();
    IIC_SCL=0;
    delay_us(IIC_DELAY);
    IIC_SDA=0;
    delay_us(IIC_DELAY);
    IIC_SCL=1;
    delay_us(IIC_DELAY);
    IIC_SCL=0;//SDA为低时 拉低时钟线
    delay_us(IIC_DELAY);
}
//不产生ACK应答
void IIC_NAck(void)
{
    IIC_SCL=0;
    SDA_OUT();
    IIC_SDA=1;
    delay_us(IIC_DELAY);
    IIC_SCL=1;
    delay_us(IIC_DELAY);
    IIC_SCL=0;// SDA为高时 SCL的脉冲
    delay_us(IIC_DELAY);
}

6、应答检测

经过我的检验,当STM32写MPU6050时,是不需要进行应答检测的;但是当STM32读MPU6050时,如果不进行应答检测,就会出现数据出错/检测不到MPU6050等奇怪的错误,所以在应用IIC总线协议时,一律增加应答检测是比较好的一种规范做法

应答检测返回一个值,但是大多数情况中不需要用到这个返回值。

//应答信号确认
//1有ACK
//0无ACK
u8 IIC_Wait_Ack(void)
{
    u8 ucErrTime=0;
    
    SDA_IN();//       SDA输入模式
    IIC_SCL=0;
    delay_us(IIC_DELAY);
    IIC_SDA=1;
    delay_us(IIC_DELAY);
    IIC_SCL=1;
    delay_us(IIC_DELAY);

    while(READ_SDA)
    {
        ucErrTime++;
        if(ucErrTime>250)
        {
            IIC_Stop();
            return 0;
        }
    }
    IIC_SCL=0;//关闭时钟
    return 1;
}

如果SDA一直是高电平没有被从设备【此处为MPU6050】拉低,则说明MPU没有应答,此时停止传输,并返回0.

如果接收到应答了,则把时钟线拉低,等待下一次开始信号。

7、MPU6050相关。

关于IIC的所有函数已经讲完了,下面贴一下MPU6050相关的操作。

//写MPU60X0
u8 IIC_Write_One_Byte(u8 regaddr, u8 data)
{
    IIC_Start();                  //起始信号
    IIC_Send_Byte(SlaveAddress);   //发送设备地址+写信号
    if(IIC_Wait_Ack()==0) 
    {
        IIC_Stop();
        return 0;
    }
    IIC_Send_Byte(regaddr);    //内部寄存器地址
    //IIC_Wait_Ack();
    IIC_Send_Byte(data);       //内部寄存器数据
    //IIC_Wait_Ack();
    IIC_Stop();                   //发送停止信号

    return 1;
}
//读MPU60X0
u8 IIC_Read_One_Byte(u8 regaddr)
{
    u8 REG_data=0;
    IIC_Start();                  //起始信号
    IIC_Send_Byte(SlaveAddress);   //发送设备地址+写信号
    if(IIC_Wait_Ack()==0) 
    {
        IIC_Stop();
        return 0;
    }
    IIC_Send_Byte(regaddr);     //发送存储单元地址,从0开始    
    IIC_Wait_Ack();
    IIC_Start();                   //起始信号    
    IIC_Send_Byte(SlaveAddress+1);  //发送设备地址+读信号    
    IIC_Wait_Ack();
    REG_data=IIC_Read_Byte();       //读出寄存器数据,并且不应答
    IIC_NAck();                        //不回应
    IIC_Stop();                    //停止信号
    return REG_data;
}

可以看到写一个字节的应答检测被我注释掉了,实验证明依旧可以正确写入MPU

以上就是IIC的所有内容。

总结:IIC主要使用SDA,SCL两条线进行传输,其中SCL是独立的SDA是接入总线的。当SCL为高时,说明有“事件”:比如开始信号、终止信号或者传输过程;当SCL为低时,说明总线闲,只要某一个设备拉高总线,并使得SDA总线产生一个下降沿,则主设备就可以得知是哪个设备的请求。这种通过独立SCL电平+SDA跳变的组合信号进行多设备整合的总线方案简单、有效,容错高,软件上易于实现,硬件上则更加方便。

posted @ 2013-10-31 15:00  Lancelod_Liu  阅读(289)  评论(0编辑  收藏  举报