datasheet

STM32驱动MPU6050(二)——软件实现

2019-01-09来源: eefocus关键字:STM32  驱动MPU6050  软件实现

软件实现将从下面三个部分来介绍:IIC通信;MPU6050数据读取;数据融合。


1. IIC通信

为了移植的方便,这里的 IIC 采用软件模拟的方式实现。关于 IIC 的基础知识介绍,可参考IIC专题(一)——基础知识准备。


下面以程序的实现过程,梳理一下 IIC 的通信时序。注:这里就采用正点原子的 mpu6050 的学习教程进行学习。


1.1 SDA 和SCL初始化


//初始化IIC

void MPU_IIC_Init(void)

{      

  GPIO_InitTypeDef  GPIO_InitStructure;


RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);//先使能外设IO PORTC时钟 


  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12|GPIO_Pin_11; // 端口配置

  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出

  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度为50MHz

  GPIO_Init(GPIOC, &GPIO_InitStructure); //根据设定参数初始化GPIO 


  GPIO_SetBits(GPIOC,GPIO_Pin_12|GPIO_Pin_11); //PB10,PB11 输出高

 

}


对于 SDA 输入输出的方向切换,这里直接通过配置寄存器实现:

//IO方向设置

#define MPU_SDA_IN()  {GPIOC->CRH&=0XFFFF0FFF;GPIOC->CRH|=8<<12;}

#define MPU_SDA_OUT() {GPIOC->CRH&=0XFFFF0FFF;GPIOC->CRH|=3<<12;}


也可通过配置库函数配置来实现:


void MPU_SDA_OUT(void)

{

GPIO_InitTypeDef  GPIO_InitStructure;


RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);


GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_11;

GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_Out_PP;

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

GPIO_Init(GPIOC, &GPIO_InitStructure);

}

void MPU_SDA_IN(void)

{

GPIO_InitTypeDef  GPIO_InitStructure;


RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);


GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_11;

GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_IN_FLOATING;

GPIO_Init(GPIOC, &GPIO_InitStructure);

}



1.2 IIC时序模拟实现

程序需要根据 IIC 的时序要求进行书写。注意:SDA和SCL需要接上拉电阻。


void MPU_IIC_Start(void)

{

MPU_SDA_OUT();     //sda线输出

MPU_IIC_SDA=1;     

MPU_IIC_SCL=1;

MPU_IIC_Delay();

  MPU_IIC_SDA=0;//START:when CLK is high,DATA change form high to low 

MPU_IIC_Delay();

MPU_IIC_SCL=0;//钳住I2C总线,准备发送或接收数据 

}   

//产生IIC停止信号

void MPU_IIC_Stop(void)

{

MPU_SDA_OUT();//sda线输出

MPU_IIC_SCL=0;

MPU_IIC_SDA=0;//STOP:when CLK is high DATA change form low to high

  MPU_IIC_Delay();

MPU_IIC_SCL=1;  

MPU_IIC_SDA=1;//发送I2C总线结束信号

MPU_IIC_Delay();    

}



//IIC发送一个字节

//返回从机有无应答

//1,有应答

//0,无应答   

void MPU_IIC_Send_Byte(u8 txd)

{                        

    u8 t;   


MPU_SDA_OUT();     

    MPU_IIC_SCL=0;//拉低时钟开始数据传输

    for(t=0;t<8;t++)

    {              

        MPU_IIC_SDA=(txd&0x80)>>7;

        txd<<=1;   

MPU_IIC_SCL=1;

MPU_IIC_Delay(); 

MPU_IIC_SCL=0;

MPU_IIC_Delay();

    }  

}     

//读1个字节,ack=1时,发送ACK,ack=0,发送nACK   

u8 MPU_IIC_Read_Byte(unsigned char ack)

{

unsigned char i,receive=0;

MPU_SDA_IN();//SDA设置为输入

    for(i=0;i<8;i++ )

{

        MPU_IIC_SCL=0; 

        MPU_IIC_Delay();

MPU_IIC_SCL=1;

        receive<<=1;

        if(MPU_READ_SDA)

receive++;   

MPU_IIC_Delay(); 

    }  

    if (!ack)

        MPU_IIC_NAck();//发送nACK

    else

        MPU_IIC_Ack(); //发送ACK   

    return receive;

}


//等待应答信号到来

//返回值:1,接收应答失败

//        0,接收应答成功

u8 MPU_IIC_Wait_Ack(void)

{

u8 ucErrTime=0;


MPU_SDA_IN();      //SDA设置为输入  

MPU_IIC_SDA=1;

MPU_IIC_Delay();    

MPU_IIC_SCL=1;

MPU_IIC_Delay();  

while(MPU_READ_SDA)

{

ucErrTime++;

if(ucErrTime>250)

{

MPU_IIC_Stop();

return 1;

}

}

MPU_IIC_SCL=0;//时钟输出0    

return 0;  

//产生ACK应答

void MPU_IIC_Ack(void)

{

MPU_IIC_SCL=0;

MPU_SDA_OUT();

MPU_IIC_SDA=0;

MPU_IIC_Delay();

MPU_IIC_SCL=1;

MPU_IIC_Delay();

MPU_IIC_SCL=0;

}

//不产生ACK应答     

void MPU_IIC_NAck(void)

{

MPU_IIC_SCL=0;

MPU_SDA_OUT();

MPU_IIC_SDA=1;

MPU_IIC_Delay();

MPU_IIC_SCL=1;

MPU_IIC_Delay();

MPU_IIC_SCL=0;

}


1.3 IIC 通信写入读取数据


//IIC写一个字节 

//reg:寄存器地址

//data:数据

//返回值:0,正常

//    其他,错误代码

u8 MPU_Write_Byte(u8 reg,u8 data)  

    MPU_IIC_Start(); 

MPU_IIC_Send_Byte((MPU_ADDR<<1)|0);//发送器件地址+写命令

if(MPU_IIC_Wait_Ack()) //等待应答

{

MPU_IIC_Stop();  

return 1;

}

    MPU_IIC_Send_Byte(reg); //写寄存器地址

    MPU_IIC_Wait_Ack(); //等待应答 

MPU_IIC_Send_Byte(data);//发送数据

if(MPU_IIC_Wait_Ack()) //等待ACK

{

MPU_IIC_Stop();  

return 1;  

}  

    MPU_IIC_Stop();  

return 0;

}


//IIC读一个字节 

//reg:寄存器地址 

//返回值:读到的数据

u8 MPU_Read_Byte(u8 reg)

{

u8 res;

    MPU_IIC_Start(); 

MPU_IIC_Send_Byte((MPU_ADDR<<1)|0);//发送器件地址+写命令

MPU_IIC_Wait_Ack(); //等待应答 

    MPU_IIC_Send_Byte(reg); //写寄存器地址

    MPU_IIC_Wait_Ack(); //等待应答

    MPU_IIC_Start();

MPU_IIC_Send_Byte((MPU_ADDR<<1)|1);//发送器件地址+读命令

    MPU_IIC_Wait_Ack(); //等待应答 

res=MPU_IIC_Read_Byte(0);//读取数据,发送nACK 

    MPU_IIC_Stop(); //产生一个停止条件 

return res;

}


//IIC连续写

//addr:器件地址 

//reg:寄存器地址

//len:写入长度

//buf:数据区

//返回值:0,正常

//    其他,错误代码

u8 MPU_Write_Len(u8 addr,u8 reg,u8 len,u8 *buf)

{

u8 i; 

    MPU_IIC_Start(); 

MPU_IIC_Send_Byte((addr<<1)|0);//发送器件地址+写命令

if(MPU_IIC_Wait_Ack()) //等待应答

{

MPU_IIC_Stop();  

return 1;

}

    MPU_IIC_Send_Byte(reg); //写寄存器地址

    MPU_IIC_Wait_Ack(); //等待应答

for(i=0;i

{

MPU_IIC_Send_Byte(buf[i]); //发送数据

if(MPU_IIC_Wait_Ack()) //等待ACK

{

MPU_IIC_Stop();  

return 1;  

}

}    

    MPU_IIC_Stop();  

return 0;

//IIC连续读

//addr:器件地址

//reg:要读取的寄存器地址

//len:要读取的长度

//buf:读取到的数据存储区

//返回值:0,正常

//    其他,错误代码

u8 MPU_Read_Len(u8 addr,u8 reg,u8 len,u8 *buf)

  MPU_IIC_Start(); 

MPU_IIC_Send_Byte((addr<<1)|0);//发送器件地址+写命令

if(MPU_IIC_Wait_Ack()) //等待应答

{

MPU_IIC_Stop();  

return 1;

}

    MPU_IIC_Send_Byte(reg); //写寄存器地址

    MPU_IIC_Wait_Ack(); //等待应答

    MPU_IIC_Start();

MPU_IIC_Send_Byte((addr<<1)|1);//发送器件地址+读命令

    MPU_IIC_Wait_Ack(); //等待应答 

while(len)

{

if(len==1)

*buf=MPU_IIC_Read_Byte(0);//读数据,发送nACK 

else 

*buf=MPU_IIC_Read_Byte(1); //读数据,发送ACK  

len--;

buf++; 

}    

    MPU_IIC_Stop(); //产生一个停止条件 

return 0;

}


至此,与 IIC 相关的代码已经实现了,上面的程序只要是 IIC 通信的设备,均可以使用。


2. MPU6050读取数据

首先需要进行设备初始化,为数据通信做好准备。


2.1 分为以下几个步骤:

(来自原子教程)


(1) 初始化 IIC 接口


MPU6050 采用 IIC 与 STM32F1 通信,需要先初始化与 MPU6050 连接的 SDA和 SCL 数据线。


(2)复位 MPU6050


这一步让 MPU6050 内部所有寄存器恢复默认值,通过对电源管理寄存器 1(0X6B)的bit7 写 1 实现。 复位后, 电源管理寄存器 1 恢复默认值(0X40),然后必须设置该寄存器为0X00,以唤醒 MPU6050,进入正常工作状态。


(3)设置角速度传感器(陀螺仪)和加速度传感器的满量程范围


这一步设置两个传感器的满量程范围(FSR),分别通过陀螺仪配置寄存器(0X1B)和加速度传感器配置寄存器(0X1C)设置。一般设置陀螺仪的满量程范围为±2000dps,加速度传感器的满量程范围为±2g。


(4)其他参数设置


需要配置的参数有:关闭中断、关闭 AUX IIC 接口、禁止 FIFO、设置陀螺仪采样率和设置数字低通滤波器(DLPF)等。


这里不用中断方式读取数据,关闭中断即可,也没用到 AUX IIC 接口外接其他传感器,同样的关闭这些接口。分别通过中断使能寄存器(0X38)和用户控制寄存器(0X6A)控制。 MPU6050 可以使用 FIFO 存储传感器数据,此处没有用到,关闭所有 FIFO 通道,这个通过 FIFO 使能寄存器(0X23)控制,默认都是 0(即禁止 FIFO),所以用默认值就可以了。陀螺仪采样率通过采样率分频寄存器(0X19)控制,这个采样率一般设置为 50 。数字低通滤波器(DLPF)则通过配置寄存器(0X1A)设置,一般设置 DLPF 为带宽的 1/2 。


(5)配置系统时钟源并使能角速度传感器和加速度传感器


系统时钟源同样是通过电源管理寄存器 1(0X1B)来设置,该寄存器的最低三位用于设置系统时钟源选择,默认值是 0(内部 8M RC 震荡),不过一般设置为 1,选择 x 轴陀螺 PLL 作为时钟源,以获得更高精度的时钟。同时,使能角速度传感器和加速度传感器,这两个操作通过电源管理寄存器 2(0X6C)来设置,设置对应位为 0 即可开启。


至此, MPU6050 的初始化就完成了,可以正常工作了(其他未设置的寄存器全部采用默认值即可),接下来,就可以通过读取相关寄存器,得到加速度传感器、角速度传感器和温度传感器的数据。


对于寄存器介绍,因内容繁多,此处省略,自行查看数据手册即可。



//初始化MPU6050

//返回值:0,成功

//    其他,错误代码

u8 MPU_Init(void)

u8 res; 


MPU_IIC_Init();//初始化IIC总线

MPU_Write_Byte(MPU_PWR_MGMT1_REG,0X80); //复位MPU6050

    delay_ms(100);

MPU_Write_Byte(MPU_PWR_MGMT1_REG,0X00); //唤醒MPU6050 

MPU_Set_Gyro_Fsr(3); //陀螺仪传感器,±2000dps

MPU_Set_Accel_Fsr(0); //加速度传感器,±2g

MPU_Set_Rate(50); //设置采样率50Hz

MPU_Write_Byte(MPU_INT_EN_REG,0X00); //关闭所有中断

MPU_Write_Byte(MPU_USER_CTRL_REG,0X00); //I2C主模式关闭

MPU_Write_Byte(MPU_FIFO_EN_REG,0X00); //关闭FIFO

MPU_Write_Byte(MPU_INTBP_CFG_REG,0X80); //INT引脚低电平有效

res=MPU_Read_Byte(MPU_DEVICE_ID_REG); 

if(res==MPU_ADDR)//器件ID正确

{

MPU_Write_Byte(MPU_PWR_MGMT1_REG,0X01); //设置CLKSEL,PLL X轴为参考

MPU_Write_Byte(MPU_PWR_MGMT2_REG,0X00); //加速度与陀螺仪都工作

MPU_Set_Rate(50); //设置采样率为50Hz

  }

else 

return 1;

return 0;

}


2.2 初始化用到的参数设置:


//设置MPU6050陀螺仪传感器满量程范围

//fsr:0,±250dps;1,±500dps;2,±1000dps;3,±2000dps

//返回值:0,设置成功

//    其他,设置失败 

u8 MPU_Set_Gyro_Fsr(u8 fsr)

{

return MPU_Write_Byte(MPU_GYRO_CFG_REG,fsr<<3);//设置陀螺仪满量程范围  

}

//设置MPU6050加速度传感器满量程范围

//fsr:0,±2g;1,±4g;2,±8g;3,±16g

//返回值:0,设置成功

//    其他,设置失败 

u8 MPU_Set_Accel_Fsr(u8 fsr)

{

return MPU_Write_Byte(MPU_ACCEL_CFG_REG,fsr<<3);//设置加速度传感器满量程范围  

}

//设置MPU6050的数字低通滤波器

//lpf:数字低通滤波频率(Hz)

//返回值:0,设置成功

//    其他,设置失败 

u8 MPU_Set_LPF(u16 lpf)

{

u8 data=0;

if(lpf>=188)data=1;

else if(lpf>=98)data=2;

else if(lpf>=42)data=3;

else if(lpf>=20)data=4;

else if(lpf>=10)data=5;

else data=6; 

return MPU_Write_Byte(MPU_CFG_REG,data);//设置数字低通滤波器  

}

//设置MPU6050的采样率(假定Fs=1KHz)

//rate:4~1000(Hz)

//返回值:0,设置成功

//    其他,设置失败 

u8 MPU_Set_Rate(u16 rate)

{

u8 data;

if(rate>1000)rate=1000;

if(rate<4)rate=4;

data=1000/rate-1;

data=MPU_Write_Byte(MPU_SAMPLE_RATE_REG,data); //设置数字低通滤波器

  return MPU_Set_LPF(rate/2); //自动设置LPF为采样率的一半

}


2.3 MPU6050 原始数据的读取:


//得到温度值

//返回值:温度值(扩大了100倍)

short MPU_Get_Temperature(void)

{

    u8 buf[2]; 

    short raw;

float temp;


MPU_Read_Len(MPU_ADDR,MPU_TEMP_OUTH_REG,2,buf); 

    raw=((u16)buf[0]<<8)|buf[1];  

    temp=36.53+((double)raw)/340;  

    return temp*100;;

}

//得到陀螺仪值(原始值)

//gx,gy,gz:陀螺仪x,y,z轴的原始读数(带符号)

//返回值:0,成功

//    其他,错误代码

u8 MPU_Get_Gyroscope(short *gx,short *gy,short *gz)

{

    u8 buf[6],res;  

res=MPU_Read_Len(MPU_ADDR,MPU_GYRO_XOUTH_REG,6,buf);

if(res==0)

{

*gx=((u16)buf[0]<<8)|buf[1];  

*gy=((u16)buf[2]<<8)|buf[3];  

*gz=((u16)buf[4]<<8)|buf[5];

}

    return res;;

}

//得到加速度值(原始值)

//gx,gy,gz:陀螺仪x,y,z轴的原始读数(带符号)

//返回值:0,成功

//    其他,错误代码

u8 MPU_Get_Accelerometer(short *ax,short *ay,short *az)

{

    u8 buf[6],res; 


res=MPU_Read_Len(MPU_ADDR,MPU_ACCEL_XOUTH_REG,6,buf);

if(res==0)

{

*ax=((u16)buf[0]<<8)|buf[1];  

*ay=((u16)buf[2]<<8)|buf[3];  

*az=((u16)buf[4]<<8)|buf[5];

}

    return res;;

}



至此,MPU6050的原始数据就读取完毕,但我们得到的这些数据无法直接使用,我们期望得到的是姿态数据,也就是欧拉角:航向角(yaw)、横滚角(roll)和俯仰角(pitch)。要得到欧拉角数据,需利用我们得到的原始数据,进行姿态融合解算,直接计算是比较复杂的,但MPU6050 自带了数字运动处理器 DMP,并且,InvenSense 提供了一个 MPU6050 的嵌入式运动驱动库,结合 MPU6050 的 DMP,可以将得到的原始数据,直接转换成四元数输出,而得到四元数之后,就可以很方便的计算出欧拉角,从而得到 yaw、 roll 和 pitch。


通过内置的 DMP,大大简化了代码的设计,且 MCU 不用进行姿态解算过程,大大降低了 MCU 的负担,从而有更多的时间去处理其他事件,提高系统实时性。


3. 数据融合

通过 MPU6050 的 DMP 输出的四元数是 q30 格式的,也就是浮点数放大了 2 的 30 次方倍。在换算成欧拉角之前,必须先将其转换为浮点数,也就是除以 2 的 30 次方,然后再进行计算,计算公式为:


q0=quat[0] / q30; //q30 格式转换为浮点数

q1=quat[1] / q30;

q2=quat[2] / q30;

q3=quat[3] / q30;

//计算得到俯仰角/横滚角/航向角

pitch=asin(-2 * q1 * q3 + 2 * q0* q2)* 57.3; //俯仰角

roll=atan2(2 * q2 * q3 + 2 * q0 * q1, -2 * q1 * q1 - 2 * q2* q2 + 1)* 57.3; //横滚角

yaw=atan2(2*(q1q2 + q0q3),q0q0+q1q1-q2q2-q3q3) * 57.3; //航向角


其中 quat[0]~ quat[3]是 MPU6050 的 DMP 解算后的四元数, q30 格式,所以要除以一个2 的 30 次方,其中 q30 是一个常量: 1073741824,即 2 的 30 次方,然后带入公式,计算出欧拉角。上述计算公式的 57.3 是弧度转换为角度,即 180/π,这样得到的结果就是以度(°)为单位的。


InvenSense 提供的 MPU6050 运动驱动库是基于 MSP430 的,需要将其移植一下,才可以用到 STM32F1 上面。


在这里插入图片描述


驱动库文件如图。重点就是两个 c 文件: inv_mpu.c 和 inv_mpu_dmp_motion_driver.c。


官方 DMP 驱动库移植起来,还是比较简单的,主要是实现这 4 个函数: **i2c_write,i2c_read, delay_ms 和 get_ms。**对应着我们实现的:

MPU_Write_Len,MPU_Read_Len,delay_ms(系统滴答实现),get_ms空函数替代。


3.1 添加的函数

DMP初始化:


//mpu6050,dmp初始化

//返回值:0,正常

//    其他,失败

u8 mpu_dmp_init(void)

{

u8 res=0;

MPU_IIC_Init(); //初始化IIC总线

if(mpu_init()==0) //初始化MPU6050

{  

res=mpu_set_sensors(INV_XYZ_GYRO|INV_XYZ_ACCEL);//设置所需要的传感器

if(res)return 1; 

res=mpu_configure_fifo(INV_XYZ_GYRO|INV_XYZ_ACCEL);//设置FIFO

if(res)return 2; 

res=mpu_set_sample_rate(DEFAULT_MPU_HZ); //设置采样率

if(res)return 3; 

res=dmp_load_motion_driver_firmware(); //加载dmp固件

if(res)return 4; 

res=dmp_set_orientation(inv_orientation_matrix_to_scalar(gyro_orientation));//设置陀螺仪方向

if(res)return 5; 

res=dmp_enable_feature(DMP_FEATURE_6X_LP_QUAT|DMP_FEATURE_TAP| //设置dmp功能

    DMP_FEATURE_ANDROID_ORIENT|DMP_FEATURE_SEND_RAW_ACCEL|DMP_FEATURE_SEND_CAL_GYRO|

    DMP_FEATURE_GYRO_CAL);

if(res)return 6; 

res=dmp_set_fifo_rate(DEFAULT_MPU_HZ); //设置DMP输出速率(最大不超过200Hz)

if(res)return 7;   

res=run_self_test(); //自检

if(res)return 8;    

res=mpu_set_dmp_state(1); //使能DMP

if(res)return 9;     

}else return 10;

return 0;

}



读取姿态解算后的数据:



//得到dmp处理后的数据(注意,本函数需要比较多堆栈,局部变量有点多)

//pitch:俯仰角 精度:0.1°   范围:-90.0°

//roll:横滚角  精度:0.1°   范围:-180.0°

//yaw:航向角   精度:0.1°   范围:-180.0°

//返回值:0,正常

//    其他,失败

u8 mpu_dmp_get_data(float *pitch,float *roll,float *yaw)

{

float q0=1.0f,q1=0.0f,q2=0.0f,q3=0.0f;

unsigned long sensor_timestamp;

short gyro[3], accel[3], sensors;

unsigned char more;

long quat[4]; 


if(dmp_read_fifo(gyro, accel, quat, &sensor_timestamp, &sensors,&more))return 1;  

if(sensors&INV_WXYZ_QUAT) 

{

q0 = quat[0] / q30; //q30格式转换为浮点数

q1 = quat[1] / q30;

q2 = quat[2] / q30;

q3 = quat[3] / q30; 

//计算得到俯仰角/横滚角/航向角

*pitch = asin(-2 * q1 * q3 + 2 * q0* q2)* 57.3; // pitch

*roll  = atan2(2 * q2 * q3 + 2 * q0 * q1, -2 * q1 * q1 - 2 * q2* q2 + 1)* 57.3; // roll

*yaw   = atan2(2*(q1*q2 + q0*q3),q0*q0+q1*q1-q2*q2-q3*q3) * 57.3; //yaw

}else return 2;

return 0;

}


这里是移植了 DMP 的库,也可以通过读取原始数据,通过 MCU 用数学公式计算出欧拉角。


3.2 主函数实现及数据上报到PC端上位机

//串口1发送1个字符 

//c:要发送的字符

void usart1_send_char(u8 c)

{

while((USART1->SR&0X40)==0);//等待上一次发送完毕   

USART1->DR=c;   


//传送数据给匿名四轴上位机软件(V2.6版本)

//fun:功能字. 0XA0~0XAF

//data:数据缓存区,最多28字节!!

//len:data区有效数据个数

void usart1_niming_report(u8 fun,u8*data,u8 len)

{

u8 send_buf[32];

u8 i;


if(len>28)

return; //最多28字节数据 

send_buf[len+3]=0; //校验数置零

send_buf[0]=0X88; //帧头

send_buf[1]=fun; //功能字

send_buf[2]=len; //数据长度

for(i=0;i

send_buf[3+i]=data[i]; //复制数据

for(i=0;i

send_buf[len+3]+=send_buf[i]; //计算校验和

for(i=0;i

usart1_send_char(send_buf[i]); //发送数据到串口1 

}


//发送加速度传感器数据和陀螺仪数据

//aacx,aacy,aacz:x,y,z三个方向上面的加速度值

//gyrox,gyroy,gyroz:x,y,z三个方向上面的陀螺仪值

void mpu6050_send_data(short aacx,short aacy,short aacz,short gyrox,short gyroy,short gyroz)

{

u8 tbuf[12]; 

tbuf[0]=(aacx>>8)&0XFF;

tbuf[1]=aacx&0XFF;

tbuf[2]=(aacy>>8)&0XFF;

tbuf[3]=aacy&0XFF;

tbuf[4]=(aacz>>8)&0XFF;

tbuf[5]=aacz&0XFF; 

tbuf[6]=(gyrox>>8)&0XFF;

tbuf[7]=gyrox&0XFF;

tbuf[8]=(gyroy>>8)&0XFF;

tbuf[9]=gyroy&0XFF;

tbuf[10]=(gyroz>>8)&0XFF;

tbuf[11]=gyroz&0XFF;

usart1_niming_report(0XA1,tbuf,12);//自定义帧,0XA1

}


//通过串口1上报结算后的姿态数据给电脑

//aacx,aacy,aacz:x,y,z三个方向上面的加速度值

//gyrox,gyroy,gyroz:x,y,z三个方向上面的陀螺仪值

//roll:横滚角.单位0.01度。 -18000 -> 18000 对应 -180.00  ->  180.00度

//pitch:俯仰角.单位 0.01度。-9000 - 9000 对应 -90.00 -> 90.00 度

//yaw:航向角.单位为0.1度 0 -> 3600  对应 0 -> 360.0度

void usart1_report_imu(short aacx,short aacy,short aacz,short gyrox,short gyroy,short gyroz,short roll,short pitch,short yaw)

{

u8 tbuf[28]; 

u8 i;

for(i=0;i<28;i++)

tbuf[i]=0;//清0

tbuf[0]=(aacx>>8)&0XFF;

tbuf[1]=aacx&0XFF;

tbuf[2]=(aacy>>8)&0XFF;

tbuf[3]=aacy&0XFF;

tbuf[4]=(aacz>>8)&0XFF;

tbuf[5]=aacz&0XFF; 

tbuf[6]=(gyrox>>8)&0XFF;

tbuf[7]=gyrox&0XFF;

tbuf[8]=(gyroy>>8)&0XFF;

tbuf[9]=gyroy&0XFF;

tbuf[10]=(gyroz>>8)&0XFF;

tbuf[11]=gyroz&0XFF;

tbuf[18]=(roll>>8)&0XFF;

tbuf[19]=roll&0XFF;

tbuf[20]=(pitch>>8)&0XFF;

tbuf[21]=pitch&0XFF;

tbuf[22]=(yaw>>8)&0XFF;

tbuf[23]=yaw&0XFF;

usart1_niming_report(0XAF,tbuf,28);//飞控显示帧,0XAF

}   



 int main(void)

 { 

u8 t=0,report=1; //默认开启上报

u8 key;

float pitch,roll,yaw; //欧拉角

short aacx,aacy,aacz; //加速度传感器原始数据

short gyrox,gyroy,gyroz; //陀螺仪原始数据

short temp; //温度     

 

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);

delay_init();     //延时函数初始化   

//uart_init(500000); //串口初始化为9600

uart_init(128000);

usmart_dev.init(72); //初始化USMART

LED_Init();   //初始化与LED连接的硬件接口

KEY_Init(); //初始化按键

LCD_Init();     //初始化LCD  

MPU_Init(); //初始化MPU6050

  POINT_COLOR=RED; //设置字体为红色 

LCD_ShowString(30,50,200,16,16,"Mini STM32");

LCD_ShowString(30,70,200,16,16,"MPU6050 TEST");

LCD_ShowString(30,90,200,16,16,"ATOM@ALIENTEK");

LCD_ShowString(30,110,200,16,16,"2015/4/18"); 

while(mpu_dmp_init())

  {

LCD_ShowString(30,130,200,16,16,"MPU6050 Error");

delay_ms(200);

LCD_Fill(30,130,239,130+16,WHITE);

  delay_ms(200);

}  

LCD_ShowString(30,130,200,16,16,"MPU6050 OK");

LCD_ShowString(30,150,200,16,16,"KEY0:UPLOAD ON/OFF");

POINT_COLOR=BLUE;//设置字体为蓝色 

  LCD_ShowString(30,170,200,16,16,"UPLOAD ON ");  

  LCD_ShowString(30,200,200,16,16," Temp:    . C");

  LCD_ShowString(30,220,200,16,16,"Pitch:    . C");

  LCD_ShowString(30,240,200,16,16," Roll:    . C");  

  LCD_ShowString(30,260,200,16,16," Yaw :    . C");  

  while(1)

{

key=KEY_Scan(0);

if(key==KEY0_PRES)

{

report=!report;

if(report)LCD_ShowString(30,170,200,16,16,"UPLOAD ON ");

else LCD_ShowString(30,170,200,16,16,"UPLOAD OFF");

}

if(mpu_dmp_get_data(&pitch,&roll,&yaw)==0) //!!读取欧拉角!!

temp=MPU_Get_Temperature(); //得到温度值

MPU_Get_Accelerometer(&aacx,&aacy,&aacz); //得到加速度传感器数据

MPU_Get_Gyroscope(&gyrox,&gyroy,&gyroz); //得到陀螺仪数据

if(report)

mpu6050_send_data(aacx,aacy,aacz,gyrox,gyroy,gyroz);//用自定义帧发送加速度和陀螺仪原始数据

if(report)

usart1_report_imu(aacx,aacy,aacz,gyrox,gyroy,gyroz,(int)(roll*100),(int)(pitch*100),(int)(yaw*10));

if((t%10)==0)

if(temp<0)

{

LCD_ShowChar(30+48,200,'-',16,0); //显示负号

temp=-temp; //转为正数

}

else 

LCD_ShowChar(30+48,200,' ',16,0); //去掉负号 

LCD_ShowNum(30+48+8,200,temp/100,3,16); //显示整数部分     

LCD_ShowNum(30+48+40,200,temp%10,1,16); //显示小数部分 

temp=pitch*10;

if(temp<0)

{

LCD_ShowChar(30+48,220,'-',16,0); //显示负号

temp=-temp; //转为正数

}else LCD_ShowChar(30+48,220,' ',16,0); //去掉负号 

LCD_ShowNum(30+48+8,220,temp/10,3,16); //显示整数部分     

LCD_ShowNum(30+48+40,220,temp%10,1,16); //显示小数部分 

temp=roll*10;

if(temp<0)

{

LCD_ShowChar(30+48,240,'-',16,0); //显示负号

temp=-temp; //转为正数

}else LCD_ShowChar(30+48,240,' ',16,0); //去掉负号 

LCD_ShowNum(30+48+8,240,temp/10,3,16); //显示整数部分     

LCD_ShowNum(30+48+40,240,temp%10,1,16); //显示小数部分 

temp=yaw*10;

if(temp<0)

{

LCD_ShowChar(30+48,260,'-',16,0); //显示负号

temp=-temp; //转为正数

}else LCD_ShowChar(30+48,260,' ',16,0); //去掉负号 

LCD_ShowNum(30+48+8,260,temp/10,3,16); //显示整数部分     

LCD_ShowNum(30+48+40,260,temp%10,1,16); //显示小数部分  

t=0;

LED0=!LED0;//LED闪烁

}

}

t++; 

}

}


参考:


1.ATK-MPU6050六轴传感器模块使用说明(Mini V3)_AN1507.pdf


关键字:STM32  驱动MPU6050  软件实现

编辑:什么鱼 引用地址:http://www.eeworld.com.cn/mcu/2019/ic-news010942848.html
本网站转载的所有的文章、图片、音频视频文件等资料的版权归版权所有人所有,本站采用的非本站原创文章及图片等内容无法一一联系确认版权者。如果本网所选内容的文章作者及编辑认为其作品不宜公开自由传播,或不应无偿使用,请及时通过电子邮件或电话通知我们,以迅速采取适当措施,避免给双方造成不必要的经济损失。

上一篇:STM32驱动NRF24L01
下一篇:STM32驱动WS2812D全彩LED

关注eeworld公众号 快捷获取更多信息
关注eeworld公众号
快捷获取更多信息
关注eeworld服务号 享受更多官方福利
关注eeworld服务号
享受更多官方福利

推荐阅读

STM32堆栈设置

1.堆和栈大小 定义大小在startup_stm32f2xx.sStack_Size      EQU     0x00000400                AREA    STACK, NOINIT, READWRITE, ALIGN=3Stack_Mem      
发表于 2019-04-16
STM32堆栈设置

STM32堆和栈(Heap & Stack)的资料理解

源起:在移植cjson的过程中,解析json包的时候发现动态内存分配不足而导致解析失败,为解决这一问题,而深入了解stm32的堆和栈。stm32的存储器结构。Flash,SRAM寄存器和输入输出端口被组织在同一个4GB的线性地址空间内。可访问的存储器空间被分成8个主要块,每个块为512MB。FLASH存储下载的程序。SRAM是存储运行程序中的数据。而SRAM一般分这几个部分:静态存储区:内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。它主要存放静态数据、全局数据和常量。栈区:在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率
发表于 2019-04-16
STM32堆和栈(Heap & Stack)的资料理解

STM32定义堆栈地址到ram区顶部

本设置针对stm32f103rbt6的设置,该芯片RAM大小为20kB,故RAM区地址范围为0x20000000—0x20005000,芯片信息如下图所示;第一步:设置.sct文件;;*************************************************************; *** Scatter-Loading Description Filegenerated by uVision ***; *************************************************************LR_IROM1 0x08000000 0x00020000  
发表于 2019-04-16
STM32定义堆栈地址到ram区顶部

STM32之程序如何防止堆栈溢出

近日为某个项目写了个草稿程序,即非正式程序,后来发现老是进入hardfaulthandler,原来是堆栈溢出,后仔细查看发现函数调用纵深太深,最多的时候可保持7个函数在堆栈中调用。因此有心得如下:一、函数调用不要纵深太深,即以下模式:main(){   fun1();}fun1(){  fun2();}fun2(){   fun3();}fun3(){  fun4();}fun4(){  fun5();}fun5(){  fun6();}fun6(){   fun7();}这样子main函数要调用fun1函数完成某个功能,则要一直调到
发表于 2019-04-16

stm32之堆栈

stm32中的堆栈设置keil编译完成时存储情况当编译成功时,会出现: BUILD://Program Size: Code=340 RO-data=252 RW-data=0 ZI-data=1632Code:程序代码部分RO-data: 程序定义的常量const tempRW-data:已初始化的全局变量ZI-data:未初始化的全局变量片中的:flash=Code+RO-data+RW-dataRAM=RW-data+ZI-data通过上面的BUILD可以看出,这个程序已经用了1600多的RAM,为什么会出用到这么多的RAM呢?在startup_stm32f10x_md.s文件中存在:St
发表于 2019-04-16

说说STM32的堆栈与内存

1.概念这里所说的堆栈,是针对单片机所说的“堆”与“栈”,指的是内存中一片特殊用途的区域。而不是数据结构中的堆栈(虽然其实规则一样)。这里所说的内存,是指RAM,RAM包括SRAM,DRAM等。而不是什么手机内存卡之类。这里所说的flash,指的是用作为ROM的存储器,保存代码与常量数据。而不是动画制作。。。栈的生长方向:指的是入栈方向,从高地址向低地址生长叫做向下生长,或逆向生长;反过来就叫向上生长,或正向生长。STM32的栈是向下生长。2.内存中的堆栈安排确切地说,是keil mdk根据STM32的特性,对stm32的RAM甚至flash进行部署。编译工程后,在生成的.map文件里可以看到具体的安排。双击工程界面的工程根目录
发表于 2019-04-16
说说STM32的堆栈与内存

小广播

何立民专栏

单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

电子工程世界版权所有 京ICP证060456号 京ICP备10001474号 电信业务审批[2006]字第258号函 京公海网安备110108001534 Copyright © 2005-2019 EEWORLD.com.cn, Inc. All rights reserved