STM32——软件I2C通讯解析

目录

 一、I2C通信的硬件基础

二、I2C协议的基本时序单元

1.起始条件:

2.终止条件:

 3.(主机)发送一个字节(给从机):

4.(主机)接收(从机)应答:

5.(主机)接受(从机)一个字节:

6.(主机)发送应答(给从机):

三. 由基本的I2C时序构成的通讯帧 

1.(主机)指定从机地址写 :

2.(主机)指定从机地址读:

​编辑


        最近打算好好研究一下四阵脚的OLED屏幕,所以在这之前复习一下I2C通讯协议及其应用。

 一、I2C通信的硬件基础

        如下图所示,配置I2C时,需要将引脚配置成开漏输出的模式,开漏输出的特性即需要产生低电平时可以强下拉志低电平,产生高电平时,通过外接一个上拉电阻弱上拉至高电平。这样的好处是,防止配置成推挽输出时,一个设备强上拉一个设备强下拉导致的电源短路。

        STM32——软件I2C通讯解析_第1张图片STM32——软件I2C通讯解析_第2张图片

二、I2C协议的基本时序单元

        下面所说的一切都是在主机的角度上进行发送数据,接受数据,以及发送应答,接受应答的,这一点需要特别的注意。

代码前提:利用宏定义实现:

#define MPU6050_SCL_PORT    GPIOE
#define MPU6050_SCL_PIN    GPIO_Pin_12
#define MPU6050_SDA_PORT    GPIOE
#define MPU6050_SDA_PIN    GPIO_Pin_13
void MyI2C_W_SCL(uint8_t value)
{
    GPIO_WriteBit(MPU6050_SCL_PORT,MPU6050_SCL_PIN,(BitAction)value);
    delay_us(10);
}
void MyI2C_W_SDA(uint8_t value)
{
    GPIO_WriteBit(MPU6050_SDA_PORT,MPU6050_SDA_PIN,(BitAction)value);
    delay_us(10);
}
uint8_t MyI2C_R_SDA(void )
{
    uint8_t temp=0;
    temp=GPIO_ReadInputDataBit(MPU6050_SDA_PORT,MPU6050_SDA_PIN);
    delay_us(10);
    return temp;
}

1.起始条件:

        即在SCL高电平期间,SDA由高电平变为低电平:由主机发起,这表征着通讯的开始。

STM32——软件I2C通讯解析_第3张图片

void MyI2C_Start(void )
{
    MyI2C_W_SDA(1);
    MyI2C_W_SCL(1);
    MyI2C_W_SDA(0);
    MyI2C_W_SCL(0);
}

2.终止条件:

        即在SCL高电平期间,SDA由低电平变为高电平:由主机发起,这表征着通讯的结束。

STM32——软件I2C通讯解析_第4张图片

void MyI2C_Stop(void )
{
    MyI2C_W_SDA(0);
    MyI2C_W_SCL(1);
    MyI2C_W_SDA(1);
}

        注意,起始条件和终止条件都是在在SCL高电平的时候SDA变化,这是区别发送、接收、发送应答、接收应答的重要标志! 

3.(主机)发送一个字节(给从机):

        即在SCL低电平期间,主机将想要发送的数据放在SDA线上,随后拉高SCL,告知从机在SCL高电平期间将数据取走,SCL高电平期间,SDA线上的数据一定是稳定不变化的(SDA在SCL高电平期间变换就会产生起始条件或者终止条件导致通讯时序错误),连续进行8次即可完成一个字节的发送。值得注意的是:发送字节时高位先行,由D7->D0的方向发送。

STM32——软件I2C通讯解析_第5张图片

void MyI2C_SendByte(uint8_t Byte)
{
    for (int i = 0; i < 8; i++)
    {
        MyI2C_W_SDA(Byte & (0x80>>i));
        MyI2C_W_SCL(1);
        MyI2C_W_SCL(0);
    }
}

4.(主机)接收(从机)应答:

        主机发送完一个字节后,在下一个时钟里,释放SDA的控制权,并在SCL高电平期间接收来自从机的应答指令以判断从机是否收到数据。即0表示应答(即从机接收到了数据),1表示非应答(从机没有响应)。

STM32——软件I2C通讯解析_第6张图片

uint8_t MyI2C_ReceiveAck(void )
{
    uint8_t temp;
    MyI2C_W_SDA(1);
    MyI2C_W_SCL(1);
    temp=MyI2C_R_SDA();
    MyI2C_W_SCL(0);
    return temp;
}

5.(主机)接受(从机)一个字节:

        通讯开始之前,主机一定要释放对SDA线的控制,即置为SDA线为1。在SCL低电平期间,从机将需要发送的数据放在SDA线上,之后主机置为SCL,并在SCL高电平期间读取从机传输的数据(高位在前)。同理在SCL高电平期间,从机的数据(即SDA线上的值不能改变),反复进行8次,主机即可完成一个字节的接受。        

STM32——软件I2C通讯解析_第7张图片

uint8_t MyI2C_ReceiveByte(void )
{
    uint8_t Data=0x00;
    MyI2C_W_SDA(1);
    for (int i = 0; i < 8; i++)
    {
        MyI2C_W_SCL(1);
        if(MyI2C_R_SDA()==1)
        {
            Data |=(0x80>>i);
        }
        MyI2C_W_SCL(0);
    }
    return Data;
}

6.(主机)发送应答(给从机):

        主机在接收一个字节之后,在下一个时钟周期内发送一个应答位给从机,已告诉从机是否收到数据。0表示主机收到从机数据,1表示没有收到。同理是在SCL高电平期间从机读取SDA总线上的数据。

STM32——软件I2C通讯解析_第8张图片

void MyI2C_SendAck(uint8_t Ackbit)
{
    MyI2C_W_SDA(Ackbit);
    MyI2C_W_SCL(1);
    MyI2C_W_SCL(0);
}

三. 由基本的I2C时序构成的通讯帧 

1.(主机)指定从机地址写 :

        对于指定的设备(Slave Address),对该地址下指定的寄存器(Register Address)进行写数据(Data)的操作。 (0表示写,1表示读)

STM32——软件I2C通讯解析_第9张图片

void I2C_W_REG(uint8_t RegAddress,uint8_t Data )
{
    MyI2C_Start();
    MyI2C_SendByte(MPU6050ADDRESS);
    MyI2C_ReceiveAck();
    MyI2C_SendByte(RegAddress);
    MyI2C_ReceiveAck();
    MyI2C_SendByte(Data);
    MyI2C_ReceiveAck();
    MyI2C_Stop();
}

2.(主机)指定从机地址读:

        对于指定的设备(Slave Address),对该地址下指定的寄存器(Register Address)进行读数据(Data)的操作。      

STM32——软件I2C通讯解析_第10张图片

STM32——软件I2C通讯解析_第11张图片

uint8_t I2C_R_REG(uint8_t RegAddress )
{
    uint8_t temp=0;
    MyI2C_Start();
    MyI2C_SendByte(MPU6050ADDRESS);
    MyI2C_ReceiveAck();
    MyI2C_SendByte(RegAddress);
    MyI2C_ReceiveAck();

    MyI2C_Start();
    MyI2C_SendByte(MPU6050ADDRESS|0x01);
    MyI2C_ReceiveAck();
    temp=MyI2C_ReceiveByte();
    MyI2C_SendAck(1);
    MyI2C_Stop();
    return temp;
}

        I2C的基本介绍就是这样,根据不同的外设还有不同的用法。

下图是用Clion配置的STM32标准库开发环境,有兴趣的朋友可以私聊我。

你可能感兴趣的:(stm32,嵌入式硬件,单片机)