Mașini de stat finite, cum să programați fără blocaje

Buna ziua tuturor, numele meu este Vladimir Vasiliev si aici sunt gata in sfarsit sa scriu un articol nou pe care sa-l grabesc cu dragii dvs de cititori.

Mașini de stat finite, cum să programați fără blocaje

Astăzi vom vorbi despre dispozitivele automate, dar nu deloc despre cele care sunt ținute în mâinile soldaților armatei ruse. Este vorba despre un astfel de stil interesant de programare a microcontrolerelor ca programare automată. Mai exact, acest lucru nu este chiar un stil de programare, ci un concept întreg, datorită căruia un programator de microcontrolere îi poate facilita foarte mult viața. Datorită faptului că multe sarcini prezentate programatorului sunt rezolvate mult mai ușor și mai ușor, salvând programatorul de o durere de cap. Apropo, programarea automată este numită adesea tehnologia SWITCH.






Vreau să menționez că stimulul pentru scrierea acestui post a fost o serie de articole despre tehnologia SWITCH a lui Vladimir Tatarchevsky. Ciclul articolelor se numește "Aplicarea tehnologiei SWITCH în dezvoltarea aplicațiilor software pentru microcontrolere". Astfel, în acest articol voi încerca mai ales să dau un exemplu de cod de lucru și descrierea acestuia.

Apropo, am planificat un număr de articole dedicate programării, în care voi examina în detaliu tehnicile de programare pentru microcontrolerele AVR, nu ratați .... Să mergem!

Înainte de a ne ocupa de stilul automat de programare, să vedem cum funcționează programul.

Programul execută secvențial comenzile stabilite de programator. Pentru un program normal de calculator, este perfect normal ca programul să funcționeze și să oprească executarea acestuia, în timp ce afișează rezultatele lucrărilor sale pe monitor.

Programul pentru microcontroler nu poate termina executarea acestuia. Imaginați-vă că ați pornit playerul sau magnetofonul. Ați apăsat butonul de alimentare, ați selectat melodia dorită și vă bucurați de muzică. Cu toate acestea, când muzica a încetat să zboare în timpanul urechii tale, jucătorul a atârnat și nu a răspuns la apăsarea butoanelor, să nu mai vorbim de dansurile tale cu o tamburină.

Și ce e în neregulă cu asta? Totul este normal - controlerul, cel care se află în intestinul playerului, tocmai și-a terminat programul. Aici vedeți inconfortabil cumva se pare.

De aici rezultă că programul pentru microcontroler nu trebuie să se oprească. În esență, ar trebui să fie o buclă infinită - numai în acest caz, jucătorul nostru ar funcționa corect. În continuare, vă voi arăta care este designul codului software pentru microcontrolere, nu este chiar design și unele stiluri de programare.

Stiluri de programare.

"Stiluri de programare" - suna cumva de neînțeles, dar oh bine. Ce vreau sa spun prin aceasta? Imaginati-va ca o persoana nu a mai facut niciodata programare, adica, in general, un cazan complet.

Acest om a citit mai multe cărți despre programare, a studiat toate construcțiile de bază ale limbii. El a colectat informații bit-by-bit, acum accesul la informații este nelimitat. Toate acestea sunt bune, dar cum vor arăta primele sale programe? Mi se pare că nu va filosofa, ci va urma calea de la simplu la complex.

Deci, aceste stiluri sunt pașii care conduc de la un nivel simplu la un sistem mai complex, dar în același timp mai eficient.

La început nu m-am gândit la nicio caracteristică a programului. Tocmai am format logica programului - am desenat o diagramă și am scris codul. Din ceea ce se întâmpla în mod constant o rake. Dar a fost pentru prima oară când nu m-am îmbăiat și am folosit stilul de "simplă buclă", apoi am început să folosesc întreruperi, apoi au existat automate și s-au dus ...

1. Ciclism simplu. Programul în acest caz bate fără nici o înțelepciune și acest lucru are argumentele sale pro și contra. În plus, doar în simplitatea abordării, nu trebuie să inventăm construcții vicioase, scrieți așa cum credeți (treptat săpătați-vă un mormânt).

Punctul de lucru al programului se mută în ordine. În acest caz, toate acțiunile, condițiile și ciclurile sunt executate secvențial. Codul începe să încetinească, trebuie să introduceți o mulțime de condiții inutile, complicând astfel percepția.

Toate acestea confundă foarte mult programul, făcând codul o mulțime de condiții. Ca rezultat, acest cod nu este adăugat pentru a lua, devine o piesă monolit. Desigur, atunci când volumul nu este mare, codul este supus unor modificări, dar cu atât mai dificil.

Cu această abordare, am scris mai multe programmok, nu au fost mari și destul de lucru, dar claritatea a lăsat mult pentru a fi dorit. Pentru a adăuga o nouă condiție. A trebuit să strâng tot codul, pentru că totul era legat. Acest lucru a cauzat o mulțime de greșeli și o durere de cap. Compilatorul a blestemat cât mai curând posibil, depanând un astfel de program transformat în iad.

În parte, ciclul de frânare infinit poate fi întrerupt prin întreruperi. Întrerupe vă ajută să ieșiți din cercul vicios, ajutați-vă să nu ratați un eveniment important, adăugați funcționalități suplimentare (întreruperea temporizării, întreruperile externe).

Să presupunem că puteți închide procesarea butoanelor sau puteți urmări un eveniment important. Ca rezultat, programul devine mai intuitiv, dar la fel de complicat.

Din nefericire, întreruperea nu te va salva de dezordinea în care se transformă programul. Nu va fi posibil să împărțiți un singur întreg.

3. Programare automată.

Așa că ajungem la subiectul principal al acestui articol. Programarea în mașini de stat finit evită programul din dezavantajele inerente primelor două exemple. Programul devine mai ușor, este ușor de modificat.

Programul scris în stilul automat este similar cu comutatorul, care comută la una sau alta stare, în funcție de condiții. Numărul de state pe care un programator la cunoaște inițial.

Mașini de stat finite, cum să programați fără blocaje

Într-o vedere grosolană, este ca un comutator de lumină. Există două stări de pornire și oprire și cele două condiții se activează și deconectează. Ei bine, despre totul în ordine.

Implementarea multitasking-ului în tehnologia comutatoarelor.

Microcontrolerul este capabil să controleze sarcina, să lumineze intermitent LED-urile, să observe intrările de la tastatură și multe altele. Dar cum să faceți totul în același timp. Pentru a rezolva această problemă, există multe soluții. Cea mai simplă dintre ele am menționat este folosirea întreruperilor.







În procesul de program, atunci când apare o întrerupere, controlerul este distras de la executarea codului de program și execută pentru scurt timp o altă piesă a programului pentru care întrerupe răspunsul. Întreruperea funcționează, atunci punctul de funcționare al programului va continua din acel loc. din care controlerul a fost întrerupt de o întrerupere (cuvântul însuși indică faptul că controlerul este întrerupt).

O altă modalitate de a implementa multitasking este utilizarea sistemelor de operare. Da, într-adevăr, au început deja să apară mici AOS care pot fi aplicate pe un controler de putere redusă. Dar de multe ori această metodă este oarecum redundantă. La urma urmei, de ce să cheltuiți resursele controlorilor inutile atunci când este posibil să ajungeți cu puțin sânge.

În programele scrise în tehnologie de comutare, o astfel de "iluzie" de multitasking este obținută printr-un sistem de mesagerie. Am scris o "iluzie", pentru că este într-adevăr, deoarece programul nu poate executa fizic diferite părți ale codului în același timp. Voi vorbi mai multe despre sistemul de mesagerie.

Sistem de schimb de mesaje.

Puteți utiliza sistemul de mesagerie pentru a distruge numeroase procese și a crea iluzia multitasking-ului.

Să presupunem că avem nevoie de un program în care LED-ul este comutat. Aici avem două automate, să le numim LEDON - responsabile automat pentru includerea LED-urilor și automatelor LEDOFF - automatul responsabil pentru oprirea LED-ului.

Fiecare dintre mașini are două stări, adică mașina poate fi în stare activă și inactivă, deoarece întrerupătorul este pornit sau oprit.

Când o mașină este activată, LED-ul se aprinde, iar când este activat celălalt LED este stins. Luați în considerare un exemplu mic:

În liniile 3-7 există diferite inițializări, deci nu ne interesează în mod special acest lucru acum. Dar atunci se întâmplă următoarele: înainte de a rula bucla principală (în timp ce (1)), trimitem un mesaj la automaton

responsabil pentru iluminarea LED-ului. Fără acest mic pas organul nostru nu va funcționa. Apoi, principala buclă infinită în timp ce își desfășoară activitatea principală.

Mesajul are trei stări. Anume starea mesajului poate fi inactivă, este setată, dar inactivă și starea activă.

Se pare că mesajul a fost inițial inactiv, când am trimis mesajul, acesta a primit statutul "este setat, dar inactiv". Și asta ne dă următoarele. Când programul este secvențiat, mașina LEDON nu primește un mesaj. Există o iterație goală a LEDON-ului când mesajul pur și simplu nu poate fi recepționat. Deoarece mesajul are o stare "instalat, dar inactiv", programul continuă executarea acestuia.

După ce toate mașinile sunt inactive, coada ajunge la funcția ProcessMessages (). Această funcție este întotdeauna pusă la sfârșitul bucla, după ce toate iterațiile automatelor sunt executate. Funcția ProcessMessages () traduce pur și simplu mesajul din starea "instalat, dar inactiv" în starea "active".

Cu ajutorul mesageriei corecte putem controla ordinea de lucru a mașinilor de stat finit, dar numai mesajele pe care nu le putem face.

Poate că ați observat că fragmentul anterior al programului, citat ca exemplu, nu va funcționa așa cum a fost intenționat. Mașinile vor schimba mesajele, LED-urile vor comuta, dar nu o vom vedea. Vom vedea doar un LED slab luminat.

Toate pentru că nu ne-am gândit prin antrenamentul adecvat al întârzierilor. La urma urmei, nu avem suficiente LED-uri alternative, LED-ul ar trebui întârziat în fiecare stat, să spunem pentru o secundă.

Click pentru marire

Am uitat să adaug în acest bloc schema că atunci când timerul este docil, desigur acțiunea este efectuată - iluminarea LED-ului sau amortizarea acestuia.

1. Introduceți starea acceptând mesajul.

2. Verificăm citirile cronometrului / contorului, dacă este cazul, atunci efectuăm acțiunea, altfel pur și simplu trimitem un mesaj către noi înșine.

3. Trimiteți mesajul la următorul aparat.

În intrarea următoare, totul se repetă.

Program pentru tehnologia SWITCH. Trei pași.

Și să scriem un program în mașini de stat finit și pentru asta trebuie să facem doar trei pași simpli. Programul va fi simplu, dar cu lucruri simple merită să începeți. Vom folosi un program cu un LED de comutare. Acesta este un exemplu foarte bun, deci nu vom inventa nimic nou.

Programul voi fi în limbajul C, dar aceasta nu înseamnă că mașinile de stat trebuie să scrie numai în C, este posibil să se utilizeze orice alt limbaj de programare.

Programul va fi modular pentru noi și, prin urmare, va fi împărțit în mai multe fișiere. Modulele vor avea urmatoarele:

  • Modulul ciclului principal al programului conține fișiere leds_blink.c, HAL.c, HAL.h
  • Modulul temporizator conține fișierele timers.c, timers.h
  • Modulul de procesare a mesajelor conține mesaje.c, messages.h
  • Modulul 1 automat conține fișierele ledon.c, ledon.h
  • Modulul automat 2 conține fișierele ledoff.c. ledoff .h

Crearea unui proiect și conectați imediat să-l fișiere statice ale modulelor noastre: timers.c, timers.h, messages.c, messages.h.

Apoi, scriem modulul ciclului principal al programului.

Fișierul leds_blink.c al modulului ciclului principal al programului.

În primele linii, modulele rămase sunt conectate la programul principal. Aici vedem că sunt conectate modulul de temporizare și modulul de procesare a mesajelor. Mai departe, pe textul programului, există un vector de întrerupere a depășirii.

Din linia int principal (void), puteți spune că începe programul principal. Și începe cu inițierea a tot și a tot. Aici inițializăm perifericele, adică setăm valorile inițiale la porturile de intrare ale comparatorului și la toate celelalte conținuturi ale controlerului. Toate acestea se fac prin funcția INIT_PEREF, aici o executăm, deși corpul său principal este în fișierul hal.c

Apoi vedem inițializarea cronometrelor. modul de procesare a mesajelor, inițializarea automatelor. Aici, aceste funcții sunt pur și simplu pornite, deși funcțiile ele însele sunt scrise în fișierele modulelor lor. Vedeți cât de convenabil. Textul principal al programului rămâne ușor de citit și nu este aglomerat de codul redundant din care piciorul diavolului este rupt.

Inițializarea de bază se termină acum, trebuie să facem începutul bucla principală. Pentru a face acest lucru, trimiteți un mesaj de pornire și, de asemenea, începeți ceasul nostru - pornim cronometrul.

Și ciclul principal, așa cum am spus deja, pare foarte simplu. Noi scriem funcțiile tuturor automatelor, scrieți-le într-o coloană, fără a observa ordinea. Aceste funcții sunt manipulatoare de automate și se află în modulele automatelor. Modulul de procesare a mesajelor închide această piramidă automată. Acest lucru, desigur, mi-a spus deja înainte când am înțeles sistemul de trimitere a mesajelor. Acum puteți vedea cum arată cele două fișiere ale modulului ciclului principal al programului

Hal.h este fișierul antet al modulului bucla principală a programului.

Așa cum ați fi observat, în realitate acest fișier nu conține o singură linie de cod executabil - toate sunt macrosubstituții și biblioteci de conectare. Prezența acestui fișier face pur și simplu viața foarte ușoară, îmbunătățește vizibilitatea.

Dar fișierul Hal.c este deja un fișier executabil și, după cum am menționat, conține diferite inițializări ale periferiei.

Ei bine, modulul ciclului principal de program, am arătat acum că trebuie să facem ultimul pas, trebuie să scriem modulele mașinilor.

Rămâne pentru noi să scriem modulele automatelor finite, în cazul nostru automatele LEDON și LEDOFF. În primul rând, voi da textul programului de automatizare a diodei cu LED-uri ledon.c.

Aici, în primele linii, bibliotecile sunt mereu conectate și sunt declarate variabilele. Mai mult, am trecut deja funcții pe care le-am întâlnit deja. Aceasta este funcția de inițializare a mașinii InitLEDON și funcția procesatorului de procesare a proceselor în sine.

În corpul operatorului, funcțiile de la modulul de temporizare și modulul de mesaj sunt deja procesate. Iar logica mașinii se bazează pe designul carcasei. Și aici puteți observa că manipulatorul mașinii poate fi, de asemenea, complicat prin adăugarea unor comutatoare de case.

Fișierul de antet al mașinii va fi și mai ușor:

Aici conectăm fișierul de conectare hal.h și specificăm și prototipurile funcțiilor.

Fișierul responsabil pentru oprirea LED-ului va arăta aproape întotdeauna doar în imaginea oglindă, așa că nu o voi arăta aici - reticența

Iată doar trei pași și programul nostru are un aspect finalizat și asta înseamnă că misiunea mea sa încheiat astăzi și este timpul să se oprească. Mi se pare că informațiile din acest articol vor fi foarte utile pentru dvs. Dar beneficiul real pe care îl va aduce numai atunci când veți aplica aceste cunoștințe în practică.

Alo Aici sunt un ceainic plin. După ce ați citit articolul dvs. de mai multe ori, căutând, ați înțeles sensul, dar cum să îl aplicați în practică, nu am înțeles. Se pare că da, este mai ușor de scris și mai clar, dar pare chiar și mai complicat decât de obicei. Unde și de ce sunt bibliotecile conectate. Care este diferența dintre hal.h și hal.c și de ce au astfel de nume?
În prezent, este încă mai ușor să înțeleg pachetul de cod ciudat "indivizibil" și să fac ajustările necesare pentru el decât să scriu programul de la zero. Deși simt că, conform recomandărilor dvs., acest lucru este mai ușor decât înțelegerea ... Ceea ce lipsește este să-mi dau seama de simplitate. Creierul, probabil. Dar încă vă mulțumesc pentru munca voastră







Trimiteți-le prietenilor: