Multitasking - Krokový motor + tlačidlá
Doporučené preštudovať: Krokové Motory | Riadenie Unipolárneho Krokového motora #2 | Interrupty | Oživujemé tlačidlá #2
Tento výtvor vychádza z viacerých kapitol ako "Ovládame tlačidlá #1 - #2", kapitoly o krokových motoroch
a z kapitoly venovanej Interruptom.
U tochto projektu vytvorím interface medzi unipolárnym krokovým motorom a 4 tlačidlami na ovládanie rýchlosti,
smer a celkové zapnutie/vypnutie motora.
Interface bude bežať pod dvomi nezávislými vláknami.
Čo sa týka kódu, upravím kód z predchádzajúcich projektov + sefektívnim logiku tlačidiel.
Návrh schémy:
Návrh kódu:
Navrhnutá štruktúra konfigurácie krokového motoru, tlačidiel, polohovania a ostatných pomocných premenných.
stepper.h |
struct _unipolar_stepper_motor_ { struct __attribute__ ((packed)) { float err; volatile unsigned int *Port; volatile unsigned int *Tris; int * Pins; int pin_size; }Config; struct __attribute__ ((packed)) { int from; int to; int routing; int left; int right; struct __attribute__ ((packed)) { struct __attribute__ ((packed)) { bool switches; volatile unsigned int *Port; volatile unsigned int *Tris; int pin_size; }Config; int on_off; int left; int right; int slow; int fast; }Switch; }Control; struct __attribute__ ((packed)) { int i; int a; int b; int set_step; int button_ready; int max_speed; int min_speed; int current_speed; int tmp_speed; int fast_push_count; bool is_on; bool is_left; bool is_right; bool is_slow; bool is_fast; }Tmp; }Stepper; |
Navrhnutá časť riadiaca krokový motor.
stepper.h |
void Stepper_Init(void) { int stepper_pins[] = {1,2,3,7}; int switch_pins[] = {Stepper.Control.Switch.on_off, Stepper.Control.Switch.left, Stepper.Control.Switch.right, Stepper.Control.Switch.slow, Stepper.Control.Switch.fast }; Stepper.Config.err = 245; if (*Stepper.Config.Port == PORTA && PORTA != __No__Used__ && TRISA != __No__Used__) { Stepper.Config.Tris = uintptr &TRISA; Stepper.Config.err=0; } if (*Stepper.Config.Port == PORTB && PORTB != __No__Used__ && TRISB != __No__Used__) { Stepper.Config.Tris = uintptr &TRISB; Stepper.Config.err=0; } if (*Stepper.Config.Port == PORTC && PORTC != __No__Used__ && TRISC != __No__Used__) { Stepper.Config.Tris = uintptr &TRISC; Stepper.Config.err=0; } if (*Stepper.Config.Port == PORTD && PORTD != __No__Used__ && TRISD != __No__Used__) { Stepper.Config.Tris = uintptr &TRISD; Stepper.Config.err=0; } if(Stepper.Control.Switch.Config.switches == true && Stepper.Config.err==0 ) { Stepper.Config.err = 245; if (*Stepper.Control.Switch.Config.Port == PORTA && PORTA != __No__Used__ && TRISA != __No__Used__) { Stepper.Control.Switch.Config.Tris = uintptr &TRISA; Stepper.Config.err=-1; } if (*Stepper.Control.Switch.Config.Port == PORTB && PORTB != __No__Used__ && TRISB != __No__Used__) { Stepper.Control.Switch.Config.Tris = uintptr &TRISB; Stepper.Config.err=-1; } if (*Stepper.Control.Switch.Config.Port == PORTC && PORTC != __No__Used__ && TRISC != __No__Used__) { Stepper.Control.Switch.Config.Tris = uintptr &TRISC; Stepper.Config.err=-1; } if (*Stepper.Control.Switch.Config.Port == PORTD && PORTD != __No__Used__ && TRISD != __No__Used__) { Stepper.Control.Switch.Config.Tris = uintptr &TRISD; Stepper.Config.err=-1; } } if(Stepper.Config.err==0 || Stepper.Config.err==1) { Stepper.Config.pin_size = sizeof(stepper_pins)/sizeof(int); Stepper.Config.Pins = malloc(Stepper.Config.pin_size); Stepper.Control.left = 0; Stepper.Control.right = 1; for (Stepper.Tmp.i = 0; Stepper.Tmp.i < Stepper.Config.pin_size; Stepper.Tmp.i++) { Stepper.Config.Pins[Stepper.Tmp.i] = stepper_pins[Stepper.Tmp.i]; } for (Stepper.Tmp.i = 0; Stepper.Tmp.i < Stepper.Config.pin_size; Stepper.Tmp.i++) { Init_Port(*Stepper.Config.Port,Stepper.Config.Pins[Stepper.Tmp.i],"digital"); Config_OUT(*Stepper.Config.Tris,Stepper.Config.Pins[Stepper.Tmp.i]); Bit_Clr(*Stepper.Config.Port,Stepper.Config.Pins[Stepper.Tmp.i]); } if (Stepper.Config.err==-1) { Stepper.Control.Switch.Config.pin_size = sizeof(switch_pins)/sizeof(int); for(Stepper.Tmp.i = 0;Stepper.Tmp.i < Stepper.Control.Switch.Config.pin_size; Stepper.Tmp.i++) { Init_Port(*Stepper.Control.Switch.Config.Port,switch_pins[Stepper.Tmp.i],"digital"); Config_IN(*Stepper.Control.Switch.Config.Tris,switch_pins[Stepper.Tmp.i]); Bit_Clr(*Stepper.Control.Switch.Config.Port,switch_pins[Stepper.Tmp.i]); } Stepper.Tmp.is_on = false; Stepper.Tmp.is_left = false; Stepper.Tmp.is_right = false; Stepper.Tmp.button_ready = 0; } Stepper.Tmp.max_speed = 3000; Stepper.Tmp.min_speed = 20; Stepper.Tmp.fast_push_count = -1; } } void Stepper_Go(void) { if(Stepper.Tmp.is_left==true && Stepper.Tmp.is_right==false) { Stepper_Controll(BACKWARD,30,1); } else { Stepper_Controll(FORWARD,30,1); } } void Stepper_Controll(int way,int timer_ms,int rount) { if(way == Stepper.Control.left) { Stepper.Control.from = 0; Stepper.Control.to = Stepper.Config.pin_size; Stepper.Control.routing = BACKWARD; } else if(way == Stepper.Control.right) { Stepper.Control.from = Stepper.Config.pin_size-1; Stepper.Control.to = -1; Stepper.Control.routing = FORWARD; } if(Stepper.Tmp.fast_push_count == -1) { Stepper.Tmp.current_speed = timer_ms; Stepper.Tmp.fast_push_count=0; } for (Stepper.Tmp.b=0;Stepper.Tmp.b < rount && Stepper.Tmp.is_on == true;Stepper.Tmp.b++) { for (Stepper.Tmp.i = Stepper.Control.from; Stepper.Tmp.i!=Stepper.Control.to;) { for (Stepper.Tmp.a=Stepper.Control.from; Stepper.Tmp.a!=Stepper.Control.to;) { if(Stepper.Tmp.i==0) //1.takt - start { if(Stepper.Tmp.a==0 || Stepper.Tmp.a==1) { Bit_Set(*Stepper.Config.Port,Stepper.Config.Pins[Stepper.Tmp.a]); } else { Bit_Clr(*Stepper.Config.Port,Stepper.Config.Pins[Stepper.Tmp.a]); } } if(Stepper.Tmp.i==1) //2.takt - start { if(Stepper.Tmp.a==1 || Stepper.Tmp.a==2) { Bit_Set(*Stepper.Config.Port,Stepper.Config.Pins[Stepper.Tmp.a]); } else { Bit_Clr(*Stepper.Config.Port,Stepper.Config.Pins[Stepper.Tmp.a]); } } if(Stepper.Tmp.i==2) //3.takt - start { if(Stepper.Tmp.a==2 || Stepper.Tmp.a==3) { Bit_Set(*Stepper.Config.Port,Stepper.Config.Pins[Stepper.Tmp.a]); } else { Bit_Clr(*Stepper.Config.Port,Stepper.Config.Pins[Stepper.Tmp.a]); } } if(Stepper.Tmp.i==3) //4.takt - start { if(Stepper.Tmp.a==3 || Stepper.Tmp.a==0) { Bit_Set(*Stepper.Config.Port,Stepper.Config.Pins[Stepper.Tmp.a]); }else { Bit_Clr(*Stepper.Config.Port,Stepper.Config.Pins[Stepper.Tmp.a]); } } if(Stepper.Control.routing == Stepper.Control.left) { Stepper.Tmp.a++; } else { Stepper.Tmp.a--; } } Delay_ms(Stepper.Tmp.current_speed); if(Stepper.Control.routing == Stepper.Control.left) { Stepper.Tmp.i++; } else { Stepper.Tmp.i--; } } } } |
Táto časť modulu pozostáva z troch funkcií Stepper_Init, Stepper_Go, Stepper_Controll.
Funkcia Stepper_Init ako z predošlých projektov inicializovanie rozhranie motoru a tláčidiel, definuje maximálnu/minimálnu rýchlosť motora, atď. .
Premennou Stepper.Config.err sa identifikuje stav správnosti definície premennej - pri chybnej alebo žiadnej definícií sa inicializácia nevykoná.
Príznak tejto premennej môžme používať globálne ako podmienený blok. Ak sa inicializovanie nevykoná kôli chybe, tak sa jednotlivé obsahy ostatných funkcií nevykonajú a ušetrí sa ďalší čas.
Funkcia Stepper_Controll je funkcia, ktorá mala v minulých projektov názov Stepper_Go.
Riadi krokový motor - rieši logiku smeru, rýchlosť otáčiek, pristupuje priamo do globálnej štruktúry.
Funkcia Stepper_Go je funkcia, ktorá je volaná priamo z vlákna. Táto funkcia nemá žiaden parameter a volá funkciu Stepper_Controll s defaultnými parametrami - smer, rýchlosť, počet otáčok. Defaultné hodnoty určujú parametre motora pri prvotnom spustení.
Pri ovládaní tláčidlami, sa o riadenie motora stará vnútorná logika funkcie Stepper_Controll.
Návrh modulu ovládania tláčidiel.
stepper.h |
void StepperSwitch (void) { if((Get_Switch(*Stepper.Control.Switch.Config.Port,Stepper.Control.Switch.on_off)) !=0 && Stepper.Tmp.button_ready==0) { Stepper.Tmp.button_ready=1; ReSwitching(Stepper.Tmp.is_on); if(Stepper.Tmp.is_on==false) { Stepper.Tmp.is_on = true; Bit_Set(PORTB,0); } else { Stepper.Tmp.is_on = false; Bit_Clr(PORTB,0); } } if(Stepper.Tmp.is_on == true) { if((Get_Switch(*Stepper.Control.Switch.Config.Port,Stepper.Control.Switch.left)) !=0 && Stepper.Tmp.button_ready==0) { Stepper.Tmp.button_ready=2; ReSwitching(Stepper.Tmp.is_left); if(Stepper.Tmp.is_left==false) { Stepper.Tmp.is_left = true; Stepper.Tmp.is_right = false; } else { Stepper.Tmp.is_left = false; Stepper.Tmp.is_right = true; } } if((Get_Switch(*Stepper.Control.Switch.Config.Port,Stepper.Control.Switch.fast)) !=0) { if(Stepper.Tmp.current_speed > Stepper.Tmp.min_speed) { Stepper.Tmp.current_speed = (Stepper.Tmp.current_speed-(10*2)); } } if((Get_Switch(*Stepper.Control.Switch.Config.Port,Stepper.Control.Switch.slow)) !=0) { if(Stepper.Tmp.current_speed < Stepper.Tmp.max_speed) { Stepper.Tmp.current_speed = (Stepper.Tmp.current_speed+10)*2; } } } if(Stepper.Tmp.button_ready==1 && (Get_Switch(*Stepper.Control.Switch.Config.Port,Stepper.Control.Switch.on_off))==0) { Stepper.Tmp.button_ready=0; return; } if(Stepper.Tmp.button_ready==2 && (Get_Switch(*Stepper.Control.Switch.Config.Port,Stepper.Control.Switch.left))==0) { Stepper.Tmp.button_ready=0; return; }
} |
Z predošlých príkladoch o tlačidlách vidíme, že som vynechal všetky bloky spomalenia Delay_ms() a cyklus while
podmienený stlačeným tlačidlom som nahradil príznakmi.
Riešenie je bezpečné a predchádza zacykleniam či spomaleniu.
Tlačidlá pre zrýchlenie/spomalenie motoru sú aktívne pri ich držaní. Motor sa zrýchľuje/spomaľuje nie len pri mačkaní,
ale aj pri stálom držaní.
Konfigurácia modulu v súbore main.c:
main.c |
#define __enable__threading_mode__ AND #define _disable_Thread_C AND #define _disable_Thread_D AND #define _disable_Thread_E END #include "config_diall.h" #include "stepper.h" #include "config_words.h" char *PageSys(void) { return "0x21"; } void ThreadA(void) { Stepper_Go(); } void ThreadB(void) { StepperSwitch(); } int main_diall(void) { Init_Port(PORTB,0,"digital"); Config_OUT(TRISB,0); Bit_Clr(PORTB,0); Stepper.Config.Port = uintptr &PORTB; Stepper.Control.Switch.Config.Port = uintptr &PORTB; Stepper.Control.Switch.on_off = 8; Stepper.Control.Switch.left= 9; Stepper.Control.Switch.slow = 10; Stepper.Control.Switch.fast = 11; Stepper_Init(); while(1) { } return 0; } |
Ako prvé v metóde main_diall nastavujeme rýchlu konfiguráciu pre LED.
Ako druhé, nastavujeme názvy pracovných registrov pre krokový motor a tlačidlá.
Pri tlačidlách do premenných nastavujeme čísla pinov. Keďze pri univerzálnosti nebudeme v budúcnu možno požadovať všetky tlačidlá, nie je nutné alokovať pamäť pre dynamické pole. S jednotlivými spínačmi musíme pracovať "natvrdo", čo by nebolo efektívne pri poli.
Nasleduje inicializácia modulu a zacyklená podmienka while.
Jednotlivé metody modulu bežia pod vlastným vláknom.
Celý projekt v akcií: