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

发布者:csydtc最新更新时间: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  软件实现 引用地址:STM32驱动MPU6050(二)——软件实现

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

推荐阅读最新更新时间:2024-03-16 16:22

STM32HAL库使用SPI驱动1.44寸TFTLCD
  关于STM32F4单片机,使用HAL库自带的SPI,驱动TFTLCD屏幕的资料网上好像不太多,正好最近我做了这项工作,把成果分享给大家。我的代码实现了这些功能:任意坐标画点,指定首尾坐标画线,画方框,指定区域显示彩图,显示16* 16或者12* 12的汉字、ASCII码,并附带ASCII码表与少量的汉字字库。 硬件设计   屏幕选择:使用了一款低成本十六位彩屏,只要十块钱。链接   厂家看到文章请联系我打广告费,哈哈。   虽然用这个屏幕的可能不多,但我了解到,只要其控制芯片是ST7735S,那么程序就应该差不多。不同的地方在于,厂家的封装与玻璃不太一样,玻璃有个伽马值不同,会导致颜色看上去不太一样。   屏幕的引
[单片机]
STM32HAL库使用SPI<font color='red'>驱动</font>1.44寸TFTLCD
stm32系统时钟详解&&移植
写作原由:今日接手用stm32f100xx芯片开发的项目,以前用的是stm8s 和stm32f103xx芯片;因为在别人的项目代码的基础上做2次开发,但是发现那个代码main函数中没有对系统时钟的设置的相关函数,一直纳闷,但也没有深究,直至昨日 调试时出现串口收发数据出错,源代码在原项目的板子上串口发送、接收数据正常,同样程序在项目板子上收发的数据不正确, 两块板子芯片一样,串口收发管脚一样,最后发现原来板子外部晶振是8MHZ ,新板子外部晶振是12MHZ; 而在STM32固件库中,默认的外部晶振是8MHZ,由于时钟源不正确,导致波特率不正确,当然收发的数据也不正确了.....我勒个去!都怪自己平时看问题“不求甚解”。 (波特率与
[单片机]
<font color='red'>stm32</font>系统时钟详解&&移植
STM32例程详解:GPIO之JTAG_Remap
完整的工程文件夹在这里下载 http://pan.baidu.com/s/1EFgkw KEIL—MDK 4.72a调试通过 //头文件包含 ------------------------------------------------------------------*/ #include stm32f10x.h #include stm32_eval.h //译者注:意法半导体公司为其开发板上模块所写的头文件 GPIO_InitTypeDef GPIO_InitStructure; //定义GPIO初始化结构体,其原型由GPIO_InitTypeDef确定 //私有 函数原型 ----------------------
[单片机]
毕业设计| 两轮自平衡小车
# 硬件介绍 # 主控芯片:100脚的STM32F103VET6; 陀螺仪:MPU6050; 电机驱动:TB6612; 蓝牙:汇承的HC05邮票孔封装的; WIFI:济南有人科技的USR-WIFI232-S; 小车底盘:平衡小车之家的某一款带编码器的(不是我买的,同学的); 电池:一节7.2的镍镉电池; 液晶屏:中景园电子1.3寸IIC接口的OLED; 5V降压:ASM1117-5.0; 3.3V降压:SP6203; 开关:三脚纽子开关; 电池接口:T插; 其他器件还有:拨码开关用的是4P贴片式2.54mm角距的,电阻电容这些用的基本上是0603封装按键是两脚贴片,microusb接口用的是5针 7.2四脚插板牛角母座,超声波是
[单片机]
毕业设计| 两轮自平衡小车
STM32学习笔记——SystemInit()函数
第一课 时钟频率 STM32F103内部8M的内部震荡,经过倍频后最高可以达到72M。目前TI的M3系列芯片最高频率可以达到80M。 在stm32固件库3.0中对时钟频率的选择进行了大大的简化,原先的一大堆操作都在后台进行。系统给出的函数为SystemInit()。但在调用前还需要进行一些宏定义的设置,具体的设置在system_stm32f10x.c文件中。 文件开头就有一个这样的定义: //#define SYSCLK_FREQ_HSE HSE_Value //#define SYSCLK_FREQ_20MHz 20000000 //#define SYSCLK_FREQ_36MHz 36000000
[单片机]
关于stm32f1ox.h头文件启动代码与编译器里的宏定义
今天调试stm32f107vc发现他的外部时钟竟是25M,和之前的stm32f103rbt6的8M晶振有所区别。 在此总结一下自己的发现。 stm32f107vc属于互联性器件,他的启动文件应该是.cl。对于启动文件有ji个, .ld .md .hd .cl .xl rbt6选用的是md 可以在keil c++ 宏定义那边说明STM32F10X_CL, 或者在stm32f10x。h中找到 #if !defined (STM32F10X_LD) && !defined (STM32F10X_LD_VL) && !defined (STM32F10X_MD) && !defined (STM32F10X_MD_V
[单片机]
基于STM32软件定时器设计
1 什么是软件定时器 软件定时器是用程序模拟出来的定时器,可以由一个硬件定时器模拟出成千上万个软件定时器,这样程序在需要使用较多定时器的时候就不会受限于硬件资源的不足,这是软件定时器的一个优点,即数量不受限制。 但由于软件定时器是通过程序实现的,其运行和维护都需要耗费一定的CPU资源,同时精度也相对硬件定时器要差一些。 2 软件定时器的实现原理 在Linux,uC/OS,FreeRTOS等操作系统中,都带有软件定时器,原理大同小异。典型的实现方法是:通过一个硬件定时器产生固定的时钟节拍,每次硬件定时器中断到,就对一个全局的时间标记加一,每个软件定时器都保存着到期时间。 程序需要定期扫描所有运行中的软件定时器,将各个到期时间
[单片机]
基于<font color='red'>STM32</font>的<font color='red'>软件</font>定时器设计
STM32 usb_init.c和usb_int.c文件分析
usb_init.c这个文件是主要是初始化。函数很简单: /******************************************************************************* * Function Name : USB_Init * Description : USB系统初始化 * Input : None. * Output : None. * Return : None. *******************************************************************************/ void USB_In
[单片机]
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

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

换一换 更多 相关热搜器件
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved