软件实现将从下面三个部分来介绍: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驱动NRF24L01
下一篇:STM32驱动WS2812D全彩LED
推荐阅读最新更新时间:2024-03-16 16:22