STM32二轴陀螺仪云台

基于STM32F103C8T6最小核心板设计的二轴陀螺仪云台,基于江协科技的中断和OLED例程基础上改进而成的。

江协科技的个人空间-江协科技个人主页-哔哩哔哩视频

省流版:网盘分享的文件:定时器云台.rar
链接: https://pan.baidu.com/s/14EF_TK2dO8duZfw14ER-gg?pwd=s9cg 提取码: s9cg

可以直接去获取。

本项目设计的硬件部分有,MPU6050模块、两个SG90_180度舵机、OLED(IIC协议)屏幕

屏幕主要是输出MPU6050模块的X和Y的倾斜角度,用于观察和调试。

本程序获取MPU6050的以及舵机的驱动,都是在中断里面完成的,不会阻塞主程序,这也方便我们去拓展其他的功能,比如把舵机换成步进电机,添加新的传感器等,方便后续的拓展,当然,程序也采用模块化,方便我们去做移植。

舵机采用PWM驱动,另外说一下,舵机得靠5V驱动这一点注意一下,两个舵机的信号线分别连接了STM32的PA0和PA1引脚,连接舵机的为淘宝购买的舵机打印套件。

首先让我们来看一下时钟开启部分

void Timer_Init(void)
{
	//开启TIM2 时钟
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
	//开启 GPIOA 时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	
	TIM_InternalClockConfig(TIM2);
	
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
	TIM_TimeBaseInitStructure.TIM_Period = 100000 - 1;
	TIM_TimeBaseInitStructure.TIM_Prescaler = 72 - 1;
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
	
	TIM_ClearFlag(TIM2, TIM_FLAG_Update);
	TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
	
//nvic	
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	
	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
	NVIC_Init(&NVIC_InitStructure);
	
	TIM_Cmd(TIM2, ENABLE);
}

开启TIM2 时钟和开启 GPIOA 时钟,当然可以根据自己的需求去更改时钟和引脚,但要提前去查看STM32的引脚分布和复用功能。

void GPIO_Config_PWM(void)
{
	//引脚配置
    GPIO_InitTypeDef GPIO_InitStructure;

    // 配置 A0 引脚(TIM2_CH1)
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    // 配置 A1 引脚(TIM2_CH2)
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
    GPIO_Init(GPIOA, &GPIO_InitStructure);	
}

void TIM2_PWM_Config(void)
{
    TIM_OCInitTypeDef TIM_OCInitStructure;

    // 配置 TIM2_CH1 为 PWM 模式(对应舵机 1 的 A0 引脚)
    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
    TIM_OCInitStructure.TIM_Pulse = 0;  // 初始占空比为 0
    TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
    TIM_OC1Init(TIM2, &TIM_OCInitStructure);
    TIM_OC1PreloadConfig(TIM2, TIM_OCPreload_Enable);

    // 配置 TIM2_CH2 为 PWM 模式(对应舵机 2 的 A1 引脚)
    TIM_OCInitStructure.TIM_Pulse = 0;
    TIM_OC2Init(TIM2, &TIM_OCInitStructure);
    TIM_OC2PreloadConfig(TIM2, TIM_OCPreload_Enable);
}

这里设置对应的引脚模式以及开启PWM的模式,这样一来,时钟和PWM部分就完成了,接下来让我们来看一下舵机驱动部分

#include "stm32f10x.h"                  // Device header
#include "Timer.h"


void Servo_Init(void)
{
	Timer_Init();
	GPIO_Config_PWM();
	TIM2_PWM_Config();
}

void Set_Servo_Angles(float angleX, float angleY)
{
	uint16_t pulse_widthX,pulse_widthY;
	//X舵机计算(A0)
	pulse_widthX = (500 + ((2500 - 500) * angleX / 180));
	TIM_SetCompare1(TIM2,pulse_widthX);
	//Y舵机计算(A1)
	pulse_widthY = (500 + ((2500 - 500) * (angleY+90) / 180));
	TIM_SetCompare2(TIM2,pulse_widthY);
}

注意,这里要提前引用Timer定时中断的头文件,防止后续编译的报错,第一部分就是初始化,第二部分就是通过传入的float的值来让舵机做出对应的驱动,值得注意的是,我这里的Y舵机,初始让处于中间的位置,所以在angle Y中加入了90的相位,这样可以保证Y舵机的左右转向,然后舵机函数传入的数值,是去捕获陀螺仪获取的,这里因为我们侧重讲的舵机方面,所以陀螺仪部分就先不侧重讲述,后续再看(挖坑中。。。。),这里我们会引用会调用就行,也可去看看江协科技里面讲陀螺仪部分的视频[10-2] MPU6050简介_哔哩哔哩_bilibili,我这里,做了一部分的改进,因为我们是想通过中断的方式去捕获这个数值,所以我在这选择再封装一个函数,

void MPU6050_GetData(int16_t *AccX, int16_t *AccY, int16_t *AccZ, 
						int16_t *GyroX, int16_t *GyroY, int16_t *GyroZ)
{
	uint8_t DataH, DataL;								//定义数据高8位和低8位的变量
	
	DataH = MPU6050_ReadReg(MPU6050_ACCEL_XOUT_H);		//读取加速度计X轴的高8位数据
	DataL = MPU6050_ReadReg(MPU6050_ACCEL_XOUT_L);		//读取加速度计X轴的低8位数据
	*AccX = (DataH << 8) | DataL;						//数据拼接,通过输出参数返回
	
	DataH = MPU6050_ReadReg(MPU6050_ACCEL_YOUT_H);		//读取加速度计Y轴的高8位数据
	DataL = MPU6050_ReadReg(MPU6050_ACCEL_YOUT_L);		//读取加速度计Y轴的低8位数据
	*AccY = (DataH << 8) | DataL;						//数据拼接,通过输出参数返回
	
	DataH = MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_H);		//读取加速度计Z轴的高8位数据
	DataL = MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_L);		//读取加速度计Z轴的低8位数据
	*AccZ = (DataH << 8) | DataL;						//数据拼接,通过输出参数返回
	
	DataH = MPU6050_ReadReg(MPU6050_GYRO_XOUT_H);		//读取陀螺仪X轴的高8位数据
	DataL = MPU6050_ReadReg(MPU6050_GYRO_XOUT_L);		//读取陀螺仪X轴的低8位数据
	*GyroX = (DataH << 8) | DataL;						//数据拼接,通过输出参数返回
	
	DataH = MPU6050_ReadReg(MPU6050_GYRO_YOUT_H);		//读取陀螺仪Y轴的高8位数据
	DataL = MPU6050_ReadReg(MPU6050_GYRO_YOUT_L);		//读取陀螺仪Y轴的低8位数据
	*GyroY = (DataH << 8) | DataL;						//数据拼接,通过输出参数返回
	
	DataH = MPU6050_ReadReg(MPU6050_GYRO_ZOUT_H);		//读取陀螺仪Z轴的高8位数据
	DataL = MPU6050_ReadReg(MPU6050_GYRO_ZOUT_L);		//读取陀螺仪Z轴的低8位数据
	*GyroZ = (DataH << 8) | DataL;						//数据拼接,通过输出参数返回
}

void MPU6050_Tick(void)
{
	MPU6050_GetData(&AX, &AY, &AZ, &GX, &GY, &GZ);
}

通过MPU6050_Tick函数,去获取这个数值,然后让变量的值通过extern的方式,可以让其他文件可以直接调用

void TIM2_IRQHandler(void)
{
	if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
	{
		MPU6050_Tick();
		Set_Servo_Angles(AngleX, AngleY);
		TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
	}
}

主函数中,我们通过中断的方式去掉用陀螺仪的数值以及去驱动舵机,主函数需要去实现显示的功能,这样一来不会阻塞主程序,这里我们通过i++的形式,来观察,主函数的状态

#include "Delay.h"
#include "OLED.h"
#include "LED.h"
#include "Key.h"
#include "MPU6050.h"
#include "Servo.h"

uint8_t KeyNum;
uint16_t i;

float AngleX,AngleY;

int main(void)
{
	OLED_Init();
	Key_Init();
	MPU6050_Init();
	Servo_Init();

	OLED_ShowString(0,0,"i:",OLED_8X16);
	OLED_ShowString(0,15,"X:",OLED_8X16);
	OLED_ShowString(0,31,"Y:",OLED_8X16);	
	
	while (1)
	{
		OLED_ShowNum(15,0,i++,6,OLED_8X16);
		OLED_ShowSignedNum(16, 15,AngleX,5,OLED_8X16);
		OLED_ShowSignedNum(16, 31,AngleY,5,OLED_8X16);
		OLED_Update();
		if(AX<0)AX=0;
		if(AY>1800)AY=1800;
		if(AY<-900)AY=-900;
		AngleX = AX/10;
		AngleY = AY/10;//下舵机		
	}
}

在实际的运行时,我们发现并不会阻塞主函数,这样一来,我们这给例程就可以进行拓展了。

最后,感谢你能看到这里,因为这个项目也是突发奇想的,可能有很多不足的地方以及没完善的地方,希望能多多指正,下面是本套案例的全套代码

网盘分享的文件:定时器云台.rar
链接: https://pan.baidu.com/s/14EF_TK2dO8duZfw14ER-gg?pwd=s9cg 提取码: s9cg

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值