2019年电赛准备程序STM32版本

2023-05-16

现在是2019年8月11号,昨天电赛结束,不愿意好的东西随时间而消失不见,所以将准备的程序总结一下,分享出来。

目录

一、电赛的故事

二、准备程序

1、PID

2、滤波

3、菜单

4、直流编码电机控速和控距

5、步进电机控速控距


一、电赛的故事

这是我的第三次电赛,大一炮灰,比较轻松,大二大三就很累,四天基本只能睡6个小时,所以今天从2点睡到了17点。由于今年的电赛比较诡异,没有纯粹的控制题,所以我这个做控制的比较被动,选择的电磁炮题。

二、准备程序

三次电赛准备了很多的程序,这里将一些常用的程序分享出来。

1、PID

控制题的必备品,谁用谁知道

头文件pid.h

#ifndef _PID_H
#define _PID_H

typedef struct _positional_pid{
    //PID的基本参数
    double GoalVale;      //目标值
    double ActualVale;    //真实值
    double Error;         //误差
    double LastError;     //上一次的误差
    double Kp,Ki,Kd;      //PID三参数
    double T;             //周期
    double Output;        //输出
    double Integral;      //积分值

    //状态参数
    int Flag_First;     //首次运算的标志
    double MaxOutput;  //输出限幅
    double MinOutput;  //输出限幅
    double IntePartValue;   //积分分离阈值
		double MAXInte;         //积分限幅
    double NoneActValue; //不动作区阈值
}PositionalPid;

typedef struct _incremental_pid{
    //PID的基本参数
    double GoalVale;      //目标值
    double ActualVale;    //真实值
    double Error;         //误差
    double LastError1;    //上一次的误差
    double LastError2;    //上上次的误差
    double Kp,Ki,Kd;      //PID三参数
    double T;             //周期
    double Output;        //输出

    //状态参数
    int Flag_First;     //首次运算的标志
    double MaxOutput;  //输出限幅
    double MinOutput;  //输出限幅
    double NoneActValue; //不动作区阈值
}IncrementalPid;

//位置式PID计算公式
void positionalPid_Cal(PositionalPid *pid , double ActualValue);

//增量式PID计算公式
void incrementalPid_Cal(IncrementalPid *pid , double ActualValue);

#endif

c文件

#include "pid.h"
#include <math.h>

/*************************************************************************
*Name   : positionalPid_Cal
*Funs   : 计算位置式PID的输出
*Input  : pid,位置式PID的结构体 ; ActualValue ,控制对象的测量值
*Output : None(将计算的结构存到pid结构体的参数里面)
 *************************************************************************/
void positionalPid_Cal(PositionalPid *pid , double ActualValue){
    pid->ActualVale = ActualValue;
    pid->Error = pid->GoalVale - pid->ActualVale; //设定值-目前值
    if(fabs(pid->Error) < pid->NoneActValue){  //死区
	pid->Integral=0;
	return ;
    }
    /*计算p*/
    double p_temp = pid->Kp * pid->Error;

    /*计算i,同时积分分离*/
    if(fabs(pid->Error) > pid->IntePartValue){
        pid->Integral=0;
    }else{
	if(fabs(pid->Integral)>pid->MAXInte)
	    pid->Integral=pid->Integral>0?pid->MAXInte:(-pid->MAXInte);
	else
	    pid->Integral += pid->Error;
    }
    double i_temp=pid->Integral * pid->Ki;
    double d_temp;
    /*首次不计算微分*/
    if(pid->Flag_First){
        d_temp=pid->Kd*(pid->Error-pid->LastError);
        pid->LastError=pid->Error;
    }else{
        pid->Flag_First=1;
        d_temp=0;
        pid->LastError=pid->Error;
    }
    pid->Output=p_temp+i_temp+d_temp;
    /*输出限幅*/
    if(pid->Output < pid->MinOutput)
        pid->Output = pid->MinOutput;
    if(pid->Output > pid->MaxOutput)
        pid->Output = pid->MaxOutput;
    pid->LastError = pid->Error;
}

/*******************************************************************************
*Name   : positionalPid_Cal
*Funs   : 计算位置式PID的输出
*Input  : pid,位置式PID的结构体 ; ActualValue ,控制对象的测量值
*Output : None(将计算的结构存到pid结构体的参数里面)
 *******************************************************************************/
void incrementalPid_Cal(IncrementalPid *pid , double ActualValue){
    pid->ActualVale = ActualValue;
    pid->Error = pid->GoalVale - pid->ActualVale; //设定值-目前值
    if(fabs(pid->Error) < pid->NoneActValue){  //死区
	pid->Output=0;
	return ;
    }
    if(pid->Flag_First>=2){
        pid->Output = pid->Kp * pid->Error - pid->Ki*pid->LastError1 + pid->Kd *         pid->LastError2;
        /*输出限幅*/
        if(pid->Output < pid->MinOutput)
            pid->Output = pid->MinOutput;
        if(pid->Output > pid->MaxOutput)
            pid->Output = pid->MaxOutput;
    }else{
        pid->Flag_First++;
    }
    pid->LastError2 = pid->LastError1;
    pid->LastError1 = pid->Error;
}

里面有两种形式的PID,位置式和增量式,建议使用位置式的PID,因为两种本质是一样的,但是增量式PID调参不好调(个人认为),我的里面加入了PID的常见操作,包括死区设置、输出限幅、积分限幅、积分分离。使用的时候只需要将我的文件加进工程,初始化一个PID的结构体,然后调用相应的函数进行计算就可以了,我写的函数都是进行了实际测试的,基本没有BUG,大家可以放心使用。

2、滤波

头文件

#ifndef _MYFILTER_H
#define _MYFILTER_H
#define SIZE 5

typedef float Element;

typedef struct FILTER {
	Element Data[SIZE];  //滤波核
	int head;            //队头
	int tail;            //队尾
	int flag;			 //用来判断是否已经有足够的数据
	int Mode;			 //表示滤波的方法
} Filter;

typedef struct KALAMFILTER {
	double Q;
	double R;
	double P_Last;
	double X_Last, Kg, X_mid, P_Mid, X_Now, P_Now;
	int flag;
}KalManFilter;

//初始化滤波器
void initFilter(Filter* f ,int Mode);

//初始化卡尔曼滤波器
void initKalManFilter(KalManFilter* f,double Q,double R,double P_Last);

//平均滤波
Element AverageFilter(Filter* f,Element);

//中值滤波
Element MiddleFilter(Filter* f, Element);

//卡尔曼滤波
Element KalManFilterCal(KalManFilter *,Element);
#endif

c文件

#include "myfilter.h"
#include <stdlib.h>

/******************************************************************
*Name	:initFliter
*Funs	:初始化滤波器
*Input	:f表示待初始化的滤波器,size表示滤波核的大小,Mode表示滤波方法
*Output :None
*********************************************************************/
void initFilter(Filter* f,int Mode) {
    f->flag = 0;
    f->Mode = Mode;
    f->head = 0;
    f->tail = 0;
}

/*********************************************************************
*Name	:initKalManFilter
*Funs	:初始化卡尔曼滤波器
*Input	:f表示待初始化的滤波器,Q表示系统噪声,R表示测量噪声,P_Last表示系统状态协方差初始值
*Output :None
**********************************************************************/
void initKalManFilter(KalManFilter* f, double Q, double R, double P_Last) {
    f->P_Last = P_Last;
    f->Q = Q;
    f->R = R;
    f->flag = 0;
}

/*******************************************************************
*Name	:AverageFilter
*Funs	:滑动平均滤波
*Input	:f表示滤波器,
*Output :None
*******************************************************************/
Element AverageFilter(Filter* f, Element data) {
    Element res=0;
    if (f->flag < SIZE) {
	f->Data[f->tail++] = data;
	f->flag++;
	for (int i = 0; i < f->flag; i++) {
            res += f->Data[i];
        }
        return res / f->flag;
    }
    else {
	f->head++;     //出队
	f->head = f->head%SIZE;
	f->tail++;
	f->tail = f->tail%SIZE;
	f->Data[f->tail] = data;
	for (int i = 0; i < SIZE; i++) {
    	    res += f->Data[i];
	}
	return res / SIZE;
    }
}

/**************************************************************
*Name	:MiddleFilter
*Funs	:滑动中值滤波
*Input	:f表示滤波器,data为滤波器的输入数据
*Output :None
***************************************************************/
Element MiddleFilter(Filter* f, Element data) {
    Element res = 0;
    if (f->flag <SIZE) {
	f->Data[f->tail++] = data;
	f->flag++;
	for (int i = 0; i < f->flag; i++) {
    	    res += f->Data[i];
	}
	return res / f->flag;
    }
    else {
	f->head++;     //出队
	f->head = f->head%SIZE;
	f->tail++;
	f->tail = f->tail%SIZE;
	f->Data[f->tail] = data;
	//取中值
	Element temp[SIZE];
	for (int i = 0; i < SIZE; i++)
	    temp[i] = f->Data[i];
	    Element p;
	    for (int i = 0; i <= SIZE/2; i++) {
		for (int j = 0; j < SIZE-i-1; j++) {
		    if (temp[j] > temp[j + 1]) {
			p = temp[j];
			temp[j] = temp[j + 1];
			temp[j + 1] = p;
		    }
	    	}
	}
	return temp[SIZE/2];
    }
}

/***********************************************************
*Name	:KalManFilterCal
*Funs	:卡尔曼滤波
*Input	:data为滤波器的输入数据
*Output :None
***********************************************************/
Element KalManFilterCal(KalManFilter *f, Element Data){
    if (!f->flag) {
        f->flag = 1;
	f->X_Last = Data;
    }
    f->X_mid = f->X_Last;
    f->P_Mid = f->P_Last + f->Q;
    f->Kg = f->P_Mid / (f->P_Mid+f->R);
    f->X_Now = f->X_mid + f->Kg*(Data - f->X_mid);
    f->P_Now = (1 - f->Kg)*f->P_Mid;
    return f->X_Now;
}

包含三个滤波器,均值滤波、中值滤波、卡尔曼滤波。其中均值滤波和中值滤波已经经过我的验证了,卡尔曼滤波并没有经过我的验证。使用的时候,只需要初始化一下滤波器,然后调用相应的函数即可。

3、菜单

比赛的时候,一般需要一个界面,用来人机交互,我的这个菜单可以实现任意级的菜单,相当于一个只有执行功能的文件系统

头文件

#ifndef _QZQMENU_H
#define _QZQMENU_H
#include "sys.h"
#define LCD_WIDTH  240
#define LCD_HEIGHT 320
typedef struct Menu_Item{
	u8  ChildCount;                   //子菜单个数      
	u8  CurrentChild;                 //当前孩子个数
	u8 Mode;                          //菜单类型
	char * name;                      //菜单名字
	void (*Action)();                 //菜单被选中时需执行的函数指针   
	struct Menu_Item **ChildrenMenus; //子菜单结构体数组
	struct Menu_Item *ParentMenus;   //父菜单
}MenuItem;

//创建一个菜单子项
int createOneItem(MenuItem *item,u8 mode,char* name,void (*fun)(),int ChildNum);

//建立父亲关系
int Parent(MenuItem * A,MenuItem *B);

//父亲专用行为,打印所有的子项到屏幕上
void ParentAction(MenuItem *item);

//开始行动
void StartMenu(MenuItem *ALL);

#endif

c文件

#include "qzqmenu.h"
#include "lcd.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "My_Key.h"
#include "delay.h"
/***********************************************************************************
*Name   : createOneItem
*Fun    : 创建一个菜单子项,并初始化其参数
*Input  : item,菜单子项指针;fun,菜单功能;ChildNum,该子项的子菜单个数;ParentNum 该子项的父菜单个数
          mode 表示模式,0表示功能项,1表示集合项
*Output : 0 表示初始化失败;1表示初始化成功
************************************************************************************/
int createOneItem(MenuItem *item,u8 mode,char* name,void (*fun)(),int ChildNum){
    item->ChildrenMenus = (MenuItem**)malloc(ChildNum*sizeof(MenuItem*));
    item->ChildCount=ChildNum;
    item->Action=fun;
    item->CurrentChild=0;
    item->name = name;
    item->Mode =mode;
    item->ParentMenus=NULL;
    //检查是否成功申请内存
    if(item->ChildrenMenus==0&&ChildNum){
    	return 0;
    }else 
	return 1;
}


/***********************************************************************
*Name   : Parent
*Fun    : 绑定两个菜单的父子关系
*Input  : A,父亲;B 儿子
*Output : 0 表示绑定失败 ; 1 表示绑定成功
***********************************************************************/
int Parent(MenuItem * A,MenuItem *B){
    //检查A的孩子是否已经满了
    if(A->CurrentChild>=A->ChildCount)
    	return 0;
    if(A->Mode==0)
    	return 0;
    A->ChildrenMenus[A->CurrentChild++] = B;
    B->ParentMenus= A;
    return 1;
}

//父亲专用行为
void ParentAction(MenuItem *item){
    LCD_Clear(WHITE);
    int XBais=20,YBais=40;
    int YAdd =30;
    char buf[50];
    POINT_COLOR=BLUE;
    LCD_ShowString(0,5,200,30,16,item->name);
    for(int i=0;i<item->CurrentChild;i++){
    	sprintf(buf,"%d  %s",i+1,item->ChildrenMenus[i]->name);
    	POINT_COLOR=RED;
    	LCD_DrawRectangle(XBais-5,YBais+i*YAdd-5,XBais+200,YBais+i*YAdd+25);
    	if(item->ChildrenMenus[i]->Mode)
    		POINT_COLOR=RED;
    	else
    	    POINT_COLOR=BLACK;
            LCD_ShowString(XBais,YBais+i*YAdd,200,30,16,buf);
    }
}


/******************************************************************
*Name   : StartMenu
*Fun    : 绑定两个菜单的父子关系
*Input  : ALL 表示顶层子项
*Output : None
*****************************************************************/
void StartMenu(MenuItem *ALL){
    if(!ALL->Mode)
        return;
	u8 key;
	MenuItem *cur = ALL;
	ParentAction(cur);
	while(1){
	    while(!(key=My_Key_scan()))
		;
    	    delay_ms(200);
	    if(key==16){//返回上一级菜单
		if(cur->ParentMenus){
	    	    cur = cur->ParentMenus;
	    	    ParentAction(cur);
		}
	    }else if(key>0&&key<=cur->CurrentChild){
		if(cur->ChildrenMenus[key-1]->Mode){
		    cur = cur->ChildrenMenus[key-1];
		    ParentAction(cur);
		}else{
		    if(cur->ChildrenMenus[key-1]->Action){
			cur->ChildrenMenus[key-1]->Action();
		    }
		}
	    }
	}
}

使用例子

void MenuInit(void){
    MenuItem Start,item1,item2,item3;
    MenuItem t1,t2,t3,t4,t5,t6,t;
	
    createOneItem(&t,1,"2019 NCSEDC",LED_1,6);
    createOneItem(&t1,1,"Basic Topic One",LED_1,0);
    createOneItem(&t2,1,"Basic Topic Two",LED_2,0);
    createOneItem(&t3,1,"Basic Topic Three",LED_3,0);
    createOneItem(&t4,1,"Extended Topic One",LED_4,0);
    createOneItem(&t5,1,"Extended Topic Tow",LED_5,0);
    createOneItem(&t6,1,"Others",LED_6,0);
	
    Parent(&t,&t1);
    Parent(&t,&t2);
    Parent(&t,&t3);
    Parent(&t,&t4);
    Parent(&t,&t5);
    Parent(&t,&t6);
    StartMenu(&t);
}

注:菜单需要配合按键和LCD来联合使用,这里按键我使用的是4x4按键,LCD是使用的正点原子的TFT配套例程,完整的工程会在后面给出链接

4、直流编码电机控速和控距

这份代码是同时控制三个电机的,因为当时正在做这个分为底层的电机驱动和中层的速度距离控制,中层里面提供了用户来设定速度和距离的接口。

1、底层头文件

#ifndef MYPWM_H
#define MYPWM_H
#include "sys.h"

#define FDIV (84)
#define FULL (500)

#define MOTOR1_FORWARD {PFout(0)=1;PFout(1)=0;}
#define MOTOR1_REVERSE {PFout(1)=1;PFout(0)=0;}
#define MOTOR2_FORWARD {PFout(2)=1;PFout(3)=0;}
#define MOTOR2_REVERSE {PFout(3)=1;PFout(2)=0;}
#define MOTOR3_FORWARD {PFout(4)=1;PFout(5)=0;}
#define MOTOR3_REVERSE {PFout(5)=1;PFout(4)=0;}
//电机1初始化,包括定时器、方向引脚等
void Motor1Init(void);

//设置占空比
void SetRate(float rate,u8 );

#endif

底层c文件

#include "motor.h"
#include "sys.h"
#include "usart.h"
#include "pid.h"

/******************************************************************
* Name   : setMotor1DirGpio
* Funs   : 初始化三个电机控制,包括定时器、方向引脚
* Input  : None
* Output : None
******************************************************************/
void static setMotor1DirGpio(void){
  GPIO_InitTypeDef  GPIO_InitStructure;
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE); //使能GPIOF时钟

  //GPIOF0-5初始化设置
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1|GPIO_Pin_2 |             GPIO_Pin_3|GPIO_Pin_4 | GPIO_Pin_5;//0-5作为方向控制
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;         //普通输出模式
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;        //推挽输出
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;    //100MHz
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;          //上拉
  GPIO_Init(GPIOF, &GPIO_InitStructure);                //初始化GPIO
  MOTOR1_FORWARD;
  MOTOR2_FORWARD;
  MOTOR3_FORWARD;
}

void Motor1Init(void){
    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); 	//使能PORTF时钟	
	
    GPIO_PinAFConfig(GPIOC,GPIO_PinSource6,GPIO_AF_TIM3); //GPIOC6复用为定时器3
    GPIO_PinAFConfig(GPIOC,GPIO_PinSource7,GPIO_AF_TIM3); //GPIOC7复用为定时器3
    GPIO_PinAFConfig(GPIOC,GPIO_PinSource8,GPIO_AF_TIM3); //GPIOC8复用为定时器3
	
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7|GPIO_Pin_8; //GPIOC678第一、二、三通道做pwm波输出
    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);              //初始化PC678
	
    TIM_TimeBaseStructure.TIM_Prescaler=FDIV-1;  //定时器分频
    TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up; //向上计数模式
    TIM_TimeBaseStructure.TIM_Period=FULL-1;   //自动重装载值
    TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1; 
    TIM_TimeBaseInit(TIM3,&TIM_TimeBaseStructure);//初始化定时器3
	
    //初始化TIM3 Channel1 PWM模式	 
    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; //选择定时器模式:TIM脉冲宽度调制模式2
    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
    TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; //输出极性:TIM输出比较极性低
    TIM_OC1Init(TIM3, &TIM_OCInitStructure);  //根据T指定的参数初始化外设TIM3 OC1
    TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable);  //使能TIM3在CCR1上的预装载寄存器	
	
    //初始化TIM3 Channel2 PWM模式	 
    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; //选择定时器模式:TIM脉冲宽度调制模式2
    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
    TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; //输出极性:TIM输出比较极性
    TIM_OC2Init(TIM3, &TIM_OCInitStructure);  //根据T指定的参数初始化外设TIM3 OC2
    TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable);  //使能TIM3在CCR2上的预装载寄存器	
	
    //初始化TIM3 Channel3 PWM模式	 
    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; //选择定时器模式:TIM脉冲宽度调制模式2
    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
    TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; //输出极性:TIM输出比较极性低
    TIM_OC3Init(TIM3, &TIM_OCInitStructure);  //根据T指定的参数初始化外设TIM3 OC3
    TIM_OC3PreloadConfig(TIM3, TIM_OCPreload_Enable);  //使能TIM3在CCR3上的预装载寄存器	
	
    TIM_ARRPreloadConfig(TIM3,ENABLE);//ARPE使能,对预装载值做出更改后,立即生效
    TIM_Cmd(TIM3, ENABLE);  //使能TIM3	
    setMotor1DirGpio();
}  




/******************************************************
*函数名:SetRate
*输入:rate 占空比 取值-1到1;channel 表示哪一个通道,即哪一个电机,取值1-3
*输出:无
*功能:修改电机1-3的PWM波
*******************************************************/
void SetRate(float rate,u8 channel){
    if(rate>1)
	rate=1;
    if(rate<-1)
	rate=-1;
    if(rate<0){
	rate=-rate;
	if(channel==1){
    	    MOTOR1_REVERSE;
	}else if(channel==2){
    	    MOTOR2_REVERSE;
	}else if(channel==3){
	    MOTOR3_REVERSE;
	}
    }else{
	if(channel==1){
            MOTOR1_FORWARD;
	}else if(channel==2){
	    MOTOR2_FORWARD;
        }else if(channel==3){
	    MOTOR3_FORWARD;
	}
    }
    if(channel==1){
	TIM_SetCompare1(TIM3,rate*FULL);
    }else if(channel==2){
	TIM_SetCompare2(TIM3,rate*FULL);
    }else if(channel==3){
	TIM_SetCompare3(TIM3,rate*FULL);
    }
}

 

3、中层头文件

#ifndef _CONTROL_H
#define _CONTROL_H
#include "sys.h"
#include "pid.h"
#define MINRATE 0.2
#define STATETHRE 30


typedef struct MOTORPARM{//此结构作为外部控制的接口
	int speed;                //电机当前速度
	int dis;                  //电机当前目标距离
	u8 flag_finish;           //动作完成标志
	u8 flag_state;            //调速或调距
	u8 number;                //电机编号
	float rate ;              //电机占空比
	IncrementalPid pid_speed; //电机运算pid
	PositionalPid pid_speed_p;//电机位置式速度pid
	PositionalPid pid_dis;    //电机位置pid
}MotorParm;

//变量声明
extern MotorParm motor1,motor2,motor3;

//控制电机
void MotorControl(float GoalSpeed);

//改变电机的速度
void setMotorSpeed(float speed);

//调速
void motorSpeed(MotorParm *motor);

//调距
void motorDistance(MotorParm *motor);

//电机状态更新
void motorStateUpdata(MotorParm *motor);

//电机过程控制
void motorProcessControl(MotorParm *car);

//控制初始化
void MotorsControlInit(void);

//更改电机1的目标
void setMotor1Goal(int steps,int speed);

//更改电机2的目标
void setMotor2Goal(int steps,int speed);

//更改电机3的目标
void setMotor3Goal(int steps,int speed);
#endif

中层c文件

#include "control.h"
#include "pid.h"
#include "encoder.h"
#include "motor.h"
#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "niming.h"
#include <math.h>

//全局变量
MotorParm motor1,motor2,motor3; //三个电机的控制结构体

/**************************************************************************
*Name   :MotorControl
*Funs   :电机控速函数
*Input  :GoalSpeed 目标速度,范围需要根据电机而定
*Output :None
**************************************************************************/
IncrementalPid pid;
void MotorControl(float GoalSpeed){
    pid.Error=0;
    pid.LastError1=0;
    pid.LastError2=0;
	
    pid.Flag_First=1;
    pid.MinOutput=-1;
    pid.MaxOutput=1;
    pid.NoneActValue=0;
	
    pid.GoalVale = GoalSpeed;
    pid.Kp=-0.00003;
    pid.Ki=0;
    pid.Kd=0;
	
    getSpeed(1,1);
    float speed;
    float rate=0;
    float temp;
    u8 buf1[4],buf2[4];
    u8 *temp1,*temp2;
    while(1){
	speed = getSpeed(1,1);
	incrementalPid_Cal(&pid,speed);
	rate+=pid.Output;
	SetRate(rate,1);
	temp = pid.GoalVale;
	temp1 = (u8*)&(temp);
	temp2 = (u8*)&speed;
	for(int i=0;i<4;i++){
	    buf1[i] = *(temp1+3-i);
	    buf2[i] = *(temp2+3-i);
	}
	    delay_us(1000);
	}
}

void setMotorSpeed(float speed){
    pid.GoalVale = speed;
    printf("%f\r\n",pid.GoalVale);
}

void limitValue(float* rate,float min,float max){
    if(*rate>0&&*rate<min){
	*rate = min;
    }
    if(*rate<0&&*rate>-min){
	*rate = -min;
    }
    if(*rate>0&&*rate > max){
    	*rate = max;
    }
    if(*rate<0&&*rate < -max){
        *rate = -max;
    }
}


/**************************************************************************
*Name   :motorProcessControl
*Funs   :电机控速控距离处理函数:分为两个步骤,先调速,再调
*Input  :motor 是电机控制的结构体指针
*Output :None
**************************************************************************/
void motorProcessControl(MotorParm *motor){
//	if(motor->flag_finish==1)
//		return; //如果任务完成,则啥都不干
    if(motor->flag_state==1){//调速
    	motorSpeed(motor);
    }else if(motor->flag_state==2){//调距
    	motorDistance(motor);
    }
}

/**************************************************************************
*Name   :motorSpeed
*Funs   :电机调速模块,这里对电机速度进行控制
*Input  :motor 是电机控制的结构体指针
*Output :None
**************************************************************************/
void motorSpeed(MotorParm *motor){
    motor->speed = getSpeed(0,motor->number); //获取速度
    motor->dis -= motor->speed;
    positionalPid_Cal(&motor->pid_speed_p,motor->speed);
    motor->rate=motor->pid_speed_p.Output;
    limitValue(&motor->rate,MINRATE,1);
    SetRate(motor->rate,motor->number);
}

/**************************************************************************
*Name   :motorDistance
*Funs   :电机距离调节,调节电机的转动距离
*Input  :motor 是电机控制的结构体指针
*Output :None
**************************************************************************/
void motorDistance(MotorParm *motor){
    motor->dis += getSpeed(0,motor->number); //累加距离
    positionalPid_Cal(&motor->pid_dis,motor->dis);
    motor->rate=motor->pid_dis.Output;
    limitValue(&motor->rate,MINRATE,1);
    SetRate(motor->rate,motor->number);
}


/**************************************************************************
*Name   :motorStateUpdata
*Funs   :电机状态更新
*Input  :motor 是电机控制的结构体指针
*Output :None
**************************************************************************/
void motorStateUpdata(MotorParm *motor){
    if(motor->flag_state==1){ // 当前为速度调节
        if((motor->dis>0&&motor->dis<STATETHRE)||(motor->dis<0&&motor->dis>-STATETHRE)){
	    motor->flag_state=2; //进入调距
	}
    }else if(motor->flag_state==2){
	if(motor->dis==0){
//     	    motor->flag_state=0;
	    motor->flag_finish=1;
	}
    }
}

/**************************************************************************
*Name   :MotorsControlInit
*Funs   :初始化电机控制结构体参数
*Input  :motor 是电机控制的结构体指针
*Output :None
**************************************************************************/
void MotorsControlInit(void){
    motor1.number=1;
    motor1.flag_finish=1;
    
    motor1.pid_speed_p.Error=0;
    motor1.pid_speed_p.Flag_First=0;
    motor1.pid_speed_p.MinOutput=-1;
    motor1.pid_speed_p.MaxOutput=1;
    motor1.pid_speed_p.MAXInte =1;
    motor1.pid_speed_p.Integral=0;
    motor1.pid_speed_p.IntePartValue=30;
    motor1.pid_speed_p.NoneActValue=0;
    motor1.pid_speed_p.Kp=-0.5;
    motor1.pid_speed_p.Ki=-0.3;
    motor1.pid_speed_p.Kd=0;

    motor1.pid_dis.Error=0;
    motor1.pid_dis.LastError=0;
    motor1.pid_dis.Integral=0;
    motor1.pid_dis.Flag_First=0;
    motor1.pid_dis.MaxOutput=1;
    motor1.pid_dis.MinOutput=-1;
    motor1.pid_dis.IntePartValue=100;
    motor1.pid_dis.MAXInte=0.5;
    motor1.pid_dis.NoneActValue=0;
    
    motor1.dis =0;
    motor1.pid_dis.GoalVale=0;
    motor1.pid_dis.Kp=-0.1;
    motor1.pid_dis.Ki=-0.3;
    motor1.pid_dis.Kd=-0.5;
    motor1.pid_speed_p.GoalVale=0;
    
    motor2.number=2;
    motor2.flag_finish=1;
    
    motor2.pid_speed_p.Error=0;
    motor2.pid_speed_p.Flag_First=0;
    motor2.pid_speed_p.MinOutput=-1;
    motor2.pid_speed_p.MaxOutput=1;
    motor2.pid_speed_p.MAXInte =1;
    motor2.pid_speed_p.Integral=0;
    motor2.pid_speed_p.IntePartValue=30;
    motor2.pid_speed_p.NoneActValue=0;
    motor2.pid_speed_p.Kp=-0.5;
    motor2.pid_speed_p.Ki=-0.3;
    motor2.pid_speed_p.Kd=0;

    motor2.pid_dis.Error=0;
    motor2.pid_dis.LastError=0;
    motor2.pid_dis.Integral=0;
    motor2.pid_dis.Flag_First=0;
    motor2.pid_dis.MaxOutput=1;
    motor2.pid_dis.MinOutput=-1;
    motor2.pid_dis.IntePartValue=100;
    motor2.pid_dis.MAXInte=0.5;
    motor2.pid_dis.NoneActValue=0;
    
    motor2.dis =0;
    motor2.pid_dis.GoalVale=0;
    motor2.pid_dis.Kp=-0.1;
    motor2.pid_dis.Ki=-0.3;
    motor2.pid_dis.Kd=-0.5;
    motor2.pid_speed_p.GoalVale=0;
    
    motor3.number=3;
    motor3.flag_finish=1;
    
    motor3.pid_speed_p.Error=0;
    motor3.pid_speed_p.Flag_First=0;
    motor3.pid_speed_p.MinOutput=-1;
    motor3.pid_speed_p.MaxOutput=1;
    motor3.pid_speed_p.MAXInte =1;
    motor3.pid_speed_p.Integral=0;
    motor3.pid_speed_p.IntePartValue=30;
    motor3.pid_speed_p.NoneActValue=0;
    motor3.pid_speed_p.Kp=-0.5;
    motor3.pid_speed_p.Ki=-0.3;
    motor3.pid_speed_p.Kd=0;
    motor3.pid_speed_p.GoalVale=0;    
    motor3.pid_dis.Error=0;
    motor3.pid_dis.LastError=0;
    motor3.pid_dis.Integral=0;
    motor3.pid_dis.Flag_First=0;
    motor3.pid_dis.MaxOutput=1;
    motor3.pid_dis.MinOutput=-1;
    motor3.pid_dis.IntePartValue=100;
    motor3.pid_dis.MAXInte=0.5;
    motor3.pid_dis.NoneActValue=0;
    
    motor3.dis =0;
    motor3.pid_dis.GoalVale=0;
    motor3.pid_dis.Kp=-0.1;
    motor3.pid_dis.Ki=-0.3;
    motor3.pid_dis.Kd=-0.5;
    
	
    motor1.pid_dis.MaxOutput=0.5;
    motor1.pid_dis.MinOutput=-0.5;
    
    motor2.pid_dis.MaxOutput=0.5;
    motor2.pid_dis.MinOutput=-0.5;
	
    motor3.pid_dis.MaxOutput=0.5;
    motor3.pid_dis.MinOutput=-0.5;
}

/**************************************************************************
*Name   :setMotor1Goal
*Funs   :设置(修改)电机1的控制目标
*Input  :motor 是电机控制的结构体指针
*Output :None
**************************************************************************/
void setMotor1Goal(int steps,int speed){
	motor1.flag_finish=0;
	motor1.flag_state=1; //速度控制
	motor1.dis = steps;
	motor1.pid_speed_p.GoalVale = speed;
}

/**************************************************************************
*Name   :setMotor2Goal
*Funs   :设置(修改)电机2的控制目标
*Input  :motor 是电机控制的结构体指针
*Output :None
**************************************************************************/
void setMotor2Goal(int steps,int speed){
    motor2.flag_finish=0;
    motor2.flag_state=1; //速度控制
    motor2.dis = steps;
    motor2.pid_speed_p.GoalVale = speed;
}

/**************************************************************************
*Name   :setMotor3Goal
*Funs   :设置(修改)电机3的控制目标
*Input  :motor 是电机控制的结构体指针
*Output :None
**************************************************************************/
void setMotor3Goal(int steps,int speed){
    motor3.flag_finish=0;
    motor3.flag_state=1; //速度控制
    motor3.dis = steps;
    motor3.pid_speed_p.GoalVale = speed;
}

这份代码非常容易控制成控制n个电机的代码

 

5、步进电机控速控距

我这里使用的SPTA算法来进行加减速的控制,同时控制三个步进电机,可以很容易地扩展成控制任意个步进电机,类似直流控制,步进控制也分为底层的SPTA控制和中层的控制。我的代码做到了可以中途任意变速,但是没有做到中途任意变距离。

底层头文件

#ifndef _MYSPTA_H
#define _MYSPTA_H
#include "sys.h"
typedef volatile int SPTAVAR;

//脉冲1
#define STEP1 PFout(0)

//脉冲2
#define STEP2 PFout(2)

//脉冲3
#define STEP3 PFout(4)

//电机1正方向
#define POSITIVE1 {PFout(1)=0;motor1.Direction=1;}
//电机1负方向
#define NEGATIVE1 {PFout(1)=1;motor1.Direction=0;}

//电机2正方向
#define POSITIVE2 {PFout(3)=0;motor2.Direction=1;}
//电机2负方向
#define NEGATIVE2 {PFout(3)=1;motor2.Direction=0;}

//电机3正方向
#define POSITIVE3 {PFout(5)=0;motor3.Direction=1;}
//电机3负方向
#define NEGATIVE3 {PFout(5)=1;motor3.Direction=0;}

//速度最小50 最大50000
//暂定加速度400000,在600000左右达到极致
#define MAXACCELERATION 1000000
#define SPTAOPEN  {TIM_Cmd(TIM3,ENABLE);TIM_Cmd(TIM4,ENABLE);TIM_Cmd(TIM5,ENABLE);}
#define SPTACLOSE {TIM_Cmd(TIM3,DISABLE);TIM_Cmd(TIM4,DISABLE);TIM_Cmd(TIM5,DISABLE);}
#define SPTACLOSE1 {TIM_Cmd(TIM3,DISABLE);}
#define SPTACLOSE2 {TIM_Cmd(TIM4,DISABLE);}
#define SPTACLOSE3 {TIM_Cmd(TIM5,DISABLE);}

typedef struct STEPMOTOR{
	SPTAVAR PosAccumulator;     //位置累加器
	SPTAVAR PosAdd;          		//位置增加值
	SPTAVAR ActualPosition;  		//实际位置
	SPTAVAR TargetPosion;    		//目标位置,用户输入步进电机运动的步数
	
	SPTAVAR VelAccumulator;     //速度累加器
	SPTAVAR ActualAcceleration; //实际加速度,用户设定的加速度数值
	SPTAVAR VelAdd;             //速度增加值
	SPTAVAR ActualVelocity;     //实际速度
	SPTAVAR TargetVelocity;     //目标速度
	
	u8 SPTAState;								//SPTA的状态:0 空闲 | 1 加速 | 2 匀速 | 3 减速
	
	SPTAVAR FinishFlag;					//状态完成标志,每当一个状态完成时,将它置1,其清零操作由应用层手动清零
	SPTAVAR StepFinishFlag; 		//步数完成标志
	SPTAVAR Direction;					//方向标志  1:正方向 0:负方向
	u8 Mode;										//STPA工作模式选择 0:速度调控模式 1:步数调控模式
	int AccelerationSteps;			//加速步数
	int Myposition;							//系统位置
	u8 number;     							//电机的编号
}StepMotor;


//电机结构参数
extern StepMotor motor1,motor2,motor3;

//初始化
void sptaInit(void);

//速度更新
void velUpdate(StepMotor *motor);

//位置更新
void posUpdate(StepMotor *motor);

//状态调度
void dealState(StepMotor *motor);

//更改状态
void stateTurn(StepMotor *motor,int);

//停止工作判断
void ReadySTop(void);

#endif

底层的c文件

#include "mySPTA.h"
#include "sys.h"
#include "delay.h"
//全局变量--两个电机结构体参数
StepMotor motor1,motor2,motor3;

/**********************************************************
*Name   :sptaInit
*Funs   :初始化SPTA所需的定时器,脉冲输出IO
*Input  :None
*Output :None
***********************************************************/
void sptaInit(void){
    //初始化定时器
    u16 arr=200-1,psc=84-1;  //10KHz
    TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);  ///使能TIM3时钟
    
    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_Cmd(TIM3,DISABLE); //关闭定时器3
    TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStructure);//初始化TIM3
    TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE); //允许定时器3更新中断
	
    NVIC_InitStructure.NVIC_IRQChannel=TIM3_IRQn; //定时器3中断
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0x01; //抢占优先级1
    NVIC_InitStructure.NVIC_IRQChannelSubPriority=0x03; //子优先级3
    NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
    NVIC_Init(&NVIC_InitStructure);
	

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4,ENABLE);  ///使能TIM4时钟
    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_Cmd(TIM4,DISABLE); //关闭定时器4
    TIM_TimeBaseInit(TIM4,&TIM_TimeBaseInitStructure);//初始化TIM4
    TIM_ITConfig(TIM4,TIM_IT_Update,ENABLE); //允许定时器4更新中断
	
    NVIC_InitStructure.NVIC_IRQChannel=TIM4_IRQn; //定时器4中断
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0x01; //抢占优先级1
    NVIC_InitStructure.NVIC_IRQChannelSubPriority=0x03; //子优先级3
    NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
    NVIC_Init(&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_Cmd(TIM5,DISABLE); //关闭定时器5
    TIM_TimeBaseInit(TIM5,&TIM_TimeBaseInitStructure);//初始化TIM5
    TIM_ITConfig(TIM5,TIM_IT_Update,ENABLE); //允许定时器5更新中断
	
    NVIC_InitStructure.NVIC_IRQChannel=TIM5_IRQn; //定时器5中断
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0x01; //抢占优先级1
    NVIC_InitStructure.NVIC_IRQChannelSubPriority=0x03; //子优先级3
    NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
    NVIC_Init(&NVIC_InitStructure);
	
    //初始化控制引脚
    GPIO_InitTypeDef  GPIO_InitStructure;
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE);//使能GPIOF时钟
    //GPIOF0-3初始化设置
      GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1| GPIO_Pin_2| GPIO_Pin_3|GPIO_Pin_4| GPIO_Pin_5;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//普通输出模式
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
    GPIO_Init(GPIOF, &GPIO_InitStructure);//初始化
    GPIO_ResetBits(GPIOF,GPIO_Pin_0 | GPIO_Pin_1| GPIO_Pin_2| GPIO_Pin_3|GPIO_Pin_4| GPIO_Pin_5);//默认低
	
    motor1.SPTAState=0;   //电机1状态,空闲  
    motor2.SPTAState=0;   //电机2状态,空闲
    motor3.SPTAState=0;
	
    motor1.FinishFlag=1;  //电机1结束标志,空闲
    motor2.FinishFlag=1;  //电机2结束标志,空闲
    motor3.FinishFlag=1;
	
    motor1.number=1;
    motor2.number=2;
    motor3.number=3;
}


/****************************************************************
*Name   :TIM3_IRQHandler
*Funs   :定时器3中断函数,速度更新,位置更新,IO输出
*Input  :None
*Output :None
*****************************************************************/
void TIM3_IRQHandler(void){
    if(TIM_GetITStatus(TIM3,TIM_IT_Update)==SET){
    	velUpdate(&motor1);  //速度更新
    	posUpdate(&motor1);  //位置更新
    	dealState(&motor1);  //状态
    	if(motor1.SPTAState==0){
            SPTACLOSE1;
	}
    }
    TIM_ClearITPendingBit(TIM3,TIM_IT_Update);//清楚中断标志
}

/****************************************************************
*Name   :TIM4_IRQHandler
*Funs   :定时器4中断函数,速度更新,位置更新,IO输出
*Input  :None
*Output :None
*****************************************************************/
void TIM4_IRQHandler(void){
    if(TIM_GetITStatus(TIM4,TIM_IT_Update)==SET){
	velUpdate(&motor2);  //速度更新
	posUpdate(&motor2);  //位置更新
	dealState(&motor2);  //状态
	if(motor2.SPTAState==0){
	    SPTACLOSE2;
	}
    }
    TIM_ClearITPendingBit(TIM4,TIM_IT_Update);//清楚中断标志
}

/****************************************************************
*Name   :TIM5_IRQHandler
*Funs   :定时器5中断函数,速度更新,位置更新,IO输出
*Input  :None
*Output :None
*****************************************************************/
void TIM5_IRQHandler(void){
    if(TIM_GetITStatus(TIM5,TIM_IT_Update)==SET){
	velUpdate(&motor3);  //速度更新
	posUpdate(&motor3);  //位置更新
	dealState(&motor3);  //状态
	if(motor3.SPTAState==0){
	    SPTACLOSE3;
	}
    }
    TIM_ClearITPendingBit(TIM5,TIM_IT_Update);//清楚中断标志
}

/************************************************************
*Name   :velUpdate
*Funs   :速度更新
*Input  :None
*Output :None
*************************************************************/
void velUpdate(StepMotor *motor){
    if(motor->ActualVelocity!=motor->TargetVelocity){
        motor->VelAccumulator+=motor->ActualAcceleration;    //速度累加器+实际加速度
	motor->VelAdd = motor->VelAccumulator>>17;           //右移16位,判断速度累加器是否溢出
	motor->VelAccumulator -= motor->VelAdd << 17;        //如果溢出,则速度累加器去掉溢出部分
	if(motor->ActualVelocity<motor->TargetVelocity){
	    motor->ActualVelocity = (motor->ActualVelocity+motor->VelAdd)<motor->TargetVelocity?(motor->ActualVelocity+motor->VelAdd):motor->TargetVelocity;
	}else if(motor->ActualVelocity>motor->TargetVelocity){
	    motor->ActualVelocity = (motor->ActualVelocity-motor->VelAdd)>motor->TargetVelocity?(motor->ActualVelocity-motor->VelAdd):motor->TargetVelocity;
	}
    }else{
	motor->VelAccumulator=0;
	motor->VelAdd=0;
    }
}

void stepOne(u8 which){
    if(which==1){//电机1
	STEP1 =1;                       //产生一个脉冲
	for(int j=0;j<100;j++) //经过测试,84个脉冲接近5us,或2.5us
            __nop();
    	    STEP1=0;
	}else if(which==2){//电机2
	    STEP2 =1;                       //产生一个脉冲
	    for(int j=0;j<100;j++) //经过测试,84个脉冲接近5us,或2.5us
		__nop();
	    STEP2=0;
    }else if(which==3){
	STEP3 =1;                       //产生一个脉冲
	for(int j=0;j<100;j++) //经过测试,84个脉冲接近5us,或2.5us
    	    __nop();
	STEP3=0;
    }
}

/**************************************************************************
*Name   :posUpdate
*Funs   :位置更新
*Input  :None
*Output :None
***************************************************************************/
void posUpdate(StepMotor *motor){
    motor->PosAccumulator+=motor->ActualVelocity;  //位置累加器+实际速度
    motor->PosAdd = motor->PosAccumulator >> 17;   //左移17位,判断是否溢出
    motor->PosAccumulator -= motor->PosAdd << 17;  //如果位置累加器溢出,则去掉溢出部分
    if(motor->PosAdd!=0){
	if(motor->Mode){ //计步模式
	    if(motor->SPTAState==1){
		motor->AccelerationSteps++;
	        if(motor->AccelerationSteps>(motor->TargetPosion/2)){//转至减速状态
		    motor->TargetVelocity=0;
		    stateTurn(motor,3);
		}
	    }else if(motor->SPTAState==2){
		if((--motor->TargetPosion)<=0){ //匀速过程完毕,开始减速至0
		    motor->TargetVelocity=0;
		    stateTurn(motor,3);
	    }
        }
    }
    stepOne(motor->number);
    if(motor->Direction)
        motor->Myposition++;
    else
        motor->Myposition--;
    }
}

/*****************************************************************
*Name   :dealState
*Funs   :状态调度
*Input  :motor表示电机参数结构体
*Output :None
******************************************************************/
void dealState(StepMotor *motor){
    if((motor->SPTAState==1||motor->SPTAState==3)&&(motor->ActualVelocity==motor->TargetVelocity)){//如果加减速,却实际速度等于目标速度
        motor->FinishFlag=1;    //将完成标志置1
	if(motor->SPTAState==3&&motor->ActualVelocity==0){//空闲状态
	    stateTurn(motor,0);
	}else{
	    stateTurn(motor,2);
	}
    }else if(motor->SPTAState==2&&(motor->ActualVelocity!=motor->TargetVelocity)){//如果匀速,却实际速度不等于目标速度
	motor->FinishFlag=1;    //将完成标志置1
	if(motor->ActualVelocity>motor->TargetVelocity)
	    stateTurn(motor,3);//减速
	else
	    stateTurn(motor,1);//加速
	}else if(motor->SPTAState==0){ //空闲
	    stateTurn(motor,0);
    }
}

/*****************************************************************
*Name   :stateTurn
*Funs   :更改电机状态
*Input  :State 表示要转成的状态 1:加速 2:匀速 3:减速;motor是电机参数结构体
*Output :None
******************************************************************/
void stateTurn(StepMotor * motor,int State){
    if(motor->Mode&&motor->SPTAState==3){ //步数完成,清零加速过程的距离
	motor->AccelerationSteps=0;
	motor->StepFinishFlag=1;
    }
    motor->SPTAState=State;
    if(State==1){//加速
	motor->ActualAcceleration = MAXACCELERATION;
    }else if(State==2){//变为匀速
    if(motor->Mode){//计步
	motor->TargetPosion = motor->TargetPosion -2*motor->AccelerationSteps;
	if(motor->TargetPosion<0)
	    motor->TargetPosion=0;
	}
        motor->ActualAcceleration = 0;
    }else if(State==3){//减速
	motor->ActualAcceleration = MAXACCELERATION;
    }else if(State==0){
	motor->ActualAcceleration = 0;
	motor->TargetVelocity=0;
	motor->TargetPosion=0;
    }
}


/*****************************************************************
*Name   :ReadySTop
*Funs   :判断定时器
*Input  :None
*Output :None
******************************************************************/
void ReadySTop(void){
    if(motor1.SPTAState==0&&motor2.SPTAState==0){//停止
	SPTACLOSE;
    }
}

中层头文件

#ifndef _SPTAAPP_H
#define _SPTAAPP_H
#include "sys.h"
#include "mySPTA.h"

//启动,以指定速度向前走
void sPTAStart(int vel1,int vel2,int vel3);

//停止
void sPTAStop();

//调速
void setVel(StepMotor* motor,int vel);

//向某个方向走指定步数,以指定速度
void sPTAGo(int Steps1,int vel1,int Steps2,int vel2,int Steps3,int vel3);
//急刹
void stop();

//复位
void toZero(void);
#endif

中层c文件

#include "mySPTA.h"
#include "SPTAAPP.h"
#include "usart.h"
#include "LED.h"
#include "delay.h"
#include "key.h"
#include <math.h>

/*****************************************************************
*Name   :changeDir
*Funs   :更改电机方向
*Input  :which 表示哪一个电机,范围1-3;dir表示方向,0负方向,1正方向
*Output :None
******************************************************************/
static void changeDir(u8 which,u8 dir){
    if(which==1){
    	if(dir==0){
	    NEGATIVE1;
        }else if(dir==1){
    	    POSITIVE1;
	}
    }else if(which==2){
	if(dir==0){
	    NEGATIVE2;
	}else if(dir==1){
	    POSITIVE2;
	}
    }else if(which==3){
	if(dir==0){
	    NEGATIVE3;
        }else if(dir==1){
	    POSITIVE3;
	}
    }
}

/*****************************************************************
*Name   :sPTAStart
*Funs   :三个电机以一个指定的速度开始运动
*Input  :vel1表示电机1的速度 vel2表示电机2的速度 vel3表示电机3的速度 
*Output :None
******************************************************************/
void sPTAStart(int vel1,int vel2,int vel3){
    if(!SYSTEMKEY)
	return;
    //电机1
    if(vel1>=0){
	motor1.TargetVelocity = vel1; //设置目标速度
	changeDir(motor1.number,1);
    }else{
	motor1.TargetVelocity = -vel1; //设置目标速度
	changeDir(motor1.number,0);
    }
    stateTurn(&motor1,1);         //加速
    //电机2
    if(vel2>=0){
	motor2.TargetVelocity = vel2; //设置目标速度
	changeDir(motor2.number,1);
    }else{
	motor2.TargetVelocity = -vel2; //设置目标速度
	changeDir(motor2.number,0);
    }
    stateTurn(&motor2,1);         //加速
    //电机3
    if(vel3>=0){
	motor3.TargetVelocity = vel3; //设置目标速度
	changeDir(motor3.number,1);
    }else{
	motor3.TargetVelocity = -vel3; //设置目标速度
	changeDir(motor3.number,0);
    }
    stateTurn(&motor3,1);         //加速
	
    //开始
    SPTAOPEN;
}

/**************************************************************
*Name   :sPTAStop
*Funs   :三个电机停止运动,三个电机停止后返回
*Input  :None
*Output :None
***************************************************************/
void sPTAStop(void){
    motor1.TargetVelocity=0; //电机1目标速度改为0
    stateTurn(&motor1,3);    //状态转为减速
    motor2.TargetVelocity=0; //电机2目标速度改为0
    stateTurn(&motor2,3);    //状态转为减速
    motor3.TargetVelocity=0; //电机3目标速度改为0
    stateTurn(&motor3,3);    //状态转为减速
    while(!((motor1.SPTAState==0)&&(motor2.SPTAState==0)&&(motor3.SPTAState==0)))
	;
	
    //定时器关闭
    SPTACLOSE;
}

/****************************************************************
*Name   :setVel
*Funs   :将电机速度改为指定速度,反向时由于电机先减速至0然后再加速,所以会产生阻塞
*Input  :motor表示电机结构参数,vel 表示指定的速度
*Output :None
****************************************************************/
void setVel(StepMotor* motor,int vel){
    if(!SYSTEMKEY)
	return;
    if(vel==motor->TargetVelocity&&((vel>0&&motor->Direction)||(vel<0&&!motor->Direction)))  //等速
	return;
    if(vel>0&&!motor->Direction){
	motor->TargetVelocity=0; //目标速度改为0
	stateTurn(motor,3);     //状态转为减速
	motor->FinishFlag=0;
        SPTAOPEN;
	while(!motor->FinishFlag)
	    ;
	changeDir(motor->number,1);
	motor->TargetVelocity = vel;
	stateTurn(motor,1);  //加速
    }else if(vel<0&&motor->Direction){
	motor->TargetVelocity=0; //目标速度改为0
	stateTurn(motor,3);     //状态转为减速
	motor->FinishFlag=0;
	SPTAOPEN;
	while(!motor->FinishFlag)
	    ;
	changeDir(motor->number,0); //电机方向改为负
	motor->TargetVelocity = -vel;
	stateTurn(motor,1);  //加速
    }else if(vel>0){
	motor->TargetVelocity = vel;
	if(vel>motor->TargetVelocity){
	    stateTurn(motor,1);      //加速
	}else if(vel<motor->TargetVelocity){
	    stateTurn(motor,3);      //减速
        }
    }else if(vel<0){
	motor->TargetVelocity = -vel;
	if(-vel>motor->TargetVelocity){
	    stateTurn(motor,3);      //加速
	}else if(-vel<motor->TargetVelocity){
	    stateTurn(motor,1);      //减速
	}
    }else if(vel==0){
	motor->TargetVelocity=0; //目标速度改为0
	stateTurn(motor,3);     //状态转为减速
    }
}


/******************************************************************
*Name   :sPTAGo
*Funs   :两个电机向一个方向走固定步数
*Input  :Steps1 vel1 表示电机1的步数和速度,Steps2 vel2 表示电机2的步数和速度,Steps3 vel3 表示电机3的步
				 数和速度 ,其中steps的正负表示方向,velx为正数
*Output :None
******************************************************************/
void sPTAGo(int Steps1,int vel1,int Steps2,int vel2,int Steps3,int vel3){
    if(!SYSTEMKEY)
	return;
    motor1.Mode=1;   //步数调控模式
    motor2.Mode=1;   //步数调控模式
    motor3.Mode=1;   //步数调控模式
    if(Steps1>=0){
	changeDir(motor1.number,1);
    }else{
	changeDir(motor1.number,0);
	Steps1 = -Steps1;
    }
    if(Steps2>=0){
	changeDir(motor2.number,1);
    }else{
	changeDir(motor2.number,0);
	Steps2 = -Steps2;
    }
    if(Steps3>=0){
	changeDir(motor3.number,1);
    }else{
	changeDir(motor3.number,0);
	Steps3 = -Steps3;
    }
    motor1.AccelerationSteps=0;
    motor1.TargetVelocity=vel1;
    motor1.TargetPosion = Steps1;
    stateTurn(&motor1,1);
	
    motor2.AccelerationSteps=0;
    motor2.TargetVelocity=vel2;
    motor2.TargetPosion = Steps2;
    stateTurn(&motor2,1);
	
    motor3.AccelerationSteps=0;
    motor3.TargetVelocity=vel3;
    motor3.TargetPosion = Steps3;
    stateTurn(&motor3,1);

    SPTAOPEN;
    while(!((motor1.SPTAState==0)&&(motor2.SPTAState==0)&&(motor3.SPTAState==0))){
    	__nop();
    }
    SPTACLOSE;
    motor1.Mode=0;
    motor2.Mode=0;
    motor3.Mode=0;
}

/*********************************************************************
*Name   :toZero
*Funs   :复位
*Input  :None
*Output :None
************************************************************************/
void toZero(void){
    if(SYSTEMKEY&&LIMIT1)
	;
}

/************************************************************************
*Name   :stop
*Funs   :直接停下
*Input  :None
*Output :None
**************************************************************************/
void stop(){
    motor1.TargetVelocity=0;
    motor2.TargetVelocity=0;
    motor3.TargetVelocity=0;
    motor1.SPTAState=0;
    motor2.SPTAState=0;
    motor3.SPTAState=0;
    SPTACLOSE;
}

当然,同样的,这里只是把核心代码贴出来了,如果需要完整的代码,可以看我最后面给出的链接,花一些C币购买,也可以关注我的微信公众号 “山人彤” ,回复 “STM32电赛准备“ 即可免费获取

当然,如果有问题,可以加我好友

qq:1826249828

完整的2019年电赛准备的32工程链接:https://download.csdn.net/download/qq_39545674/11523012

完整的2018年电赛准备的32工程链接:https://download.csdn.net/download/qq_39545674/11523033

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

2019年电赛准备程序STM32版本 的相关文章

  • JDK8 lamdba

    1 方法调用
  • try catch finally执行顺序

    try catch finally执行顺序 内容简介代码示例执行结果 xff1a 分析 内容简介 代码中经常会在catch 或者finally中再次抛出异常 xff0c 传给调用者 xff08 如文件上传处理工具类 xff09 xff0c
  • C++中全局变量的使用

    在C 43 43 中全局变量的定义与使用做一下下面简单的记录 xff0c 方便日后查阅 xff0c 也与大家一起学习 1 全局变量的声明 1 在头文件 h中进行声明 xff0c 如果在此文件之外也要使用该变量 xff0c 则在变量声明前加e
  • mysql innodb

    mysql s sql优化 io 使用limit返回用到的字段 xff0c 不要返回太多无用字段和一些大字段 避免索引失效 创建索引 dd
  • C语言之什么是大小端,数组大小端,位域大小端,结构体大小端

    公众号 xff1a 嵌入式不难 本文仅供参考学习 xff0c 如有错误之处 xff0c 欢迎留言指正 理解大小端之前必须明白的三点 内存地址始终以字节为单位大小端只针对基本数据类型存在位域的情况下 xff0c 小端模式下先定义的位域从LSB
  • <Linux开发> linux应用开发-之-can通信开发例程

    xff1c Linux开发 xff1e linux应用开发 之 can通信开发例程 一 简介 对于Can通信的相关介绍 xff0c 读作不过多介绍了 xff0c 网上其它网友的介绍有很多 二 环境搭建 本次测试can通信的应用例程是运行在u
  • 使用RGBD相机模拟激光数据,用于move_base中添加新图层避障功能

    参考文章 xff1a ROS depthimage to laserscan ROS导航 向cost map中添加超声波障碍图层 一 RGBD模拟激光雷达数据 我使用的是RealSense双目相机 xff0c 首先使用的是ros自带的功能包
  • 公安视图库1400的协议

    一 平台注册 注销 1 1 注册 注销流程 注册流程1如图所示 图 1 下级平台主动向上级平台注册 xff1b 注册鉴权信息 xff08 用户名和密码 xff09 由上级平台提供 xff1b 1 2 报文说明 1 2 1 下级第一次注册 P
  • linux cmake交叉编译

    linux cmake交叉编译 linux cmake交叉编译下载测试添加依赖库需要注意的问题 linux cmake交叉编译 linux cmake交叉编译 xff0c 使用环境 xff1a ubuntu cmake gcc arm 10
  • OpenCV中使用RANSAC算法实现多张图像拼接

    思路 xff1a xff08 1 xff09 获取图像的特征点 xff0c 将每张图片的特征点保存到一个vector中 xff1b xff08 2 xff09 通过特征点匹配的方法 xff0c 找到每张图片的共有特征点 xff0c 并将其保
  • 数据安全--安全网关

    简介 对于数据安全来说 xff0c 安全网关是数据安全建设中极其重要的一部分 xff0c 我这里把它做了几种分类 xff0c 如下 xff1b 对内 xff1a 零信任安全网关 xff08 7层和4层 xff09 对外 xff1a 应用安全
  • idea使用svn拉取源码,创建分支,合并分支教程

    svn拉取源码 xff0c 创建分支 xff0c 合并分支教程 最近对svn的分支使用比较感兴趣了 xff0c 花时间研究了一下 xff0c 分享给大家 xff1a 此文章记录了idea使用svn的过程 xff1a 包括拉取源码 建立分支
  • Qt中QSS的简单使用

    样式表的组成 样式表由两大元素组成 xff1a 选择器和声明 选择器实际上可以理解为对象 xff0c 声明则是对该对象的属性的设置 样式表的使用 方法一 xff1a 在代码中调用setStyleSheet 来设置样式 xff1b 方法二 x
  • jetson nano pytorch安装及文件系统扩容

    jetson nano扩容 我安装的系统是16GB的 xff0c 如果不扩容的话文件系统只有16GB 首先安装gparted span class token function sudo span span class token func
  • Robust Real-time UAV Replanning Using Guided Gradient-based Optimization and Topological Paths

    fastplanner2 摘要相关工作基于梯度的路径优化拓扑路径规划 路径制导轨迹优化A 优化失效分析B 问题公式化 拓扑路径搜索A 拓扑等价关系B 拓扑路径图C 路径缩短和修剪 实时拓扑路径规划 这篇论文是港科大开源的无人机运动规划fas
  • curl 参数配置详情

    第一类 xff1a 对于下面的这些option的可选参数 xff0c value应该被设置一个bool类型的值 xff1a CURLOPT AUTOREFERER 当根据Location 重定向时 xff0c 自动设置header中的Ref
  • 分区隔离与数据安全交换技术

    一 背景 网络的物理隔离是很多网络设计者都不愿意的选择 xff0c 网络上要承载专用的业务 xff0c 其安全性一定要得到保障 然而网络的建设就是为了互通的 xff0c 没有数据的共享 xff0c 网络的作用也缩水了不少 xff0c 因此网
  • g++编译命令大全

    gcc amp g 43 43 现在是gnu中最主要和最流行的c amp c 43 43 编译器 g 43 43 是c 43 43 的命令 xff0c 以 cpp为主 xff0c 对于c语言后缀名一般为 c 这时候命令换做gcc即可 其实是
  • Ubuntu 14.04 配置iptables防火墙

    Ubuntu默认安装是没有开启任何防火墙的 xff0c 为了学习redis数据库 xff0c 需要启用防火墙设置 whereis iptables 查看系统是否安装防火墙可以看到 iptables sbin iptables usr sha
  • Qt显示汉字乱码问题

    转载自http blog csdn net u012790503 article details 52485263 测试1 新建test工程用于测试 xff0c main c文件内容如下 xff1a span class hljs prep

随机推荐

  • Anaconda使用总结

    原文地址 xff1a https www jianshu com p 2f3be7781451 序 Python易用 xff0c 但用好却不易 xff0c 其中比较头疼的就是包管理和Python不同版本的问题 xff0c 特别是当你使用Wi
  • A*寻路算法介绍

    你是否在做一款游戏的时候想创造一些怪兽或者游戏主角 xff0c 让它们移动到特定的位置 xff0c 避开墙壁和障碍物呢 xff1f 如果是的话 xff0c 请看这篇教程 xff0c 我们会展示如何使用A星寻路算法来实现它 xff01 在网上
  • RTK固定解什么意思

    RTK固定解什么意思 金丝大环刀的回答 知乎 RTK固定解什么意思 知乎 rtk固定解一般是指载波相位窄巷整周模糊度已经固定之后的解算结果 narrow int xff0c 精度可达厘米级至毫米级 除此之外 xff0c 还有宽巷解 xff0
  • QtCreate由MinGW编译的项目,换为MSVC编译器后编译无法通过

    解决方法 xff1a 1 将文件的编码格式设置为utf 8且BOM格式 xff1b 2 在 pro文件中添加如下代码 msvc QMAKE CFLAGS 43 61 utf 8 QMAKE CXXFLAGS 43 61 utf 8 其中方法
  • 2D nav goal 后小车不能沿着路径走

    2D nav goal 后小车不能沿着路径走 解决方法 xff1a 下载turtlebot3的代码 xff0c 编译 xff0c 把原来的模型改成turtlebot3的 真正的错因 xff1a xacro在melodic版本上的inorde
  • vscode配置header指令添加头部注释或KoroFileHeader自动添加头部解释

    1 xff08 1 xff09 新建Python文件输入header添加头部注释 vscode gt file gt Perferences gt User Snippets gt python 也可选其他语言 xff1a 则会生成pyth
  • Android Okhttp工具类的封装(okhttpUtils)

    Android Okhttp工具类 Android Okhttp工具类的封装 xff08 okhttpUtils xff09 近期的项目频繁使用到了网络交互 xff0c 采用的是第三方类库okhttp 以下是我对okhttp主要使用到的方法
  • 通过requests登录店小秘解决验证码问题

    登录常用手段就是 request post请求 selenium等自动化工具 这里讲一下使用requests实现自动登录 网站 现在网站登录基本上都有验证码 xff0c requests登录的难点在于将验证码与账号联系起来 xff0c 这里
  • 关于ubuntu18系统~/.bashrc文件中ros环境无法生效问题

    项目场景 xff1a ubuntu18 43 melodic 问题描述 xff1a 这几天一直遇到一个问题 xff0c 就是ubuntu18中ros的环境问题 xff0c 最开始的时候是一直用到好好的ros系统 xff0c 突然说无法识别r
  • 关于move_base无法加载的问题[move_base-11] process has died

    项目场景 xff1a kinetic下使用move base进行导航 问题描述 xff1a 之前move base使用的好好的 xff0c 结果突然之间不能用了 xff0c 出现move base功能包无法加载的情况 报错如下 xff1a
  • Jetson Nano利用普通引脚进行PWM波输出

    这篇博客我就简单介绍一下好了 xff0c 不深入讲了 需要注意的是 xff0c 我这里用到了ROS xff0c 所以运行之前需要安装ROS 初始化中有一个12和50 xff0c 分别代表频率 xff0c 完之后他就会让pin12自动输出50
  • Git基础 - git tag 一文真正的搞懂git标签的使用

    1 什么是tag 1 1 tag的简单理解 tag 中文我们可以称它为 标签 简单的理解 xff0c tag 就是 对某次 commit 的一个标识 xff0c 相当于起了一个别名 例如 xff0c 在项目发布某个版本的时候 xff0c 针
  • C++学习笔记十六:使用OpenCv(c++)调用yolo模型实现目标检测

    一 前言 因为之前都是直接使用python编程来实现目标检测 xff0c 而且是直接使用模型 于是就想了解一下使用c 43 43 语言如何进行目标检测 xff0c 也能帮助自己更好的熟悉c 43 43 的语法 简单起见 xff0c 使用op
  • requests python

    requests是模拟客户端向服务端发送http请求的模块 可以方便的对网页进行爬取 xff0c 是学习python爬虫的较好的http请求模块 1 安装 pip install requests 2 使用方法 方法解释requests r
  • QGraphicsView加入到布局,所在的窗口变大,视图和场景都变大,使场景中的矩形也变大

    需求 窗口变化时 xff0c 窗口中的矩形框也变化 效果展示 当窗口变大时其矩形框也变大 要求绿色矩形框与蓝色背景的边距始终为10 代码 直接上项目的代码 项目结构 各文件的代码 main cpp span class token macr
  • TX2通过串口或CAN和其他设备通信(一)--- 串口通信

    TX2通过串口或CAN和其他设备通信 xff08 一 xff09 串口通信 背景介绍 Jetson TX2是NVIDIA推出的一款嵌入式开发平台 xff0c TX2所提供的性能为早前版本的两倍 xff0c 即能够以两倍以上的功效运行 xff
  • Micropython——九轴传感器(MPU6050)的使用及算法(二)

    前言 xff1a 在上篇文章中 xff0c 简单地实现了九轴传感器 xff08 MPU6050 xff09 的获取加速度 角速度以及温度的数值 但是 xff0c 我们知道 xff0c 对于MPU6050来说 xff0c 其提供的数据会夹杂有
  • Micropython——九轴传感器(MPU6050)的使用及算法(三)

    简介 xff1a 上篇文章中 xff0c 关于九轴传感器 xff08 MPU6050 xff09 xff0c 学习其如何对其生成的数据偏移进行校准 xff0c 这节课我们来将其中的一个问题来进行解决 关于MPU6050地址问题 xff0c
  • C语言库函数——string.h

    目录 摘要 xff1a 一 头文件 string h 中定义的函数 strcpy 用法 xff1a 代码示例 xff1a 参数 xff1a memchr 用法 xff1a 代码示例 xff1a 参数 xff1a strlen 用法 xff1
  • 2019年电赛准备程序STM32版本

    现在是2019年8月11号 xff0c 昨天电赛结束 xff0c 不愿意好的东西随时间而消失不见 xff0c 所以将准备的程序总结一下 xff0c 分享出来 目录 一 电赛的故事 二 准备程序 1 PID 2 滤波 3 菜单 4 直流编码电