单片机开发教程3——串口发送MPU6050姿态角

转载自CSDN

1. 简介

MPU6050 是 InvenSense 公司推出的整合性 6 轴运动处理组件,其内部整合了 3 轴陀螺仪和 3 轴加速度传感器,并且含有一个IIC 接口, 可用于连接外部磁力传感器,并利用自带的数字运动处理器(DMP: Digital Motion Processor) 硬件加速引擎,通过主 IIC 接口,向应用端输出完整的 9 轴融合演算数据。

InvenSense 公司提供了一套基于DMP的运动处理驱动库,可大大降低单片机对动处理运算的负荷,同时也大大降低了编程难度。该模块广泛运用于飞控、计步等电子产品中。

1.1 模块原理图

在这里插入图片描述

1.2 引脚说明

序号 引脚 说明
1 VCC 3.3V/5V
2 GND 地线
3 SCL 作为从机时 IIC 时钟线
4 SDA 作为从机时 IIC 数据线
5 XDA 作为主机时 IIC 数据线
6 XCL 作为主机时 IIC 时钟线
7 AD0 作为从机时 IIC 地址
8 INT 中断输出引脚

XDA和XCL是在MPU6050作为主机时的信号线,在与单片机的IIC通信中,MPU6050作为从机,所以用不到

AD0 是从 IIC 接口(接 MCU)的地址控制引脚,该引脚控制IIC 地址的最低位。由原理图可知,AD0 接了 GND,所以 MPU6050 的 IIC 地址默认为:0X68

INT也用不到,当模块需要输出数据时,可以通过该引脚给单片机中断信号,一般都不需要

1.3 接线方式

单片机 —— MPU6050

5V <——> VCC
GND <——> GND
Px.x <——> SCL
Px.x <——> SDA

2. IIC通信

如果你学过一段时间单片机,你或多或少会听说过IIC通信(也写作I2C,通常读作:I方C),这是硬件开发里很常见、很常用的一种通信协议。

其实协议并不是听上去的那么高不可攀,它终究是人为定义的一个标准而已,我们只需遵循这个标准就可以了。

这里面的协议层会涉及到时序,如果你还不是“老司机”,暂且可以放下不去专研,因为这些都会被封装为函数,使用格式也非常固定,完全不必担心。比如通信中最频繁的 读写操作 ,会被封装成 IIC读/写函数 ,而读写只能算是最基础的操作,还可以利用它进一步封装成各种设置函数和数据采集函数。

2.1 IIC介绍

I2C(Inter-Integrated Circuit)总线是由 PHILIPS 公司开发的两线式串行总线,用于连接微控制器及其外围设备。是微电子通信控制领域广泛采用的一种总线标准。它是 同步通信的一种特殊形式,具有接口线少,控制方式简单, 器件封装形式小,通信速率较高等优点。 I2C 总线 只有 两根双向信号线 。一根是 数据线 SDA ,另一根是 时钟线 SCL 。由于其管脚少,硬件实现简单,可扩展性强等特点,因此被广泛的使用在各大集成芯片内。

2.2 例程讲解

开发资料中的例程代码虽然很多,但最后在main函数里对MPU6050操作的只有两个函数,其他函数大多作为这两个函数的内容。其实,对于这种不小的工程项目,我们往往会写成多个文件,方便查看和使用,这就涉及到了工程项目的管理,后续可能还会出一篇教程 (>﹏<)

// 初始化MPU6050
void InitMPU6050()
{
    Single_WriteI2C(PWR_MGMT_1, 0x00);  // #define PWR_MGMT_1 0x6B
    Single_WriteI2C(SMPLRT_DIV, 0x07);  // #define SMPLRT_DIV 0x19
    Single_WriteI2C(CONFIG, 0x06);      // #define CONFIG     0x1A
    Single_WriteI2C(GYRO_CONFIG, 0x18); // #define GYRO_CONFIG 0x1B
    Single_WriteI2C(ACCEL_CONFIG, 0x01);// #define ACCEL_CONFIG 0x1C
}

// 合成数据
int GetData(uchar REG_Address)
{
    uchar H,L;
    H=Single_ReadI2C(REG_Address);  // 相应寄存器地址
    L=Single_ReadI2C(REG_Address+1);
    return ((H<<8)+L);   
}

InitMPU6050 可以初始化模块,函数里对MPU6050的各个寄存器写入参数,从而实现初始化的功能,如果你想了解寄存器内部的配置原理,可以根据寄存器地址找到它的数据手册上对应的说明

GetData 可以返回采集的数据,值得注意的是这里得到数据不是角度,还需要进一步融合数据才能解算出姿态角

3. 姿态解算

3.1 欧拉角

欧拉角是最直观的一种姿态描述方式,所以在谈到姿态时,我们往往会使用欧拉角表达

一个坐标系到另一个坐标系的变换,可以通过绕不同坐标轴的3次连续转动来实现。这三次的转动角度统一称之为欧拉角

  • 偏航角(Yaw)
    机体系x轴投影到水平面与参考系x轴的夹角,顺时针旋转为正。

  • 俯仰角(Pitch)
    机体系x轴与水平面的夹角,抬头为正。

  • 横滚角(Roll)
    机体坐标系z轴与通过机体系x轴的铅垂面间的夹角,机体右旋为正。

在这里插入图片描述

3.2 解算方法

上面得到数据只是10位原始数据,我们需要把原始数据融合成与姿态有关的
欧拉角

这里有两种方法:

第一种是自己添加滤波算法,常用的有一阶互补滤波、二阶互补滤波和卡尔曼滤波等

第二种是直接使用MPU6050内部的DMP

DMP 是什么意思? DMP 就是指 MPU6050 内部集成的处理单元,可以直接运算出四元数和姿态,而不再需要另外进行数学运算。DMP 的使用大大简化了四轴的代码设计。DMP 是数字运动处理器的缩写,顾名思义 mpu6050 并不单单是一款传感器,其内部还包含了可以独立完成姿态解算算法的处理单元。如在设计中使用 DMP 来实现传感器融合算法优势很明显。首先,invensense 官方提供的姿态解算算法应该比像我们自己写的要可靠得多。其次,由 DMP 实现姿态解算算法将单片机从算法处理的压力中解放出来,单片机所要做的是等待DMP 解算完成后产生的外部中断,在外部中断里去读取姿态解算的结果。这样单片机有大量的时间来处理其他任务,提高了系统的实时性

经过 DMP 你就可以得到四元数,四元数就是 4 个数,可以表征姿态,经过几个数学公式之后就可以得出姿态,姿态包括 pitch,roll,yaw

下面介绍的是第一种方法的一阶互补滤波,因为51单片机对DMP的几个库函数不太兼容,或许后面会尝试解决并出教程 ╮(╯_╰)╭ ,而第一种算法网上有不少案例,实际使用上,两种方法对于初学者的难度都差不多

3.3 一阶互补滤波

需要注意的是,以下代码只能得到俯仰和滚转的数据,偏航角(Yaw)由于偏差太大没有输出值

以下代码不是完整的,还需要使用开发资料中的例程

//*******************************************************************************************************
//主程序
//*******************************************************************************************************
void main()
{ 

    unsigned int time;  // 保存定时器计数值
    float halfT;    // 每次采样的时间
    int Ax,Ay,Az,Gx,Gy,Gz;  // 加速度计和陀螺仪的原始数据 
    unsigned long Gravity;
    float AngleX1,AngleY1,AngleZ1,AngleX2,AngleY2,AngleZ2=0,dx,dy,dz;
    float Filter;
    Filter=0.8; //互补滤波系数

    delay(500);     //上电延时
    init_uart();
    InitMPU6050();  ////初始化MPU6050
    delay(150);

    while(1)
    {

        Ax=GetData(ACCEL_XOUT_H);
        Ay=GetData(ACCEL_YOUT_H);
        Az=GetData(ACCEL_ZOUT_H);
        Gx=GetData(GYRO_XOUT_H);
        Gy=GetData(GYRO_YOUT_H);
        Gz=GetData(GYRO_ZOUT_H);

        TR0 = 0;
        time = (TH0<<8)|TL0;
        halfT = (time/1000000.)*(12/11.0592);
        // Display10BitData((int)(halfT*1000000));  //显示采样时间
        TH0 = 0;
        TL0 = 0;
        TR0 = 1;

        Gravity=sqrt((float)Ax*Ax+(float)Ay*Ay+(float)Az*Az);  //Ax*Ax+Ay*Ay+Az*Az
        AngleX1=acos((float)Ax/Gravity)*180.0/3.14-90;;
        AngleY1=acos((float)Ay/Gravity)*180.0/3.14-90;
        AngleZ1=acos((float)Az/Gravity)*180.0/3.14;
        dy=halfT*Gx/-16.4; //陀螺仪测的转角y
        dx=halfT*Gy/16.4; //陀螺仪测的转角x
        dz=halfT*Gz/16.4; //陀螺仪测的转角z

        //x和y轴数据是融合加速度计和陀螺仪数据, z轴只采用陀螺仪数据
        AngleX2=Filter*(AngleX2+dx)+(1-Filter)*AngleX1;        
        AngleY2=Filter*(AngleY2+dy)+(1-Filter)*AngleY1;    
        // z轴数据有两种方式,一种是只使用陀螺仪的数据,舍弃z轴加速度(z轴零飘严重):
        AngleZ2=AngleZ2+dz;        // 注意MPU6050必须芯片正面朝上


        SeriPushSend(0x20);SeriPushSend('X'); SeriPushSend(':');
        Display10BitData((int)AngleX2);     //显示X轴角度
        SeriPushSend(0x20);SeriPushSend('Y'); SeriPushSend(':');
        Display10BitData((int)AngleY2);     //显示Y轴角度
        SeriPushSend(0x20);SeriPushSend('Z'); SeriPushSend(':');
        Display10BitData((int)AngleZ2);     //显示Z轴角度

        SeriPushSend(0x0d); 
        SeriPushSend(0x0a);//换行,回车

        delay(500); // 控制采样频率
    }
}

4. 串口通信

4.1 概念

串行通讯是指仅用一根接收线和一根发送线就能将数据以位进行传输的一种通讯方式。尽管串行通讯的比按字节传输的并行通信慢,但是串口可以在仅仅使用两根线的情况下就能实现数据的传输

典型的串口通信使用3根线完成,分别是地线、发送、接收。由于串口通信是异步的,所以端口能够在一根线上发送数据同时在另一根线上接收数据。串口通信最重要的参数是波特率、数据位、停止位和奇偶的校验。对于两个需要进行串口通信的端口,这些参数必须匹配,这也是能够实现串口通讯的前提

我们使用的STC89C516RD+内部集成有一个串行通信口,使用时只需配置波特率、工作方式和中断开关,因为这块内容比较吃单片机前面的中断和定时器的基础,所以没掌握也无须担心,这些都有比较固定的初始化模板,直接使用例程提供的函数即可

4.2 串口显示姿态角

代码下载到单片机内之后,使用串口助手显示数据

在这里插入图片描述

4.3 接线图

上电时,MPU6050的绿色电源指示灯亮起,程序下载完成后,串口模块会有LED一直闪烁,表示串口通信正在工作

在这里插入图片描述

暂无评论

相关推荐

微信扫一扫,分享到朋友圈

单片机开发教程3——串口发送MPU6050姿态角