找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 1054|回复: 0
打印 上一主题 下一主题
收起左侧

MPU6050用51单片机角度融合怎么计算?

[复制链接]
跳转到指定楼层
楼主
ID:954170 发表于 2021-10-19 17:08 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
50黑币
我把mpu6050的计算程序移植到51单片机上面,程序没有报错,却计算不出角度,烦请指导一下,或给出正确的计算函数程序如下:
  1. #include <REG52.H>
  2. #include <math.h>    //Keil library
  3. #include <stdio.h>   //Keil library
  4. #include <INTRINS.H>
  5. typedef unsigned char  uchar;
  6. typedef unsigned short ushort;
  7. typedef unsigned int   uint;
  8. typedef unsigned char  u8;
  9. //--声明一些需要使用的变量--//
  10. uchar MPU6050_DATA[14];
  11. int Gyro_x,Gyro_y,Gyro_z;
  12. int Gyro_angle_x=0,Gyro_angle_y=0,Gyro_angle_z=0;
  13. int Acc_x,Acc_y,Acc_z;
  14. int Temp;

  15. int                xdata g_x=0,g_y=0,g_z=0;                                        //陀螺仪矫正参数
  16. float        xdata a_x=0,a_y=0;                                                        //角度矫正参数
  17. float        data  AngleX=0,AngleY=0;                                        //四元数解算出的欧拉角
  18. float        xdata Angle_gx=0,Angle_gy=0,Angle_gz=0;                //由角速度计算的角速率(角度制)
  19. float        xdata Angle_ax=0,Angle_ay=0,Angle_az=0;                //由加速度计算的加速度(弧度制)
  20. //****************************************
  21. // 定义51单片机端口
  22. //****************************************
  23. #define DataPort P0                //LCD1602数据端口
  24. sbit    SCL=P1^1;                        //IIC时钟引脚定义
  25. sbit    SDA=P1^0;                        //IIC数据引脚定义
  26. sbit    LCM_RS=P3^5;                //LCD1602命令端口
  27. sbit    LCM_RW=P3^6;                //LCD1602命令端口
  28. sbit    LCM_EN=P3^4;                //LCD1602命令端口

  29. //****************************************
  30. // 定义MPU6050内部地址
  31. //****************************************
  32. #define        SMPLRT_DIV                0x19        //陀螺仪采样率,典型值:0x07(125Hz)
  33. #define        CONFIG                        0x1A        //低通滤波频率,典型值:0x06(5Hz)
  34. #define        GYRO_CONFIG                0x1B        //陀螺仪自检及测量范围,典型值:0x18(不自检,2000deg/s)
  35. #define        ACCEL_CONFIG        0x1C        //加速计自检、测量范围及高通滤波频率,典型值:0x01(不自检,2G,5Hz)
  36. #define        ACCEL_XOUT_H        0x3B
  37. #define        ACCEL_XOUT_L        0x3C
  38. #define        ACCEL_YOUT_H        0x3D
  39. #define        ACCEL_YOUT_L        0x3E
  40. #define        ACCEL_ZOUT_H        0x3F
  41. #define        ACCEL_ZOUT_L        0x40
  42. #define        TEMP_OUT_H                0x41
  43. #define        TEMP_OUT_L                0x42
  44. #define        GYRO_XOUT_H                0x43
  45. #define        GYRO_XOUT_L                0x44
  46. #define        GYRO_YOUT_H                0x45
  47. #define        GYRO_YOUT_L                0x46
  48. #define        GYRO_ZOUT_H                0x47
  49. #define        GYRO_ZOUT_L                0x48
  50. #define        PWR_MGMT_1                0x6B        //电源管理,典型值:0x00(正常启用)
  51. #define        WHO_AM_I                        0x75        //IIC地址寄存器(默认数值0x68,只读)
  52. #define        SlaveAddress        0xD0        //IIC写入时的地址字节数据,+1为读取
  53. //****************************************
  54. //定义类型及变量
  55. //****************************************
  56. uchar dis[4];                                                        //显示数字(-511至512)的字符数组
  57. int        dis_data;                                                //变量
  58. //int        Temperature,Temp_h,Temp_l;        //温度及高低位数据
  59. //****************************************
  60. //函数声明
  61. //****************************************
  62. void  delay(unsigned int k);                                                                                //延时
  63. //LCD相关函数
  64. void  InitLcd();                                                                                                                //初始化lcd1602
  65. void  lcd_printf(uchar *s,int temp_data);
  66. void  WriteDataLCM(uchar dataW);                                                                        //LCD数据
  67. void  WriteCommandLCM(uchar CMD,uchar Attribc);                                //LCD指令
  68. void  DisplayOneChar(uchar X,uchar Y,uchar DData);                        //显示一个字符
  69. void  DisplayListChar(uchar X,uchar Y,uchar *DData,L);        //显示字符串
  70. //MPU6050操作函数
  71. void  InitMPU6050();                                                                                                        //初始化MPU6050
  72. void  Delay5us();
  73. void  I2C_Start();
  74. void  I2C_Stop();
  75. void  I2C_SendACK(bit ack);
  76. bit   I2C_RecvACK();
  77. void  I2C_SendByte(uchar dat);
  78. uchar I2C_RecvByte();
  79. void  I2C_ReadPage();
  80. void  I2C_WritePage();
  81. void  display_ACCEL_x();
  82. void  display_ACCEL_y();
  83. void  display_ACCEL_z();
  84. uchar Single_ReadI2C(uchar REG_Address);                                                //读取I2C数据
  85. void  Single_WriteI2C(uchar REG_Address,uchar REG_data);//向I2C写入数据
  86. void Delay2us(void)
  87. {
  88.         u8 i;
  89.         i = 11;                // @24MHZ, 6 + 33 = 39T, 1.625us
  90.         while (--i);
  91. }
  92. void Read_MPU6050(u8 *buf)
  93. {
  94.         u8        i;
  95.         
  96.         I2C_Start();                  //起始信号
  97.         I2C_SendByte(SlaveAddress);   //发送设备地址+写信号
  98.         I2C_SendByte(ACCEL_XOUT_H);    //内部寄存器地址,
  99.         I2C_Start();                   //起始信号
  100.         I2C_SendByte(SlaveAddress+1);  //发送设备地址+读信号
  101.         for(i=0; i<13; i++)
  102.         {
  103.                 buf[i] = I2C_RecvByte();       //读出寄存器数据
  104.                 SDA = 0;                    //写应答信号
  105.                 SCL = 1;                    //拉高时钟线
  106.                 Delay2us();                 //延时
  107.                 SCL = 0;                    //拉低时钟线
  108.                 Delay2us();                 //延时
  109.         }
  110.                 buf[i] = I2C_RecvByte();        //最后一个字节
  111.                 SDA = 1;                    //写非应答信号
  112.                 SCL = 1;                    //拉高时钟线
  113.                 Delay2us();                 //延时
  114.                 SCL = 0;                    //拉低时钟线
  115.                 Delay2us();                 //延时
  116.         I2C_Stop();                    //停止信号
  117. }



  118. //****************************************
  119. //整数转字符串
  120. //****************************************

  121. void lcd_printf(uchar *s,int temp_data)
  122. {
  123.         if(temp_data<0)
  124.         {
  125.                 temp_data=-temp_data;
  126.                 *s='-';
  127.         }
  128.         else *s=' ';
  129.         *++s =temp_data/100+0x30;
  130.         temp_data=temp_data%100;     //取余运算
  131.         *++s =temp_data/10+0x30;
  132.         temp_data=temp_data%10;      //取余运算
  133.         *++s =temp_data+0x30;
  134. }
  135. //****************************************
  136. //延时
  137. //****************************************
  138. void delay(unsigned int k)
  139. {
  140.         unsigned int i,j;
  141.         for(i=0;i<k;i++)
  142.         {
  143.                 for(j=0;j<121;j++);
  144.         }
  145. }
  146. //****************************************
  147. //LCD1602初始化
  148. //****************************************
  149. void InitLcd()
  150. {
  151.         WriteCommandLCM(0x38,1);
  152.         WriteCommandLCM(0x08,1);
  153.         WriteCommandLCM(0x01,1);
  154.         WriteCommandLCM(0x06,1);
  155.         WriteCommandLCM(0x0c,1);
  156.         DisplayOneChar(0,0,'A');
  157.         DisplayOneChar(0,1,'G');
  158. }
  159. //****************************************
  160. //LCD1602写允许
  161. //****************************************
  162. void WaitForEnable(void)
  163. {
  164.         DataPort=0xff;
  165.         LCM_RS=0;LCM_RW=1;_nop_();
  166.         LCM_EN=1;_nop_();_nop_();
  167.         while(DataPort&0x80);
  168.         LCM_EN=0;
  169. }
  170. //****************************************
  171. //LCD1602写入命令
  172. //****************************************
  173. void WriteCommandLCM(uchar CMD,uchar Attribc)
  174. {
  175.         if(Attribc)WaitForEnable();
  176.         LCM_RS=0;LCM_RW=0;_nop_();
  177.         DataPort=CMD;_nop_();
  178.         LCM_EN=1;_nop_();_nop_();LCM_EN=0;
  179. }
  180. //****************************************
  181. //LCD1602写入数据
  182. //****************************************
  183. void WriteDataLCM(uchar dataW)
  184. {
  185.         WaitForEnable();
  186.         LCM_RS=1;LCM_RW=0;_nop_();
  187.         DataPort=dataW;_nop_();
  188.         LCM_EN=1;_nop_();_nop_();LCM_EN=0;
  189. }
  190. //****************************************
  191. //LCD1602写入一个字符
  192. //****************************************
  193. void DisplayOneChar(uchar X,uchar Y,uchar DData)
  194. {
  195.         Y&=1;
  196.         X&=15;
  197.         if(Y)X|=0x40;
  198.         X|=0x80;
  199.         WriteCommandLCM(X,0);
  200.         WriteDataLCM(DData);
  201. }
  202. //****************************************
  203. //LCD1602显示字符串
  204. //****************************************
  205. void DisplayListChar(uchar X,uchar Y,uchar *DData,L)
  206. {
  207.         uchar ListLength=0;
  208.         Y&=0x1;
  209.         X&=0xF;
  210.         while(L--)
  211.         {
  212.                 DisplayOneChar(X,Y,DData[ListLength]);
  213.                 ListLength++;
  214.                 X++;
  215.         }
  216. }
  217. //**************************************
  218. //延时5微秒(STC90C52RC@12M)
  219. //不同的工作环境,需要调整此函数
  220. //当改用1T的MCU时,请调整此延时函数
  221. //**************************************
  222. void Delay5us()
  223. {
  224.         _nop_();_nop_();_nop_();_nop_();
  225.         _nop_();_nop_();_nop_();_nop_();
  226.         _nop_();_nop_();_nop_();_nop_();
  227.         _nop_();_nop_();_nop_();_nop_();
  228.         _nop_();_nop_();_nop_();_nop_();
  229.         _nop_();_nop_();_nop_();_nop_();
  230. }
  231. //**************************************
  232. //I2C起始信号
  233. //**************************************
  234. void I2C_Start()
  235. {
  236.             Delay2us();                 //延时
  237.         Delay2us();                 //延时
  238.         SDA = 1;                    //拉高数据线
  239.         SCL = 1;                    //拉高时钟线
  240.         Delay2us();                 //延时
  241.         Delay2us();                 //延时
  242.         SDA = 0;                    //产生下降沿
  243.         Delay2us();                 //延时
  244.         SCL = 0;                    //拉低时钟线
  245.         Delay2us();                 //延时
  246. }
  247. //**************************************
  248. //I2C停止信号
  249. //**************************************
  250. void I2C_Stop()
  251. {
  252.     Delay2us();                 //延时
  253.     SDA = 0;                    //拉低数据线
  254.     SCL = 1;                    //拉高时钟线
  255.         Delay2us();                 //延时
  256.     SDA = 1;                    //产生上升沿
  257.         Delay2us();                 //延时
  258. }
  259. //**************************************
  260. //I2C发送应答信号
  261. //入口参数:ack (0:ACK 1:NAK)
  262. //**************************************
  263. void I2C_SendACK(bit ack)
  264. {
  265.     SDA = ack;                  //写应答信号
  266.     SCL = 1;                    //拉高时钟线
  267.     Delay5us();                 //延时
  268.     SCL = 0;                    //拉低时钟线
  269.     Delay5us();                 //延时
  270. }
  271. //**************************************
  272. //I2C接收应答信号
  273. //**************************************
  274. bit I2C_RecvACK(void)
  275. {
  276.     SDA = 1;
  277.         Delay2us();                 //延时
  278.     SCL = 1;                    //拉高时钟线
  279.     Delay2us();                 //延时
  280.     CY = SDA;                   //读应答信号
  281.     SCL = 0;                    //拉低时钟线
  282.     Delay2us();                 //延时
  283.     return CY;
  284. }
  285. //**************************************
  286. //向I2C总线发送一个字节数据
  287. //**************************************
  288. void I2C_SendByte(u8 dat)
  289. {
  290.     u8 i;
  291.     for (i=0; i<8; i++)         //8位计数器
  292.     {
  293.         dat <<= 1;              //移出数据的最高位
  294.         SDA = CY;               //送数据口
  295.         SCL = 1;                //拉高时钟线
  296.         Delay2us();             //延时
  297.         SCL = 0;                //拉低时钟线
  298.         Delay2us();             //延时
  299.     }
  300.     I2C_RecvACK();
  301. }
  302. //**************************************
  303. //从I2C总线接收一个字节数据
  304. //**************************************
  305. uchar I2C_RecvByte()
  306. {
  307.     u8 i;
  308.     u8 dat = 0;
  309.     SDA = 1;                    //使能内部上拉,准备读取数据,
  310.     for (i=0; i<8; i++)         //8位计数器
  311.     {
  312.         dat <<= 1;
  313.         SCL = 1;                //拉高时钟线
  314.         Delay2us();             //延时
  315.         dat |= SDA;             //读数据
  316.         SCL = 0;                //拉低时钟线
  317.         Delay2us();             //延时
  318.     }
  319.     return dat;
  320. }
  321. //**************************************
  322. //向I2C设备写入一个字节数据
  323. //**************************************
  324. void Single_WriteI2C(u8 REG_Address,u8 REG_data)
  325. {
  326.     I2C_Start();                  //起始信号
  327.     I2C_SendByte(SlaveAddress);   //发送设备地址+写信号
  328.     I2C_SendByte(REG_Address);    //内部寄存器地址,
  329.     I2C_SendByte(REG_data);       //内部寄存器数据,
  330.     I2C_Stop();                   //发送停止信号
  331. }
  332. //**************************************
  333. //从I2C设备读取一个字节数据
  334. //**************************************
  335. u8 Single_ReadI2C(u8 REG_Address)
  336. {
  337.         u8 REG_data;
  338.         I2C_Start();                   //起始信号
  339.         I2C_SendByte(SlaveAddress);    //发送设备地址+写信号
  340.         I2C_SendByte(REG_Address);     //发送存储单元地址,从0开始
  341.         I2C_Start();                   //起始信号
  342.         I2C_SendByte(SlaveAddress+1);  //发送设备地址+读信号
  343.         REG_data=I2C_RecvByte();       //读出寄存器数据
  344.         
  345.         SDA = 1;                    //写应答信号
  346.         SCL = 1;                    //拉高时钟线
  347.         Delay2us();                 //延时
  348.         SCL = 0;                    //拉低时钟线
  349.         Delay2us();                 //延时
  350.         
  351.         I2C_Stop();                    //停止信号
  352.         return REG_data;
  353. }
  354. //**************************************
  355. //初始化MPU6050
  356. //**************************************
  357. void InitMPU6050()
  358. {
  359.         Single_WriteI2C(PWR_MGMT_1, 0x00);        //解除休眠状态
  360.         Single_WriteI2C(SMPLRT_DIV, 0x07);
  361.         Single_WriteI2C(CONFIG, 0x04);
  362.         Single_WriteI2C(GYRO_CONFIG, 0x08);
  363.         Single_WriteI2C(ACCEL_CONFIG, 0x08);
  364. }
  365. //**************************************
  366. //合成数据
  367. //**************************************
  368. int GetData(uchar REG_Address)
  369. {
  370.         char H,L;
  371.         H=Single_ReadI2C(REG_Address);
  372.         L=Single_ReadI2C(REG_Address+1);
  373.         return (H<<8)+L;   //合成数据
  374. }
  375. //**************************************
  376. //在1602上显示10位数据
  377. //**************************************
  378. void Display10BitData(int value,uchar x,uchar y)
  379. {
  380.         value/=64;                                                        //转换为10位数据
  381.         lcd_printf(dis, value);                        //转换数据显示
  382.         DisplayListChar(x,y,dis,4);        //启始列,行,显示数组,显示长度
  383. }
  384. //*********************************************************************
  385. //****************角度计算*********************************************
  386. //*********************************************************************
  387. #define        pi                3.14159265f                           
  388. #define        Kp                0.8f                        
  389. #define        Ki                0.001f                        
  390. #define        halfT        0.004f           

  391. float idata q0=1,q1=0,q2=0,q3=0;   
  392. float idata exInt=0,eyInt=0,ezInt=0;  

  393. void IMUupdate(float gx, float gy, float gz, float ax, float ay, float az)
  394. {
  395.         float data norm;
  396.         float idata vx, vy, vz;
  397.         float idata ex, ey, ez;

  398.         norm = sqrt(ax*ax + ay*ay + az*az);        
  399.         ax = ax / norm;
  400.         ay = ay / norm;
  401.         az = az / norm;

  402.         
  403.         vx = 2*(q1*q3 - q0*q2);
  404.         vy = 2*(q0*q1 + q2*q3);
  405.         vz = q0*q0 - q1*q1 - q2*q2 + q3*q3 ;

  406.         ex = (ay*vz - az*vy) ;
  407.         ey = (az*vx - ax*vz) ;
  408.         ez = (ax*vy - ay*vx) ;

  409.         exInt = exInt + ex * Ki;
  410.         eyInt = eyInt + ey * Ki;
  411.         ezInt = ezInt + ez * Ki;

  412.         gx = gx + Kp*ex + exInt;
  413.         gy = gy + Kp*ey + eyInt;
  414.         gz = gz + Kp*ez + ezInt;

  415.         q0 = q0 + (-q1*gx - q2*gy - q3*gz) * halfT;
  416.         q1 = q1 + ( q0*gx + q2*gz - q3*gy) * halfT;
  417.         q2 = q2 + ( q0*gy - q1*gz + q3*gx) * halfT;
  418.         q3 = q3 + ( q0*gz + q1*gy - q2*gx) * halfT;

  419.         norm = sqrt(q0*q0 + q1*q1 + q2*q2 + q3*q3);
  420.         q0 = q0 / norm;
  421.         q1 = q1 / norm;
  422.         q2 = q2 / norm;
  423.         q3 = q3 / norm;

  424.         AngleX = asin(2*(q0*q2 - q1*q3 )) * 57.2957795f;
  425.         AngleY = asin(2*(q0*q1 + q2*q3 )) * 57.2957795f;
  426. }
  427. //*********************************************************
  428. //主程序
  429. //*********************************************************
  430. void main()
  431. {               
  432.         delay(500);                //上电延时
  433.         InitLcd();                //液晶初始化
  434.         InitMPU6050();        //初始化MPU6050

  435.         delay(150);

  436.         while(1)
  437.         {                   Read_MPU6050(MPU6050_DATA);
  438.                 Acc_x = MPU6050_DATA[0]<<8|MPU6050_DATA[1];
  439.                 Acc_y = MPU6050_DATA[2]<<8|MPU6050_DATA[3];
  440.                 Acc_z = MPU6050_DATA[4]<<8|MPU6050_DATA[5];
  441.                 Temp  = MPU6050_DATA[6]<<8|MPU6050_DATA[7];
  442.                 Gyro_x = MPU6050_DATA[8]<<8|MPU6050_DATA[9];
  443.                 Gyro_y = MPU6050_DATA[10]<<8|MPU6050_DATA[11];
  444.                 Gyro_z = MPU6050_DATA[12]<<8|MPU6050_DATA[13];
  445.                
  446.                 Angle_ax = Acc_x/8192.0;
  447.                 Angle_ay = Acc_y/8192.0;
  448.                 Angle_az = Acc_z/8192.0;
  449.                
  450.                 Angle_gx = Gyro_x/65.5;
  451.                 Angle_gy = Gyro_y/65.5;
  452.                 Angle_gz = Gyro_z/65.5;
  453.                
  454.                 IMUupdate(Angle_gx*0.0174533f,Angle_gy*0.0174533f,Angle_gz*0.0174533f,Angle_ax,Angle_ay,Angle_az);
  455.                 Display10BitData(GetData(ACCEL_XOUT_H),2,0);        //显示X轴加速度
  456.                 Display10BitData(GetData(ACCEL_YOUT_H),7,0);        //显示Y轴加速度
  457.                 Display10BitData(GetData(ACCEL_ZOUT_H),12,0);        //显示Z轴加速度
  458.                 Display10BitData(AngleX,2,1);        //显示俯仰角
  459.                 Display10BitData(AngleY,7,1);        //显示横滚角
  460.                 Display10BitData(GetData(GYRO_ZOUT_H),12,1);        //未改,显示Z轴角速度
  461.                 delay(500);
  462.         }
  463. }
复制代码


分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享淘帖 顶 踩
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表