1、什么是I2C通信协议,为什么要设计I2C通信协议?

  (1)I2C(Inter - Integrated Circuit)通信协议是一种 同步半双工(对话式)多模块有应答机制 的串行通信协议,主要用于连接低速设备,如微控制器、传感器、EEPROM

  (2)设计目的为了简化硬件连接支持多设备通信低功耗可软件实现

2、采用I2C协议的模块:MPU6050模块OLED模块AT24C02存储器模块DS3231实时时钟模块

3、两根通信线:SCL时钟线 + SDA数据线

4、I2C硬件电路:

 硬件要求:

 ① 从机不允许控制SCL总线

 ② 从机不能主动发起控制SDA总线

   (只能在主机发送读取命令后或者从机应答时,控制SDA总线)

 ③ I2C设备从机地址一般是7位/10位

 

5、I2C时序基本单元

(1)起始条件

       

(2)终止条件

       

(3)发送一个字节

        主机拉低SCL,SCL从1 --->0; 

        ② SCL=0, 主机写SDA;

        主机释放SCL,SCL从0 --->1;

        SCL=1,从机读SDA;

(4)接收一个字节

        主机释放SDA控制权

        ① 主机拉低SCL,SCL从1 --->0; 

        ② SCL=0,从机写SDA;

        ③ 主机释放SCL,SCL从0--->1;

        ④  SCL=1,主机读SDA;

(5)发送应答

        主机接收到数据后,发送0表示主机应答,发送1表示主机非应答

(6)接收应答

        主机发送数据后,释放SDA控制权

        接收到0表示从机应答,接收到1表示从机非应答

 

6、I2C读写时序

(1)指定地址写数据

        起始条件 + 从机地址(7位)+ 读写位(0写)+ 从机应答 + 寄存器地址/指令控制字/存储器地址 + 从机应答(0) + 据 + 终止条件  

 

(2)当前地址读数据

        起始条件 + 从机地址(7位)+ 读写位(1读)+ 从机应答 + 主机接收数据 + 主机应答(0) + 终止条件

 

(3)指定地址读数据

        起始条件 + 从机地址(7位)+ 读写位(0写)+ 从机应答 + 寄存器地址/指令控制字/存储器地址 + 从机应答(0)+

        重复起始条件 + 主机接收数据 + 主机应答(0)+ 终止条件 

 

7、I2C点名MPU6050实验MPU6050作为从机的地址是0xD0或者0xD1,取决于AD0引脚

MyI2C.c

#include "stm32f10x.h"
#include "Delay.h"

void MyI2C_W_SCL(uint8_t uiValue)
{
    GPIO_WriteBit(GPIOB, GPIO_Pin_10, (BitAction)uiValue);
    Delay_us(10);
}

void MyI2C_W_SDA(uint8_t uiValue)
{
    GPIO_WriteBit(GPIOB, GPIO_Pin_11, (BitAction)uiValue);
    Delay_us(10);
}

uint8_t MyI2C_R_SDA(void)
{
    uint8_t uiRet = 0;
    uiRet = GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11);
    Delay_us(10);
    return uiRet;
}

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

void MyI2C_Stop(void)
{
    MyI2C_W_SDA(0);

    MyI2C_W_SCL(1);
    MyI2C_W_SDA(1);
}

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

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


uint8_t MyI2C_ReceiveByte(void)
{
    uint8_t uiLoop = 0;
    uint8_t uiByte = 0;
    
    MyI2C_W_SDA(1);

    for(uiLoop=0; uiLoop<8; uiLoop++)
    {
        MyI2C_W_SCL(1);
        if( Bit_SET == MyI2C_R_SDA() )
        {
            uiByte |= (0x80 >> uiLoop);
        }
        MyI2C_W_SCL(0);
    }

    return uiByte;
}

uint8_t MyI2C_ReceiveAck(void)
{
    uint8_t uiAckBit = 0;
    
    MyI2C_W_SDA(1);
    MyI2C_W_SCL(1);
    
    uiAckBit = MyI2C_R_SDA();
        
    MyI2C_W_SCL(0);

    return uiAckBit;
}

void MyI2C_Init(void)
{
    /* 1、开启GPIOB时钟 */
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
    
    /* 2、GPIO引脚初始化,SCL和SDA引脚分别是PB10和PB11引脚 */
	GPIO_InitTypeDef GPIO_InitStructure;
 	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD; /* 开漏输出模式 */
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11;
 	GPIO_Init(GPIOB, &GPIO_InitStructure);

    /* 3、空闲状态将引脚初始化为高电平 */
    GPIO_SetBits(GPIOB, GPIO_Pin_10 | GPIO_Pin_11);

}

 

main.c

#include "stm32f10x.h" 
#include "Delay.h"
#include "OLED.h"
#include "MyI2C.h"

int main(void)
{
    uint8_t uiAck = 0;
    OLED_Init();
    MyI2C_Init();

    MyI2C_Start();
    MyI2C_SendByte(0xD2); // AD0引脚接高电平从机地址为0xD1, AD0引脚接低电平从机地址为0xD0。
    uiAck = MyI2C_ReceiveAck();
    MyI2C_Stop();
    
    OLED_ShowNum(1, 1, uiAck,5);
    
    while(1)
    {
        
    }
}

 

 7、I2C读写MPU6050寄存器实验

MPU6050.C

#include "stm32f10x.h"
#include "MyI2C.h"

#define     MPU6050_ADDRESS         0xD0   //MPU6050设备地址

void MPU6050_WriteReg(uint8_t uiAddr, uint8_t uiData)
{
    MyI2C_Start();
    MyI2C_SendByte(MPU6050_ADDRESS);
    MyI2C_ReceiveAck();
    MyI2C_SendByte(uiAddr);
    MyI2C_ReceiveAck();
    MyI2C_SendByte(uiData);
    MyI2C_ReceiveAck();
    MyI2C_Stop();
}

uint8_t MPU6050_ReadReg(uint8_t uiAddr)
{ 
    uint8_t uiData = 0x00;
    MyI2C_Start();
    MyI2C_SendByte(MPU6050_ADDRESS);
    MyI2C_ReceiveAck();
    MyI2C_SendByte(uiAddr);
    MyI2C_ReceiveAck();

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

void MPU6050_Init(void)
{
    MyI2C_Init();
}

 

main.c

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "MPU6050.h"

int main(void)
{
    uint8_t uiID = 0x00;

    /* 模块初始化 */
    OLED_Init();
    MPU6050_Init();

    MPU6050_WriteReg(0x6B, 0x02);

    /* 读取寄存器0x6B */
    uiID = MPU6050_ReadReg(0x6B);
    OLED_ShowHexNum(1, 1, uiID, 2);	
	
    Delay_s(2);
	
    /* 读取寄存器0x75 */
    uiID = MPU6050_ReadReg(0x75);
    OLED_ShowHexNum(1, 1, uiID, 2);
    
    while(1)
    {
        
    }
}

 

posted on 2025-02-12 22:03  轩~邈  阅读(290)  评论(0)    收藏  举报