6050角度测试demo,stc8a。

实验目的
测试mpu6050的输出角度。
实验设备
mpu6050模块,
stc8a自制开发板,
oled模块
实验步骤
1、理解mpu6050的代码
2、测试代码;
3、将模块旋转,测试显示的角度。

1、详细代码主程序

//****************************************
// Update to MPU6050 by shinetop
// MCU: STC89C52
// 2012.3.1
// 功能: 显示加速度计和陀螺仪的10位原始数据
//****************************************
// GY-52 MPU3050 IIC测试程序
// 使用单片机STC89C51 
// 晶振:11.0592M
// 显示:LCD1602
// 编译环境 Keil uVision2
// 参考宏晶网站24c04通信程序
// 时间:2011年9月1日
// QQ:531389319
//****************************************
#include 	
#include 	
#include 	
#include     //Keil library  
#include    //Keil library	
#include 
typedef unsigned char  uchar;
typedef unsigned short ushort;
typedef unsigned int   uint;
//****************************************
// 定义51单片机端口
//****************************************


sbit    SCL=P2^0;			//IIC时钟引脚定义
sbit    SDA=P2^1;			//IIC数据引脚定义

//****************************************
// 定义MPU6050内部地址
//****************************************
#define	SMPLRT_DIV		0x19	//陀螺仪采样率,典型值:0x07(125Hz)
#define	CONFIG			0x1A	//低通滤波频率,典型值:0x06(5Hz)
#define	GYRO_CONFIG		0x1B	//陀螺仪自检及测量范围,典型值:0x18(不自检,2000deg/s)
#define	ACCEL_CONFIG	0x1C	//加速计自检、测量范围及高通滤波频率,典型值:0x01(不自检,2G,5Hz)
#define	ACCEL_XOUT_H	0x3B
#define	ACCEL_XOUT_L	0x3C
#define	ACCEL_YOUT_H	0x3D
#define	ACCEL_YOUT_L	0x3E
#define	ACCEL_ZOUT_H	0x3F
#define	ACCEL_ZOUT_L	0x40
#define	TEMP_OUT_H		0x41
#define	TEMP_OUT_L		0x42
#define	GYRO_XOUT_H		0x43
#define	GYRO_XOUT_L		0x44	
#define	GYRO_YOUT_H		0x45
#define	GYRO_YOUT_L		0x46
#define	GYRO_ZOUT_H		0x47
#define	GYRO_ZOUT_L		0x48
#define	PWR_MGMT_1		0x6B	//电源管理,典型值:0x00(正常启用)
#define	WHO_AM_I			0x75	//IIC地址寄存器(默认数值0x68,只读)
#define	SlaveAddress	0xD0	//IIC写入时的地址字节数据,+1为读取
//****************************************
//定义类型及变量
//****************************************
uchar dis[4];							//显示数字(-511至512)的字符数组
int	dis_data;						//变量
//int	Temperature,Temp_h,Temp_l;	//温度及高低位数据
//****************************************
//函数声明
//****************************************
void  delay(unsigned int k);										//延时
//LCD相关函数
void  InitLcd();														//初始化lcd1602
void  lcd_printf(uchar *s,int temp_data);
void  WriteDataLCM(uchar dataW);									//LCD数据
void  WriteCommandLCM(uchar CMD,uchar Attribc);				//LCD指令
void  DisplayOneChar(uchar X,uchar Y,uchar DData);			//显示一个字符
void  DisplayListChar(uchar X,uchar Y,uchar *DData,L);	//显示字符串
//MPU6050操作函数
void  InitMPU6050();													//初始化MPU6050
void  Delay5us();
void  I2C_Start();
void  I2C_Stop();
void  I2C_SendACK(bit ack);
bit   I2C_RecvACK();
void  I2C_SendByte(uchar dat);
uchar I2C_RecvByte();
void  I2C_ReadPage();
void  I2C_WritePage();
void  display_ACCEL_x();
void  display_ACCEL_y();
void  display_ACCEL_z();
uchar Single_ReadI2C(uchar REG_Address);						//读取I2C数据
void  Single_WriteI2C(uchar REG_Address,uchar REG_data);	//向I2C写入数据
//****************************************
//整数转字符串
//****************************************
void oled_printf(uchar *s,int temp_data)
{
     
	if(temp_data<0)
	{
     
		temp_data=-temp_data;
		*s='-';
	}
	else *s=' ';
	*++s =temp_data/100+0x30;
	temp_data=temp_data%100;     //取余运算
	*++s =temp_data/10+0x30;
	temp_data=temp_data%10;      //取余运算
	*++s =temp_data+0x30; 	
}
//****************************************
//延时
//****************************************
void delay(unsigned int k)	
{
     						
	unsigned int i,j;				
	for(i=0;i<k;i++)
	{
     			
		for(j=0;j<1200;j++);
	}						
}

		
		
				

//**************************************
//延时5微秒(STC90C52RC@12M)
//不同的工作环境,需要调整此函数
//当改用1T的MCU时,请调整此延时函数
//**************************************
void Delay5us()
{
     
	int i;
	for(i=0;i<20;i++){
     
	_nop_();_nop_();_nop_();_nop_();
	_nop_();_nop_();_nop_();_nop_();
	_nop_();_nop_();_nop_();_nop_();
	_nop_();_nop_();_nop_();_nop_();
	_nop_();_nop_();_nop_();_nop_();
	_nop_();_nop_();_nop_();_nop_();
	}
}
//**************************************
//I2C起始信号
//**************************************
void I2C_Start()
{
     
    SDA = 1;                    //拉高数据线
    SCL = 1;                    //拉高时钟线
    Delay5us();                 //延时
    SDA = 0;                    //产生下降沿
    Delay5us();                 //延时
    SCL = 0;                    //拉低时钟线
}
//**************************************
//I2C停止信号
//**************************************
void I2C_Stop()
{
     
    SDA = 0;                    //拉低数据线
    SCL = 1;                    //拉高时钟线
    Delay5us();                 //延时
    SDA = 1;                    //产生上升沿
    Delay5us();                 //延时
}
//**************************************
//I2C发送应答信号
//入口参数:ack (0:ACK 1:NAK)
//**************************************
void I2C_SendACK(bit ack)
{
     
    SDA = ack;                  //写应答信号
    SCL = 1;                    //拉高时钟线
    Delay5us();                 //延时
    SCL = 0;                    //拉低时钟线
    Delay5us();                 //延时
}
//**************************************
//I2C接收应答信号
//**************************************
bit I2C_RecvACK()
{
     
    SCL = 1;                    //拉高时钟线
    Delay5us();                 //延时
    CY = SDA;                   //读应答信号
    SCL = 0;                    //拉低时钟线
    Delay5us();                 //延时
    return CY;
}
//**************************************
//向I2C总线发送一个字节数据
//**************************************
void I2C_SendByte(uchar dat)
{
     
    uchar i;
    for (i=0; i<8; i++)         //8位计数器
    {
     
        dat <<= 1;              //移出数据的最高位
        SDA = CY;               //送数据口
        SCL = 1;                //拉高时钟线
        Delay5us();             //延时
        SCL = 0;                //拉低时钟线
        Delay5us();             //延时
    }
    I2C_RecvACK();
}
//**************************************
//从I2C总线接收一个字节数据
//**************************************
uchar I2C_RecvByte()
{
     
    uchar i;
    uchar dat = 0;
    SDA = 1;                    //使能内部上拉,准备读取数据,
    for (i=0; i<8; i++)         //8位计数器
    {
     
        dat <<= 1;
        SCL = 1;                //拉高时钟线
        Delay5us();             //延时
        dat |= SDA;             //读数据               
        SCL = 0;                //拉低时钟线
        Delay5us();             //延时
    }
    return dat;
}
//**************************************
//向I2C设备写入一个字节数据
//**************************************
void Single_WriteI2C(uchar REG_Address,uchar REG_data)
{
     
    I2C_Start();                  //起始信号
    I2C_SendByte(SlaveAddress);   //发送设备地址+写信号
    I2C_SendByte(REG_Address);    //内部寄存器地址,
    I2C_SendByte(REG_data);       //内部寄存器数据,
    I2C_Stop();                   //发送停止信号
}
//**************************************
//从I2C设备读取一个字节数据
//**************************************
uchar Single_ReadI2C(uchar REG_Address)
{
     
	uchar REG_data;
	I2C_Start();                   //起始信号
	I2C_SendByte(SlaveAddress);    //发送设备地址+写信号
	I2C_SendByte(REG_Address);     //发送存储单元地址,从0开始	
	I2C_Start();                   //起始信号
	I2C_SendByte(SlaveAddress+1);  //发送设备地址+读信号
	REG_data=I2C_RecvByte();       //读出寄存器数据
	I2C_SendACK(1);                //接收应答信号
	I2C_Stop();                    //停止信号
	return REG_data;
}
//**************************************
//初始化MPU6050
//**************************************
void InitMPU6050()
{
     
	Single_WriteI2C(PWR_MGMT_1, 0x00);	//解除休眠状态
	Single_WriteI2C(SMPLRT_DIV, 0x07);
	Single_WriteI2C(CONFIG, 0x06);
	Single_WriteI2C(GYRO_CONFIG, 0x18);
	Single_WriteI2C(ACCEL_CONFIG, 0x01);
}
//**************************************
//合成数据
//**************************************
int GetData(uchar REG_Address)
{
     
	char H,L;
	H=Single_ReadI2C(REG_Address);
	L=Single_ReadI2C(REG_Address+1);
	return (H<<8)+L;   //合成数据
}
//**************************************
//在oled上显示10位数据
//**************************************
void Display10BitData(int value,uchar x,uchar y)
{
     
	value/=64;							//转换为10位数据
	oled_printf(dis, value);			//转换数据显示
  OLED_ShowString(x,y, dis);	
}
//**************************************
//显示温度
//**************************************
//void display_temp()
//{ 
//	Temp_h=Single_ReadI2C(TEMP_OUT_H); //读取温度
//	Temp_l=Single_ReadI2C(TEMP_OUT_L); //读取温度
//	Temperature=Temp_h<<8|Temp_l;     //合成温度
//	Temperature = 35+ ((double) (Temperature + 13200)) / 280; // 计算出温度
//	lcd_printf(dis,Temperature);     //转换数据显示
//	DisplayListChar(11,1,dis,4);     //启始列,行,显示数组,显示位数
//}

========================================================================
 函数: int get_x_angle()
 描述: 获取三轴角速度,三轴加速度
 参数: gyro_id:数据ID,1:x角速度,2:y角速度,3:z角速度,4:x加速度,5:y加速度,6:z加速度.
 返回: 陀螺仪值.
 版本: V2.0, 2017-07-15
========================================================================
int Get_Gyro_Data(unsigned char gyro_id)
{
     
	switch(gyro_id)
	{
     
		case 1:	return GetData(ACCEL_XOUT_H);	break;
		case 2:	return GetData(ACCEL_YOUT_H);	break;
		case 3:	return GetData(ACCEL_ZOUT_H);	break;
		case 4:	return GetData(GYRO_XOUT_H) ;	break;
		case 5:	return GetData(GYRO_YOUT_H) ;	break;
		case 6:	return GetData(GYRO_ZOUT_H) ;	break;
	}
	return 0;
}

//
//========================================================================
// 函数: int MPU6050_Get_Angle(float x,float y,float z,u8 dir)
// 描述: 转化成与三个方向的夹角.
// 参数: x:x方向数据.
//		 y:y方向数据.
//		 z:z方向数据.
//		 dir:方向ID.
// 返回: 夹角角度值(放大10倍).
// 版本: V1.0, 2017-06-23
//========================================================================
int MPU6050_Get_Angle(int x,int y,int z,unsigned char dir)
{
     
	float xdata temp;
	float xdata res = 0;
	switch(dir)
	{
     
		case 0://与z轴的夹角
				temp = sqrt(((float)x*(float)x+(float)y*(float)y))/(float)z;
				res  = atan(temp);
		break;
		case 1://与x轴的夹角
				temp = (float)x/sqrt(((float)y*(float)y+(float)z*(float)z));
				res  = atan(temp);
		break;
		case 2://与y轴的夹角
				temp = (float)y/sqrt(((float)x*(float)x+(float)z*(float)z));
				res  = atan(temp);
		break;
	}
	return (int)(res*1800/3.1416);//弧度转换为角度,扩大10倍
}
//========================================================================
// int get_included_angle(unsigned dat)
// 描述: 获取角度或加速度
// 参数: angle_id:方向指示变量(1:X轴角度,2:Y轴角度,3:Z轴角度,4:X轴加速度,5:Y轴加速度,6:Z轴加速度).
// 返回: 夹角角度值(放大10倍).
// 版本: V1.0, 2017-06-23
//========================================================================
int MPU6050_Get_Data(unsigned angle_id)
{
     
	switch(angle_id)
	{
     
		case 1:return MPU6050_Get_Angle( Get_Gyro_Data(1), Get_Gyro_Data(2), Get_Gyro_Data(3), 1);break;
		case 2:return MPU6050_Get_Angle( Get_Gyro_Data(1), Get_Gyro_Data(2), Get_Gyro_Data(3), 2);break;
		case 3:return MPU6050_Get_Angle( Get_Gyro_Data(1), Get_Gyro_Data(2), Get_Gyro_Data(3), 0);break;
		case 4:return (int)((float)((float)Get_Gyro_Data(4)/16384)*9.8*100);
		case 5:return (int)((float)((float)Get_Gyro_Data(5)/16384)*9.8*100);
		case 6:return (int)((float)((float)Get_Gyro_Data(6)/16384)*9.8*100);
	}
	return 0;
}

//*********************************************************
//主程序
//*********************************************************
void main()
{
      
	int xvalue,yvalue,zvalue;
	unsigned char disbuf[9];
	float xx,yy,zz;
	delay(500);		//上电延时		
	P2M0=0x00;
	P2M1=0x00;
	OLED_Init();
	InitMPU6050();	//初始化MPU6050
	delay(150);
	OLED_Clear();
	while(1)
	{
     
// 		Display10BitData(GetData(ACCEL_XOUT_H),0,1);	//显示X轴加速度
// 		Display10BitData(GetData(ACCEL_YOUT_H),0,2);	//显示Y轴加速度
// 		Display10BitData(GetData(ACCEL_ZOUT_H),0,3);	//显示Z轴加速度
// 		Display10BitData(GetData(GYRO_XOUT_H),0,4);	//显示X轴角速度
// 		Display10BitData(GetData(GYRO_YOUT_H),0,5);	//显示Y轴角速度
// 		Display10BitData(GetData(GYRO_ZOUT_H),0,6);	//显示Z轴角速度
		delay(1000);
		xvalue = MPU6050_Get_Data(1);					//获取与x轴的夹角,角度被放大10倍
		yvalue = MPU6050_Get_Data(2);					//获取与x轴的夹角,角度被放大10倍
		zvalue = MPU6050_Get_Data(3);					//获取与x轴的夹角,角度被放大10倍
		xx=xvalue/10.0;
		yy=yvalue/10.0;
		zz=zvalue/10.0;
		sprintf(disbuf,"%.2f",xx);
		OLED_ShowString(0,0,disbuf);
		
		sprintf(disbuf,"%.2f",yy);
		OLED_ShowString(0,2,disbuf);	
		
		sprintf(disbuf,"%.2f",zz);
		OLED_ShowString(0,3,disbuf);	
		//OLED_ShowNum(0,1,xvalue,8,12);
		//OLED_ShowNum(0,2,yvalue,8,12);
		//OLED_ShowNum(0,3,zvalue,8,12);
	}
}

实验效果。
oled显示。
-8.6----------(绕着箭头y旋转显示)
-2.2----------(绕着箭头x旋转显示)
-8.9-----没效果。

实验结果分析:
第一行的结果是绕着箭头y旋转的时候,的角度,往一个方向为正,往一个方向为负方向
-90—0---90
第二行是绕着x箭头旋转的角度,也是
-90—0---90
出现的z轴角度不正确。网上说要配套地磁传感器,才能修正。

因此如果要绕着z轴水平的测量,买gy26的传感器就可以,但是价格比价贵,50元。

综上。
1、mpu6050可以测量两个方向的角度,而且如果算法不对,需要用一阶滤波,或者卡尔曼滤波算法进行滤波矫正。可以用来做两轮平衡小车的控制。
2、如果要测量航向之类的,买gy26模块比较合适。适合做小车的旋转方向。

你可能感兴趣的:(单片机)