舵机的使用

2019 电子设计大赛 激光炮题目 写的程序

依赖:

pwm.h pwm.c

调用场景:
代码:

pwm.h :

#ifndef __PWM_H
#define __PWM_H

#include "sys.h"

void TIM14_PWM_Init(u32 arr,u32 psc);
void TIM3_PWM_Init(u32 arr,u32 psc);

#endif

pwm.c :

#include "pwm.h"


//TIM14 PWM部分初始化 PF9输出
//arr:自动重装值
//psc:时钟预分频数
void TIM14_PWM_Init(u32 arr,u32 psc)
{		 					 
	//初始化结构体定义
	GPIO_InitTypeDef GPIO_InitStructure;
	TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
	TIM_OCInitTypeDef  TIM_OCInitStructure;
	
	//时钟配置
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM14,ENABLE);  	//TIM14时钟使能    
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE); 	//使能PORTF时钟	
	
	//引脚复用
	GPIO_PinAFConfig(GPIOF,GPIO_PinSource9,GPIO_AF_TIM14); //GPIOF9复用为定时器14
	
	//引脚配置
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;           //GPIOF9
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;        //复用功能
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;	//速度100MHz
	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;      //推挽复用输出
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;        //上拉
	GPIO_Init(GPIOF,&GPIO_InitStructure);              //初始化PF9
	
	//定时器配置
	TIM_TimeBaseStructure.TIM_Period=arr;   //自动重装载值
	TIM_TimeBaseStructure.TIM_Prescaler=psc;  //定时器分频
	TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up; //向上计数模式
	TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1; 
	TIM_TimeBaseInit(TIM14,&TIM_TimeBaseStructure);//初始化定时器14
	
	//定时器通道1配置模式为PWM
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //选择定时器模式:TIM脉冲宽度调制模式2
 	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性:TIM输出比较极性低
	TIM_OC1Init(TIM14, &TIM_OCInitStructure);  //根据T指定的参数初始化外设TIM1 4OC1

	//使能预装载值
	TIM_OC1PreloadConfig(TIM14, TIM_OCPreload_Enable);  //使能TIM14在CCR1上的预装载寄存器

	//使能APRE
	TIM_ARRPreloadConfig(TIM14,ENABLE);//ARPE使能 
	
	//定时器使能
	TIM_Cmd(TIM14, ENABLE);  //使能TIM14									  
}  


//TIM3 PWM部分初始化 PC6
//PWM输出初始化
//arr:自动重装值
//psc:时钟预分频数
void TIM3_PWM_Init(u32 arr,u32 psc)
{		 					 
	//初始化结构体定义
	GPIO_InitTypeDef GPIO_InitStructure;
	TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
	TIM_OCInitTypeDef  TIM_OCInitStructure;
	
	//时钟配置
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);  	//TIM3时钟使能    
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE); 	//使能PORTC时钟	
	
	//引脚复用
	GPIO_PinAFConfig(GPIOC,GPIO_PinSource6,GPIO_AF_TIM3); //GPIOC6复用为定时器14
	
	//引脚配置
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;           //GPIOC6
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;        //复用功能
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;	//速度100MHz
	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;      //推挽复用输出
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;        //上拉
	GPIO_Init(GPIOC,&GPIO_InitStructure);              //初始化PC6
	
	//定时器配置
	TIM_TimeBaseStructure.TIM_Prescaler=psc;  //定时器分频
	TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up; //向上计数模式
	TIM_TimeBaseStructure.TIM_Period=arr;   //自动重装载值
	TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1; 
	TIM_TimeBaseInit(TIM3,&TIM_TimeBaseStructure);//初始化定时器14
	
	//定时器通道1配置模式为PWM
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //选择定时器模式:TIM脉冲宽度调制模式2
 	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性:TIM输出比较极性低
	TIM_OC1Init(TIM3, &TIM_OCInitStructure);  //根据T指定的参数初始化外设TIM1 4OC1

	//使能预装载值
	TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable);  //使能TIM3在CCR1上的预装载寄存器
 
	//使能APRE
	TIM_ARRPreloadConfig(TIM3,ENABLE);//ARPE使能 
	
	//定时器使能
	TIM_Cmd(TIM3, ENABLE);  //使能TIM3							  
}  

servo.h :

#ifndef __SERVO_H
#define __SERVO_H

#include "sys.h"

extern u16 Servo_X_ccr_Laster;
extern u16 Servo_Y_ccr_Laster;

extern const u16 Servo_X_ccr_neg30;
extern const u16 Servo_X_ccr_pos30;


extern u16 Servo_X_ccr_pos0;	//x方向0°pwm
extern u16 Servo_Y_ccr_pos0;	//y方向0°pwm
extern u16 Angle2PWMX[61];		//61个角度和pwmx的映射
extern u16 Distance2PWMY[101];	//101个距离和pwmy的映射
extern u16 Servo_X_Scan_Resolution; //扫描函数使用,控制扫描分辨率,越大扫地越快
extern u8 Servo_X_Scan_Times;       //扫描函数使用,单程扫描次数

extern u16 Servo_X_ccr_LimitL;
extern u16 Servo_X_ccr_LimitH;
extern u8 Servo_X_Scan_StepFlag;

void SERVO_Init(void);

//输入参数为舵机的pwm占空比
void SERVO_X_SetPWM(u16 Servo_X_ccr);
void SERVO_Y_SetPWM(u16 Servo_Y_ccr);

//水平舵机pwm和角度的转换
s8 SERVO_X_PWM2Angle(u16 pwmx);

//输入参数为角度和距离
void SERVO_X_SetAngle(s16 angle_x);
void SERVO_Y_SetDistance(u16 distance, s16 angle_x);

//输入参数为舵机的相对于当前角度的变化量
void SERVO_X_ChangeAngle(s8 pwm_delta_x);
void SERVO_Y_ChangeAngle(s8 pwm_delta_y);

返回舵机当前pwm
//u16 SERVO_X_GetPWM(void);
//u16 SERVO_Y_GetPWM(void);

void SERVO_X_Scan_Start(u16 startPwm, u16 endPWM, u16 pwm_Resolution, u16 pwm_BuildPeriod, u8 continuous);
void SERVO_X_Scan_End(void);

#endif

servo.c :

#include "servo.h"
#include "pwm.h"

//记录上一次pwm的占空比,从而能知道每次转动的起点,以及将要转动的方向
u16 Servo_X_ccr_Laster = 0;
u16 Servo_Y_ccr_Laster = 0;


//舵机扫描函数使用的全局变量
u16 Servo_X_ccr_Start = 0;
u16 Servo_X_ccr_End = 0;
u16 Servo_X_ccr_Next = 0;
u16 Servo_Y_ccr_Next = 0;
s16 Servo_X_Scan_Direction = 0;	 //扫描方向
u16 Servo_X_Scan_Resolution = 0; //扫描步进
u8 Servo_X_Scan_Continuous = 0;  //是否连续扫描
u8 Servo_X_Scan_Times = 0;       //单程扫描次数
u8 Servo_X_Scan_StepFlag = 0;    //步进标志

//设置pwm占空比的限制值,超出时会自动拉回
u16 Servo_X_ccr_LimitL = 895;
u16 Servo_X_ccr_LimitH = 1518;
u16 Servo_Y_ccr_LimitL = 560;
u16 Servo_Y_ccr_LimitH = 1120;

//设置角度和距离对应的pwm占空比的限制值,超出时会自动拉回
s16 angle_LimitL = -30;
s16 angle_LimitH = +30;
s16 distance_LimitL = 200;
s16 distance_LimitH = 300;

const u16 Servo_X_ccr_neg30 = 995;
const u16 Servo_X_ccr_pos30 = 1418;

u16 Servo_X_ccr_pos0 = (Servo_X_ccr_neg30+Servo_X_ccr_pos30)/2;
u16 Servo_Y_ccr_pos0 = 559;


//61个角度和pwmx的映射
u16 Angle2PWMX[61] = {995,
1002 ,1009 ,1016 ,1023 ,1030 ,1037 ,1044 ,1051 ,1058 ,1066 ,
1073 ,1080 ,1087 ,1094 ,1101 ,1108 ,1115 ,1122 ,1129 ,1136 ,
1143 ,1150 ,1157 ,1164 ,1171 ,1178 ,1185 ,1192 ,1199 ,1207 ,
1214 ,1221 ,1228 ,1235 ,1242 ,1249 ,1256 ,1263 ,1270 ,1277 ,
1284 ,1291 ,1298 ,1305 ,1312 ,1319 ,1326 ,1333 ,1340 ,1348 ,
1355 ,1362 ,1369 ,1376 ,1383 ,1390 ,1397 ,1404 ,1411 ,1418 };                                         

101个距离和pwmy的映射
//u16 Distance2PWMY[101] = {
//729,730,731,732,733,734,735,736,737,737,
//738,739,740,740,741,742,743,743,744,745,
//746,746,747,748,749,749,750,752,754,756,
//758,760,762,765,767,769,771,773,775,775,
//777,778,780,782,783,785,787,788,790,792,
//793,795,797,800,802,805,807,809,811,814,
//816,818,820,823,825,827,830,832,834,836,
//839,841,843,845,848,850,852,855,857,859,
//861,864,866,868,870,873,875,877,879,882,
//884,886,889,891,893,895,898,900,902,904,906};

//101个距离和pwmy的映射
u16 Distance2PWMY[101] = {
680,681,682,683,685,686,687,689,690,691,693,694,695,697,698,699,//200-215
700,702,704,707,709,711,714,716,717,721,723,//216到226
725,726,727,728,729,730,731,732,733,734,//227到236
735,736,736,737,738,739,740,740,741,742,743,743,744,745,746,746,747,748,749,749,//237-256
750,752,754,756,758,760,762,764,766,768,771,773,//257-268
775,777,778,780,782,783,785,787,788,790,792,793,795,797,799,//269-283
800,802,804,807,809,811,813,816,818,820,823,//283-294
825,827,830,832,834,836
};

void TIM5_Int_Init(u16 arr,u16 psc);


//舵机初始化
void SERVO_Init(void) {
	TIM14_PWM_Init(20000-1,84-1);
	TIM3_PWM_Init(20000-1,84-1);
	SERVO_X_SetPWM(Servo_X_ccr_pos0);
	SERVO_Y_SetPWM(Servo_Y_ccr_pos0);
}

//输入参数为舵机的pwm占空比
void SERVO_X_SetPWM(u16 Servo_X_ccr) {
	Servo_X_ccr = Servo_X_ccr<Servo_X_ccr_LimitL?Servo_X_ccr_LimitL:Servo_X_ccr;
	Servo_X_ccr = Servo_X_ccr>Servo_X_ccr_LimitH?Servo_X_ccr_LimitH:Servo_X_ccr;
	TIM_SetCompare1(TIM14,Servo_X_ccr);
	Servo_X_ccr_Laster = Servo_X_ccr;
}
void SERVO_Y_SetPWM(u16 Servo_Y_ccr) {
	Servo_Y_ccr = Servo_Y_ccr<Servo_Y_ccr_LimitL?Servo_Y_ccr_LimitL:Servo_Y_ccr;
	Servo_Y_ccr = Servo_Y_ccr>Servo_Y_ccr_LimitH?Servo_Y_ccr_LimitH:Servo_Y_ccr;
	TIM_SetCompare1(TIM3,Servo_Y_ccr);
	Servo_Y_ccr_Laster = Servo_Y_ccr;
}


//函数:pwm转换成角度
//输入为水平舵机pwm
//输出为水平舵机角度
s8 SERVO_X_PWM2Angle(u16 pwmx) {
	return (pwmx-Servo_X_ccr_pos0)*60/(Servo_X_ccr_pos30 - Servo_X_ccr_neg30);
}

//输入参数为目的角度和目的距离
//对输入的参数大小不做要求
//但注意 角度超出+-30°时会转化成+-30°
void SERVO_X_SetAngle(s16 angle_x) {
	angle_x = angle_x<-30?-30:angle_x;
	angle_x = angle_x>30?30:angle_x;
	SERVO_X_SetPWM(Angle2PWMX[angle_x+30]);
}
//在不同角度下距离的pwm映射表不一样
void SERVO_Y_SetDistance(u16 distance, s16 angle_x) {
	u8 p_left = 0;
	u8 p_right = 0;
	distance = distance<200?200:distance;
	distance = distance>300?300:distance;
	if(angle_x>0) {
		//左偏时
		SERVO_Y_SetPWM(Distance2PWMY[distance-200]+21-(angle_x)*p_left);
	} else if(angle_x<0){
		//右偏时
		SERVO_Y_SetPWM(Distance2PWMY[distance-200]+21-(-angle_x)*p_right);
	} else {
		//中间时
		SERVO_Y_SetPWM(Distance2PWMY[distance-200]+21);
	}
}


//输入参数为舵机的相对于当前角度的变化量
void SERVO_X_ChangeAngle(s8 pwm_delta_x) {
	SERVO_X_SetPWM(Servo_X_ccr_Laster + pwm_delta_x);
}
void SERVO_Y_ChangeAngle(s8 pwm_delta_y) {
	SERVO_Y_SetPWM(Servo_Y_ccr_Laster + pwm_delta_y);
}

获得
//u16 Get_X_PWM(TIM_TypeDef* TIMx)
//{
//	return TIM_GetCounter(TIMx);
//}

//舵机X扫描函数
//输入:开始处的pwm,结束处的pwm(左不包含右包含),矩形波周期数
void SERVO_X_Scan_Start(u16 startPwm, u16 endPWM, u16 pwm_Resolution, u16 pwm_BuildPeriod, u8 continuous) {
	SERVO_X_SetPWM(startPwm);
	Servo_X_ccr_Start = startPwm;
	Servo_X_ccr_End = endPWM;
	Servo_X_Scan_Resolution = pwm_Resolution;
	Servo_X_Scan_Continuous = continuous;
	Servo_X_Scan_Direction = endPWM - startPwm;
	Servo_X_Scan_Times = 0;
	
	if(Servo_X_Scan_Direction>0) { 
		//30°<-- -30°(从右往左)
		Servo_X_ccr_Next = startPwm+Servo_X_Scan_Resolution;
	} else {				
		//30°--> -30°(从左往右)
		Servo_X_ccr_Next = startPwm-Servo_X_Scan_Resolution;
	}
	
	//舵机的pwm为50Hz,所以一个周期是20ms,所以舵机在一个角度的停留时间为 pwm_BuildPeriod*20ms
	TIM5_Int_Init(20000-1, 84*pwm_BuildPeriod-1);
	TIM_Cmd(TIM5,ENABLE);
}

void SERVO_X_Scan_End(void) {
	
	TIM_Cmd(TIM5,DISABLE);
}

void TIM5_Int_Init(u16 arr,u16 psc) {
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;
	
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5,ENABLE);  ///使能TIM5时钟
	
	TIM_TimeBaseInitStructure.TIM_Period = arr; 	//自动重装载值
	TIM_TimeBaseInitStructure.TIM_Prescaler=psc;  //定时器分频
	TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up; //向上计数模式
	TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1; 
	
	TIM_TimeBaseInit(TIM5,&TIM_TimeBaseInitStructure);//初始化TIM5
	
	TIM_ClearITPendingBit(TIM5,TIM_IT_Update);  //清除中断标志位
	TIM_ITConfig(TIM5,TIM_IT_Update,ENABLE); //允许定时器5更新中断

	NVIC_InitStructure.NVIC_IRQChannel=TIM5_IRQn; //定时器4中断
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0x01; //抢占优先级1
	NVIC_InitStructure.NVIC_IRQChannelSubPriority=0x03; //子优先级3
	NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
	NVIC_Init(&NVIC_InitStructure);
}

void TIM5_IRQHandler ()
{
	u16 temp;
	if(TIM_GetITStatus(TIM5,TIM_IT_Update)==SET) //溢出中断
	{
		SERVO_X_SetPWM(Servo_X_ccr_Next);		//舵机X移动到下一个占空比处
		Servo_X_Scan_StepFlag = 1;
		if(Servo_X_Scan_Direction > 0) {	
			//30°<-- -30°(从右往左)
			Servo_X_ccr_Next += Servo_X_Scan_Resolution;
		} else if(Servo_X_Scan_Direction < 0) {
			//30°--> -30°(从左往右)
			Servo_X_ccr_Next -= Servo_X_Scan_Resolution;
		} else {
			TIM_Cmd(TIM5,DISABLE);							  //停止扫描
			return;
		}
		
		if((Servo_X_Scan_Direction>0?Servo_X_ccr_Next-Servo_X_ccr_End:Servo_X_ccr_End-Servo_X_ccr_Next)>=0) {
			//单程扫描完成
			Servo_X_Scan_Times++;								  //单程扫描次数加一
			if(Servo_X_Scan_Continuous==0) {
				//单次扫描
				TIM_Cmd(TIM5,DISABLE);							  //停止扫描
			} else {
				//连续扫描
				temp = Servo_X_ccr_End;
				Servo_X_ccr_End = Servo_X_ccr_Start;    		  
				Servo_X_ccr_Start = temp;						  //交换开始pwm和结束pwm
				Servo_X_Scan_Direction = -Servo_X_Scan_Direction; //改变扫描方向
			}
		}
	}
	TIM_ClearITPendingBit(TIM5,TIM_IT_Update);  //清除中断标志位	
}
//TIM_Cmd(TIM5,ENABLE);	
//TIM_Cmd(TIM5,DISABLE);	

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

匿名匿名匿名11

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

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

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

打赏作者

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

抵扣说明:

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

余额充值