Folosind întreruperi pe Arduino. Cum să efectuați sarcini paralele (Fire) într-un program Arduino

În general, Arduino nu acceptă paralelizarea sarcinilor adevărate sau multithreading. Dar este posibil cu fiecare repetare a ciclului buclă() instruiți microcontrolerul să verifice dacă este timpul să efectueze ceva suplimentar, sarcină de fundal. În acest caz, utilizatorului i se va părea că mai multe sarcini sunt efectuate simultan.

De exemplu, să clipim un LED la o anumită frecvență și, în același timp, să scoatem sunete crescătoare și descrescătoare ca o sirenă de la un emițător piezo. Am conectat deja atât LED-ul, cât și emițătorul piezo la Arduino de mai multe ori. Să asamblam circuitul așa cum se arată în figură.

Dacă conectați LED-ul la un pin digital altul decât „13”, nu uitați de un rezistor de limitare a curentului de aproximativ 220 ohmi.

2 Control cu ​​LED și emițător piezo folosind operatorul delay().

Să scriem o schiță ca aceasta și să o încărcăm în Arduino.

Const int soundPin = 3; /* declară o variabilă cu Numarul pin, la care este conectat elementul piezoelectric */ const int ledPin = 13; // declară o variabilă cu numărul pin LED void setup() ( pinMode(soundPin, OUTPUT); // declar pin 3 ca ieșire. pinMode(ledPin, OUTPUT); // declar pinul 13 ca ieșire. } void loop() (// Control sunet: ton (soundPin, 700); // scoate un sunet la o frecvență de 700 Hz delay(200); ton (soundPin, 500); // la o frecvență de 500 Hz delay(200); ton (soundPin, 300); // la o frecvență de 300 Hz delay(200); ton (soundPin, 200); // la o frecvență de 200 Hz delay(200); // Control LED: digitalWrite(ledPin, HIGH); // întârziere luminoasă (200); digitalWrite(ledPin, LOW); // oprire întârziere (200); }

După pornire, este clar că schița nu este realizată exact așa cum avem nevoie: până când sirena este complet funcțională, LED-ul nu va clipi, dar am dori ca LED-ul să clipească pe parcursul sunetul unei sirene. Care este problema aici?

Cert este că această problemă nu poate fi rezolvată în mod obișnuit. Sarcinile sunt efectuate de microcontroler strict secvenţial. Operator întârziere()întârzie execuția programului pentru o anumită perioadă de timp și până la expirarea acestui timp, următoarele comenzi programele nu vor fi executate. Din acest motiv, nu putem seta o durată de execuție diferită pentru fiecare sarcină din buclă buclă() programe. Prin urmare, trebuie să simulați cumva multitasking.

3 Procese paralele fără operatorul „delay()”.

O opțiune în care Arduino va efectua sarcini pseudo-paralele a fost propusă de dezvoltatorii Arduino. Esența metodei este aceea cu fiecare repetare a ciclului buclă() verificăm dacă este timpul să clipim LED-ul (efectuați o sarcină de fundal) sau nu. Și dacă a sosit, atunci inversăm starea LED-ului. Acesta este un fel de opțiune de bypass pentru operator întârziere().

Const int soundPin = 3; // variabilă cu numărul de pin al elementului piezoelectric const int ledPin = 13; // variabilă cu numărul de pin LED const long ledInterval = 200; // Interval de clipire a LED-ului, ms. int ledState = LOW; // starea inițială a LED-ului unsigned long previousMillis = 0; // stochează ora activării anterioare a LED-ului void setup() ( pinMode(soundPin, OUTPUT); // setați pinul 3 ca ieșire. pinMode(ledPin, OUTPUT); // setați pinul 13 ca ieșire. } void loop() (// Control sunet: ton (soundPin, 700); întârziere (200); ton (soundPin, 500); întârziere (200); ton (soundPin, 300); întârziere (200); ton (soundPin, 200); întârziere (200); // LED intermitent: // timp de când Arduino a fost pornit, ms: unsigned long currentMillis = millis(); // Dacă a sosit momentul să clipească, dacă (currentMillis - previousMillis >= ledInterval) ( previousMillis = currentMillis; // atunci amintiți-vă ora curentă if (ledState == LOW) ( // și inversează starea LED-ului ledState = HIGH; ) else ( ledState = LOW; ) digitalWrite(ledPin, ledState); // comută starea LED-ului) }

Dezavantaj semnificativ aceasta metoda este că secțiunea de cod dinaintea blocului de control LED trebuie să fie executată mai rapid decât intervalul de timp intermitent LED-ul „ledInterval”. În caz contrar, clipirea va apărea mai rar decât este necesar și nu vom obține efectul executării paralele a sarcinilor. În special, în schița noastră, durata schimbării sunetului sirenei este de 200+200+200+200 = 800 ms, iar intervalul de clipire a LED-ului este de 200 ms. Dar LED-ul va clipi cu o perioadă de 800 ms, adică de 4 ori În plus ceea ce am întrebat.

În general, dacă codul folosește operatorul întârziere(), în acest caz este dificil de simulat pseudo-paralelismul, așa că este indicat să îl evitați.

ÎN în acest caz, Ar fi necesar ca unitatea de control al sunetului sirenei să verifice și dacă timpul a venit sau nu și să nu folosească întârziere(). Dar acest lucru ar crește cantitatea de cod și ar face programul mai puțin lizibil.

4 Folosind biblioteca ArduinoThread pentru a crea fire paralele

Pentru a rezolva problema, vom folosi o bibliotecă minunată ArduinoThread, care vă permite să creați cu ușurință procese pseudo-paralele. Funcționează într-un mod similar, dar vă permite să evitați scrierea codului pentru a verifica sincronizarea - indiferent dacă trebuie să efectuați o sarcină în această buclă sau nu. Acest lucru reduce cantitatea de cod și îmbunătățește lizibilitatea schiței. Să verificăm biblioteca în acțiune.


În primul rând, descărcați arhiva bibliotecii de pe site-ul oficial și dezarhivați-o într-un director biblioteci/ mediu inconjurator Dezvoltare Arduino IDE. Apoi redenumiți folderul ArduinoThread-master V ArduinoThread.

Schema de conectare va rămâne aceeași. Numai codul programului se va schimba.

#include // conectarea bibliotecii ArduinoThread const int soundPin = 3; // variabilă cu numărul de pin al elementului piezoelectric const int ledPin = 13; // variabilă cu numărul de pin LED Thread ledThread = Thread(); // creează un fir de control LED Thread soundThread = Thread(); // creează un fir de control al sirenei void setup() ( pinMode(soundPin, OUTPUT); // declar pin 3 ca ieșire. pinMode(ledPin, OUTPUT); // declar pinul 13 ca ieșire. ledThread.onRun(ledBlink); // atribuie o sarcină firului de execuție ledThread.setInterval(1000); // setează intervalul de răspuns, ms soundThread.onRun(sound); // atribuie o sarcină firului soundThread.setInterval(20); // setați intervalul de răspuns, ms } void loop() (// Verificați dacă este timpul să comute LED-ul: if (ledThread.shouldRun()) ledThread.run(); // începe firul // Verificați dacă este timpul să schimbați tonul sirenei: if (soundThread.shouldRun()) soundThread.run(); // începe firul } // Flux LED: void ledBlink() ( static bool ledStatus = false; // Stare LED On/Off ledStatus = !ledStatus; // inversează starea digitalWrite(ledPin, ledStatus); // aprinde/oprește LED-ul } // Flux de sirenă: sunet gol() ( ton int static = 100; // tonul sunetului, tonul Hz(soundPin, ton); // pornește sirena la "ton" Hz dacă (ton )

În program creăm două fire - ledFireȘi soundThread, fiecare efectuează propria sa operație: unul clipește LED-ul, al doilea controlează sunetul sirenei. În fiecare iterație a buclei, pentru fiecare thread verificăm dacă a sosit momentul sau nu pentru executarea acesteia. Dacă ajunge, este lansat pentru execuție folosind metoda alerga(). Principalul lucru este să nu folosiți operatorul întârziere(). Codul oferă explicații mai detaliate.


Să încărcăm codul în memoria Arduino și să-l rulăm. Acum totul funcționează exact așa cum ar trebui!

Întârzierile în Arduino joacă un rol foarte important. Fără ele, chiar și cel mai simplu exemplu de Blink, care clipește un LED după o anumită perioadă de timp, nu poate funcționa. Dar majoritatea programatorilor începători știu puțin despre întârzieri și folosesc numai întârzierea Arduino fără a cunoaște efectele secundare ale acestei comenzi. În acest articol voi vorbi în detaliu despre funcțiile temporare și caracteristicile utilizării lor în mediul de dezvoltare Arduino IDE.

Arduino are mai multe diverse echipe, care sunt responsabili pentru lucrul cu timp și pauze:

  • întârziere()
  • delayMicrosecunde()
  • milis()
  • micros()

Ele diferă în precizie și au propriile lor caracteristici care ar trebui să fie luate în considerare la scrierea codului.

Folosind funcția de întârziere arduino

Sintaxă

Arduino delay este cea mai simplă comandă și este cel mai des folosită de începători. În esență, este o întârziere care întrerupe programul pentru numărul de milisecunde indicat în paranteze. (Sunt 1000 de milisecunde într-o secundă.) Valoarea maximă poate fi 4294967295 ms, care este aproximativ egală cu 50 de zile. Să ne uităm la un exemplu simplu care arată clar cum funcționează această comandă.

Void setup() ( pinMode(13, OUTPUT); ) void loop() ( digitalWrite(13, HIGH); // trimite semnal ridicatîntârziere la 13 pini (10000); // pauză 10000ms sau 10 secunde digitalWrite13, LOW); // trimite un semnal scăzut la pinul 13 delay(10000); // pauză 10000 ms sau 10 secunde)

In metoda înființat Precizăm că pinul 13 va fi folosit ca ieșire. În partea principală a programului, un semnal ridicat este mai întâi trimis la pin, apoi facem o întârziere de 10 secunde. În acest timp, programul pare să fie suspendat. Apoi se dă un semnal scăzut și din nou există o întârziere și totul începe din nou. Ca rezultat, obținem că pinul este alimentat alternativ fie cu 5 V, fie cu 0.

Trebuie să înțelegeți clar că în timpul unei pauze de utilizare a întârzierii, activitatea programului este suspendată, aplicația nu va primi date de la senzori. Acesta este cel mai mare dezavantaj al folosirii funcției de întârziere Arduino. Puteți ocoli această limitare folosind întreruperi, dar vom vorbi despre asta într-un articol separat.

Exemplu de întârziere cu LED-ul care clipește

Un exemplu de circuit pentru a ilustra modul în care funcționează funcția de întârziere.
Puteți construi un circuit cu un LED și un rezistor. Atunci o putem face exemplu standard– LED intermitent. Pentru a face acest lucru, trebuie să conectați un LED cu un contact pozitiv la pin, pe care l-am desemnat ca ieșire. Conectăm piciorul liber al LED-ului la masă printr-un rezistor de aproximativ 220 Ohmi (este posibil puțin mai mult). Puteți determina polaritatea uitându-vă la interiorul acesteia. Cupa mare din interior este conectată la minus, iar piciorul mic la plus. Dacă LED-ul este nou, atunci puteți determina polaritatea după lungimea cablurilor: piciorul lung este plus, piciorul scurt este minus.

Funcția delayMicroseconds

Această funcție este un analog complet al întârzierii, cu excepția faptului că unitățile sale de măsură nu sunt milisecunde, ci microsecunde (într-o secundă sunt 1.000.000 de microsecunde). Valoarea maximă va fi 16383, care este egală cu 16 milisecunde. Rezoluția este 4, adică numărul va fi întotdeauna un multiplu de patru. Un exemplu de fragment ar arăta astfel:

DigitalWrite(2, HIGH); // trimite un semnal mare la pinul 2 delayMicroseconds(16383); // pauză 16383 µs digitalWrite(2, LOW); // trimite un semnal scăzut la pinul 2 delayMicroseconds(16383); // pauză 16383 µs

Problema cu întârziereaMicrosecundelor este exact aceeași ca și cu întârzierea - aceste funcții „atârnă” complet programul și literalmente îngheață pentru o perioadă. În acest moment, este imposibil să lucrați cu porturi, să citiți informații de la senzori și să efectuați operații matematice. Pentru lumini intermitente această opțiune se potrivește, dar utilizatori experimentați nu-l folosi pentru proiecte mari, deoarece astfel de eșecuri nu sunt necesare acolo. Prin urmare, este mult mai bine să utilizați funcțiile descrise mai jos.

Funcția Millis în loc de întârziere

Funcția millis() vă va permite să efectuați o întârziere fără întârziere pe Arduino, eludând astfel deficiențele metode anterioare. Valoarea maximă a parametrului millis este aceeași cu cea a funcției de întârziere (4294967295ms sau 50 de zile).

Folosind millis, nu oprim execuția întregii schițe, ci pur și simplu indicăm cât timp Arduino ar trebui pur și simplu să „ocolească” blocul exact de cod pe care vrem să-l întrerupem. Spre deosebire de delay millis, nu oprește nimic de la sine. Această comandă pur și simplu ne returnează de la temporizatorul încorporat al microcontrolerului numărul de milisecunde care au trecut de la început. De fiecare dată când apelăm bucla, noi înșine măsurăm timpul care a trecut de atunci ultimul apel a codului nostru și dacă diferența de timp este mai mică decât pauza dorită, atunci ignorăm codul. De îndată ce diferența devine mai mare decât pauza necesară, executăm codul, obținem ora curentă folosind aceleași milimetri și ne amintim - de această dată va fi noul punct de plecare. ÎN ciclul următor numărătoarea inversă va fi deja de la punct nouși vom ignora din nou codul până când noua diferență dintre milis și valoarea noastră salvată anterior ajunge din nou la pauza dorită.

Întârzierea fără întârziere folosind millis necesită mai mult cod, dar cu ajutorul acestuia puteți clipi un LED și puteți întrerupe o schiță fără a opri sistemul.

Iată un exemplu care ilustrează clar munca echipei:

Nesemnat de lungă durată; // Variabilă pentru stocarea punctului de referință void setup() ( Serial.begin(9600); ) void loop() ( /* În acest moment începe execuția analogului delay(). Calculați diferența dintre momentul curent și punct de referință salvat anterior. Dacă diferența este mai mare decât valoarea dorită, atunci executați codul. Dacă nu, nu faceți nimic */ if (millis() - timing > 10000)( // În loc de 10000, înlocuiți valoarea de pauză de care aveți nevoie de sincronizare = millis(); Serial.println ("10 secunde" ; ) )

Mai întâi introducem variabila de sincronizare, care va stoca numărul de milisecunde. În mod implicit, valoarea variabilei este 0. În partea principală a programului, verificăm condiția: dacă numărul de milisecunde de la începutul microcontrolerului minus numărul scris în variabila de sincronizare este mai mare de 10000, atunci se efectuează acțiunea de a trimite un mesaj către monitorul portului și valoarea curentă a timpului este scrisă în variabilă. Ca urmare a funcționării programului, mesajul 10 secunde va fi afișat pe monitorul portului la fiecare 10 secunde. Aceasta metoda vă permite să clipiți LED-ul fără întârziere.

Micros funcționează în loc de întârziere

Această funcție poate efectua și o întârziere fără a utiliza comanda delay. Funcționează exact la fel ca milisecunde, dar numără mai degrabă microsecunde decât milisecunde cu o rezoluție de 4 μs. Valoarea sa maximă este de 4294967295 microsecunde sau 70 de minute. Dacă depășește, valoarea este pur și simplu resetată la 0, nu uitați de asta.

rezumat

Platforma Arduino ne oferă mai multe modalități de a implementa o întârziere în proiectul nostru. Folosind întârziere, puteți întrerupe rapid execuția unei schițe, dar, în același timp, veți bloca funcționarea microcontrolerului. Utilizarea comenzii millis vă permite să faceți fără întârziere în Arduino, dar acest lucru va necesita puțin mai multă programare. Alege Cel mai bun modîn funcție de complexitatea proiectului dvs. De regulă, în schițe simple și cu o întârziere mai mică de 10 secunde, se folosește întârzierea. Dacă logica de operare este mai complexă și este necesară o întârziere mare, atunci este mai bine să folosiți milis în loc de întârziere.

Optimizați-vă programele Arduino cu întreruperi - calea usoara pentru a răspunde la evenimente în timp real!

Ne întrerupem transmisia...

După cum se dovedește, există un mecanism grozav (dar subutilizat) încorporat în toate Arduinos, care este ideal pentru urmărirea evenimentelor în timp real. Acest mecanism se numește întrerupere. Sarcina întreruperii este de a se asigura că procesorul răspunde rapid la evenimentele importante. Când a fost găsit anumit semnal O întrerupere (după cum sugerează și numele) întrerupe orice făcea procesorul și execută un cod conceput pentru a răspunde apelantului. cauza externă, care afectează Arduino. După ce acest cod este executat, procesorul revine la ceea ce făcea inițial ca și cum nimic nu s-ar fi întâmplat!

Ceea ce este uimitor este că întreruperile vă permit să vă organizați programul pentru a răspunde rapid și eficient la evenimentele importante care nu sunt atât de ușor anticipate în bucla de program. Cel mai bine, întreruperile permit procesorului să facă alte lucruri, mai degrabă decât să piardă timpul în așteptarea unui eveniment.

Butonul se întrerupe

Sa incepem cu exemplu simplu: Folosiți o întrerupere pentru a urmări apăsările butoanelor. Pentru a începe, vom face o schiță pe care probabil ați văzut-o deja: exemplul „Button” inclus în IDE-ul Arduino ( îl puteți găsi în directorul „Exemple”, verificați meniul Fișier → Exemple → 02. Digital → Buton ).

Const int buttonPin = 2; // numărul de pin cu butonul const int ledPin = 13; // numărul pin cu LED int buttonState = 0; // variabilă pentru citirea stării butonului void setup() ( // setați pin-ul LED-ului la ieșire: pinMode(ledPin, OUTPUT); // setați pin-ul butonului la intrare: pinMode(buttonPin, INPUT); ) void loop () ( // citește starea butonului: buttonState = digitalRead(buttonPin); // verifică dacă butonul este apăsat. // dacă este apăsat, atunci buttonState este HIGH: if (buttonState == HIGH) ( // aprinde LED-ul : digitalWrite(ledPin, HIGH); ) else ( // stinge LED-ul: digitalWrite(ledPin, LOW); ) )

Nu este nimic șocant sau surprinzător în ceea ce vedeți aici: tot ceea ce face programul este să treacă prin buclă () și să citească valoarea buttonPin . Să presupunem pentru o secundă că ai dori să faci ceva mai mult cu loop(), ceva mai mult decât să citești starea de ieșire. Aici este utilă întreruperea. În loc să monitorizăm constant starea pinului, putem atribui această sarcină unei bucle de întrerupere și libere () pentru a face ceea ce avem nevoie între timp! Cod nou va arata asa:

Const int buttonPin = 2; // numărul de pin cu butonul const int ledPin = 13; // numărul pin cu LED volatil int buttonState = 0; // variabilă pentru citirea stării butonului void setup() ( // setați pin-ul LED-ului la ieșire: pinMode(ledPin, OUTPUT); // setați pin-ul butonului la intrare: pinMode(buttonPin, INPUT); // atașați o întrerupere a vectorului ISR ​​attachInterrupt (0, pin_ISR, CHANGE); ) void loop() ( // Nimic aici! ) void pin_ISR() ( buttonState = digitalRead(buttonPin); digitalWrite(ledPin, buttonState); )

Bucle și moduri de întrerupere

Veți observa câteva modificări aici. Prima și cea mai evidentă este că loop() nu conține acum instrucțiuni! Ne putem descurca fără ele, deoarece toată munca care a fost făcută anterior în instrucțiunea if/else este acum realizată în optiune noua pin_ISR() . Acest tip de funcție se numește un handler de întrerupere: sarcina sa este să ruleze rapid, să gestioneze întreruperile și să permită procesorului să revină la programul principal (adică conținutul buclei()). Există mai multe lucruri de luat în considerare atunci când scrieți un handler de întrerupere: Puncte importante, pe care îl puteți vedea reflectat în codul de mai sus:

  • manipulatorii trebuie să fie scurti și conciși. Nu doriți să întrerupeți bucla principală pentru mult timp!
  • Handlerii nu au parametri de intrare sau valori returnate. Toate modificările trebuie făcute variabilelor globale.

Probabil vă întrebați: de unde știm când se va declanșa întreruperea? Ce o cauzează? A treia funcție, numită în funcția setup(), setează o întrerupere pentru întregul sistem. Această funcție, attachInterrupt(), ia trei argumente:

  1. un vector de întrerupere care determină ce pin poate genera o întrerupere. Acesta nu este numărul pinului în sine, ci o referință la o locație din memorie pe care procesorul Arduino trebuie să o urmărească pentru a vedea dacă a avut loc o întrerupere. Acest spațiuîn acest vector corespunde unui pin extern specific și nu toți pinii pot genera o întrerupere! Pe Arduino Uno, pinii 2 și 3 cu vectorii de întrerupere 0 și, respectiv, 1, pot genera întreruperi. Pentru o listă de pini care pot genera întreruperi, consultați documentația funcției Arduino attachInterrupt;
  2. Nume funcție de gestionare a întreruperii: definește codul care va fi executat când este îndeplinită condiția de declanșare a întreruperii;
  3. modul de întrerupere, care determină ce acțiune pin declanșează întreruperea. Arduino Uno acceptă patru moduri de întrerupere:
    • RISING - activează o întrerupere a frontului ascendent pe pinul de întrerupere;
    • FALLING - activează întreruperea căderii;
    • SCHIMBARE - reacționează la orice modificare a valorii pinului de întrerupere;
    • LOW - Apelează ori de câte ori PIN-ul este scăzut.

Și pentru a recapitula, configurația noastră attachInterrupt() corespunde urmăririi vectorului de întrerupere 0 (pin 2) pentru a răspunde la întrerupere cu pin_ISR() și apelarea pin_ISR() ori de câte ori există o schimbare de stare pe pinul 2.

Volatil

Un alt aspect demn de subliniat este faptul că gestionarea de întreruperi folosește variabila buttonState pentru a stoca starea ieșirii. Verificați definiția buttonState: în loc să fie de tip int , am definit-o să fie de tip volatile int . Ce se întâmplă aici? volatil este un cuvânt cheie în limbaj C care se aplică variabilelor. Înseamnă că valoarea variabilei nu este sub control total programe. Adică, valoarea buttonState se poate schimba și modifica în ceva ce programul însuși nu poate prezice - în acest caz, intrarea utilizatorului.

Încă unul lucru utilîn cuvântul cheie volatil este de a proteja împotriva oricărei optimizări accidentale. Se pare că compilatorii mai fac câteva sarcini suplimentare atunci când convertiți codul sursă al unui program în cod executabil de mașină. Una dintre aceste sarcini este eliminarea celor neutilizate cod sursa variabile de la Codul mașinii. Deoarece variabila buttonState nu este folosită sau apelată direct în funcțiile loop() sau setup(), există riscul ca compilatorul să o elimine ca variabilă neutilizată. Evident, acest lucru este greșit - avem nevoie de această variabilă! Cuvânt cheie volatil are ca efect secundar de a spune compilatorului că variabila ar trebui lăsată în pace.

Eliminarea variabilelor neutilizate din cod este caracteristica functionala, nu o eroare a compilatorului. Oamenii lasă uneori variabile neutilizate în codul lor care ocupă memorie. Nu e ca asta o problema mare, dacă scrieți un program C pentru un computer cu gigaocteți de memorie RAM. Cu toate acestea, pe Arduino RAM limitat și nu vrei să-l irosești! Chiar și compilatoarele C pentru computere vor face exact același lucru, în ciuda masei disponibile memorie de sistem. Pentru ce? Din același motiv pentru care oamenii fac curățenie după un picnic - este bun antrenament, nu lăsa gunoi în urmă.

Rezumând

Întreruperile sunt o modalitate simplă de a face sistemul dumneavoastră să răspundă mai rapid la sarcinile sensibile la timp. Au si ei beneficiu suplimentar- eliberarea buclei principale loop(), permițându-i să se concentreze pe sarcina principală a sistemului (găsesc că utilizarea întreruperilor tinde să-mi mențină codul puțin mai organizat: este mai ușor să vezi pentru ce este proiectată piesa principală de cod și ce evenimente periodice sunt gestionate de întreruperi). Exemplul prezentat aici este cel mai elementar caz de utilizare pentru întreruperi; puteți folosi pentru a citi date de pe dispozitivul I2C, transmisie fără firși primirea datelor, sau chiar pentru a porni sau opri motorul.

Vreo proiecte interesante de întrerupere? Lasă-ți comentariile mai jos!

În general, Arduino nu acceptă paralelizarea sarcinilor adevărate sau multithreading. Dar este posibil cu fiecare repetare a ciclului buclă() instruiți microcontrolerul să verifice dacă este timpul să efectueze o sarcină suplimentară, de fundal. În acest caz, utilizatorului i se va părea că mai multe sarcini sunt efectuate simultan.

De exemplu, să clipim un LED la o anumită frecvență și, în același timp, să scoatem sunete crescătoare și descrescătoare ca o sirenă de la un emițător piezo. Am conectat deja atât LED-ul, cât și emițătorul piezo la Arduino de mai multe ori. Să asamblam circuitul așa cum se arată în figură.

Dacă conectați LED-ul la un pin digital altul decât „13”, nu uitați de un rezistor de limitare a curentului de aproximativ 220 ohmi.

2 Control cu ​​LED și emițător piezo folosind operatorul delay().

Să scriem o schiță ca aceasta și să o încărcăm în Arduino.

Const int soundPin = 3; /* declară o variabilă cu numărul pinului la care este conectat elementul piezoelectric */ const int ledPin = 13; // declară o variabilă cu numărul pin LED void setup() ( pinMode(soundPin, OUTPUT); // declar pin 3 ca ieșire. pinMode(ledPin, OUTPUT); // declar pinul 13 ca ieșire. } void loop() (// Control sunet: ton (soundPin, 700); // scoate un sunet la o frecvență de 700 Hz delay(200); ton (soundPin, 500); // la o frecvență de 500 Hz delay(200); ton (soundPin, 300); // la o frecvență de 300 Hz delay(200); ton (soundPin, 200); // la o frecvență de 200 Hz delay(200); // Control LED: digitalWrite(ledPin, HIGH); // întârziere luminoasă (200); digitalWrite(ledPin, LOW); // oprire întârziere (200); }

După pornire, este clar că schița nu este realizată exact așa cum avem nevoie: până când sirena este complet funcțională, LED-ul nu va clipi, dar am dori ca LED-ul să clipească pe parcursul sunetul unei sirene. Care este problema aici?

Cert este că această problemă nu poate fi rezolvată în mod obișnuit. Sarcinile sunt efectuate de microcontroler strict secvenţial. Operator întârziere()întârzie execuția programului pentru o anumită perioadă de timp, iar până la expirarea acestui timp, următoarele comenzi ale programului nu vor fi executate. Din acest motiv, nu putem seta o durată de execuție diferită pentru fiecare sarcină din buclă buclă() programe. Prin urmare, trebuie să simulați cumva multitasking.

3 Procese paralele fără operatorul „delay()”.

O opțiune în care Arduino va efectua sarcini pseudo-paralele a fost propusă de dezvoltatorii Arduino. Esența metodei este aceea cu fiecare repetare a ciclului buclă() verificăm dacă este timpul să clipim LED-ul (efectuați o sarcină de fundal) sau nu. Și dacă a sosit, atunci inversăm starea LED-ului. Acesta este un fel de opțiune de bypass pentru operator întârziere().

Const int soundPin = 3; // variabilă cu numărul de pin al elementului piezoelectric const int ledPin = 13; // variabilă cu numărul de pin LED const long ledInterval = 200; // Interval de clipire a LED-ului, ms. int ledState = LOW; // starea inițială a LED-ului unsigned long previousMillis = 0; // stochează ora activării anterioare a LED-ului void setup() ( pinMode(soundPin, OUTPUT); // setați pinul 3 ca ieșire. pinMode(ledPin, OUTPUT); // setați pinul 13 ca ieșire. } void loop() (// Control sunet: ton (soundPin, 700); întârziere (200); ton (soundPin, 500); întârziere (200); ton (soundPin, 300); întârziere (200); ton (soundPin, 200); întârziere (200); // LED intermitent: // timp de când Arduino a fost pornit, ms: unsigned long currentMillis = millis(); // Dacă a venit timpul să clipească, if (currentMillis - previousMillis >= ledInterval) ( previousMillis = currentMillis; // atunci amintiți-vă ora curentă dacă (ledState == LOW) ( // și inversați starea LED-ului ledState = HIGH; ) else (ledState = LOW; ) digitalWrite(ledPin, ledState); // comută starea LED-ului) }

Un dezavantaj semnificativ al acestei metode este că secțiunea de cod dinaintea blocului de control LED trebuie să fie executată mai rapid decât intervalul de timp intermitent LED-ul „ledInterval”. În caz contrar, clipirea va apărea mai rar decât este necesar și nu vom obține efectul executării paralele a sarcinilor. În special, în schița noastră, durata schimbării sunetului sirenei este de 200+200+200+200 = 800 ms, iar intervalul de clipire a LED-ului este de 200 ms. Dar LED-ul va clipi cu o perioadă de 800 ms, care este de 4 ori mai lungă decât cea setată.

În general, dacă codul folosește operatorul întârziere(), în acest caz este dificil de simulat pseudo-paralelismul, așa că este indicat să îl evitați.

În acest caz, ar fi necesar ca unitatea de control al sunetului sirenei să verifice și dacă timpul a sosit sau nu și să nu folosească întârziere(). Dar acest lucru ar crește cantitatea de cod și ar face programul mai puțin lizibil.

4 Folosind biblioteca ArduinoThread pentru a crea fire paralele

Pentru a rezolva problema, vom folosi o bibliotecă minunată ArduinoThread, care vă permite să creați cu ușurință procese pseudo-paralele. Funcționează într-un mod similar, dar vă permite să evitați scrierea codului pentru a verifica sincronizarea - indiferent dacă trebuie să efectuați o sarcină în această buclă sau nu. Acest lucru reduce cantitatea de cod și îmbunătățește lizibilitatea schiței. Să verificăm biblioteca în acțiune.


În primul rând, descărcați arhiva bibliotecii de pe site-ul oficial și dezarhivați-o într-un director biblioteci/ Mediul de dezvoltare Arduino IDE. Apoi redenumiți folderul ArduinoThread-master V ArduinoThread.

Schema de conectare va rămâne aceeași. Numai codul programului se va schimba.

#include // conectarea bibliotecii ArduinoThread const int soundPin = 3; // variabilă cu numărul de pin al elementului piezoelectric const int ledPin = 13; // variabilă cu numărul de pin LED Thread ledThread = Thread(); // creează un fir de control LED Thread soundThread = Thread(); // creează un fir de control al sirenei void setup() ( pinMode(soundPin, OUTPUT); // declar pin 3 ca ieșire. pinMode(ledPin, OUTPUT); // declar pinul 13 ca ieșire. ledThread.onRun(ledBlink); // atribuie o sarcină firului de execuție ledThread.setInterval(1000); // setează intervalul de răspuns, ms soundThread.onRun(sound); // atribuie o sarcină firului soundThread.setInterval(20); // setați intervalul de răspuns, ms } void loop() (// Verificați dacă este timpul să comute LED-ul: if (ledThread.shouldRun()) ledThread.run(); // începe firul // Verificați dacă este timpul să schimbați tonul sirenei: if (soundThread.shouldRun()) soundThread.run(); // începe firul } // Flux LED: void ledBlink() ( static bool ledStatus = false; // Stare LED On/Off ledStatus = !ledStatus; // inversează starea digitalWrite(ledPin, ledStatus); // aprinde/oprește LED-ul } // Flux de sirenă: sunet gol() ( ton int static = 100; // tonul sunetului, tonul Hz(soundPin, ton); // pornește sirena la "ton" Hz dacă (ton )

În program creăm două fire - ledFireȘi soundThread, fiecare efectuează propria sa operație: unul clipește LED-ul, al doilea controlează sunetul sirenei. În fiecare iterație a buclei, pentru fiecare thread verificăm dacă a sosit momentul sau nu pentru executarea acesteia. Dacă ajunge, este lansat pentru execuție folosind metoda alerga(). Principalul lucru este să nu folosiți operatorul întârziere(). Codul oferă explicații mai detaliate.


Să încărcăm codul în memoria Arduino și să-l rulăm. Acum totul funcționează exact așa cum ar trebui!

Instrucțiuni

În general, Arduino nu acceptă paralelizarea sarcinilor adevărate sau multithreading.
Dar de fiecare dată când se repetă bucla „loop()”, puteți specifica să verificați dacă este timpul să efectuați o sarcină suplimentară, de fundal. În acest caz, utilizatorului i se va părea că mai multe sarcini sunt efectuate simultan.
De exemplu, să clipim la o anumită frecvență și, în același timp, să scoatem sunete crescătoare și descrescătoare ca o sirenă de la un emițător piezo.
Am conectat deja atât LED-ul, cât și Arduino la Arduino de mai multe ori. Să asamblam circuitul așa cum se arată în figură. Dacă conectați LED-ul la un pin digital altul decât „13”, nu uitați de un rezistor de limitare a curentului de aproximativ 220 ohmi.

Să scriem o schiță ca aceasta și să o încărcăm în Arduino.
După ce ne uităm la tablă, este clar că schița nu este realizată exact așa cum avem nevoie: până când sirena este complet funcțională, LED-ul nu va clipi și am dori ca LED-ul să clipească ÎN TIMPUL sirenei. Care este problema aici?
Cert este că această problemă nu poate fi rezolvată în mod obișnuit. Sarcinile sunt efectuate de microcontroler strict secvenţial. Operatorul „delay()” întârzie execuția programului pentru o perioadă de timp specificată, iar până la expirarea acestui timp, următoarele comenzi din program nu vor fi executate. Din această cauză, nu putem seta un timp de execuție diferit pentru fiecare sarcină din bucla „loop()” a programului.
Prin urmare, trebuie să simulați cumva multitasking.

O opțiune în care Arduino va efectua sarcini pseudo-paralele este propusă de dezvoltatorii Arduino în articolul https://www.arduino.cc/en/Tutorial/BlinkWithoutDelay.
Esența metodei este că cu fiecare repetare a buclei () verificăm dacă este timpul să clipim LED-ul (efectuați o sarcină de fundal) sau nu. Și dacă a sosit, atunci inversăm starea LED-ului. Acesta este un fel de bypass pentru „delay()”.
Un dezavantaj semnificativ al acestei metode este că secțiunea de cod dinaintea blocului de control LED trebuie să fie executată mai mult decât intervalul de timp intermitent LED-ul „ledInterval”. În caz contrar, clipirea va apărea mai rar decât este necesar și nu vom obține efectul executării paralele a sarcinilor. În special, în schița noastră, durata schimbării sunetului sirenei este de 200+200+200+200 = 800 ms, iar intervalul de clipire a LED-ului este de 200 ms. Dar LED-ul va clipi cu o perioadă de 800 ms, ceea ce este de 4 ori diferit de ceea ce am setat. În general, dacă codul dvs. folosește operatorul „delay()”, este dificil să simulați pseudo-paralelismul, așa că este indicat să îl evitați.
În acest caz, ar fi necesar ca unitatea de comandă a sirenei să verifice și dacă timpul a sosit sau nu și să nu folosească „delay()”. Dar acest lucru ar crește cantitatea de cod și ar face programul mai puțin lizibil.

Pentru a rezolva această problemă, vom folosi minunata bibliotecă ArduinoThread, care vă permite să creați cu ușurință procese pseudo-paralele. Este similar, dar vă permite să nu scrieți cod pentru a verifica timpul - dacă trebuie să efectuați o sarcină în acest ciclu sau nu. Acest lucru reduce cantitatea de cod și îmbunătățește lizibilitatea schiței. Să verificăm biblioteca în acțiune.
În primul rând, descărcați arhiva bibliotecii de pe site-ul oficial https://github.com/ivanseidel/ArduinoThread/archive/master.zip și dezarhivați-o în directorul „biblioteci” al mediului de dezvoltare Arduino IDE. Apoi redenumiți folderul „ArduinoThread-master” în „ArduinoThread”.

Schema de conectare va rămâne aceeași. Numai codul programului se va schimba. Acum va arăta ca în insert.
În program creăm două fire, fiecare efectuând propria sa operație: unul clipește LED-ul, al doilea controlează sunetul sirenei. În fiecare iterație a buclei, pentru fiecare thread verificăm dacă a sosit momentul sau nu pentru executarea acesteia. Dacă ajunge, este lansat pentru execuție folosind metoda „run()”. Principalul lucru este să nu folosiți operatorul „delay()”.
Codul oferă explicații mai detaliate.
Să încărcăm codul în memoria Arduino și să-l rulăm. Acum totul funcționează exact așa cum ar trebui!

Entuziasmul, dependenta de muncă sau ambiția crescută ne împing literalmente să ne asumăm cât mai multe sarcini și obligații. Drept urmare, în ajunul perioadei de raportare, capul tău este plin de sarcini neîndeplinite, iar „lista de făcut” tinde să fie lungimea unei suluri de tapet.

Vei avea nevoie

  • - organizator
  • - software pentru organizarea timpului de lucru (de exemplu, cronometrul ChromoDoro - aplicația Google http://clck.ru/9ZC1)
  • - Program de sarcini sau „listă de făcut” sub forma unui tabel.
  • - autocolante și markere

Instrucțiuni

„Pentru tine și pentru mine”

Toate sarcinile sunt împărțite în cele care trebuie făcute sau pot fi atribuite cuiva care este mai puțin ocupat. Există o serie de manageri excesiv de responsabili care cred că trebuie să ducă asupra lor munca întregului departament. Deci, părinții nu vă permit să vă legați șireturile pentru că părinții se pricep mai bine la asta. Nu poți jefui și priva un subordonat de oportunitatea de a-și etala abilitățile. Acasa e la fel. În loc să-ți promiți în fiecare seară că vei repara un robinet care picura, trebuie să chemi un instalator și să tai sarcina de pe listă.

„Jos amânarea”

Acest fenomen psihologic are multe varietăți și, în consecință, definiții, dar în general este o reticență de a începe rezolvarea unei probleme. Uneori, primul pas este greu de făcut din cauza unei frici subconștiente de eșec, uneori este o reticență de-a dreptul de a trece de la o stare confortabilă de lene la una incomodă. Cel mai adesea o persoană își spune: „Voi mânca un măr, voi juca solitaire și apoi...”. Trebuie să ne schimbăm mințile. Și fă din Apple și Solitaire o recompensă pentru eforturile tale. Și, mantra de zi cu zi va suna ca: „Dacă muncesc 15 minute, voi mânca un măr! Îl merit.” Dar arta managementului timpului nu este ușoară.

Grupuri de sarcini

În afaceri, acestea ar putea fi sarcini legate de domenii de lucru (logistică sau prețuri). Elevul are blocuri tematice ale materialului studiat. Persoanele avansate împart apartamentul în zone. „Baie”, „coridor”, „spațiu lângă televizor” - fiecare are o abordare individuală. Și nu petreceți mai mult de 15 minute în fiecare zonă pentru a restabili ordinea. Acest lucru este suficient pentru a menține cu ușurință curățenia, pentru a economisi timp și pentru a nu înnebuni de vinovăție pentru sarcinile neterminate. Lângă fiecare grupă de sarcini este bine să scrieți procentul aproximativ din munca finalizată. Rezultatele vizibile ale muncii contribuie la confortul psihologic și la creșterea anxietății, ceea ce inhibă îndeplinirea sarcinii.

Motivația

Dacă există, atunci trucurile tehnice vor optimiza munca. Dacă există probleme cu acesta, atunci mementourile și autocolantele electronice vor provoca iritații. Prin urmare, este important să înțelegeți beneficiile rezolvării problemelor și să abordați munca în mod conștient. Dacă mintea rătăcește, distrasă de prostii plăcute, atunci poate că nu este suficientă bucurie în ea. Este mult mai dificil pentru o persoană deprimată să fie eficientă. Aceasta înseamnă că stimulentele pentru rezolvarea problemelor, inspirația și o atitudine creativă pozitivă deosebită trebuie căutate din exterior.

Video pe tema

Notă

Toate sarcinile pot fi împărțite în patru categorii binecunoscute: important și nu urgent, important și urgent, neimportant și urgent, neimportant și nu urgent. Atunci va fi mai ușor să calculați timpul pentru finalizarea fiecărei sarcini. Principalul lucru este să nu uitați să includeți pauze de odihnă de cincisprezece minute.

Sfaturi utile

Nu pulverizați. Cu toții încercăm să ne ridicăm la înălțimea așteptărilor altora. Dar trebuie să învățăm să spunem: „Opriți”. Pentru a finaliza eficient o sarcină, trebuie să vă concentrați complet asupra ei. Și pentru aceasta, aparent, trebuie să lucrați într-o singură direcție, încordând toate eforturile minții pentru a rezolva problemele.