<p>Pošto je ovo zanimljivo mesto za svakakve predloge i pomoc pocetnicima i onima koji su donekle stigli u programerskom poslu, evo jednog priloga iz realnog sveta kontrole industrijskih mašina.<br><br>
Pominjao sam PIC16F877, kao model za koga postoji bezbroj aplikacija i gotovih rešenja na net-u.<br><br>
Kao njegovog mladeg naslednika sa nekoliko poboljšanja PIC18F452, gde su kodovi za PIC16F877 vrlo portabilni sa beznacajnim izmenama.<br><br>
I sada pominjem PIC18F4620, pin kompatibilnog, sasvim slicnog sa prethodna dva, osim što ima neuporedivo veci kapacitet svih memorija (fleš,ram,eeprom) i par dodatnih modula.<br><br>
Takode se u njega mogu portovati kodovi za 877 i 452, a da bi svi korisnicki pinovi bili digitalni (pomoc pocetnicima) u inicijalizaciji treba iskljuciti A/D konvertore i oba komparatora.<br><br>
Ovako:<br><br>
ADCON1 = 0b00001111; /* adc off */ //4620<br>
CMCON = 0b00000111; /* comparators off */ //4620<br><br>
Prica o ovim osmobitnim MCU je ovde da bi pocetnicima pokazala da nema potrebe bez razloga se odmah na pocetku hvatati za MCU sa preteranim resursima unutra.<br><br>
Sa bilo kojim od tri prethodno nabrojana MCU, mogu se napraviti stvarno mocni PLC.<br><br>
Primera radi, najsnažniji PLC koji sam napravio sa jednim ortakom, sa PIC16F877 ima 128 in/out, gde je napravljeno nekoliko in/out expandera sa Altera PLD.<br><br>
Jedan malo manji od tog, takode sa PIC16F877, sa 80 in/out, upravlja jednu mašinu za brizganje plastike, kapaciteta 2Kg plastike i 800 Tona sile zatvaranja alata, u Ugrinovcima, blizu našeg forumaša HMAP.<br><br>
Mašina je istocno nemacko-ruska, sa 39 hidraulicnih ventila i 40 senzora.<br><br>
Ogromne špulne na ventilima (ruska posla) 24V 4,5A svaki.<br><br>
U nekim fazama procesa, kroz moj PLC prolazi preko 70A struje pri 24V!!! Tim se može komotno zavarivati nešto...<br><br>
No, uz malo pažnje pri rutovanju vodova, brižljivo izvedenog hardvera, sve to super funkcioniše godinama. Mrzim releje i nikada ih ne stavljam u neku mašinu, pa sve te ventilčine pokrecem low-Rds mosfetima.<br><br>
---------------------------------------------------------------<br><br>
Evo jednog primera sa PIC18F452, gde se istim MCU upravljaju tri mašine odjednom.<br><br>
Prica je takva da je iskorišcen VIŠAK resursa na MCU PIC18f452 (da se ne baci :-) i udružene su tri mašine pod jedno upravljanje.<br><br>
Elem, na početku je nasleđen jedan sučeoni aparat za termootporno zavarivanje (punktovanje), sa višestepenim prekidačem za kontrolu snage i kontaktorima za kontrolu vremena zavarivanja.<br><br>
Ono što je kritično važno u oblasti termootpornog zavarivanja (punktovanja), osim poznatog materijala i komada, su tri parametra: jačina struje, vreme, i pritisak delova međusobno.<br><br>
Da bi zavar bio perfektno ponovljiv, neophodno je da ova tri parametra budu perfektno ponovljivi.<br><br>
Naravno, odmah sam izbacio kontaktore, i trafou od 15KVA napravio tiristorsku kontrolu snage, faznim zasekom pomocu ultra pouzdanog TCA785. Kontrolu vremena sam izveo plasiranjem celobrojnih iznosa mrežnih perioda radi sprečavanja DC bias trafoa, ujedno precizne vremenske kontrole. Kontrolu pritiska pneumatskim cilindrima, sa preciznim regulatorom pritiska ispred...<br><br>
Ono što je trebalo da se proizvodi, gde bi taj aparat bio jedan u lancu operacija, je neki žicani okvir, koji je prethodno trebalo saviti poluautomatom za savijanje žice, potom suceono zapunktovati, pa onda zapunktovati ga u više tacaka za konacni proizvod drugim punkt aparatom, nalik punkt kleštima.<br><br>
Pošto tri prethodno nabrojana PIC "prirodno" obezbeduju 25 in/out, preticala je velika kolicina neupotrebljenih resursa kod kontrole samo suceonog aparata.<br><br>
Naravno, odmah sam predložio da sve tri mašine koje su vezane datim procesom, stavim pod zajedničko upravljanje, i iskoristim isti trafo od sučeonog aparata za drugo punktovanje.<br><br>
Tim bi sve bilo blizu i u lancu proizvodnje, istovremeno ušteđena čitava jedna punkt mašina, uz samo dodavanje još jednog pneumatskog cilindra visokostrujnih kablova i para elektroda za taj treći proces.<br><br>
Predlog je prihvacen.<br><br>
Pošto sve tri mašine mogu potpuno asinhrono raditi svoje poluautomatske procese, neophodno je bilo koristiti neki brzi multitasking, koji bi za korisnika izgledao kao rad u realnom vremenu.<br><br>
Odmah da se ogradim:<br><br>
Ja nisam školovan programer. Kada se prvi kucni racunar pojavio imao sam 16 godina i za mene je tada bio ekonomski nedostižna naprava,<br><br>
Programiranje sam naucio uz put, uglavnom sam, uz povremenu pomoc jednog mog ortaka Mladena, onda Mikijevog sjajnog oca, pocivšeg Zorana Markovica (YU1ZM), i još par drugara koji su se vec bavili tim, pre otprilike 16 godina.<br><br>
Tada je prilicno snažan PIC (generacija sa 16F84) bio neki PIC16F72, sa prozorcetom za brisanje pomocu UV lampe :-).<br><br>
He he, tada je se stvarno moralo voditi racuna o bagovima u kodu, jer svako ponovno programiranje je koštalo 15 minuta brisanja UV lampom :-).<br><br>
Tada je se pisalo u asembleru, normalno :-).<br><br>
Asembler je stvarno naporan za pisanje, ali se njegovim korišcenjem dobiju par nezamenljivih koristi:<br><br>
-prvo, nauci se da se u glavi drži par stotina stavki pod cvrstom konrolom,<br><br>
-drugo, lako je provaliti bag koji eventualno napravi sam kompajler, posmatranjem koda kog je izgenerisao.<br><br>
Da se vratim na pricu. Pocetke sam naucio od tih ljudi, posle (što je vrlo svojstveno za mene) razvijao neke sopstvene metode.<br>
</p>
<p>Mnogim školovanim programerima se ova moja metoda multitaskinga neće svideti, ali ona neoprostivo dobro i pouzdano radi i čitava povorka od sedam taskova se vrti sa par desetina KHz, a može i brže jer sam žrtvovao pola od tog vremena za softversko filtriranje tastera "escape".</p>
<p> </p>
<p>To isto pisano u asembleru je brže samo oko 15%.</p>
<p> </p>
<p>Dakle. uzmite u obzir da nisam školovan programer, već samouk, ali prilično efikasan.</p>
<p>Takođe da zbog ranijeg pisanja u asm volim da koristim globalne varijable.</p>
<p> </p>
<p>Nisam imao vremena za optimizaciju koda. Sve sam napisao ad-hok, na licu mesta.</p>
<p> </p>
<p>Od ključne važnosti mi je bila brzina prolaska kroz sve taskove, tako da ni u kom slučaju ne propustim ni jedan input signal.</p>
<p> </p>
<p>MCU radi sa clk od 40MHz (HSPLL), a vreme jedne instrukcije je 100nS (f_clk/4).</p>
<p> </p>
<p>Samo sam sada prilegao da napišem komentare za ovu priliku, bukvalno od linije do linije, jer to je ono što je najkorisnije početnicima (a sve nas mrzi da ih pišemo, i uvek se posle par godina gorko pokajemo što smo bili lenji :-).</p>
<p> </p>
<p>Za stroge početnike u C jeziku, komentari su:</p>
<p> </p>
<p>//jednolinijski komentar, važi do kraja dotične linije</p>
<p> </p>
<p>/* višelinijski komentar,</p>
<p> važi od znaka do znaka */</p>
<p> </p>
<p>Radi se samo o dve funkcije: funkcije main i funkcije automatskog rada.</p>
<p>Biće dovoljno za razumevanje poente, nekima odmah, nekima kad dođe vreme...</p>
<pre class="_prettyXprint">
/*-----------------------------------------------------------------------------------------------------*/
void autom_rad(void) //funkcija automatskog ili poluautomatskog rada masine
{
err_no=nema_err; l_err_no=nema_err; //ponisti sve greske kod ulaska
lcd_clear(); //ocisti displej
if(op_status==poluautom){ //ako je poluautom. rad u pitanju
lcd_puts("POLUAUTOM."); //prikazi tekst
}
if(op_status==autom){ //ako je autom. rad u pitanju
lcd_puts("AUTOMATSKI"); //prikazi tekst
}
if(broj_grupa>1){ //ako postoji vise grupa parametara za set razlicitih proizvoda, prikazi indeks proizvoda
lcd_puts(" Pr"); //prikazi tekst
lcd_goto(13); //i broj indeksa proizvoda, pocev od 13. karaktera LCD
lcd_number(ofset_grupe+1,2,3,0); //varijabla, broj decimala, mesto zapete, prisustvo predznaka.
}
semafor_red=0; semafor_green=0; semafor_yelow=0; //pogasi trobojni semafor
clr_line2(); // oslobodi liniju 2 displeja za eventualni brojac komada
/* ovde, ako treba, staviti potrebne preduslove za autom/poluautom rad */
var_busy_flag=0; //!!! obavezno, da bi ponovo radila klesta i suceoni po izlasku iz greske
suc_seq_no=0; kl_seq_no=0; sav_seq_no=0; TRseq_no=0; //svi taskovi u stop stanje, bitno kod povratka iz greske
ped_suc_seq_no=0; ped_kl_seq_no=0; tast_sav_seq_no=0;//svi taskovi u stop stanje, bitno kod povratka iz greske
/*---------------------petlja autom/poluautom rada--------------------*/
while(((op_status==poluautom)||(op_status==autom))&&(!auto_escape)){ //uslovi odrzanja while petlje
//task_ticker^=1; //pin za testiranje vremena prolaza svih taskova, dok se ne zavrsi pisanje programa
if(esc_test())err_no=err_ciklus; //prekinut polu/autom. rad tasterom escape, fatal err.
if(not_aus)err_no=err_n_aus; //stisnut SVE-STOP, fatal err.
///////////////////////////////task pedale suceone punkt masine/////////////////////////////////////////
switch(ped_suc_seq_no){
case 0: //cekamo da bude pustena pedala suceonog
if(!pedala_suc)ped_suc_seq_no=0; //cekaj
else ped_suc_seq_no=1; //inace udji u proveru busy i stisnute
break;
case 1: //cekamo da bude stisnuta ako nije busy, inace ignorisi
if(!pedala_suc){ //stisnuta?
if(!var_busy_flag){ //ako nije zauzeto od klesta
var_busy_flag=1;semafor_red=1; //rezervisi trafo za suceoni, indikacija rada
suc_seq_jump=1;suc_seq_no=1; //aktiviraj task suceonog
ped_suc_seq_no=2; //idi na cekanje zavrsetka suceonog
}
else ped_suc_seq_no=0; //inace ignorisi pedalu, na pocetak za ponovni pokusaj
}
else ped_suc_seq_no=1; //inace cekaj da bude stisnuta
break;
case 2: //cekanje zavrsetka suceonog, ili njegove greske
if((suc_seq_no==8)||(suc_seq_no==7)){ //zavrsio suceoni, ili greska suceonog?
if(suc_seq_no==7)l_err_no=l_err_suceoni; //prijavi gresku suceonog, prekoracen polozaj, light err.
suc_seq_jump=0;suc_seq_no=0; //deaktiviranje taska suceonog
var_busy_flag=0;semafor_red=0; //oslobodi rezervaciju
ped_suc_seq_no=0; //vrati se na cekanje pedale
}
else ped_suc_seq_no=2; //inace cekaj
break;
}
//////////////////////////////////////////////////////////////////////////////////
///////////////////////////////task pedale klesta za punktovanje okvira/////////////////////////////////
switch(ped_kl_seq_no){
case 0: //cekamo da bude pustena pedala klesta
if(!pedala_kl)ped_kl_seq_no=0; //cekaj
else ped_kl_seq_no=1; //inace udji u proveru busy i stisnute
break;
case 1: // cekamo da bude stisnuta, ako nije busy, inace ignorisi
if(!pedala_kl){ //stisnuta?
if(!var_busy_flag){ //ako nije zauzeto od suceonog
var_busy_flag=1;semafor_yelow=1; //rezervisi klesta, tj. trafo
kl_seq_jump=1;kl_seq_no=1; //aktiviraj task klesta
ped_kl_seq_no=2; //idi na cekanje zavrsetka klesta
}
else ped_kl_seq_no=0; //inace ignorisi pedalu, na pocetak za ponovni pokusaj
}
else ped_kl_seq_no=1; //inace cekaj da bude stisnuta
break;
case 2: //cekanje zavrsetka klesta
if((kl_seq_no==6)||(kl_seq_no==5)){ //zavrsila klesta, ili greska?
if(kl_seq_no==5)l_err_no=l_err_klesta; //prijavi gresku, nedovoljan je pritisak, light err.
var_busy_flag=0;semafor_yelow=0; //oslobodi rezervaciju i vrati se na cekanje pedale
ped_kl_seq_no=0; //ovaj task u pocetni polozaj, stop
}
else ped_kl_seq_no=2; //inace cekaj
break;
}
////////////////////////////////////////////////////////////////////////////////
///////////////////////////////task oba tastera savijanja zice////////////////////////////////////////////
switch(tast_sav_seq_no){
case 0: //cekamo da oba tastera budu pustena
if((!tast1_sav)||(!tast2_sav))tast_sav_seq_no=0;//cekaj dok ne budu pustena oba
else tast_sav_seq_no=1; //inace idi na cekanje oba stisnuta
break;
case 1: //cekamo da oba budu stisnuta
if((!tast1_sav)&&(!tast2_sav)){ //stisnuta oba?
sav_seq_jump=1; sav_seq_no=1; //aktiviraj task savijanja
semafor_green=1; //indikacija rada
tast_sav_seq_no=2; //idi na cekanje zavrsetka savijanja
}
else tast_sav_seq_no=1; //inace cekaj da oba budu stisnuta
break;
case 2: //cekamo zavrsetak savijanja
if((sav_seq_no==6)||(sav_seq_no==5)){ //task savijanja zavrsio, ili greska?
if(sav_seq_no==5)l_err_no=l_err_savijanje; //prijavi gresku, istekao wdt, light err.
sav_seq_no=0; //stavi task savijanja u stop stanje
semafor_green=0;tast_sav_seq_no=0; //ukloni indikaciju i vrati se na pocetak
}
else tast_sav_seq_no=2; //inace cekaj
break;
}
/////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////task suceonog punkt aparata////////////////////////////////////////////
switch(suc_seq_no){
case 0: //stop, pasivno stanje
blokiraj_suc=0; stegni_suc=0;
break;
case 1: //blokiraj komad, t_blok_suc
if(suc_seq_jump){ //jednokratan prolaz
CCPR1L=param[pow_suc]; //dodeli snagu power jedinici od 15KVA
temp_per1=param[per_suc]; //uzmi potreban broj perioda zavarivanja iz parametara
timer10ms=param[t_blok_suc]; //dodeli timeru vreme blokiranja
blokiraj_suc=1; //pokreni cilindar blokiranja
timeout=0; //startuj timer
suc_seq_jump=0; //spreci ponavljanje jednokratnog prolaza
}
else{ //inace se vrti ovde posle jednokratnog prolaza
if(timeout){ //ispunjeno vreme blokiranja?
suc_seq_jump=1;suc_seq_no=2; //idi na stezanje komada
}
}
break;
case 2: //stegni, t_stez_suc
if(suc_seq_jump){ //jednokratan prolaz
stegni_suc=1; //pokreni cilindar stezanja
timer10ms=param[t_stez_suc]; //dodeli timeru vreme stezanja
timeout=0; //startuj timer
suc_seq_jump=0; //spreci ponavljanje jednokratnog prolaza
}
else{ //inace se vrti ovde posle jednokratnog prolaza
if(timeout)suc_seq_no=3; //ispunjeno vreme, idi na kontrolu mikroprekidaca polozaja
}
break;
case 3: //proveri mikroprekidac, ako JE, zavar stop, err_suceoni, inace zavari
if(!prek_suc)suc_seq_no=7; //greska, idi na poziciju greske?
else{
suc_seq_jump=1; suc_seq_no=4; //sve ok. idi na zavarivanje
}
break;
case 4: //zavari
if(suc_seq_jump){ //jednokratan prolaz
TR_jump=1; TRseq_no=1; //start sync taska i brojanja perioda, napajanje sa T i R faza
suc_seq_jump=0; //spreci ponavljanje jednokratnog prolaza
}
else{ //inace se vrti ovde posle jednokratnog prolaza
if(TRseq_no==3){ //sync TR task zavrsio?
TRseq_no=0; //stavljanje TR taska u stop stanje
suc_seq_jump=1; suc_seq_no=5; //idi na poststezanje-hladjenje komada
}
}
break;
case 5: //poststezanje, hladjenje komada
if(suc_seq_jump){ //jednokratan prolaz
CCPR1L=0; //svedi snagu zaseka na nulu, dodatna sigurnost
timer10ms=param[t_hlad_suc]; //dodeli timeru vreme hladjenja
timeout=0; //startuj timer
suc_seq_jump=0; //spreci ponavljanje jednokratnog prolaza
}
else{ //inace se vrti ovde posle jednokratnog prolaza
if(timeout){ //isteklo vreme?
suc_seq_jump=1;suc_seq_no=6; //ispunjeno vreme idi dalje
}
}
break;
case 6: //pusti oba cilindra
if(suc_seq_jump){ //jednokratan prolaz
blokiraj_suc=0; stegni_suc=0; //iskljucenje oba cilindra
timer10ms=10; //vreme sigurnog odvajanja kontakta komada=100mS
timeout=0; //startuj timer
suc_seq_jump=0; //spreci ponavljanje jednokratnog prolaza
}
else{ //inace se vrti ovde posle jednokratnog prolaza
if(timeout)suc_seq_no=8; //idi na poziciju uspesnog zavrsetka
}
break;
case 7: //pozicija greske suceonog, stopiraj zavar i pusti oba cilindra
enable1=0; TRseq_no=0; blokiraj_suc=0; stegni_suc=0;
break;
case 8: //pasivno stanje, indikator uspesnog zavrsetka
blokiraj_suc=0; stegni_suc=0;
break;
}
/////////////////////////end task suceonog///////////////////////
//////////////////////////////////////////////////////////////////////////////////
////////////////////////task klesta za punktovanje///////////////////////////////////////////////
switch(kl_seq_no){
case 0: //stop, pasivno stanje
stegni_kl=0; //cilindar off
break;
case 1: //predstezanje i proveri pritisak
if(kl_seq_jump){ //jednokratan prolaz
stegni_kl=1; //aktiviraj cilindar
CCPR1L=param[pow_kl]; //dodeli snagu power grupi pomocu pwm
temp_per1=param[per_kl]; //dodeli power grupi broj perioda zavarivanja
timer10ms=param[t_stez_kl]; //navij timer na vreme predstezanja klesta
timeout=0; //startuj timer
kl_seq_jump=0; //spreci ponavljanje jednokratnog prolaza
}
else{
if(timeout){ //inace se vrti ovde posle jednokratnog prolaza
if(!v_prit_klesta)kl_seq_no=5; //greska err_p_vazd
else{kl_seq_jump=1;kl_seq_no=2;} //inace zavari
}
}
break;
case 2: //zavari
if(kl_seq_jump){ //jednokratan prolaz
TR_jump=1;TRseq_no=1; //start sync taska i brojanja perioda, tj. start power grupe
kl_seq_jump=0; //spreci ponavljanje jednokratnog prolaza
}
else{ //inace se vrti ovde posle jednokratnog prolaza
if(TRseq_no==3){ //zavrsio sync task?
TRseq_no=0; //stavljanje TR taska u stop stanje
kl_seq_jump=1;kl_seq_no=3; //idi na poststezanje, hladjenje
}
}
break;
case 3: //poststezanje, hladjenje
if(kl_seq_jump){ //jednokratan prolaz
CCPR1L=0; //svedi snagu pow. grupe na nulu, dodatna sigurnost, pwm=0
timer10ms=param[t_hlad_kl]; //navij timer na vreme hladjenja
timeout=0; //startuj timer
kl_seq_jump=0; //spreci ponavljanje jednokratnog prolaza
}
else{ //inace se vrti ovde posle jednokratnog prolaza
if(timeout){ //isteklo vreme?
kl_seq_jump=1;kl_seq_no=4; //idi na otpustanje cilindra
}
}
break;
case 4: //pusti cilindar
if(kl_seq_jump){ //jednokratan prolaz
stegni_kl=0; //deaktiviraj cilindar
timer10ms=20; //sigurnosno vreme ukidanja elektricnog kontakta komada, 200mS
timeout=0; //startuj timer
kl_seq_jump=0; //spreci ponavljanje jednokratnog prolaza
}
else{ //inace se vrti ovde posle jednokratnog prolaza
if(timeout)kl_seq_no=6; //idi na piziciju uspesnog zavrsetka
}
break;
case 5: //pozicija greske klesta
stegni_kl=0; //deaktiviraj cilindar za svaki slucaj, ako sam negde zaboravio
break;
case 6: //indikator uspesnog zavrsetka, pasivno stanje
stegni_kl=0; //deaktiviraj cilindar za svaki slucaj, ako sam negde zaboravio
break;
}
////////////////////////end task klesta//////////////////////////
///////////////////////////////////////////////////////////////////////////////////
////////////////////////task savijanja zice///////////////////////////
switch(sav_seq_no){
case 0: //stop, pasivno stanje
spusti_spoljne=0;spoljni_cil=0;unutras_cil=0; //svi cilindri off
break;
case 1: //zatvori spoljne
if(sav_seq_jump){ //jednokratan prolaz
spoljni_cil=1; //aktiviraj prvu fazu savijanja
timer_sav=0; //startuj timer kao WDT
sav_seq_jump=0; //spreci ponavljanje jednokratnog prolaza
}
else{ //inace se vrti ovde posle jednokratnog prolaza
if(timer_sav>(param[wdt_sav]*10))sav_seq_no=5; //wdt, rezol. 100mS, na pozic.err
if(spoljni_su_in){ //prvo savijanje zavrseno?
sav_seq_jump=1;sav_seq_no=2; //idi na sledecu fazu savijanja
}
}
break;
case 2: //otvori spoljne i spusti ih dole
if(sav_seq_jump){ //jednokratan prolaz
spoljni_cil=0;spusti_spoljne=1; //aktiviraj potrebna stanja cilindara
timer_sav=0; //startuj timer
sav_seq_jump=0; //spreci ponavljanje jednokratnog prolaza
}
else{ //inace se vrti ovde posle jednokratnog prolaza
if(timer_sav>50){ //fiksno vreme 500mS
sav_seq_jump=1;sav_seq_no=3; //idi na drugu fazu procesa
}
}
break;
case 3: //zatvori unutrasnje
if(sav_seq_jump){ //jednokratan prolaz
unutras_cil=1; //aktiviraj unutrasnje
timer_sav=0; //startuj timer kao WDT
sav_seq_jump=0; //spreci ponavljanje jednokratnog prolaza
}
else{
if(timer_sav>(param[wdt_sav]*10))sav_seq_no=5; //wdt, rezol. 100mS, na pozic.err
if(unutr_su_in){ //drugo savijanje zavrseno?
sav_seq_jump=1;sav_seq_no=4; //idi na pripremu za nov komad
}
}
break;
case 4: //otvori unutrasnje i digni spoljne
if(sav_seq_jump){ //jednokratan prolaz
spusti_spoljne=0;spoljni_cil=0;unutras_cil=0; //aktiviraj povratak u pocetne pozicije
timer_sav=0; //startuj timer
sav_seq_jump=0; //spreci ponavljanje jednokratnog prolaza
}
else{ //inace se vrti ovde posle jednokratnog prolaza
if(timer_sav>100)sav_seq_no=6; //isteklo vreme povratka (1000mS), idi na indikator zavrsetka
}
break; //pozicija greske
case 5:
spusti_spoljne=0;spoljni_cil=0;unutras_cil=0; //off za svaki slucaj
break;
case 6: //indikator uspesnog zavrsetka
spusti_spoljne=0;spoljni_cil=0;unutras_cil=0; //off za svaki slucaj
break;
}
///////////////////////end task savijanja////////////////////////
///////////////////////TR sinhro i brojac perioda task, scr kontroler je TCA785////////////////////////////
switch(TRseq_no){
case 0: // stop, pasivno stanje power grupe
enable1=0;
break;
case 1: //cekamo prvi TR polutalas
if(TRsync){ //pozitivna tranzicija
TR_jump=1; TRseq_no=2;
}
break;
case 2: //drugi TR polutalas, siguran prolaz kroz nulu
if(TR_jump){ //jednokratan prolaz
if(temp_per1>0)temp_per1--; //ako ima jos nepotrosenih perioda, umanji za jednu
enable1=1; //odobri power grupu 15KVA, sada i ubuduce
TR_jump=0; //spreci ponavljanje jednokratnog prolaza
}
else{ //inace se vrti ovde posle jednokratnog prolaza
if(!TRsync){ //negativna tranzicija
if(temp_per1<1){ //potrosene sve periode?
TR_jump=0; TRseq_no=3; //idi na zavrsetak
}
else TRseq_no=1; //inace radi dok ne potrosis periode
}
}
break;
case 3: //indikator zavrsetka 1.power grupe, pasivno stanje
enable1=0; //zabrana SCR power grupe
break;
} /////////////////////end TR sinhro//////////////////////////////////////
if(l_err_no)disp_l_err(l_err_no); //obrada lajt gresaka, bez zaustavljanja druge dve masine
if(err_no){ //ka obradi fatalnih gresaka, opsti stop, izlaz iz polu/autom.
iskljuci_izlaze(); //iskljuci sve izlaze
auto_escape=1; //pripremi break while petlje
memo_brojac(pisi); //zapamti brojac, ako je u upotrebi
op_status=bazni; //ponisti status autom.
}
if(auto_escape)break; //prekini while
} ////////////////////////////////end while autom. rada //////////////////////////////////
/*-------------------------------------------------------------------*/
} //////////////////////////////////end autom. funkcije////////////////////////////
/*-------------------------------------------------*/
void main(void)
{
/////////////////////////////
processor_init(); //inicijalizacija (konfig) MCU
lcd_init(); //inicijalizacija LCD
semafor_red=1; //test alarm lampe i butovanja MCU
animate(message[0]); //reklamna animacija skrolujucim tekstom
lcd_goto(line2); //idi na liniju 2 LCD
lcd_puts(message[1]); //ime masine
delay_ms(1500); //vreme prikaza
lcd_clear(); //ocisti LCD
lcd_puts(message[2]); //ime firme
lcd_goto(line2);
delay_ms(3000); //vreme do kraja reklamne poruke
//ocisti_ee(); /* obavezno uraditi kod prvog upisivanja programa */
get_group(); //dohvati parametre iz eeprom
delay_ms(10);
memo_brojac(citaj); //preuzmi stanje brojaca iz eeprom
// if(opt_p&&(n<2))opcija_p(); //opcija plati gazda! Za one koji su sumnjivi oko placanja
delay_ms(10);
TMR0ON=1; /* Ukljucen timer0 */
//TMR1ON=1; /* ukljucen timer1 */
lcd_clear(); //ocisti LCD
disp_base(); //prikazuj set baznih poruka
semafor_red=0; //iskljuci alarmnu lampu
CCPR1L=0; //pwm1=0
////////////////////////////////
while(1){ //radi
kb_sc_execute(); //sken i izvrsenje tastature, test rad i rucni rad
if((op_status==poluautom)||(op_status==autom))autom_rad(); //poluautomatski i automatski rad
}//end main while(1)
} //end main
///////////////////////////////////////////END programa////////////////////////////////
//line1297
//////////////////////////////////////////////////////////////////////////////
</pre>
<p>Iskusnijima će možda ovo nešto i koristiti, možda i ne, ali početnicima će sigurno razjasniti mogućnosti upravljanja pomoću prilično "siromašnog" 8 bit MCU i neke od načina za realizaciju.</p>
<p> </p>
<p>Početnici će to sve "pohvatati" iz komentara (a razbio sam se pišući ih samo za vas :-).</p>
<p>Kasnije će im biti jasnije (zanimljiv stih).</p>
<p> </p>
<p>Pozz</p>
<p> </p>
<p>P.S.</p>
<p> </p>
<p>Zaboravih da napomenem: iz hardverskih razloga za inpute koristim negativnu logiku, tj. if(!inputx) je pitanje tipa da li JESTE.</p>
Pominjao sam PIC16F877, kao model za koga postoji bezbroj aplikacija i gotovih rešenja na net-u.<br><br>
Kao njegovog mladeg naslednika sa nekoliko poboljšanja PIC18F452, gde su kodovi za PIC16F877 vrlo portabilni sa beznacajnim izmenama.<br><br>
I sada pominjem PIC18F4620, pin kompatibilnog, sasvim slicnog sa prethodna dva, osim što ima neuporedivo veci kapacitet svih memorija (fleš,ram,eeprom) i par dodatnih modula.<br><br>
Takode se u njega mogu portovati kodovi za 877 i 452, a da bi svi korisnicki pinovi bili digitalni (pomoc pocetnicima) u inicijalizaciji treba iskljuciti A/D konvertore i oba komparatora.<br><br>
Ovako:<br><br>
ADCON1 = 0b00001111; /* adc off */ //4620<br>
CMCON = 0b00000111; /* comparators off */ //4620<br><br>
Prica o ovim osmobitnim MCU je ovde da bi pocetnicima pokazala da nema potrebe bez razloga se odmah na pocetku hvatati za MCU sa preteranim resursima unutra.<br><br>
Sa bilo kojim od tri prethodno nabrojana MCU, mogu se napraviti stvarno mocni PLC.<br><br>
Primera radi, najsnažniji PLC koji sam napravio sa jednim ortakom, sa PIC16F877 ima 128 in/out, gde je napravljeno nekoliko in/out expandera sa Altera PLD.<br><br>
Jedan malo manji od tog, takode sa PIC16F877, sa 80 in/out, upravlja jednu mašinu za brizganje plastike, kapaciteta 2Kg plastike i 800 Tona sile zatvaranja alata, u Ugrinovcima, blizu našeg forumaša HMAP.<br><br>
Mašina je istocno nemacko-ruska, sa 39 hidraulicnih ventila i 40 senzora.<br><br>
Ogromne špulne na ventilima (ruska posla) 24V 4,5A svaki.<br><br>
U nekim fazama procesa, kroz moj PLC prolazi preko 70A struje pri 24V!!! Tim se može komotno zavarivati nešto...<br><br>
No, uz malo pažnje pri rutovanju vodova, brižljivo izvedenog hardvera, sve to super funkcioniše godinama. Mrzim releje i nikada ih ne stavljam u neku mašinu, pa sve te ventilčine pokrecem low-Rds mosfetima.<br><br>
---------------------------------------------------------------<br><br>
Evo jednog primera sa PIC18F452, gde se istim MCU upravljaju tri mašine odjednom.<br><br>
Prica je takva da je iskorišcen VIŠAK resursa na MCU PIC18f452 (da se ne baci :-) i udružene su tri mašine pod jedno upravljanje.<br><br>
Elem, na početku je nasleđen jedan sučeoni aparat za termootporno zavarivanje (punktovanje), sa višestepenim prekidačem za kontrolu snage i kontaktorima za kontrolu vremena zavarivanja.<br><br>
Ono što je kritično važno u oblasti termootpornog zavarivanja (punktovanja), osim poznatog materijala i komada, su tri parametra: jačina struje, vreme, i pritisak delova međusobno.<br><br>
Da bi zavar bio perfektno ponovljiv, neophodno je da ova tri parametra budu perfektno ponovljivi.<br><br>
Naravno, odmah sam izbacio kontaktore, i trafou od 15KVA napravio tiristorsku kontrolu snage, faznim zasekom pomocu ultra pouzdanog TCA785. Kontrolu vremena sam izveo plasiranjem celobrojnih iznosa mrežnih perioda radi sprečavanja DC bias trafoa, ujedno precizne vremenske kontrole. Kontrolu pritiska pneumatskim cilindrima, sa preciznim regulatorom pritiska ispred...<br><br>
Ono što je trebalo da se proizvodi, gde bi taj aparat bio jedan u lancu operacija, je neki žicani okvir, koji je prethodno trebalo saviti poluautomatom za savijanje žice, potom suceono zapunktovati, pa onda zapunktovati ga u više tacaka za konacni proizvod drugim punkt aparatom, nalik punkt kleštima.<br><br>
Pošto tri prethodno nabrojana PIC "prirodno" obezbeduju 25 in/out, preticala je velika kolicina neupotrebljenih resursa kod kontrole samo suceonog aparata.<br><br>
Naravno, odmah sam predložio da sve tri mašine koje su vezane datim procesom, stavim pod zajedničko upravljanje, i iskoristim isti trafo od sučeonog aparata za drugo punktovanje.<br><br>
Tim bi sve bilo blizu i u lancu proizvodnje, istovremeno ušteđena čitava jedna punkt mašina, uz samo dodavanje još jednog pneumatskog cilindra visokostrujnih kablova i para elektroda za taj treći proces.<br><br>
Predlog je prihvacen.<br><br>
Pošto sve tri mašine mogu potpuno asinhrono raditi svoje poluautomatske procese, neophodno je bilo koristiti neki brzi multitasking, koji bi za korisnika izgledao kao rad u realnom vremenu.<br><br>
Odmah da se ogradim:<br><br>
Ja nisam školovan programer. Kada se prvi kucni racunar pojavio imao sam 16 godina i za mene je tada bio ekonomski nedostižna naprava,<br><br>
Programiranje sam naucio uz put, uglavnom sam, uz povremenu pomoc jednog mog ortaka Mladena, onda Mikijevog sjajnog oca, pocivšeg Zorana Markovica (YU1ZM), i još par drugara koji su se vec bavili tim, pre otprilike 16 godina.<br><br>
Tada je prilicno snažan PIC (generacija sa 16F84) bio neki PIC16F72, sa prozorcetom za brisanje pomocu UV lampe :-).<br><br>
He he, tada je se stvarno moralo voditi racuna o bagovima u kodu, jer svako ponovno programiranje je koštalo 15 minuta brisanja UV lampom :-).<br><br>
Tada je se pisalo u asembleru, normalno :-).<br><br>
Asembler je stvarno naporan za pisanje, ali se njegovim korišcenjem dobiju par nezamenljivih koristi:<br><br>
-prvo, nauci se da se u glavi drži par stotina stavki pod cvrstom konrolom,<br><br>
-drugo, lako je provaliti bag koji eventualno napravi sam kompajler, posmatranjem koda kog je izgenerisao.<br><br>
Da se vratim na pricu. Pocetke sam naucio od tih ljudi, posle (što je vrlo svojstveno za mene) razvijao neke sopstvene metode.<br>
</p>
<p>Mnogim školovanim programerima se ova moja metoda multitaskinga neće svideti, ali ona neoprostivo dobro i pouzdano radi i čitava povorka od sedam taskova se vrti sa par desetina KHz, a može i brže jer sam žrtvovao pola od tog vremena za softversko filtriranje tastera "escape".</p>
<p> </p>
<p>To isto pisano u asembleru je brže samo oko 15%.</p>
<p> </p>
<p>Dakle. uzmite u obzir da nisam školovan programer, već samouk, ali prilično efikasan.</p>
<p>Takođe da zbog ranijeg pisanja u asm volim da koristim globalne varijable.</p>
<p> </p>
<p>Nisam imao vremena za optimizaciju koda. Sve sam napisao ad-hok, na licu mesta.</p>
<p> </p>
<p>Od ključne važnosti mi je bila brzina prolaska kroz sve taskove, tako da ni u kom slučaju ne propustim ni jedan input signal.</p>
<p> </p>
<p>MCU radi sa clk od 40MHz (HSPLL), a vreme jedne instrukcije je 100nS (f_clk/4).</p>
<p> </p>
<p>Samo sam sada prilegao da napišem komentare za ovu priliku, bukvalno od linije do linije, jer to je ono što je najkorisnije početnicima (a sve nas mrzi da ih pišemo, i uvek se posle par godina gorko pokajemo što smo bili lenji :-).</p>
<p> </p>
<p>Za stroge početnike u C jeziku, komentari su:</p>
<p> </p>
<p>//jednolinijski komentar, važi do kraja dotične linije</p>
<p> </p>
<p>/* višelinijski komentar,</p>
<p> važi od znaka do znaka */</p>
<p> </p>
<p>Radi se samo o dve funkcije: funkcije main i funkcije automatskog rada.</p>
<p>Biće dovoljno za razumevanje poente, nekima odmah, nekima kad dođe vreme...</p>
<pre class="_prettyXprint">
/*-----------------------------------------------------------------------------------------------------*/
void autom_rad(void) //funkcija automatskog ili poluautomatskog rada masine
{
err_no=nema_err; l_err_no=nema_err; //ponisti sve greske kod ulaska
lcd_clear(); //ocisti displej
if(op_status==poluautom){ //ako je poluautom. rad u pitanju
lcd_puts("POLUAUTOM."); //prikazi tekst
}
if(op_status==autom){ //ako je autom. rad u pitanju
lcd_puts("AUTOMATSKI"); //prikazi tekst
}
if(broj_grupa>1){ //ako postoji vise grupa parametara za set razlicitih proizvoda, prikazi indeks proizvoda
lcd_puts(" Pr"); //prikazi tekst
lcd_goto(13); //i broj indeksa proizvoda, pocev od 13. karaktera LCD
lcd_number(ofset_grupe+1,2,3,0); //varijabla, broj decimala, mesto zapete, prisustvo predznaka.
}
semafor_red=0; semafor_green=0; semafor_yelow=0; //pogasi trobojni semafor
clr_line2(); // oslobodi liniju 2 displeja za eventualni brojac komada
/* ovde, ako treba, staviti potrebne preduslove za autom/poluautom rad */
var_busy_flag=0; //!!! obavezno, da bi ponovo radila klesta i suceoni po izlasku iz greske
suc_seq_no=0; kl_seq_no=0; sav_seq_no=0; TRseq_no=0; //svi taskovi u stop stanje, bitno kod povratka iz greske
ped_suc_seq_no=0; ped_kl_seq_no=0; tast_sav_seq_no=0;//svi taskovi u stop stanje, bitno kod povratka iz greske
/*---------------------petlja autom/poluautom rada--------------------*/
while(((op_status==poluautom)||(op_status==autom))&&(!auto_escape)){ //uslovi odrzanja while petlje
//task_ticker^=1; //pin za testiranje vremena prolaza svih taskova, dok se ne zavrsi pisanje programa
if(esc_test())err_no=err_ciklus; //prekinut polu/autom. rad tasterom escape, fatal err.
if(not_aus)err_no=err_n_aus; //stisnut SVE-STOP, fatal err.
///////////////////////////////task pedale suceone punkt masine/////////////////////////////////////////
switch(ped_suc_seq_no){
case 0: //cekamo da bude pustena pedala suceonog
if(!pedala_suc)ped_suc_seq_no=0; //cekaj
else ped_suc_seq_no=1; //inace udji u proveru busy i stisnute
break;
case 1: //cekamo da bude stisnuta ako nije busy, inace ignorisi
if(!pedala_suc){ //stisnuta?
if(!var_busy_flag){ //ako nije zauzeto od klesta
var_busy_flag=1;semafor_red=1; //rezervisi trafo za suceoni, indikacija rada
suc_seq_jump=1;suc_seq_no=1; //aktiviraj task suceonog
ped_suc_seq_no=2; //idi na cekanje zavrsetka suceonog
}
else ped_suc_seq_no=0; //inace ignorisi pedalu, na pocetak za ponovni pokusaj
}
else ped_suc_seq_no=1; //inace cekaj da bude stisnuta
break;
case 2: //cekanje zavrsetka suceonog, ili njegove greske
if((suc_seq_no==8)||(suc_seq_no==7)){ //zavrsio suceoni, ili greska suceonog?
if(suc_seq_no==7)l_err_no=l_err_suceoni; //prijavi gresku suceonog, prekoracen polozaj, light err.
suc_seq_jump=0;suc_seq_no=0; //deaktiviranje taska suceonog
var_busy_flag=0;semafor_red=0; //oslobodi rezervaciju
ped_suc_seq_no=0; //vrati se na cekanje pedale
}
else ped_suc_seq_no=2; //inace cekaj
break;
}
//////////////////////////////////////////////////////////////////////////////////
///////////////////////////////task pedale klesta za punktovanje okvira/////////////////////////////////
switch(ped_kl_seq_no){
case 0: //cekamo da bude pustena pedala klesta
if(!pedala_kl)ped_kl_seq_no=0; //cekaj
else ped_kl_seq_no=1; //inace udji u proveru busy i stisnute
break;
case 1: // cekamo da bude stisnuta, ako nije busy, inace ignorisi
if(!pedala_kl){ //stisnuta?
if(!var_busy_flag){ //ako nije zauzeto od suceonog
var_busy_flag=1;semafor_yelow=1; //rezervisi klesta, tj. trafo
kl_seq_jump=1;kl_seq_no=1; //aktiviraj task klesta
ped_kl_seq_no=2; //idi na cekanje zavrsetka klesta
}
else ped_kl_seq_no=0; //inace ignorisi pedalu, na pocetak za ponovni pokusaj
}
else ped_kl_seq_no=1; //inace cekaj da bude stisnuta
break;
case 2: //cekanje zavrsetka klesta
if((kl_seq_no==6)||(kl_seq_no==5)){ //zavrsila klesta, ili greska?
if(kl_seq_no==5)l_err_no=l_err_klesta; //prijavi gresku, nedovoljan je pritisak, light err.
var_busy_flag=0;semafor_yelow=0; //oslobodi rezervaciju i vrati se na cekanje pedale
ped_kl_seq_no=0; //ovaj task u pocetni polozaj, stop
}
else ped_kl_seq_no=2; //inace cekaj
break;
}
////////////////////////////////////////////////////////////////////////////////
///////////////////////////////task oba tastera savijanja zice////////////////////////////////////////////
switch(tast_sav_seq_no){
case 0: //cekamo da oba tastera budu pustena
if((!tast1_sav)||(!tast2_sav))tast_sav_seq_no=0;//cekaj dok ne budu pustena oba
else tast_sav_seq_no=1; //inace idi na cekanje oba stisnuta
break;
case 1: //cekamo da oba budu stisnuta
if((!tast1_sav)&&(!tast2_sav)){ //stisnuta oba?
sav_seq_jump=1; sav_seq_no=1; //aktiviraj task savijanja
semafor_green=1; //indikacija rada
tast_sav_seq_no=2; //idi na cekanje zavrsetka savijanja
}
else tast_sav_seq_no=1; //inace cekaj da oba budu stisnuta
break;
case 2: //cekamo zavrsetak savijanja
if((sav_seq_no==6)||(sav_seq_no==5)){ //task savijanja zavrsio, ili greska?
if(sav_seq_no==5)l_err_no=l_err_savijanje; //prijavi gresku, istekao wdt, light err.
sav_seq_no=0; //stavi task savijanja u stop stanje
semafor_green=0;tast_sav_seq_no=0; //ukloni indikaciju i vrati se na pocetak
}
else tast_sav_seq_no=2; //inace cekaj
break;
}
/////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////task suceonog punkt aparata////////////////////////////////////////////
switch(suc_seq_no){
case 0: //stop, pasivno stanje
blokiraj_suc=0; stegni_suc=0;
break;
case 1: //blokiraj komad, t_blok_suc
if(suc_seq_jump){ //jednokratan prolaz
CCPR1L=param[pow_suc]; //dodeli snagu power jedinici od 15KVA
temp_per1=param[per_suc]; //uzmi potreban broj perioda zavarivanja iz parametara
timer10ms=param[t_blok_suc]; //dodeli timeru vreme blokiranja
blokiraj_suc=1; //pokreni cilindar blokiranja
timeout=0; //startuj timer
suc_seq_jump=0; //spreci ponavljanje jednokratnog prolaza
}
else{ //inace se vrti ovde posle jednokratnog prolaza
if(timeout){ //ispunjeno vreme blokiranja?
suc_seq_jump=1;suc_seq_no=2; //idi na stezanje komada
}
}
break;
case 2: //stegni, t_stez_suc
if(suc_seq_jump){ //jednokratan prolaz
stegni_suc=1; //pokreni cilindar stezanja
timer10ms=param[t_stez_suc]; //dodeli timeru vreme stezanja
timeout=0; //startuj timer
suc_seq_jump=0; //spreci ponavljanje jednokratnog prolaza
}
else{ //inace se vrti ovde posle jednokratnog prolaza
if(timeout)suc_seq_no=3; //ispunjeno vreme, idi na kontrolu mikroprekidaca polozaja
}
break;
case 3: //proveri mikroprekidac, ako JE, zavar stop, err_suceoni, inace zavari
if(!prek_suc)suc_seq_no=7; //greska, idi na poziciju greske?
else{
suc_seq_jump=1; suc_seq_no=4; //sve ok. idi na zavarivanje
}
break;
case 4: //zavari
if(suc_seq_jump){ //jednokratan prolaz
TR_jump=1; TRseq_no=1; //start sync taska i brojanja perioda, napajanje sa T i R faza
suc_seq_jump=0; //spreci ponavljanje jednokratnog prolaza
}
else{ //inace se vrti ovde posle jednokratnog prolaza
if(TRseq_no==3){ //sync TR task zavrsio?
TRseq_no=0; //stavljanje TR taska u stop stanje
suc_seq_jump=1; suc_seq_no=5; //idi na poststezanje-hladjenje komada
}
}
break;
case 5: //poststezanje, hladjenje komada
if(suc_seq_jump){ //jednokratan prolaz
CCPR1L=0; //svedi snagu zaseka na nulu, dodatna sigurnost
timer10ms=param[t_hlad_suc]; //dodeli timeru vreme hladjenja
timeout=0; //startuj timer
suc_seq_jump=0; //spreci ponavljanje jednokratnog prolaza
}
else{ //inace se vrti ovde posle jednokratnog prolaza
if(timeout){ //isteklo vreme?
suc_seq_jump=1;suc_seq_no=6; //ispunjeno vreme idi dalje
}
}
break;
case 6: //pusti oba cilindra
if(suc_seq_jump){ //jednokratan prolaz
blokiraj_suc=0; stegni_suc=0; //iskljucenje oba cilindra
timer10ms=10; //vreme sigurnog odvajanja kontakta komada=100mS
timeout=0; //startuj timer
suc_seq_jump=0; //spreci ponavljanje jednokratnog prolaza
}
else{ //inace se vrti ovde posle jednokratnog prolaza
if(timeout)suc_seq_no=8; //idi na poziciju uspesnog zavrsetka
}
break;
case 7: //pozicija greske suceonog, stopiraj zavar i pusti oba cilindra
enable1=0; TRseq_no=0; blokiraj_suc=0; stegni_suc=0;
break;
case 8: //pasivno stanje, indikator uspesnog zavrsetka
blokiraj_suc=0; stegni_suc=0;
break;
}
/////////////////////////end task suceonog///////////////////////
//////////////////////////////////////////////////////////////////////////////////
////////////////////////task klesta za punktovanje///////////////////////////////////////////////
switch(kl_seq_no){
case 0: //stop, pasivno stanje
stegni_kl=0; //cilindar off
break;
case 1: //predstezanje i proveri pritisak
if(kl_seq_jump){ //jednokratan prolaz
stegni_kl=1; //aktiviraj cilindar
CCPR1L=param[pow_kl]; //dodeli snagu power grupi pomocu pwm
temp_per1=param[per_kl]; //dodeli power grupi broj perioda zavarivanja
timer10ms=param[t_stez_kl]; //navij timer na vreme predstezanja klesta
timeout=0; //startuj timer
kl_seq_jump=0; //spreci ponavljanje jednokratnog prolaza
}
else{
if(timeout){ //inace se vrti ovde posle jednokratnog prolaza
if(!v_prit_klesta)kl_seq_no=5; //greska err_p_vazd
else{kl_seq_jump=1;kl_seq_no=2;} //inace zavari
}
}
break;
case 2: //zavari
if(kl_seq_jump){ //jednokratan prolaz
TR_jump=1;TRseq_no=1; //start sync taska i brojanja perioda, tj. start power grupe
kl_seq_jump=0; //spreci ponavljanje jednokratnog prolaza
}
else{ //inace se vrti ovde posle jednokratnog prolaza
if(TRseq_no==3){ //zavrsio sync task?
TRseq_no=0; //stavljanje TR taska u stop stanje
kl_seq_jump=1;kl_seq_no=3; //idi na poststezanje, hladjenje
}
}
break;
case 3: //poststezanje, hladjenje
if(kl_seq_jump){ //jednokratan prolaz
CCPR1L=0; //svedi snagu pow. grupe na nulu, dodatna sigurnost, pwm=0
timer10ms=param[t_hlad_kl]; //navij timer na vreme hladjenja
timeout=0; //startuj timer
kl_seq_jump=0; //spreci ponavljanje jednokratnog prolaza
}
else{ //inace se vrti ovde posle jednokratnog prolaza
if(timeout){ //isteklo vreme?
kl_seq_jump=1;kl_seq_no=4; //idi na otpustanje cilindra
}
}
break;
case 4: //pusti cilindar
if(kl_seq_jump){ //jednokratan prolaz
stegni_kl=0; //deaktiviraj cilindar
timer10ms=20; //sigurnosno vreme ukidanja elektricnog kontakta komada, 200mS
timeout=0; //startuj timer
kl_seq_jump=0; //spreci ponavljanje jednokratnog prolaza
}
else{ //inace se vrti ovde posle jednokratnog prolaza
if(timeout)kl_seq_no=6; //idi na piziciju uspesnog zavrsetka
}
break;
case 5: //pozicija greske klesta
stegni_kl=0; //deaktiviraj cilindar za svaki slucaj, ako sam negde zaboravio
break;
case 6: //indikator uspesnog zavrsetka, pasivno stanje
stegni_kl=0; //deaktiviraj cilindar za svaki slucaj, ako sam negde zaboravio
break;
}
////////////////////////end task klesta//////////////////////////
///////////////////////////////////////////////////////////////////////////////////
////////////////////////task savijanja zice///////////////////////////
switch(sav_seq_no){
case 0: //stop, pasivno stanje
spusti_spoljne=0;spoljni_cil=0;unutras_cil=0; //svi cilindri off
break;
case 1: //zatvori spoljne
if(sav_seq_jump){ //jednokratan prolaz
spoljni_cil=1; //aktiviraj prvu fazu savijanja
timer_sav=0; //startuj timer kao WDT
sav_seq_jump=0; //spreci ponavljanje jednokratnog prolaza
}
else{ //inace se vrti ovde posle jednokratnog prolaza
if(timer_sav>(param[wdt_sav]*10))sav_seq_no=5; //wdt, rezol. 100mS, na pozic.err
if(spoljni_su_in){ //prvo savijanje zavrseno?
sav_seq_jump=1;sav_seq_no=2; //idi na sledecu fazu savijanja
}
}
break;
case 2: //otvori spoljne i spusti ih dole
if(sav_seq_jump){ //jednokratan prolaz
spoljni_cil=0;spusti_spoljne=1; //aktiviraj potrebna stanja cilindara
timer_sav=0; //startuj timer
sav_seq_jump=0; //spreci ponavljanje jednokratnog prolaza
}
else{ //inace se vrti ovde posle jednokratnog prolaza
if(timer_sav>50){ //fiksno vreme 500mS
sav_seq_jump=1;sav_seq_no=3; //idi na drugu fazu procesa
}
}
break;
case 3: //zatvori unutrasnje
if(sav_seq_jump){ //jednokratan prolaz
unutras_cil=1; //aktiviraj unutrasnje
timer_sav=0; //startuj timer kao WDT
sav_seq_jump=0; //spreci ponavljanje jednokratnog prolaza
}
else{
if(timer_sav>(param[wdt_sav]*10))sav_seq_no=5; //wdt, rezol. 100mS, na pozic.err
if(unutr_su_in){ //drugo savijanje zavrseno?
sav_seq_jump=1;sav_seq_no=4; //idi na pripremu za nov komad
}
}
break;
case 4: //otvori unutrasnje i digni spoljne
if(sav_seq_jump){ //jednokratan prolaz
spusti_spoljne=0;spoljni_cil=0;unutras_cil=0; //aktiviraj povratak u pocetne pozicije
timer_sav=0; //startuj timer
sav_seq_jump=0; //spreci ponavljanje jednokratnog prolaza
}
else{ //inace se vrti ovde posle jednokratnog prolaza
if(timer_sav>100)sav_seq_no=6; //isteklo vreme povratka (1000mS), idi na indikator zavrsetka
}
break; //pozicija greske
case 5:
spusti_spoljne=0;spoljni_cil=0;unutras_cil=0; //off za svaki slucaj
break;
case 6: //indikator uspesnog zavrsetka
spusti_spoljne=0;spoljni_cil=0;unutras_cil=0; //off za svaki slucaj
break;
}
///////////////////////end task savijanja////////////////////////
///////////////////////TR sinhro i brojac perioda task, scr kontroler je TCA785////////////////////////////
switch(TRseq_no){
case 0: // stop, pasivno stanje power grupe
enable1=0;
break;
case 1: //cekamo prvi TR polutalas
if(TRsync){ //pozitivna tranzicija
TR_jump=1; TRseq_no=2;
}
break;
case 2: //drugi TR polutalas, siguran prolaz kroz nulu
if(TR_jump){ //jednokratan prolaz
if(temp_per1>0)temp_per1--; //ako ima jos nepotrosenih perioda, umanji za jednu
enable1=1; //odobri power grupu 15KVA, sada i ubuduce
TR_jump=0; //spreci ponavljanje jednokratnog prolaza
}
else{ //inace se vrti ovde posle jednokratnog prolaza
if(!TRsync){ //negativna tranzicija
if(temp_per1<1){ //potrosene sve periode?
TR_jump=0; TRseq_no=3; //idi na zavrsetak
}
else TRseq_no=1; //inace radi dok ne potrosis periode
}
}
break;
case 3: //indikator zavrsetka 1.power grupe, pasivno stanje
enable1=0; //zabrana SCR power grupe
break;
} /////////////////////end TR sinhro//////////////////////////////////////
if(l_err_no)disp_l_err(l_err_no); //obrada lajt gresaka, bez zaustavljanja druge dve masine
if(err_no){ //ka obradi fatalnih gresaka, opsti stop, izlaz iz polu/autom.
iskljuci_izlaze(); //iskljuci sve izlaze
auto_escape=1; //pripremi break while petlje
memo_brojac(pisi); //zapamti brojac, ako je u upotrebi
op_status=bazni; //ponisti status autom.
}
if(auto_escape)break; //prekini while
} ////////////////////////////////end while autom. rada //////////////////////////////////
/*-------------------------------------------------------------------*/
} //////////////////////////////////end autom. funkcije////////////////////////////
/*-------------------------------------------------*/
void main(void)
{
/////////////////////////////
processor_init(); //inicijalizacija (konfig) MCU
lcd_init(); //inicijalizacija LCD
semafor_red=1; //test alarm lampe i butovanja MCU
animate(message[0]); //reklamna animacija skrolujucim tekstom
lcd_goto(line2); //idi na liniju 2 LCD
lcd_puts(message[1]); //ime masine
delay_ms(1500); //vreme prikaza
lcd_clear(); //ocisti LCD
lcd_puts(message[2]); //ime firme
lcd_goto(line2);
delay_ms(3000); //vreme do kraja reklamne poruke
//ocisti_ee(); /* obavezno uraditi kod prvog upisivanja programa */
get_group(); //dohvati parametre iz eeprom
delay_ms(10);
memo_brojac(citaj); //preuzmi stanje brojaca iz eeprom
// if(opt_p&&(n<2))opcija_p(); //opcija plati gazda! Za one koji su sumnjivi oko placanja
delay_ms(10);
TMR0ON=1; /* Ukljucen timer0 */
//TMR1ON=1; /* ukljucen timer1 */
lcd_clear(); //ocisti LCD
disp_base(); //prikazuj set baznih poruka
semafor_red=0; //iskljuci alarmnu lampu
CCPR1L=0; //pwm1=0
////////////////////////////////
while(1){ //radi
kb_sc_execute(); //sken i izvrsenje tastature, test rad i rucni rad
if((op_status==poluautom)||(op_status==autom))autom_rad(); //poluautomatski i automatski rad
}//end main while(1)
} //end main
///////////////////////////////////////////END programa////////////////////////////////
//line1297
//////////////////////////////////////////////////////////////////////////////
</pre>
<p>Iskusnijima će možda ovo nešto i koristiti, možda i ne, ali početnicima će sigurno razjasniti mogućnosti upravljanja pomoću prilično "siromašnog" 8 bit MCU i neke od načina za realizaciju.</p>
<p> </p>
<p>Početnici će to sve "pohvatati" iz komentara (a razbio sam se pišući ih samo za vas :-).</p>
<p>Kasnije će im biti jasnije (zanimljiv stih).</p>
<p> </p>
<p>Pozz</p>
<p> </p>
<p>P.S.</p>
<p> </p>
<p>Zaboravih da napomenem: iz hardverskih razloga za inpute koristim negativnu logiku, tj. if(!inputx) je pitanje tipa da li JESTE.</p>