I2C总线读取MPU6050

文章目录

  • 实验目的
  • 实验步骤
  • 实验分析
  • 源码分析
    • 主函数
    • I2C初始化
    • I2C_Write_Byte
    • IIC_Start
    • IIC_SendByte
    • GetMPU6050Offset
    • GetMPU6050Data
    • MPU6050Manager_t结构体

实验目的

  • 基于MSP430处理器的 I2C总线读取MPU6050传感器数据
  • 将读取到的数据通过串口显示在上位机控制终端上

I2C总线读取MPU6050_第1张图片

I2C总线读取MPU6050_第2张图片

实验步骤

  • 硬件连接
  • 下载程序
    I2C总线读取MPU6050_第3张图片

I2C总线读取MPU6050_第4张图片

实验分析

  • 硬件I2C功能(直接使用,X不用在意)
  • 普通I/O口模拟I2C时序(本实验采用)

I2C总线读取MPU6050_第5张图片

源码分析

主函数

#include "include.h"
void main()
{
    WDT_A_hold(WDT_A_BASE);
    _DINT();  //禁止所有中断
    
    Hardware_Init();//硬件初始化  
    
    _EINT();//使能中断
    while (1)
    {
        PollingKernel();  
    }
}

  • 使用定时器中断定时读取数据
  • 使用串口中断传输数据(显示在控制终端上)
void Hardware_Init(void)
{
    System_Clock_Init();
    
    I2C_Init();			//IO口模拟IIC时序
    Motor_Init();
    
    LEDInit();			//LED灯闪初始化
    
    MPU6050Init();    	//g_MPUManager初始化
    
    SPL06_Init();		//SPL06初始化
    NRF_Radio_Init(); 
   	if(HARDWARE_CHECK)  //硬件检测
    {
        g_LedManager.emLEDPower = PowerOn;
    }
    
    gcs_init();   		//地面站通信初始化
    
    PID_Init();    		//PID参数初始化   
    
    USART_Init(USCI_A_UART_CLOCKSOURCE_ACLK,115200);
    Timer_Init();
    
}

I2C初始化

/*************************************************************************************
  *函数名称:I2C_Init
  *函数描述:初始化I2C总线
  *输   入:void
  *输   出:void
  *返   回:void
  *备   注:null
  *
  *
*************************************************************************************/

void I2C_Init()
{
    //P3.1是SCL时钟
    P3DIR |= (1 << SCL); //方向寄存器,P3.1置1,设为输出,看上图
}
//宏定义
#define 	SCL 	1

#define 	IIC_SDA_Out		P3DIR |= BIT0
#define 	IIC_SDA_In		P3DIR &= ~BIT0
#define 	IIC_SCL_Out		P3DIR |= BIT1

##MPU6050的寄存器初始化

/*************************************************************************************
  *函数名称:MPU6050Init
  *函数描述:g_MPUManager的初始化
  *输   入:void
  *输   出:g_MPUManager初始化结果
  *		   0:初始化成功
  *		   1:初始化失败
  *返   回:void
  *备   注:null
  *
  *
*************************************************************************************/
//就是初始化寄存器
bool MPU6050Init(void)
{
    uint8_t check =0;
    
    //从机地址B11010000(最后一位是写位),寄存器地址
    I2C_Write_Byte(MPU6050_ADDRESS, PWR_MGMT_1,		0x80);  //复位
    delay_ms(30);
    
    //陀螺仪采样率,0x00(333Hz) //经验总结的333Hz=1000/(1+2)
    I2C_Write_Byte(MPU6050_ADDRESS, SMPLRT_DIV,		0x02);
    
    //设置设备时钟源,陀螺仪Z轴
    I2C_Write_Byte(MPU6050_ADDRESS, PWR_MGMT_1,		0x03); 
    
    //低通滤波频率,0x03(42Hz)
    I2C_Write_Byte(MPU6050_ADDRESS, CONFIGL,		0x03); 
    
    // +-2000deg/s  //陀螺仪量程
    I2C_Write_Byte(MPU6050_ADDRESS, GYRO_CONFIG,	0x18); 
    
    // +-4g  //加速度量程
    I2C_Write_Byte(MPU6050_ADDRESS, ACCEL_CONFIG,	0x09); 
    
    //0x75寄存器中保存了从机地址
    check = I2C_Read_Byte(MPU6050_ADDRESS, 0x75);   //判断g_MPUManager地址
    
    if(check != MPU6050_PRODUCT_ID)//如果地址不正确
    {
        g_MPUManager.Check = false;
        return false;
	}
    else
    {
        //静止状态下,除了Z轴的加速度g外全都为零
        GetMPU6050Offset(); //调用校准数据
        g_MPUManager.Check = true;
        return true;
	}
}

I2C总线读取MPU6050_第6张图片

I2C总线读取MPU6050_第7张图片

I2C总线读取MPU6050_第8张图片

I2C总线读取MPU6050_第9张图片

I2C总线读取MPU6050_第10张图片

I2C_Write_Byte



//遵循I2C协议的时序图
void I2C_Write_Byte(uint8_t Slaveaddr, uint8_t REG_Address,	uint8_t REG_data)
{
    IIC_Start();  //发送起始信号
    IIC_SendByte(Slaveaddr);   //从机地址B11010000(最后一位是写位)
    IIC_SendByte(REG_Address);
    IIC_SendByte(REG_data);
    IIC_Stop();
}

//宏定义
#define 	SDA 	0
//P3OUT是P3口寄存器
#define 	IIC 	P3OUT

IIC_Start


void IIC_Start()
{
    //P3.0是SDA线
    P3DIR |= (1 << SDA);  //P3.0置1,设为输出
    IIC |= (1 << SDA);    //P3.0输出1,SDA高电平
    delay(1);   //让数据保持一段时间(电平嘛)
    
    IIC |= (1 << SCL);    //P3.1输出1,SCL高电平
    delay(1);  //延时1ms
    
    IIC &= ~(1 << SDA);  //P3.0输出0,SDA低电平
    delay(1);
    
    IIC &= ~(1 << SCL);  //P3.1输出0,SCL低电平 为后面发送地址做准备
    //所以将SCL拉低
    delay(1);
}

IIC_SendByte

  • 延时的作用当然是让数据保持一段时间(电平嘛),因为传输信号是 TTL 电平信号。 确保数据已经写入,从主机写入外设需要时间的


void IIC_SendByte(uint32_t dat)
{
    uint32_t i;
    
    P3DIR |= (1 << SDA);   //P3.0置1,设为输出
    
    i = 8; //八位数据需要发送
    
    IIC &= ~(1 << SCL);  //P3.1输出0,SCL低电平
    delay(1);  //延时1ms
    
    while(i--)
    {
        //为啥要从最高位发送呢?规定
        if((dat & 0x80) >> 7)  //判断最高位是0还是1,然后写入P3.0,写入总线
        {
            IIC |=  (1 << SDA); //P3.0输出1
		}
        else
        {
            IIC &= ~1 << SDA); //P3.0输出0
        }
        
        dat  <<= 1;   //将数据(地址)左移一位
        
        delay(1);  
        
        IIC |= (1 << SCL);  //P3.1高电平,SCL高电平
        delay(1);
        
        IIC &= ~(1 << SCL); //P3.1低电平,SCL低电平,为写下一位数据做准备
        delay(1);
    }
    
    IIC |= (1 << SDA); //P3.0高电平,SDA高电平
    IIC RecvACK();  //等待应答信号
}

GetMPU6050Offset

/*************************************************************************************
  *函数名称:GetMPU6050Offset
  *函数描述:获取g_MPUManager静态下传感器偏差
  *输   入:void
  *输   出:void
  *返   回:void
  *备   注:null
  *
  *
*************************************************************************************/

void GetMPU6050Offset(void) //校准
{
    int32_t buffer[6] = {0};
    int16_t i = 0;
   	uint8_t k = 30;
    const int8_t MAX_GYRO_QUIET = 5;  //最大最小误差
    const int8_t MIN_GYRO_QUIET = -5;
    
    int16_t LastGyro[3] = {0};		//wait for calm down
    int16_t ErrorGyro[3] = {0};		//set offset initial to zero
    
    memset(g_MPUManager.Offset,0,12);
    g_MPUManager.Offset[2] = 8192;    //根据手册量程设定加速度标定值
    
    while(k--)  //判断飞控是否处于静止状态
    {
        do
        {
            delay_ms(10);
            GetMPU6050Data();
            
            for(i=0; i<3; i++)
            {
                ErrotGyro[i] = pMpu[I + 3] - LastGyro[i];
                LastGyro[i] = pMpu[i + 3];
            }
        }while((ErrorGyro[0] > MAX_GYRO_QUIET)
            || (ErrorGyro[0] < MIN_GYRO_QUIET)
            || (ErrorGyro[1] > MAX_GYRO_QUIET)
            || (ErrorGyro[1] < MIN_GYRO_QUIET)
            || (ErrorGyro[2] > MAX_GYRO_QUIET)
            || (ErrorGyro[2] < MIN_GYRO_QUIET));
        //在误差范围内,认为静止的,退出循环,不在的话则是运动的,死循环
	}
    
    for(i=0; i<356; i++) //取第100到第356组的平均值作为校准值
    {
        
    }
}

GetMPU6050Data

/*************************************************************************************
  *函数名称:GetMPU6050Data
  *函数描述:读取陀螺仪和加速度计的数据并做滤波处理
  *输   入:void
  *输   出:void
  *返   回:void
  *备   注:null
  *
  *
*************************************************************************************/

void GetMPU6050Data(void)
{
    uint8_t buffer[12];
    const float factor = 0.15f;   //滤波因素
    static float tBuff[3] = {0};
    static EKF_Filter_t s_EKF[3] = {{0.02, 0, 0, 0, 0.001, 0.543},
                                    {0.02, 0, 0, 0, 0.001, 0.543},
                                    {0.02, 0, 0, 0, 0.001, 0.543}};
    
    Acc_Read(buffer);
    Gyro_Read(buffer);
    
    for(int i=0; i<6; i++)  //将buffer中的高八位和低八位合起来
    {
        pMpu[i] = (((int16_t)buffer[i << 1] << 8) | buffer[(i << 1) + 1])  //
                   - g_MPUManager.Offset[i];

        //此处对加速度做一维卡尔曼滤波
        if(i<3)
        {
            KalmanFilter(&s_EKF[i],(float)pMpu[i]);  //一维卡尔曼
            pMpu[i] = (int16_t)s_EKF[i].out;
        }

        //此处对角速度做一阶低通滤波
        if(i>2)
        {
            uint8_t k = i - 3;
            pMpu[i] = (int16_t)(tBuff[k] * (1 - factor) + pMpu[i] * factor);
            tBuff[k] = tBuff[k] * (1 - factor) + pMpu[i] * factor;
        }
    }
}
//私有变量区
MPU6050Manager_t  g_MPUManager;   //g_MPUManager原始数据
int16_t *pMpu = (int16_t *)&g_MPUManager;

MPU6050Manager_t结构体

typedef struct
{
	int16_t accX;  
    int16_t accY;  
    int16_t accZ;  
    int16_t gyroX;  
    int16_t gyroY;  
    int16_t gyroZ;  
    
    int16_t Offset[6];  
    bool Check;
    
}MPU6050Manager_t;

##Acc_Read

/*************************************************************************************
  *函数名称:Acc_Read
  *函数描述:获取加速度值
  *输   入:uint8_t *ptr ,写入地址
  *输   出:void
  *返   回:void
  *备   注:null
  *
  *
*************************************************************************************/

void Acc_Read(uint8_t *ptr)
{
    for(int i=0; i<6; i++)
    {
        //0xD0,就是MPU6050从机地址
        ptr[i] = I2C_Read_Byte(0xD0,0x3B+i);
    }
}

I2C总线读取MPU6050_第11张图片

##Gyro_Read

/*************************************************************************************
  *函数名称:Gyro_Read
  *函数描述:获取陀螺仪值
  *输   入:uint8_t *ptr ,写入地址
  *输   出:void
  *返   回:void
  *备   注:null
  *
  *
*************************************************************************************/

void Gyro_Read(uint8_t *ptr)
{
    for(int i=0; i<6; i++)
    {
        //0xD0,就是MPU6050从机地址
        ptr[i+6] = I2C_Read_Byte(0xD0,0x43+i);
    }
}

I2C总线读取MPU6050_第12张图片

你可能感兴趣的:(I2C总线读取MPU6050)