一:实现效果
DMA解算舵机
从下到上分别为舵机1,2,3,分别由函数Servo_SetAngle1(),Servo_SetAngle2(),Servo_SetAngle3()控制。
舵机1:偏航角Yaw:绕Z轴转动(机头水平转)
舵机2:横滚角Roll:绕X轴转动(飞机左右翻滚)
舵机3:俯仰角Pitch:绕Y轴转动(飞机上下抬头)
二:硬件介绍
1:MPU6050

/*
MPU6050的欧拉角范围可以按照以下方式清晰地表示和归纳:Pitch角(俯仰角):
绕Y轴旋转
范围:±90°
方向:与旋转方向相反转是增大。具体来说,抬头时Pitch角为正,低头时Pitch角为负。Roll角(横滚角):
绕X轴旋转
范围:±180°
方向:与旋转方向相反转是增大。具体来说,右滚时Roll角为正,左滚时Roll角为负。Yaw角(偏航角):
绕Z轴旋转
范围:±180°
方向:与旋转方向相反转是增大。具体来说,右偏时Yaw角为正,左偏时Yaw角为负。MPU6050通过其集成的三轴陀螺仪和三轴加速度计来测量这些角度。
这些角度数据对于理解物体的空间姿态和进行姿态控制非常关键。
在读取和使用这些数据时,需要特别注意它们的范围和方向性,以确保正确的数据处理和姿态控制。*/

2:舵机

高电平宽度=某电频的持续时间
CCR=高电频
舵机的控制一般需要一个20ms的时基脉冲(周期),该脉冲的高电平部分一般为0.5ms~2.5ms范围内的角度控制脉冲部分。以180度角度舵机为例,那么对应的控制关系是这样的:
 0.5ms--------------0度;
 1.0ms------------45度;
 1.5ms------------90度;
 2.0ms-----------135度;
 2.5ms-----------180度;

二:软件部分
pwm
#include "stm32f1xx_hal.h"
#include <stdio.h>
#include <stdarg.h>
TIM_HandleTypeDef TIM_Handle;
void PWM2_Init(uint16_t psc,uint16_t arr)
{
		TIM_Handle.Instance=TIM2;
		TIM_Handle.Init.Prescaler=psc;
		TIM_Handle.Init.CounterMode=TIM_COUNTERMODE_UP;  //计数模式
		TIM_Handle.Init.Period=arr;
		TIM_Handle.Init.ClockDivision=TIM_CLOCKDIVISION_DIV1; //时钟分频值--不分频
		TIM_Handle.Init.AutoReloadPreload=TIM_AUTORELOAD_PRELOAD_DISABLE;//自动重装载值
		HAL_TIM_PWM_Init(&TIM_Handle);
			
		TIM_OC_InitTypeDef TIM_OC_Init={0};
		TIM_OC_Init.OCMode=TIM_OCMODE_PWM1;  //模式  CNT<CCR输出有效值
		TIM_OC_Init.Pulse=0;    //CCR
		TIM_OC_Init.OCPolarity=TIM_OCPOLARITY_HIGH;   //输出极性(有效值)
		HAL_TIM_PWM_ConfigChannel(&TIM_Handle,&TIM_OC_Init,TIM_CHANNEL_1);
		HAL_TIM_PWM_ConfigChannel(&TIM_Handle,&TIM_OC_Init,TIM_CHANNEL_2);
		HAL_TIM_PWM_ConfigChannel(&TIM_Handle,&TIM_OC_Init,TIM_CHANNEL_3);
		HAL_TIM_PWM_Start(&TIM_Handle,TIM_CHANNEL_1);
		HAL_TIM_PWM_Start(&TIM_Handle,TIM_CHANNEL_2);
		HAL_TIM_PWM_Start(&TIM_Handle,TIM_CHANNEL_3);
	
}
void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim)
{
		if(htim->Instance==TIM2)
		{
			
			if(htim->Channel==TIM_CHANNEL_1)
			{
					//TIM2_CH1为PA0
				//TIM2_CH2为PA1
					//TIM2_CH3为PA2
				__HAL_RCC_GPIOA_CLK_ENABLE();
				__HAL_RCC_TIM2_CLK_ENABLE();
				GPIO_InitTypeDef GPIO_Init;
				GPIO_Init.Mode=GPIO_MODE_AF_PP;    /*复用推挽输出*/
				GPIO_Init.Pin=GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2;
				GPIO_Init.Speed=GPIO_SPEED_FREQ_HIGH;
				HAL_GPIO_Init(GPIOA,&GPIO_Init);
			}
		}
			
} 
一个定时器可以开启多个,PWM的通道我们这里开启3个通道输出PWM。
在STM32F103C8T6中,定时器,它们各自拥有独立的通道和相应的捕获/比较寄存器(CCR)。关于TIM是否使用一个CCR的问题,可以明确地表示:
- 定时器独立性:TIM是独立的定时器,它们各自有自己的寄存器和功能设置。
 - 通道与CCR的关系:对于通用定时器,如TIM2和TIM3,它们都具备多个通道(Channel),每个通道都对应一个捕获/比较寄存器(CCR)。例如,TIM2的通道1对应CCR1,TIM3的通道1对应CCR1,以此类推。
 - 通道1的CCR使用情况:
 
- TIM2的通道1(TIM2_CH1)使用CCR1进行捕获或比较操作。
 - TIM3的通道1(TIM3_CH1)也使用CCR1进行捕获或比较操作,但这是TIM3的CCR1,与TIM2的CCR1是独立的。
 - 总结:TIM2的通道1和TIM3的通道1不是使用一个CCR。它们各自有自己的CCR,即TIM2_CCR1和TIM3_CCR1。
 这样的设计允许每个定时器独立地配置和操作其通道,从而实现更灵活和多样的功能。在编写代码时,你需要根据具体需求分别配置TIM2和TIM3的通道及其对应的CCR。
舵机
#include "stm32f1xx_hal.h"
#include "PWM.h"
void Servo_Init()
{
		PWM2_Init(72-1,20000);  //T=(72*20000)/72000 000=0.02s=20ms
}
//Angle:舵机的角度 0~180
void Servo_SetAngle1(float Angle)
{
	__HAL_TIM_SET_COMPARE(&TIM_Handle,TIM_CHANNEL_1,Angle / 180 * 2000 + 500);
}
void Servo_SetAngle2(float Angle)
{
	__HAL_TIM_SET_COMPARE(&TIM_Handle,TIM_CHANNEL_2,Angle / 180 * 2000 + 500);
}
void Servo_SetAngle3(float Angle)
{
	__HAL_TIM_SET_COMPARE(&TIM_Handle,TIM_CHANNEL_3,Angle / 180 * 2000 + 500);
} 
__HAL_TIM_SET_COMPARE 读取CCR的值
主控程序
#include "stm32f1xx_hal.h"
#include "rcc.h"
#include "delay.h"
#include "UART.h"
#include "OLED.h"
#include <stdio.h>
#include <stdarg.h>
#include "IIC.h"
#include "inv_mpu.h"
#include "inv_mpu_dmp_motion_driver.h"
#include "mpu6050.h"
#include "steering_engine.h"
float Pitch,Roll,Yaw;								
uint8_t display_buf[20];
int main(void)
{
	HAL_Init();                         /* 初始化HAL库 */
  sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
  delay_init(72);                     /* 延时初始化 */
	OLED_Init();
	Uart_Init(115200);
	MPU_Init();
	mpu_dmp_init();
	
	Servo_Init();
	printf("串口初始化成功\r\n");
	while(1)
	{
	
			mpu_dmp_get_data(&Pitch,&Roll,&Yaw);				//欧拉角
			sprintf((char *)display_buf,"pitch:%.2f   ",Pitch);
			OLED_ShowString(1,2,display_buf);
			sprintf((char *)display_buf,"roll:%.2f   ",Roll);
			OLED_ShowString(2,2,display_buf);
			sprintf((char *)display_buf,"yaw:%.2f   ",Yaw);
			OLED_ShowString(3,2,display_buf);
		
			//偏航角Yaw:绕Z轴转动(机头水平转)
		//横滚角Roll:绕X轴转动	(飞机左右翻滚)  
		//俯仰角Pitch:绕Y轴转动(飞机上下抬头)
	
			
				Servo_SetAngle1(Yaw+90);    //偏航角Yaw:绕Z轴转动(机头水平转)
				Servo_SetAngle2(Pitch+90);    //横滚角Roll:绕X轴转动(飞机左右翻滚)
				
				
				
				if(Pitch >= 0) 								//俯仰角Pitch:绕Y轴转动(飞机上下抬头)
				{
					
					Servo_SetAngle3(90-Pitch);   //俯仰角Pitch:绕Y轴转动(飞机上下抬头)
				}
			
				else if(Pitch <0)
				{
					float a = abs(Pitch);     
					Servo_SetAngle3(a+90);
				}
					
	}
			
}
/*
MPU6050的欧拉角范围可以按照以下方式清晰地表示和归纳:
Pitch角(俯仰角):
绕Y轴旋转
范围:±90°
方向:与旋转方向相反转是增大。具体来说,抬头时Pitch角为正,低头时Pitch角为负。
Roll角(横滚角):
绕X轴旋转
范围:±180°
方向:与旋转方向相反转是增大。具体来说,右滚时Roll角为正,左滚时Roll角为负。
Yaw角(偏航角):
绕Z轴旋转
范围:±180°
方向:与旋转方向相反转是增大。具体来说,右偏时Yaw角为正,左偏时Yaw角为负。
MPU6050通过其集成的三轴陀螺仪和三轴加速度计来测量这些角度。
这些角度数据对于理解物体的空间姿态和进行姿态控制非常关键。
在读取和使用这些数据时,需要特别注意它们的范围和方向性,以确保正确的数据处理和姿态控制。
*/
 
                

















