zoukankan      html  css  js  c++  java
  • 51获取6050原始数据并通过匿名上位机显示波形

    其实之前就用stm32获取过6050的原始数据,但是当时只是为了用而用,对他的理解并不深入,也不明白到底是怎么获得这些数据的。

    这几天回归原始,通过STC89C52RC,再一次对6050进行驱动,并获取原始数据。经过几天的努力,现在其实感觉6050没有想象中的那么复杂,复杂的是后面对他的数据进行的滤波和姿态解算。获取数据其实就是对里面的一些寄存器进行配置。但是要求熟练掌握单片机的 IIC 通信协议。通信协议这东西其实看着很简单,但是实际用起来有很多细节方面的东旭需要注意。关于具体 IIC 的通信协议发内容,可以看这里。但是最重要的还是通过自己真正的把 IIC 的协议给写出来,在遇到问题的过程中靠自己慢慢的去查找错误,其实这个过程最能锻炼人。在这个过程中,能收货很多东西。好了,貌似扯远了。

    这是GY-521模块:

    鉴于大部分同学使用的可能都是这个模块,就在说明一下 这个模块的内部电路图把。

    这是进一步的封装的姿态解算模块:

    这才是真正的MPU6050:

    下面开始介绍重点,关于MPU6050的我的一些理解:

    首先要知道6050的内置模块。包括一个三轴MEMS(微机电系统(MEMS, Micro-Electro-Mechanical System))陀螺仪、三轴MEMS加速度计,一个数字运动处理引擎(DMP)、还有用于第三方数字传感器接口的辅助IIC端口,(GY-521上面的xda,xcl),常用于扩展磁力计,当辅助IIC接口连接到一个三轴磁力计上面时,6050能提供一个完整的九轴融合输出到其主IIC接口上。同时,6050内部还内置了一个可编程的低通滤波器,可用于传感器数据的滤波。

    要玩转6050,首先要明白 驱动IIC总线,然后初始化6050,最后从6050读取原始数据。其实就是读取几个寄存器。

    其实之前就用过IIC,但是当时只是随用随学,导致现在也找不到当时的程序了,虽然这种程序网上一找一大堆,但是不是自己写的,心里难免有点难受。所以以后在平时学习的基础上一定要注重积累,积累自己常用的代码库,方便以后开发。

    其次,我们手上的芯片可能不是单纯的MPU6050芯片,可能是别人包装好的GY-521模块,这个模块是对原始MPU6050进行了外围电路的设计,使之成为一个可以拿来就用的模块。在某宝上面售价基本在10RMB左右。当然,还有更厉害的,网上卖的直接 一个成品,模块上面集成了32位高速处理器,直接输出的就是经过滤波和姿态融合之后的数据。价格也基本在100RMB左右。更加方便。但是相应的可扩展性也就更差。
    我们要做的就是在GY-521的基础上,自己通过单片机编程,来输出自己获取的姿态数据。进而可以用在自己的小制作上面。

    1、关于DMP,知乎上有个网友的回答感觉很好:

    【DMP就是指 MPU6050内部集成的处理单元,可以直接运算出四元数和姿态,而不再需要另外进行数学运算。DMP的使用大大简化了代码设计。DMP是数字运动处理器的缩写,顾名思义mpu9150(mpu6050)并不单单是一款传感器,其内部还包含了可以独立完成姿态解算算法的处理单元。如在设计中使用DMP来实现传感器融合算法优势很明显。首先,invensense官方提供的姿态解算算法应该比一般的小白要可靠的多。其次,由DMP实现姿态解算算法将单片机从算法处理的压力中解放出来,单片机所要做的是等待DMP解算完成后产生的外部中断,在外部中断里去读取姿态解算的结果。这样单片机有大量的时间来处理其他任务,提高了系统的实时性。经过 DMP你就可以得到四元数,四元数就是4个数,可以表征姿态,经过几个数学公式之后就可以的出姿态,姿态包括pitch,roll,yaw。综上,dmp之后直接出结果,可以直接用,当然你如果有特殊需要自己还要加滤波也没有问题。】

    使用DMP虽然能减轻很多单片机的负载和压力,但是听说DMP的参考平面有点蛋疼。他解算出来的姿态角是以上电时的平面为基准平面的。也就是说每次上电都要把装置摆到绝对水平。但这有点困难。

    2、加速度传感器是干嘛用的?
    总而言这,加速度传感器,其实是力传感器。用来检查上下左右前后哪几个面都受了多少力(包括重力),然后计算角度。

    3、陀螺仪是干嘛用的?
    简而言之,陀螺仪就是角速度检测仪。比如,一块板,以X轴为轴心,在一秒钟的时间转到了90度,那么它在X轴上的角速度就是 90度/秒 (DPS, 角速度单位,Degree Per Second的缩写°/S ,体现了转动的快慢)

    4、MPU6050分辨率是多少?
    3轴加速度 和3轴陀螺仪分别用了3个16位的ADC,加速度有3个16位ADC,其中每个轴使用了一个。也是说,每个轴输出的数据,是2^16 也就是 -32768 —- +32768。陀螺仪也是一样。

    单位换算 :
    上面说的-32768 — +32768 ,那么这个数字到底代表了什么呢?比如陀螺仪 32768 到底是指角速度达到多少度/秒 ?
    这个其实是根据MPU6050设置的量程来决定的,量程不一样,32768代表的值就不一样。

    分别设置为,250度/秒 , 2g

    按陀螺仪来说,MPU6050 有四个量程可选:
    ±250,±500,±1000,±2000 度/s
    比方说,设置了是 ±250 , 那么-32768 —- +32768 就代表了 -250 —- +250 。此时它的LSB(拉傻B,最低有效位) 是 32768 / 250 = 131 LSB/(度/s)。如果你一秒内旋转的角度超过了250度,那么检测出来的值仍然是250BPS,此时对于你的要求来说这个值是不准确的。

    -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

    下面是程序的讲解了:

    //------------初始化---------------
    unsigned char mpu6050_init(void)
    {  
    
        if(iic_read_addr_byte(WHO_AM_I) != 0X68) //检查6050是否正常
        {
           return 0;
        }
    
        iic_write_addr_byte(PWR_MGMT_1, 0x00);  //电源管理,解除休眠状态
        delay_1ms(1);  
        iic_write_addr_byte(SMPLRT_DIV, 0x00);  //陀螺仪采样率,典型值0x00,陀螺仪输出频率为8KHZ,
        delay_1ms(1); 
        iic_write_addr_byte(CONFIG, 0x00);   //配置加速度计和陀螺仪的滤波器,加速度计始终1KHZ输出率,滤波的话。陀螺仪1KHZ输出,通过datasheet可以看到,滤波就是滤掉了250hz的频率分量
        delay_1ms(1); 
        iic_write_addr_byte(GYRO_CONFIG, 0x18);  //陀螺仪自检及测量范围,典型值0x18,不自检,2000dps ,LSB:16.4
        delay_1ms(1); 
        iic_write_addr_byte(ACCEL_CONFIG, 0x1F);  //加速度计自检、测量范围,典型值0x1f,不自检,16G    ,LSB:2048
        
        return 1;  
    
    }
    
    //------读取三轴加速度计的原始值,16位二进制数------
    void mpu6050_read_acc(int* acc_data)    //16位adc,
    {
        unsigned char buff[6];
        unsigned char i;
    
        for(i = 0;i<6;i++)
        {
            buff[i] = iic_read_addr_byte(ACC_ADDR + i);
        }
    
        acc_data[0] = (buff[0] << 8) | buff[1];//x轴高8位左移8位或上低8位
        acc_data[1] = (buff[2] << 8) | buff[3];
        acc_data[2] = (buff[4] << 8) | buff[5];
    
    }
    
    //------读取三轴陀螺仪原始值-----
    void mpu6050_read_gyro(int* gyro_data)
    {
        unsigned char buff[6];
        unsigned char i;
    
        for(i = 0;i<6;i++)
        {
            buff[i] = iic_read_addr_byte(GYRO_ADDR + i);
        }
    
        gyro_data[0] = (buff[0] << 8) | buff[1];//x轴高8位左移8位或上低8位
        gyro_data[1] = (buff[2] << 8) | buff[3];
        gyro_data[2] = (buff[4] << 8) | buff[5];
    }

    然后我们可以通过串口助手查看6050输出的数据是否正确(注意这时串口波特率的配置)

    ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

    当已经能正确输出6050数据的时候,就可以准备使用匿名上位机来显示波形了。因为后面需要进行滤波,单纯的看这些数据的话,估计一天下来,眼都要瞎了吧。所以为了保护好自己的眼睛,我们要 学会使用上位机来帮助我们。注意,这里不是打广告,具体使用什么上位机自己可以自由选择。

    首先,我也不知道该怎么用上位机来显示波形,于是我请教了一位大佬,大佬和我说了这句话:

    把传感器的数据按照地面站协议打包用串口发给电脑就行了

    这句话得增大加粗来显示,因为这是接下来的核心思想。下面就是匿名的通信协议。打包,最后的函数是调用串口的发送函数,进行发送。

    /**********为了匿名四轴上位机的协议定义的变量****************************/
    //cup为小端模式存储,也就是在存储的时候,低位被存在0字节,高位在1字节
    #define BYTE0(dwTemp)       (*(char *)(&dwTemp))     //取出int型变量的低字节
    #define BYTE1(dwTemp)       (*((char *)(&dwTemp) + 1))     //    取存储在此变量下一内存字节的内容,高字节
    #define BYTE2(dwTemp)       (*((char *)(&dwTemp) + 2))
    #define BYTE3(dwTemp)       (*((char *)(&dwTemp) + 3))
    
    
    /* 
    发送给上位机的数据帧定义 
    @桢头--功能字--长度--数据(一个或多个,具体看协议说明)-校验
    @前2个字节为帧头0xAAAA 
    @第3个字节为帧ID,应设置为0xF1~0xFA中的一个 
    @第4个字节为报文数据长度(dlc) 
    @第5个字节开始到第5+dlc-1个字节为要传输的数据内容段,每个数据场为高字节在前,地字节在后 
    @第5+dlc个字节为CheckSum,为第1个字节到第5+dlc-1个字节所有字节的值相加后,保留结果的低八位作为CheckSum 
    */  
    void data_to_computer(int* acc_data,int* gyro_data)
    {
        unsigned char  data_to_send[23] = {0};
        unsigned char i = 0;
        unsigned char cnt = 0;
        unsigned char sum = 0;
        
        data_to_send[cnt++]=0xAA;     //帧头:AAAA
        data_to_send[cnt++]=0xAA;
        data_to_send[cnt++]=0x02;     //功能字:OXF2
        data_to_send[cnt++]=0;         //需要发送数据的字节数,暂时给0,后面在赋值。
    
        data_to_send[cnt++] = BYTE1(acc_data[0]);//取data[0]数据的高字节,
        data_to_send[cnt++] = BYTE0(acc_data[0]);
        data_to_send[cnt++] = BYTE1(acc_data[1]);
        data_to_send[cnt++] = BYTE0(acc_data[1]);
        data_to_send[cnt++] = BYTE1(acc_data[2]);
        data_to_send[cnt++] = BYTE0(acc_data[2]);
    
        data_to_send[cnt++] = BYTE1(gyro_data[0]);//取data[0]数据的高字节,
        data_to_send[cnt++] = BYTE0(gyro_data[0]);
        data_to_send[cnt++] = BYTE1(gyro_data[1]);
        data_to_send[cnt++] = BYTE0(gyro_data[1]);
        data_to_send[cnt++] = BYTE1(gyro_data[2]);
        data_to_send[cnt++] = BYTE0(gyro_data[2]);
    
        data_to_send[cnt++]=0;
        data_to_send[cnt++]=0;
        data_to_send[cnt++]=0;
        data_to_send[cnt++]=0;
        data_to_send[cnt++]=0;
        data_to_send[cnt++]=0;
    
        data_to_send[3] = cnt-4;//计算总数据的字节数。
    
        for(i=0;i<cnt;i++) //对于for语句,当不写大括号的时候,只执行到下面第一个分号结束。
        {
            sum+=data_to_send[i];
        }
        data_to_send[cnt++] = sum;    //计算校验位
        uart_send_string(data_to_send,cnt);
    
    }

    但是呢,虽然能出波形,但是慢的像**一样。。当使用11.0592晶振的单片机时,能与匿名上位机上面的波特率匹配的最高只有9600波特率了,虽然单片机还能产生更高的波特率,但是匿名上位机上面没有匹配的。这就很尴尬,使用9600波特率的时候,波形出来的很慢,不容易观察。

    查资料才知道,51单片机使用11.0592晶振的时候,最大只能产生28800波特率,离上位机中基本的115200还差了近5倍。

    查资料中。。。。。。。。。。。。。。。。。。。。。。。

    哦,原来还可以通过定时器2来作为波特率的通信发生器。但是我到现在还从来没用过定时器2呢。

    如何使用单片机的定时器2,我参考了这下面三篇文章(都很好):
    https://www.cnblogs.com/perfy/p/3782166.html
    https://www.cnblogs.com/CodeHXH/archive/2011/05/25/2057242.html
    http://blog.csdn.net/lile777/article/details/45719283

    看完之后,发现还 方便了很多呢。

        //使用定时器2做波特率发生器,@11.0292M
        SCON = 0X50;
        TH2 = RCAP2H = 0XFF;//baud : 115200
        TL2 = RCAP2L = 0XFD;
        T2CON = 0X34;//设置定时器 2 工作模式,波特率发生器
        IE = 0X90;//IE中断允许寄存器,0x90是开总中断和串口中断。

    通过这几行代码,你就可以使用定时器2来产生波特率了。搭配11.0592M 晶振,最大可以产生110592bps的波特率。

    啊,自从使用了定时器2,腰不酸,腿不疼了,上楼也有劲儿了..........................

    2017.12.4 下午
    终于在stc89c52rc上面使用定时器2,产生115200波特率的传输速率,将mpu6050获取的原始数据通过匿名上位机折线图显示了出来。
    虽然目前还不是很懂定时器2的具体用法和串口怎么和上位机通信的原理。但是目前第一步做到了。后面就是学习一些滤波算法,并初步在51单片机上面实现,对这些嘈杂的原始数据进行滤波,和四元数姿态解算。

    一点教训:当单片机电压过高的话,单片机不会工作,但是在8V以内,单片机也没有烧。
    同样的程序,同样的操作,一开始早不到合适的电源,只能使用8V电源供电,但是单片机不工作,但是也没有烧掉,这是单片机的自我保护原理吗?还是其他?

    这篇博客展示了如何将6050的数据发送给匿名上位机通过波形显示:
    http://bbs.elecfans.com/jishu_536667_1_5.html

    匿名上位机使用介绍:(但是我还没看。)
    http://v.youku.com/v_show/id_XNTkzNDkxNTU2.html

  • 相关阅读:
    PAT (Advanced Level) 1010. Radix (25)
    PAT (Advanced Level) 1009. Product of Polynomials (25)
    PAT (Advanced Level) 1008. Elevator (20)
    PAT (Advanced Level) 1007. Maximum Subsequence Sum (25)
    PAT (Advanced Level) 1006. Sign In and Sign Out (25)
    PAT (Advanced Level) 1005. Spell It Right (20)
    PAT (Advanced Level) 1004. Counting Leaves (30)
    PAT (Advanced Level) 1001. A+B Format (20)
    PAT (Advanced Level) 1002. A+B for Polynomials (25)
    PAT (Advanced Level) 1003. Emergency (25)
  • 原文地址:https://www.cnblogs.com/qsyll0916/p/7978381.html
Copyright © 2011-2022 走看看