2019年電賽準備程序STM32版本

現在是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;
}

當然,同樣的,這裏只是把核心代碼貼出來了,如果需要完整的代碼,可以看我最後面給出的鏈接

當然,如果有問題,可以加我好友

qq:1826249828

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章