硬件I2C驱动MPU6050

I2C

I2C有两条总线线路,分别是SCL(时钟线)和SDA(数据线)。
I2C的时序非常重要:读数据和写数据的时序有点不一样,在写程序的时候就体现得到。
I2C的SCL高电平时有效,SDA高电平为1,低电平为0。

I2C的驱动:

I2C.h
#ifndef  __I2c__H
#define  __I2c__H

#include "stm32f4xx.h"


#define MPU_ADDRESS         0xd0
#define I2C_SCL_CLK         RCC_AHB1Periph_GPIOB
#define I2C_SDA_CLK         RCC_AHB1Periph_GPIOB
#define MPU_I2C_CLK         RCC_APB1Periph_I2C1
#define I2C_SCL_PORT        GPIOB
#define I2C_SDA_PORT        GPIOB
#define MPU_I2C_PORT        I2C1
#define I2C_SCL_PIN         GPIO_Pin_6
#define I2C_SDA_PIN         GPIO_Pin_7
#define I2C_SCL_SOURCE_PIN  GPIO_PinSource6
#define I2C_SDA_SOURCE_PIN  GPIO_PinSource7
#define SCL_I2C_AF          GPIO_AF_I2C1
#define SDA_I2C_AF          GPIO_AF_I2C1
 

void I2C_GPIO_config(void);
void MPU_I2c_config(void);
void MPU6050_I2C_Init(void);
uint8_t MPU6050_WriteByte(uint8_t addr,uint8_t data);
uint8_t MPU6050_ReadByte(uint8_t addr); 
uint16_t MPU6050_Readbuffer(uint8_t addr,short*buffer,uint8_t num);

#endif

1.初始化I2C的引脚,开启复用功能

I2C需要配置成开漏输出

void I2C_GPIO_config(void)
{
    RCC_AHB1PeriphClockCmd(I2C_SCL_CLK|
                           I2C_SDA_CLK,
                                        ENABLE);
    
    GPIO_PinAFConfig(I2C_SCL_PORT,I2C_SCL_SOURCE_PIN,SCL_I2C_AF);
    GPIO_PinAFConfig(I2C_SDA_PORT,I2C_SDA_SOURCE_PIN,SDA_I2C_AF);
    
    GPIO_InitTypeDef GPIO_InitStruct;
    GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF;
    GPIO_InitStruct.GPIO_OType=GPIO_OType_OD;
    GPIO_InitStruct.GPIO_PuPd=GPIO_PuPd_NOPULL;
    GPIO_InitStruct.GPIO_Speed=GPIO_Speed_100MHz;
    GPIO_InitStruct.GPIO_Pin=I2C_SCL_PIN;
    GPIO_Init(I2C_SCL_PORT, & GPIO_InitStruct);
    
    GPIO_InitStruct.GPIO_Pin=I2C_SDA_PIN;
    GPIO_Init(I2C_SDA_PORT, & GPIO_InitStruct);
    
}

2.初始化I2C

I2C_OwnAddress1(MCU的设备地址,只要和I2C上挂载的设备地址不一样即可)
记得使能I2C

void MPU_I2c_config(void)
{
    RCC_APB1PeriphClockCmd(MPU_I2C_CLK,ENABLE);
    
    //I2C_DeInit(MPU_I2C_PORT);
    I2C_InitTypeDef I2C_InitStruct;
    I2C_InitStruct.I2C_Mode=I2C_Mode_I2C;
    I2C_InitStruct.I2C_ClockSpeed=100000;
    I2C_InitStruct.I2C_DutyCycle=I2C_DutyCycle_2 ;
    I2C_InitStruct.I2C_OwnAddress1=0x0a;
    I2C_InitStruct.I2C_Ack=I2C_Ack_Enable;
    I2C_InitStruct.I2C_AcknowledgedAddress=I2C_AcknowledgedAddress_7bit;
    I2C_Init(MPU_I2C_PORT,&I2C_InitStruct);
    
    I2C_Cmd(MPU_I2C_PORT,ENABLE);
}

3.编写I2C写一个字节的函数

体现I2C读数据的时序
注意应答事件,这里很容易写错,有两个EV6,分别是作为发送端和接收端

uint8_t MPU6050_WriteByte(uint8_t addr,uint8_t data)
{
    while(I2C_GetFlagStatus(MPU_I2C_PORT,I2C_FLAG_BUSY));//检查总线是否在忙
    I2C_GenerateSTART(MPU_I2C_PORT ,ENABLE);//发送起始信号
    while(I2C_CheckEvent(MPU_I2C_PORT,I2C_EVENT_MASTER_MODE_SELECT)!=SUCCESS);//应答
    I2C_Send7bitAddress(MPU_I2C_PORT,MPU_ADDRESS,I2C_Direction_Transmitter);//发送挂在在I2C上设备的地址
    while(I2C_CheckEvent(MPU_I2C_PORT,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)!=SUCCESS);//应答
    I2C_SendData(MPU_I2C_PORT,addr);//发送写入设备的内部地址
    while(I2C_CheckEvent(MPU_I2C_PORT,I2C_EVENT_MASTER_BYTE_TRANSMITTED)!=SUCCESS);//应答
    I2C_SendData(MPU_I2C_PORT,data);//发送写入的数据
    while(I2C_CheckEvent(MPU_I2C_PORT,I2C_EVENT_MASTER_BYTE_TRANSMITTED)!=SUCCESS);//应答
    I2C_GenerateSTOP(MPU_I2C_PORT ,ENABLE);//发送停止信号
    return 0;
}

4.编写I2C读入一个字节的函数

在读数据过程中,需要发送两次的起始信号,第一次发送起始信号是要告诉I2C上挂载的设备要操作它的内部地址,第二次发送起始信号告诉设备现在要读它

uint8_t MPU6050_ReadByte(uint8_t addr)
{
    uint8_t readdata=0;
    I2C_GenerateSTART(MPU_I2C_PORT ,ENABLE);//发送起始信号
    while(I2C_CheckEvent(MPU_I2C_PORT,I2C_EVENT_MASTER_MODE_SELECT)!=SUCCESS);//应答
    I2C_Send7bitAddress(MPU_I2C_PORT,MPU_ADDRESS,I2C_Direction_Transmitter);//发送设备地址
    while(I2C_CheckEvent(MPU_I2C_PORT,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)!=SUCCESS);//应答
    I2C_Cmd(MPU_I2C_PORT,ENABLE);//清除事件EV6
    I2C_SendData(MPU_I2C_PORT,addr);//发送要读取设备的内部地址
    while(I2C_CheckEvent(MPU_I2C_PORT,I2C_EVENT_MASTER_BYTE_TRANSMITTING)!=SUCCESS);//应答
    I2C_GenerateSTART(MPU_I2C_PORT ,ENABLE);发送第二次起始信号
    while(I2C_CheckEvent(MPU_I2C_PORT,I2C_EVENT_MASTER_MODE_SELECT)!=SUCCESS);//应答
    I2C_Send7bitAddress(MPU_I2C_PORT,MPU_ADDRESS,I2C_Direction_Receiver);//发送设备地址
    while(I2C_CheckEvent(MPU_I2C_PORT,I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED)!=SUCCESS);//应答
    I2C_AcknowledgeConfig(MPU_I2C_PORT ,DISABLE);//失能应答
    readdata=I2C_ReceiveData(MPU_I2C_PORT);//读取数据
    while(I2C_CheckEvent(MPU_I2C_PORT,I2C_EVENT_MASTER_BYTE_RECEIVED)!=SUCCESS);//应答
    I2C_GenerateSTOP(MPU_I2C_PORT ,ENABLE);//发送停止信号
    I2C_AcknowledgeConfig(MPU_I2C_PORT ,ENABLE);//使能应答
    return readdata;返回读取数据
}

5.编写I2C连续读取函数

和读取一个字节函数区别不大

uint16_t MPU6050_Readbuffer(uint8_t addr,short *buffer,uint8_t num)
{
    while(I2C_GetFlagStatus(MPU_I2C_PORT,I2C_FLAG_BUSY));
    I2C_GenerateSTART(MPU_I2C_PORT ,ENABLE);
    while(I2C_CheckEvent(MPU_I2C_PORT, I2C_EVENT_MASTER_MODE_SELECT)!=SUCCESS);
    I2C_Send7bitAddress(MPU_I2C_PORT, MPU_ADDRESS,I2C_Direction_Transmitter);
    while(I2C_CheckEvent(MPU_I2C_PORT, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)!=SUCCESS);
    I2C_Cmd(MPU_I2C_PORT,ENABLE);
    I2C_SendData(MPU_I2C_PORT,addr);
    while(I2C_CheckEvent(MPU_I2C_PORT, I2C_EVENT_MASTER_BYTE_TRANSMITTED)!=SUCCESS);
    
    I2C_GenerateSTART(MPU_I2C_PORT ,ENABLE);
    while(I2C_CheckEvent(MPU_I2C_PORT, I2C_EVENT_MASTER_MODE_SELECT)!=SUCCESS);
    I2C_Send7bitAddress(MPU_I2C_PORT, MPU_ADDRESS,I2C_Direction_Receiver);
    while(I2C_CheckEvent(MPU_I2C_PORT, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED)!=SUCCESS);
    while(num)
    {
        if(num==1)
        {
            I2C_AcknowledgeConfig(MPU_I2C_PORT, DISABLE);
            I2C_GenerateSTOP(MPU_I2C_PORT, ENABLE);
        }
        while(I2C_CheckEvent(MPU_I2C_PORT, I2C_EVENT_MASTER_BYTE_RECEIVED)!=SUCCESS);

        { 
          *buffer=I2C_ReceiveData(MPU_I2C_PORT);
          buffer++;
          num--;
        }//这个中括号表示连续读取
    }
    I2C_AcknowledgeConfig(MPU_I2C_PORT,ENABLE);
    return 1;
}

MPU6050的驱动

MPU6050.h

#ifndef  __MPU6050__H
#define  __MPU6050__H

#include "stm32f4xx.h"
#include "I2c.h"
#include "USART.h"
#include "stdio.h"
#include "Delay.h"

#define MPU6050_RA_WHO_AM_I       0x75
#define MPU6050_RA_PWR_MGMI       0x6B
#define MPU6050_RA_SAP_RT_DIV     0x19
#define MPU6050_RA_CONFIG         0x1A
#define MPU6050_RA_ACCEL_CONFIG   0x1C
#define MPU6050_RA_GYRO_CONFIG    0x1B
#define MPU6050_RA_FIFO_ENABLE    0X23

#define MPU6050_RA_ACCEL0_MESUME   0X3B

#define MPU6050_RA_GRYO0_MESUME    0X43

uint8_t MPU6050_ReadID(void);
void MPU6050_Init(void);
void MPU6050_Write(uint8_t addr,uint8_t data);
uint8_t MPU6050_Read(uint8_t addr);
void MPU6050_ReadACCEL(uint8_t addr,short *acc_data,uint8_t num);
void MPU6050_ReadGYRO(uint8_t addr,short *gyro_data,uint8_t num);
#endif

1.MPU6050读字节函数 写字节函数

void MPU6050_Write(uint8_t addr,uint8_t data)
{
    MPU6050_WriteByte(addr,data);
}

uint8_t MPU6050_Read(uint8_t addr)
{
    uint8_t readdata=0;
    readdata=MPU6050_ReadByte(addr);
    return readdata;
}   

uint16_t MPU6050_Readdata(uint8_t addr,short *buffer,uint8_t num)
{
    MPU6050_Readbuffer(addr,buffer,num);
    return 0;
}

2.初始化MPU6050

初始化MPU6050思路:
1.MPU6050上电后处于休眠状态,所以要解除休眠状态,“POWER MANAGEMENT 1”寄存器中sleep位置0,就解除休眠状态
2.SAMPLE RATE DIVIDER 频率采样分频器
3.配置外部引脚采样,初始化不需要你用到什么,选择不做任何配置
4.陀螺仪配置(开启自检,选择量程)
5.加速度计(开启自检,选择量程)

void MPU6050_Init(void)
{//MPU6050上电要预热
    delay (500);
    MPU6050_Write(MPU6050_RA_PWR_MGMI,0x00);
    MPU6050_Write(MPU6050_RA_SAP_RT_DIV,0x07);
    MPU6050_Write(MPU6050_RA_CONFIG,0x06);
    MPU6050_Write(MPU6050_RA_ACCEL_CONFIG,0xF8);
    MPU6050_Write(MPU6050_RA_GYRO_CONFIG,0xF8);
}

3.读取MPU6050 ID的函数

根据手册 ID在设备地址的基础上右移一位
在这里插入图片描述

uint8_t MPU6050_ReadID(void)
{
    uint8_t ID=0;
    ID=MPU6050_Read(MPU6050_RA_WHO_AM_I);
    ID = ID>>1;
    if(ID!=0x68)
    {
        printf("检测不到MPU6050\n");
        printf("error ID=0x%x",ID);
        return 0;
    }
    else
    {
        printf("检测到MPU6050\n");
        return 1;
    }
    
}

4.编写读取加速度计和陀螺仪的函数

void MPU6050_ReadACCEL(uint8_t addr,short*acc_data,uint8_t num)
{
    short buf[6];
    MPU6050_Readdata(addr,buf,num);
    acc_data[0]=((buf[0]<<8)|(buf[1]));
    acc_data[1]=((buf[2]<<8)|(buf[3]));
    acc_data[2]=((buf[4]<<8)|(buf[5]));
}

void MPU6050_ReadGYRO(uint8_t addr,short *gyro_data,uint8_t num)
{
    short buf[6];
    MPU6050_Readdata(addr,buf,num);
    gyro_data[0]=((buf[0]<<8)|buf[1]);
    gyro_data[1]=((buf[2]<<8)|buf[3]);
    gyro_data[2]=((buf[4]<<8)|buf[5]);
}

mian函数

int main(void)
{
    //uint16_t num=200;
    short Accel[3];
    short Gryo[3];
    
    USART_config();
    printf("检测MPU6050是否存在\n");
    MPU6050_I2C_Init();
    MPU6050_Init();
    MPU6050_ReadID();
    while(1)
    {
            MPU6050_ReadACCEL(MPU6050_RA_ACCEL0_MESUME,Accel,6);
            printf("加速度计X=%d  Y=%d  Z=%d\n",Accel[0],Accel[1],Accel[2]);
            MPU6050_ReadGYRO(MPU6050_RA_GRYO0_MESUME,Gryo,6);
            printf("陀螺仪X=%d  Y=%d  Z=%d\n",Gryo[0],Gryo[1],Gryo[2]);
            delay (100);
}

在这里插入图片描述

发布了13 篇原创文章 · 获赞 19 · 访问量 1816

猜你喜欢

转载自blog.csdn.net/qq_44622367/article/details/101561856