STM32 四轴无人机设计——读取MPU6050数据并上传匿名上位机

1、前言

在之前的stm32基础篇中,已经实现了软模拟IIC,调用MPU的DMP库输出四元数转化为欧拉角,以及编写匿名上位机的通讯协议。

直接点击可以看。
从程序模块化设计的角度,现在每一个模块已经测试完毕,现在需要将各个模块组合在一起,使数据通用、逻辑顺序正确即可。

2、设计思路

在四旋翼整体设计中分析到,姿态数据的读取储存非常重要,所以安排在1KHz的程序里面。
并且这次我们需要引入一个新的全局变量——report,控制是否将飞控信息发送给匿名上位机,现在设计可以直接将report软件设置为1(默认上报),在后期系统整体设计的时候,可以将report通过遥控器数据改变,比如遥控器上的辅助通道3可以用来控制report的值。
并且新增一个数据结构体,将10个数据储存到专门的结构体中。

3、程序实现

/**  
  *  功能:MPU初始化封装
  *  入口参数:
  *  返回值:
  */

void MPU6050_Init(void)
{
	MPU_Init();//自定义MPU初始化
	while(mpu_dmp_init())
		BEEP =! BEEP;//dmp库初始化		
}

/**  
  *  功能:MPU获取数据
  *  入口参数:结构体 &out_angle,&acc,&gyro
  *  返回值:
  */
void MPU_GetData(void)
{
	if(mpu_dmp_get_data(&out_angle.pitch,&out_angle.roll,&out_angle.yaw)==0)
	{	
		MPU_Get_Accelerometer(&acc.x,&acc.y,&acc.z);	//得到加速度传感器数据
		MPU_Get_Gyroscope(&gyro.x,&gyro.y,&gyro.z);	//得到陀螺仪数据
	}
}

先看第一个程序,我们将之前的MPU_Init()和mpu_dmp_init(),结合到一起,并且在dmp未初始化完成时蜂鸣器提醒。
第二个程序是获取陀螺仪数据函数,也是将之前的三个数据获取函数封装为一个函数。这样主程序调用时简单明了。

if(Count_1ms>=1)
		{
			Count_1ms=0;
			/*1000Hz任务*/
			MPU_GetData();
			if(report==1)//发送数据给上位机
			{
				usart1_report_imu(acc.x,acc.y,acc.z,gyro.x,gyro.y,gyro.z,(int)(out_angle.roll*100),(int)(out_angle.pitch*100),(int)(out_angle.yaw*10));
			}			
		}
if(Count_25ms>=25)
		{	
			Count_25ms=0;
			/*20Hz遥控器接收PPM信号*/			
			PPM_DataArrange(PPM_Databuf);//PPM数据整理 存储在 Rc结构体 中
			DataOutput_ToMOT(Rc_LOCK);
			if(report==1)//发送数据给上位机
			{				
//				usart1_report_pid(u16 rol_p,u16 rol_i,u16 rol_d,u16 pit_p,u16 pit_i,u16 pit_d,u16 yaw_p,u16 yaw_i,u16 yaw_d);
				usart1_report_rc(Rc.THROTTLE,Rc.YAW,Rc.ROLL,Rc.PITCH,Rc.AUX1,Rc.AUX2,Rc.AUX3,Rc.AUX4,0,(int)((Rc.mot1-1000)/10),(int)((Rc.mot2-1000)/10),(int)((Rc.mot3-1000)/10),(int)((Rc.mot4-1000)/10),0);
			}
		}

在主程序中,我们首先调用第一个封装好的初始化函数,然后在1KHz的程序中调用GET_DATA()函数,这样我们就完成了对MPU数据的读取。
然后加入全局变量report,判断report的值来决定是否向上位机上传数据,并且report可以自己设计让遥控器实现什么样的操作来开启上报。
在上一次设计的遥控器数据中,也要上传给上位机,而通讯协议在之前的匿名上位机通讯协议分析中已经编写完成,只需要带入相应的数据即可

4、实验现象

在这里插入图片描述
图片左侧和右侧上方的数据有变化,说明陀螺仪数据读取、传输正常。
右侧中下方,节流阀,姿态角,辅助通道1、2,以及电机PWM的占空比显示正常,说明遥控器数据读取、解析、传输正常。

5、总结

程序设计到这里,大部分的工作已经完成了,还剩下最后的PID程序加入,以及PID的调参任务。从这次四轴的制作中可以看到,程序模块化设计非常简洁方便,初期只需要将一个大型任务分解为各个小任务,逐个击破,最后整合一起,就能完成我们想要的效果

  • 3
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
首先,需要了解MPU6050数据格式和寄存器映射。MPU6050是一个六轴加速度计和陀螺仪,可以通过I2C接口读取数据。其中,加速度计的数据包括X、Y、Z三个轴的加速度值,陀螺仪的数据包括X、Y、Z三个轴的角速度值。这些数据都是通过读取MPU6050内部的寄存器获得的。 下面是一份基于STM32的代码,用于读取MPU6050数据并计算位移量: ``` C #include "stm32f10x.h" #include "stdio.h" #include "math.h" #define MPU6050_ADDRESS 0xD0 #define SMPLRT_DIV 0x19 #define CONFIG 0x1A #define GYRO_CONFIG 0x1B #define ACCEL_CONFIG 0x1C #define ACCEL_XOUT_H 0x3B #define ACCEL_YOUT_H 0x3D #define ACCEL_ZOUT_H 0x3F #define TEMP_OUT_H 0x41 #define GYRO_XOUT_H 0x43 #define GYRO_YOUT_H 0x45 #define GYRO_ZOUT_H 0x47 #define PWR_MGMT_1 0x6B float AcX,AcY,AcZ,Tmp,GyX,GyY,GyZ; float gyro_x, gyro_y, gyro_z; float accel_x, accel_y, accel_z; float gyro_x_old, gyro_y_old, gyro_z_old; float accel_x_old, accel_y_old, accel_z_old; float dt = 0.01; float angle_x = 0, angle_y = 0, angle_z = 0; float distance_x = 0, distance_y = 0, distance_z = 0; void I2C_Configuration(void) { GPIO_InitTypeDef GPIO_InitStructure; I2C_InitTypeDef I2C_InitStructure; RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1,ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD; GPIO_Init(GPIOB,&GPIO_InitStructure); I2C_InitStructure.I2C_ClockSpeed = 100000; I2C_InitStructure.I2C_Mode = I2C_Mode_I2C; I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2; I2C_InitStructure.I2C_OwnAddress1 = MPU6050_ADDRESS; I2C_InitStructure.I2C_Ack = I2C_Ack_Enable; I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit; I2C_InitStructure.I2C_ClockStretching = I2C_ClockStretching_Enable; I2C_Init(I2C1,&I2C_InitStructure); I2C_Cmd(I2C1,ENABLE); } void MPU6050_Init(void) { I2C_Configuration(); I2C_GenerateSTART(I2C1,ENABLE); while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_MODE_SELECT)); I2C_Send7bitAddress(I2C1,MPU6050_ADDRESS,I2C_Direction_Transmitter); while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)); I2C_SendData(I2C1,PWR_MGMT_1); while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTED)); I2C_SendData(I2C1,0x00); while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTED)); I2C_GenerateSTOP(I2C1,ENABLE); } void MPU6050_Read(void) { I2C_GenerateSTART(I2C1,ENABLE); while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_MODE_SELECT)); I2C_Send7bitAddress(I2C1,MPU6050_ADDRESS,I2C_Direction_Transmitter); while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)); I2C_SendData(I2C1,ACCEL_XOUT_H); while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTED)); I2C_GenerateSTART(I2C1,ENABLE); while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_MODE_SELECT)); I2C_Send7bitAddress(I2C1,MPU6050_ADDRESS,I2C_Direction_Receiver); while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED)); AcX = (float)I2C_ReceiveData(I2C1)<<8; while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_RECEIVED)); AcX |= (float)I2C_ReceiveData(I2C1); while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_RECEIVED)); AcY = (float)I2C_ReceiveData(I2C1)<<8; while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_RECEIVED)); AcY |= (float)I2C_ReceiveData(I2C1); while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_RECEIVED)); AcZ = (float)I2C_ReceiveData(I2C1)<<8; while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_RECEIVED)); AcZ |= (float)I2C_ReceiveData(I2C1); while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_RECEIVED)); GyX = (float)I2C_ReceiveData(I2C1)<<8; while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_RECEIVED)); GyX |= (float)I2C_ReceiveData(I2C1); while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_RECEIVED)); GyY = (float)I2C_ReceiveData(I2C1)<<8; while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_RECEIVED)); GyY |= (float)I2C_ReceiveData(I2C1); while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_RECEIVED)); GyZ = (float)I2C_ReceiveData(I2C1)<<8; while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_RECEIVED)); GyZ |= (float)I2C_ReceiveData(I2C1); while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_RECEIVED)); I2C_GenerateSTOP(I2C1,ENABLE); accel_x = AcX / 16384.0f; accel_y = AcY / 16384.0f; accel_z = AcZ / 16384.0f; gyro_x = GyX / 131.0f; gyro_y = GyY / 131.0f; gyro_z = GyZ / 131.0f; } int main(void) { MPU6050_Init(); while(1) { MPU6050_Read(); // 计算角度 gyro_x = gyro_x - 0.0038 * gyro_x_old; gyro_y = gyro_y - 0.0038 * gyro_y_old; gyro_z = gyro_z - 0.0038 * gyro_z_old; accel_x = accel_x - 0.0015 * accel_x_old; accel_y = accel_y - 0.0015 * accel_y_old; accel_z = accel_z - 0.0015 * accel_z_old; angle_x += gyro_x * dt; angle_y += gyro_y * dt; angle_z += gyro_z * dt; angle_x = 0.99 * (angle_x + accel_x * dt) + 0.01 * angle_x; angle_y = 0.99 * (angle_y + accel_y * dt) + 0.01 * angle_y; angle_z = 0.99 * (angle_z + accel_z * dt) + 0.01 * angle_z; // 计算位移量 distance_x += angle_x * dt; distance_y += angle_y * dt; distance_z += angle_z * dt; gyro_x_old = gyro_x; gyro_y_old = gyro_y; gyro_z_old = gyro_z; accel_x_old = accel_x; accel_y_old = accel_y; accel_z_old = accel_z; } } ``` 在上述代码中,MPU6050_Init()函数用于初始化I2C接口,MPU6050_Read()函数用于读取MPU6050数据。计算角度和位移量的代码在主函数中。其中,dt为采样时间,可以根据自己的需求进行调整。这份代码只是一个示例,需要根据实际情况进行修改和完善。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一个旅者

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值