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);