Arduino: Paralelné a sériové pripojenie slave zariadení na zbernicu SPI. Pripojenie k paralelným procesom spracovania bez operátora „delay()“.

Arduino: Paralelné a sériové pripojenie slave zariadení na zbernicu SPI.  Kontaktovanie spracovania Paralelné procesy bez operátora
Arduino: Paralelné a sériové pripojenie slave zariadení na zbernicu SPI. Pripojenie k paralelným procesom spracovania bez operátora „delay()“.

Keď si nainštalujete tento program, budete prekvapení, aký podobný je s Arduino IDE. Nečudujte sa, oba programy sú vyrobené na rovnakom engine.

Aplikácia má veľa funkcií vrátane knižnice Serial, takže môžeme prepojiť prenos dát medzi doskou a .

Spustíme Arduino IDE a vyberieme najjednoduchší príklad výstup dát do Sériový port:

Void setup() ( Serial.begin(9600); ) void loop() ( Serial.println("Hello Kitty!"); // počkajte 500 milisekúnd pred odoslaním ďalšieho oneskorenia(500); )

Spustite príklad a uistite sa, že kód funguje.

Prijímanie údajov

Teraz chceme získať rovnaký text v . Poďme spustiť nový projekt a napíšte kód.

Prvým krokom je import knižnice. Poďme do Náčrt | Importovať knižnicu | Serial. V náčrte sa objaví čiara:

Importovať spracovanie.seriál.*; sériový seriál; // vytvorenie objektu sériového portu Reťazec prijatý; // dáta prijaté zo sériového portu void setup() ( String port = Serial.list(); serial = new Serial(this, port, 9600); ) void draw() ( if (serial.available() > 0) ( // ak existujú údaje, prijaté = serial.readStringUntil("\n"); // prečítanie údajov ) println (prijaté // zobrazenie údajov v konzole);

Aby sme zabezpečili prijímanie údajov zo sériového portu, potrebujeme objekt triedy Serial. Keďže s Arduinom posielame dáta typu String, musíme reťazec prijať v Processing.

V metóde nastaviť() treba sa dostať k dispozícii sériový port. Zvyčajne ide o prvý dostupný port v zozname. Potom môžeme objekt nakonfigurovať Serial, označujúci port a rýchlosť prenosu dát (je žiaduce, aby sa rýchlosti zhodovali).

Zostáva len znova pripojiť dosku, spustiť skicu zo Spracovania a sledovať prichádzajúce dáta v aplikačnej konzole.

Spracovanie umožňuje pracovať nielen s konzolou, ale aj vytvárať štandardné okná. Prepíšme kód.

Importovať spracovanie.seriál.*; sériový seriál; // vytvorenie objektu sériového portu Reťazec prijatý; // údaje prijaté zo sériového portu void setup() ( size(320, 120); String port = Serial.list(); serial = new Serial(this, port, 9600); ) void draw() ( if (serial .available() > 0) ( // ak sú tam dáta, // prečítaj ich a zapíš do prijatej premennej prijatej = serial.readStringUntil("\n"); ) // Nastavenia pre text TextSize(24); (ak (prijaté != null) ( text(prijaté, 10, 30); ) )

Zopakujme si príklad a uvidíme okno s nápisom, ktorý je prekreslený na jednom mieste.

Takto sme sa naučili prijímať dáta z Arduina. To nám umožní kresliť krásne grafy alebo vytvárať programy na sledovanie hodnôt senzorov.

Odosielanie údajov

Môžeme nielen prijímať dáta z dosky, ale aj odosielať dáta na dosku, čím ju nútime vykonávať príkazy z počítača.

Povedzme, že odošleme znak „1“ z Processing. Keď doska deteguje odoslaný symbol, zapnite LED na porte 13 (zabudovaný).

Náčrt bude podobný predchádzajúcemu. Vytvorme si napríklad malé okno. Keď kliknete do oblasti okna, pošleme „1“ a duplikujeme ho v konzole na overenie. Ak nie sú žiadne kliknutia, odošle sa príkaz „0“.

Importovať spracovanie.seriál.*; sériový seriál; // vytvorenie objektu sériového portu Reťazec prijatý; // údaje prijaté zo sériového portu void setup() ( size(320, 120); String port = Serial.list(); serial = new Serial(this, port, 9600); ) void draw() ( if (mousePressed == true) ( ​​//ak sme klikli myšou v okne serial.write("1"); //odoslať 1 println("1"); ) else ( //ak nebolo kliknutie serial.write ("0" ) //odoslať 0 ) )

Teraz napíšme náčrt pre Arduino.

Char commandValue; // údaje prichádzajúce zo sériového portu int ledPin = 13; // vstavaná LED void setup() ( pinMode(ledPin, OUTPUT); // režim výstupu dát Serial.begin(9600); ) void loop() ( if (Serial.available()) ( commandValue = Serial.read ( ) if (commandValue == "1") ( digitalWrite(ledPin, HIGH); // zapnutie LED ) else ( digitalWrite(ledPin, LOW); // inak vypnutie ) oneskorenie(10); čítanie)

Spustíme obe skice. Klikneme do okna a všimneme si, že LED svieti. Nemusíte ani klikať, ale držať stlačené tlačidlo myši – LED dióda bude svietiť nepretržite.

Výmena dát

Teraz sa pokúsme skombinovať oba prístupy a vymieňať si správy medzi tabuľou a aplikáciou v dvoch smeroch.

Pre maximálna účinnosť pridajme boolovskú premennú. Vďaka tomu už nemusíme neustále posielať 1 alebo 0 z Processing a sériový port je vyťažený a neprenáša zbytočné informácie.

Keď doska deteguje odoslanú jednotku, zmeníme booleovskú hodnotu na opačnú hodnotu vzhľadom na aktuálny stav ( NÍZKA na VYSOKÝ a naopak). IN inak Používame reťazec „Hello Kity“, ktorý pošleme iba v prípade, že nezistíme „1“.

Funkcia založiťKontakt() odošle reťazec, ktorý očakávame v Spracúvaní. Ak príde odpoveď, Spracovanie môže prijať údaje.

Char commandValue; // údaje prichádzajúce zo sériového portu int ledPin = 13; boolean ledState = LOW; //ovládanie stavu LED void setup() ( pinMode(ledPin, OUTPUT); Serial.begin(9600); establishmentContact(); // odoslanie bajtu pre kontakt, kým prijímač odpovie) void loop() ( / / ak je možné prečítať údaje if (Serial.available() > 0) ( // prečítať údaje commandValue = Serial.read(); if (commandValue == "1") ( ledState = !ledState; digitalWrite(ledPin, ledState ) delay(100) ; else ( // Poslať späť Serial.println("Ahoj Kitty"); ) delay(50 ) void createContact() ( while (Serial.available());<= 0) { Serial.println("A"); // отсылает заглавную A delay(300); } }

Prejdime k náčrtu Spracovanie. Použijeme metódu serialEvent(), ktorý sa bude volať vždy, keď sa vo vyrovnávacej pamäti nájde určitý znak.

Pridajme novú booleovskú premennú prvýKontakt, ktorý vám umožňuje určiť, či existuje pripojenie k Arduinu.

V metóde nastaviť() pridať riadok serial.bufferUntil("\n");. To nám umožňuje ukladať prichádzajúce dáta do vyrovnávacej pamäte, kým nenarazíme na konkrétny znak. V tomto prípade vraciame (\n), pretože odosielame Serial.println() od Arduina. "\n" na konci znamená, že aktivujeme nový riadok, to znamená, že toto bude posledný údaj, ktorý uvidíme.

Keďže údaje neustále posielame, metóda serialEvent() vykonáva slučkové úlohy kresliť (), potom ho môžete nechať prázdny.

Teraz sa pozrime na hlavnú metódu serialEvent(). Zakaždým, keď zadáme nový riadok (\n), zavolá sa táto metóda. A zakaždým, keď sa vykoná nasledujúca postupnosť akcií:

  • Čítajú sa prichádzajúce dáta;
  • Kontroluje sa, či obsahujú nejaké hodnoty (teda či nám bolo odovzdané prázdne dátové pole alebo „null“);
  • Odstráňte medzery;
  • Ak sme dostali potrebné údaje prvýkrát, zmeníme hodnotu booleovskej premennej prvýKontakt a povedzte Arduinu, že sme pripravení prijať nové údaje;
  • Ak nejde o prvý príjem požadovaného typu údajov, zobrazíme ich v konzole a odošleme údaje mikrokontroléra o kliknutí, ktoré bolo vykonané;
  • Informujeme Arduino, že sme pripravení prijať nový dátový paket.
import spracovanie.seriál.*; sériový seriál; // vytvorenie objektu sériového portu Reťazec prijatý; // dáta prijaté zo sériového portu // Kontrola dát prijatých z Arduino boolean firstContact = false; void setup() ( size(320, 120); String port = Serial.list(); serial = new Serial(this, port, 9600); serial.bufferUntil("\n"); ) void draw() ( ) void serialEvent(Serial myPort) ( //vytvor reťazec z údajov, ktoré prichádzajú // "\n" - oddeľovač - koniec prijatého dátového paketu = myPort.readStringUntil("\n"); //uistite sa, že naše údaje nie je prázdne predtým, ako pokračovať, ak (prijaté != null) ( //odstrániť medzery prijaté = orezať (prijaté); println (prijaté); //vyhľadajte náš reťazec "A" na spustenie handshake //ak sa nájde, vymažte buffer a odoslať požiadavku na údaje if (firstContact == false) ( if (received.equals("A")) ( serial.clear(); firstContact = true; myPort.write("A"); println("kontakt "); ) ) else ( //ak je kontakt nadviazaný, prijmeme a analyzujeme údaje println(received); if (mousePressed == true) ( ​​​​ //ak sme klikli na okno serial.write("1 "); //odoslať 1 println(" 1"); ) // keď budete mať všetky údaje, požiadajte o nový balík serial.write("A"); ) ) )

Po pripojení a spustení by sa v konzole mala objaviť fráza „Hello Kitty“. Keď kliknete myšou v okne Spracovanie, LED na kolíku 13 sa rozsvieti a zhasne.

Okrem Processingu môžete použiť programy PuTTy alebo si napísať vlastný program v C# pomocou hotových tried pre prácu s portami.

04.Komunikácia: Stmievač

Príklad ukazuje, ako môžu byť údaje odoslané z počítača do dosky na ovládanie jasu LED. Dáta prichádzajú vo forme jednotlivých bajtov od 0 do 255. Dáta môžu pochádzať z akéhokoľvek programu v počítači, ktorý má prístup k sériovému portu, vrátane Processing.

Napríklad budete potrebovať štandardný obvod s odporom a LED na kolíku 9.

Skica pre Arduino.

Const int ledPin = 9; // LED na kolíku 9 void setup() ( Serial.begin(9600); // nastavte režim na kolík pinMode(ledPin, OUTPUT); ) void loop() (jas bajtov; // skontrolujte, či existujú údaje z počítač if (Serial.available()) ( // prečítať posledné prijaté bajty od 0 do 255 jas = Serial.read(); // nastaviť jas LED analogWrite(ledPin, jas); ) )

Kód na spracovanie

Importovať spracovanie.seriál.*; Sériový port; void setup() ( size(256, 150); println("Dostupné sériové porty:"); println(Serial.list()); // Použije prvý port v tomto zozname (číslo 0). Zmeňte túto možnosť, ak chcete port // zodpovedajúci vašej Arduino doske Posledný parameter (napr. 9600) je // rýchlosť komunikácie. Musí zodpovedať hodnote odovzdanej do // Serial.begin() vo vašom porte náčrtu Arduino = new Serial. (this, Serial.list(), 9600 // Ak poznáte názov portu používaného doskou Arduino, potom explicitne uveďte //port = new Serial(this, "COM1", 9600 ) void draw() (); // nakreslite prechod z čiernej na bielu pre (int i = 0; i

Spustite ho a presuňte myš na vytvorené okno ľubovoľným smerom. Pri pohybe doľava sa jas LED zníži, pri pohybe doprava sa zvýši.

04. Komunikácia: PhysicalPixel (rozsvieťte LED diódu pomocou myši)

Poďme trochu zmeniť problém. Prejdeme myšou nad políčko a pošleme symbol „H“ (High), aby sa rozsvietila LED na doske. Keď myš opustí štvorcovú oblasť, pošleme symbol „L“ (Low), aby sa LED dióda vypla.

Kód pre Arduino.

Const int ledPin = 13; // pin 13 pre LED int incomingByte; // premenná na príjem údajov void setup() ( Serial.begin(9600); pinMode(ledPin, OUTPUT); ) void loop() ( // ak existujú dáta if (Serial.available() > 0) ( // načítaj bajt vo vyrovnávacej pamäti incomingByte = Serial.read( // ak je to znak H (ASCII 72), potom zapni LED if (incomingByte == "H") ( digitalWrite(ledPin, HIGH); ) /); / ak je toto znak L ( ASCII 76), vypnite LED, ak (prichádzajúciByte == "L") ( digitalWrite(ledPin, LOW); ) ) )

Kód na spracovanie.

Importovať spracovanie.seriál.*; plavákový boxX; plavák Y; int boxSize = 20; boolean mouseOverBox = false; Sériový port; void setup() ( size(200, 200); boxX = šírka / 2.0; boxY = výška / 2.0; rectMode(RADIUS); println(Serial.list()); // Otvorenie portu, ku ktorému je pripojená doska Arduino (v tomto prípade #0) // Uistite sa, že otvoríte port rovnakou rýchlosťou, akú používa Arduino (9600bps) port = new Serial(this, Serial.list(), 9600 ) void draw() ( background(0); ); // Ak je kurzor nad štvorcom if (mouseX > boxX - boxSize && mouseX boxY - boxSize && mouseY);

04. Komunikácia: Graf (nakreslenie grafu)

Ak sme v predchádzajúcom príklade posielali dáta z počítača na dosku, teraz vykonáme opačnú úlohu – prijmeme dáta z potenciometra a zobrazíme ich vo forme grafu.


Hardvérové ​​prerušenia

K tejto lekcii som nenašiel vtipný obrázok, našiel som len nejakú prednášku o programovaní a hneď začiatok tejto prednášky nám dokonale vysvetľuje, čo prerušiť. Prerušenie v Arduine možno opísať presne rovnakým spôsobom: mikrokontrolér „zahodí všetko“, prepne sa na vykonanie bloku funkcií v obsluhe prerušenia, vykoná ich a potom sa vráti presne na miesto v hlavnom kóde, kde sa zastavil.

Existujú rôzne typy prerušení, teda nie samotné prerušenia, ale ich príčiny: prerušenie môže spôsobiť analógovo-digitálny prevodník, počítadlo časovača alebo doslova kolík mikrokontroléra. Tieto prerušenia sa nazývajú externé prerušenia. hardvér, a o tom si dnes povieme.

Externé hardvérové ​​prerušenie je prerušenie spôsobené zmenou napätia na kolíku mikrokontroléra. Hlavným bodom je, že mikrokontrolér (výpočtové jadro) nevyzýva PIN A nestráca tým čas, pin je riešený iným hardvérom. Hneď ako sa zmení napätie na kolíku (tzn digitálny signál, +5 použité/+5 odstránené) – mikrokontrolér prijme signál, všetko ukončí, spracuje prerušenie a vráti sa do práce. Prečo je to potrebné? Najčastejšie sa prerušenia využívajú na detekciu krátkych udalostí – impulzov, prípadne aj na počítanie ich počtu, bez načítania hlavného kódu. Hardvérové ​​prerušenie môže zachytiť krátke stlačenie tlačidla alebo spustenie senzora počas zložitých dlhých výpočtov alebo oneskorení v kóde, t.j. Zhruba povedané, prebieha prieskum PIN paralelne s hlavným kódom. Prerušenia môžu tiež prebudiť mikrokontrolér z úsporných režimov, keď sú vypnuté takmer všetky periférie. Pozrime sa, ako pracovať s hardvérovými prerušeniami v Arduino IDE.

Prerušenia v Arduine

Začnime tým, že nie všetky piny „môžu“ prerušiť. Áno, existuje niečo ako pinChangeInterrupts, ale o tom si povieme až v pokročilých lekciách. Teraz musíme pochopiť, že hardvérové ​​prerušenia môžu generovať iba určité piny:

MK / číslo prerušenia INT 0 INT 1 INT 2 INT 3 INT 4 INT 5
ATmega 328/168 (Nano, UNO, Mini) D2 D3
ATmega 32U4 (Leonardo, Micro) D3 D2 D0 D1 D7
ATmega 2560 (Mega) D2 D3 D21 D20 D19 D18

Ako ste pochopili z tabuľky, prerušenia majú svoje vlastné číslo, ktoré sa líši od čísla PIN. Mimochodom, existuje pohodlná funkcia digitalPinToInterrupt(pin), ktorý prevezme číslo PIN a vráti číslo prerušenia. Pridaním tejto funkcie číslom 3 do Arduino nano dostaneme 1. Všetko je podľa tabuľky vyššie, funkcia pre lenivých.

Pomocou funkcie sa pripojí prerušenie pripojiť prerušenie (pin, handler, režim):

  • špendlík– číslo prerušenia
  • psovod– názov funkcie obsluhy prerušení (musíte si ju vytvoriť sami)
  • režim– „režim“ prevádzky prerušenia:
    • NÍZKA(nízka) – spúšťa sa signálom NÍZKA na špendlíku
    • POVSTÁVAJÚCI(rast) – spúšťa sa pri zmene signálu na pine c NÍZKA na VYSOKÝ
    • PADAJÚCI(drop) – spúšťa sa pri zmene signálu na pine VYSOKÝ na NÍZKA
    • ZMENIŤ(zmena) – spustí sa pri zmene signálu (s NÍZKA na VYSOKÝ a naopak)

Pomocou tejto funkcie je možné prerušenie aj deaktivovať odpojiťInterrupt(pin), kde pin – opäť číslo prerušenia.

Pomocou tejto funkcie môžete tiež globálne zakázať prerušenia noInterrupts() a znova ich vyriešiť pomocou preruší(). Buďte s nimi opatrní! noInterrupts() zastaví aj prerušenia časovača a všetky časové funkcie a generovanie PWM sa prerušia.

Pozrime sa na príklad, v ktorom sa stlačenia tlačidla počítajú v prerušení, ale v hlavnej slučke sú na výstupe s oneskorením 1 sekundy. Pri práci s tlačidlom v normálnom režime nie je možné kombinovať taký hrubý výstup s oneskorením:

Prchavé int počítadlo = 0; // premenná počítadla void setup() ( Serial.begin(9600); // otvoril port na komunikáciu // pripojil tlačidlo na D2 a GND pinMode(2, INPUT_PULLUP); \ // D2 je prerušenie 0 // obsluha - funkcia buttonTick // FALLING - po stlačení tlačidla bude signál 0, zachytíme ho pripevniteInterrupt(0, buttonTick, FALLING) void buttonTick() ( counter++; // + stlačenie ) void loop() ( Serial.println (počítadlo // oneskorenie výstupu (1000));

Náš kód teda počíta stlačenia kláves aj počas oneskorenia! Skvelé. Ale čo je nestály? Deklarovali sme globálnu premennú počítadlo, v ktorom sa uloží počet kliknutí na tlačidlo. Ak sa hodnota premennej v prerušení zmení, musíte o tom informovať mikrokontrolér pomocou špecifikátora nestály, ktorý sa zapisuje pred uvedením dátového typu premennej, inak bude práca nesprávna. Toto je len niečo na zapamätanie: ak sa premenná zmení v prerušení, urobte to nestály.

Niekoľko ďalších dôležitých bodov:

  • Premenné upravené v prerušení musia byť deklarované ako nestály
  • Takéto oneskorenia nefungujú v prerušeniach meškanie ()
  • Pri prerušení nemení svoj význam milis() A mikro()
  • Pri prerušení výstup na port nefunguje správne ( Serial.print()), tiež by ste ho tam nemali používať - ​​načítava jadro
  • Pri prerušovaní by ste sa mali snažiť robiť čo najmenej výpočtov a všeobecne „dlhých“ akcií - to spomalí prevádzku MC s častými prerušeniami! Čo robiť? Prečítajte si nižšie.

Ak prerušenie zachytí nejakú udalosť, ktorú nie je potrebné okamžite spracovať, potom je lepšie použiť na prácu s prerušením nasledujúci algoritmus:

  • V obsluhe prerušení jednoducho zdvihneme vlajku
  • V hlavnej slučke programu skontrolujeme príznak, ak je zdvihnutý, resetujeme ho a vykonáme potrebné akcie
volatile boolean intFlag = false; // flag void setup() ( Serial.begin(9600); // otvoril port pre komunikáciu // pripojil tlačidlo k D2 a GND pinMode(2, INPUT_PULLUP); // D2 je prerušenie 0 // handler - funkčné tlačidloTick // FALLING - po stlačení tlačidla zaznie signál 0 a zachytíme ho pripojiťInterrupt(0, buttonTick, FALLING) void buttonTick() ( intFlag = true; // zdvihol príznak prerušenia ) void loop() ( if (intFlag) ( intFlag = false; // reset // vykonanie niektorých akcií Serial.println("Prerušenie!");

To je v podstate všetko, čo potrebujete vedieť o prerušeniach, na konkrétnejšie prípady sa pozrieme v pokročilých lekciách.

Video

Inštrukcie

Všeobecne povedané, Arduino nepodporuje skutočnú paralelizáciu úloh alebo multithreading.
Ale zakaždým, keď sa slučka „loop()“ opakuje, môžete určiť, aby ste skontrolovali, či je čas vykonať nejakú dodatočnú úlohu na pozadí. V tomto prípade sa používateľovi bude zdať, že sa súčasne vykonáva niekoľko úloh.
Napríklad žmurkajme na danej frekvencii a zároveň vydávajme zosilňujúce a klesajúce zvuky ako siréna z piezo žiariča.
LED aj Arduino sme už pripojili k Arduinu viac ako raz. Zostavme obvod, ako je znázornené na obrázku. Ak pripojíte LED k digitálny výstup, iný ako "13", nezabudnite na odpor obmedzujúci prúd približne 220 ohmov.

Napíšme takýto náčrt a nahrajte ho do Arduina.
Po pohľade na tabuľu je jasné, že náčrt nie je vykonaný presne tak, ako by sme potrebovali: kým nebude siréna plne funkčná, LED dióda nebude blikať a my by sme chceli, aby LED blikala POČAS zvuku sirény. Aký je tu problém?
Faktom je, že tento problém nemožno vyriešiť bežným spôsobom. Úlohy vykonáva mikrokontrolér striktne postupne. Operátor "delay()" oneskorí spustenie programu o určený čas a kým tento čas neuplynie, nasledujúce príkazy v programe sa nevykonajú. Z tohto dôvodu nemôžeme pre každú úlohu v slučke „loop()“ programu nastaviť iný čas vykonania.
Preto musíte nejakým spôsobom simulovať multitasking.

Možnosť, v ktorej bude Arduino vykonávať úlohy pseudoparalelne, navrhujú vývojári Arduina v článku https://www.arduino.cc/en/Tutorial/BlinkWithoutDelay.
Podstatou metódy je, že pri každom opakovaní slučky () skontrolujeme, či je čas blikať LED (vykonať úlohu na pozadí) alebo nie. A ak to prišlo, potom prevrátime stav LED. Toto je druh obídenia operátora „delay()“.
Značná nevýhoda túto metódu je, že časť kódu pred riadiacim blokom LED musí byť vykonaná rýchlejšie ako časový interval blikania LED "ledInterval". V opačnom prípade sa bude blikať menej často, ako je potrebné, a nedosiahneme efekt paralelného vykonávania úloh. Konkrétne v našom náčrte je trvanie zmeny zvuku sirény 200+200+200+200 = 800 ms a interval blikania LED sme nastavili na 200 ms. Ale LED bude blikať s periódou 800 ms, čo je 4-krát rozdiel od toho, čo sme nastavili. Vo všeobecnosti, ak váš kód používa operátor „delay()“, je ťažké simulovať pseudoparalelnosť, preto je vhodné sa tomu vyhnúť.
V tomto prípade by bolo potrebné, aby aj riadiaca jednotka zvuku sirény skontrolovala, či nastal čas alebo nie, a nepoužívala „oneskorenie()“. To by však zvýšilo množstvo kódu a znížilo čitateľnosť programu.

Na vyriešenie tohto problému použijeme nádhernú knižnicu ArduinoThread, ktorá vám umožní jednoducho vytvárať pseudoparalelné procesy. Funguje to podobným spôsobom, ale umožňuje vám vyhnúť sa písaniu kódu na kontrolu načasovania – či už potrebujete vykonať úlohu v tejto slučke alebo nie. Tým sa zníži množstvo kódu a zlepší sa čitateľnosť náčrtu. Pozrime sa na knižnicu v akcii.
Najprv si stiahnite archív knižnice z oficiálnej stránky https://github.com/ivanseidel/ArduinoThread/archive/master.zip a rozbaľte ho do adresára „libraries“ vývojového prostredia Arduino IDE. Potom premenujte priečinok „ArduinoThread-master“ na „ArduinoThread“.

Schéma zapojenia zostane rovnaká. Zmení sa iba kód programu. Teraz to bude vyzerať ako vo vložke.
V programe vytvoríme dve vlákna, z ktorých každé vykonáva svoju vlastnú operáciu: jedno bliká LED, druhé ovláda zvuk sirény. V každej iterácii cyklu pre každé vlákno kontrolujeme, či nastal čas na jeho vykonanie alebo nie. Ak príde, spustí sa na vykonanie pomocou metódy „run()“. Hlavná vec je nepoužívať operátor „delay()“.
Kód poskytuje podrobnejšie vysvetlenia.
Načítajme kód do pamäte Arduina a spustíme ho. Teraz všetko funguje presne tak, ako má!

Všeobecne povedané, Arduino nepodporuje skutočný paralelizmus úloh alebo multithreading. Ale je to možné pri každom opakovaní cyklu slučka() povedzte mikrokontroléru, aby skontroloval, či je čas vykonať nejakú dodatočnú úlohu na pozadí. V tomto prípade sa používateľovi bude zdať, že sa súčasne vykonáva niekoľko úloh.

Napríklad zablikajme LED diódou na danej frekvencii a zároveň vydajme zosilňujúce a klesajúce zvuky ako siréna z piezo žiariča. LED aj piezo žiarič sme už k Arduinu viackrát pripojili. Zostavme obvod, ako je znázornené na obrázku.

Ak pripojíte LED na iný digitálny pin ako "13", nezabudnite na odpor obmedzujúci prúd asi 220 ohmov.

2 Ovládanie LED a piezo žiariča pomocou operátora delay().

Napíšme takýto náčrt a nahrajte ho do Arduina.

Const int soundPin = 3; /* deklarujeme premennú s číslom pinu, na ktorý je pripojený piezoelektrický prvok */ const int ledPin = 13; // deklaruje premennú s číslom pinu LED void setup() ( pinMode(soundPin, OUTPUT); // vyhlási pin 3 ako výstup. pinMode(ledPin, OUTPUT); // vyhlási pin 13 ako výstup. } void loop() (// Ovládanie zvuku: tone(soundPin, 700); // vydáva zvuk s frekvenciou 700 Hz delay(200); tón (soundPin, 500); // pri frekvencii 500 Hz delay(200); tón (soundPin, 300); // pri frekvencii 300 Hz delay(200); tón (soundPin, 200); // pri frekvencii 200 Hz delay(200); // LED ovládanie: digitalWrite(ledPin, HIGH); // oneskorenie svetla(200); digitalWrite(ledPin, LOW); // oneskorenie vypnutia(200); }

Po zapnutí je jasné, že náčrt neprebieha presne tak, ako by sme potrebovali: kým nebude siréna plne funkčná, LED nebude blikať, ale chceli by sme, aby LED blikala počas zvuk sirény. Aký je tu problém?

Faktom je, že tento problém nemožno vyriešiť bežným spôsobom. Úlohy vykonáva mikrokontrolér striktne postupne. Operátor meškanie () oneskorí spustenie programu o určený čas a kým tento čas neuplynie, nasledujúce príkazy programu sa nevykonajú. Z tohto dôvodu nemôžeme nastaviť iné trvanie vykonávania pre každú úlohu v slučke slučka() programy. Preto musíte nejakým spôsobom simulovať multitasking.

3 Paralelné procesy bez operátora „delay()“.

Vývojári Arduina navrhli možnosť, v ktorej bude Arduino vykonávať úlohy pseudoparalelne. Podstatou metódy je, že pri každom opakovaní cyklu slučka() skontrolujeme, či je čas blikať LED (vykonať úlohu na pozadí) alebo nie. A ak to prišlo, potom prevrátime stav LED. Toto je druh možnosti obídenia pre operátora meškanie ().

Const int soundPin = 3; // premenná s číslom pinu piezoelektrického prvku const int ledPin = 13; // premenná s číslom pinu LED const long ledInterval = 200; // Interval blikania LED, ms. int ledState = NÍZKA; // počiatočný stav LED bez znamienka long previousMillis = 0; // uloženie času predchádzajúcej aktivácie LED void setup() ( pinMode(soundPin, OUTPUT); // nastavíme pin 3 ako výstup. pinMode(ledPin, OUTPUT); // nastavíme pin 13 ako výstup. } void loop() (// Ovládanie zvuku: tone(soundPin, 700); oneskorenie(200); tón (soundPin, 500); oneskorenie(200); tón (soundPin, 300); oneskorenie(200); tón (soundPin, 200); oneskorenie(200); // Blikajúca LED: // čas od zapnutia Arduina, ms: unsigned long currentMillis = millis(); // Ak nastal čas žmurkania, ak (currentMillis - previousMillis >= ledInterval) ( previousMillis = currentMillis; // potom si zapamätajte aktuálny čas if (ledState == LOW) ( // a invertuje stav LED ledState = HIGH; ) else ( ledState = LOW; ) digitalWrite(ledPin, ledState); // prepnite stav LED) }

Významnou nevýhodou tejto metódy je, že úsek kódu pred riadiacim blokom LED musí byť vykonaný rýchlejšie ako časový interval blikania LED „ledInterval“. V opačnom prípade sa bude blikať menej často, ako je potrebné, a nedosiahneme efekt paralelného vykonávania úloh. Konkrétne v našom náčrte je trvanie zmeny zvuku sirény 200+200+200+200 = 800 ms a interval blikania LED sme nastavili na 200 ms. Ale LED bude blikať s periódou 800 ms, čo je 4-krát dlhšie, ako sme nastavili.

Vo všeobecnosti, ak kód používa operátor meškanie (), v tomto prípade je ťažké simulovať pseudoparalelnosť, preto je vhodné sa jej vyhnúť.

V tomto prípade by bolo potrebné, aby aj riadiaca jednotka zvuku sirény skontrolovala, či nastal čas a nepoužívala meškanie (). To by však zvýšilo množstvo kódu a znížilo čitateľnosť programu.

4 Používanie knižnice ArduinoThread vytvárať paralelné vlákna

Na vyriešenie problému použijeme nádhernú knižnicu ArduinoThread, ktorý umožňuje jednoducho vytvárať pseudoparalelné procesy. Funguje to podobným spôsobom, ale umožňuje vám vyhnúť sa písaniu kódu na kontrolu načasovania – či už potrebujete vykonať úlohu v tejto slučke alebo nie. Tým sa zníži množstvo kódu a zlepší sa čitateľnosť náčrtu. Pozrime sa na knižnicu v akcii.


Najprv si stiahnite archív knižnice z oficiálnej stránky a rozbaľte ho do adresára knižnice/ Vývojové prostredie Arduino IDE. Potom priečinok premenujte ArduinoThread-master V ArduinoThread.

Schéma zapojenia zostane rovnaká. Zmení sa iba kód programu.

#include // pripojenie knižnice ArduinoThread const int soundPin = 3; // premenná s číslom pinu piezoelektrického prvku const int ledPin = 13; // premenná s číslom pinu LED Thread ledThread = Thread(); // vytvorenie riadiaceho vlákna LED Thread soundThread = Thread(); // vytvorte vlákno na ovládanie sirén void setup() ( pinMode(soundPin, OUTPUT); // vyhlási pin 3 ako výstup. pinMode(ledPin, OUTPUT); // vyhlási pin 13 ako výstup. ledThread.onRun(ledBlink); // priraďte úlohu vláknu ledThread.setInterval(1000); // nastavenie intervalu odozvy, ms soundThread.onRun(sound); // priradíme úlohu vláknu soundThread.setInterval(20); // nastavenie intervalu odozvy, ms } void loop() (// Skontrolujte, či je čas na prepnutie LED: if (ledThread.shouldRun()) ledThread.run(); // spustenie vlákna // Skontrolujte, či je čas zmeniť tón sirény: if (soundThread.shouldRun()) soundThread.run(); // spustenie vlákna } // LED stream: void ledBlink() ( static bool ledStatus = false; // Stav LED Zap/Vyp ledStatus = !ledStatus; // invertuje stav digitalWrite(ledPin, ledStatus); // zapnutie/vypnutie LED } // Stream sirény: void sound() ( statický int tón = 100; // výška zvuku, Hz tón(soundPin, tón); // zapnite sirénu na "ton" Hz, ak (ton)

V programe vytvoríme dve vlákna - ledThread A soundThread, každý vykonáva svoju vlastnú operáciu: jeden bliká LED, druhý ovláda zvuk sirény. V každej iterácii cyklu pre každé vlákno kontrolujeme, či nastal čas na jeho vykonanie alebo nie. Ak príde, spustí sa na vykonanie pomocou metódy spustiť(). Hlavná vec je nepoužívať operátora meškanie (). Kód poskytuje podrobnejšie vysvetlenia.


Načítajme kód do pamäte Arduina a spustíme ho. Teraz všetko funguje presne tak, ako má!

A uveďme príklad použitia Funkcie Arduina pripojiťPrerušenie().

Prerušenie je signál, ktorý upozorňuje procesor, že nastala udalosť, ktorá si vyžaduje okamžitú pozornosť. Procesor musí na tento signál reagovať prerušením vykonávania aktuálnych inštrukcií a prenesením riadenia na obsluhu prerušenia (ISR, Interrupt Service Rutine). Obslužná rutina je bežná funkcia, ktorú si sami napíšeme a umiestnime tam kód, ktorý by mal reagovať na udalosť.

Po obsluhe prerušenia ISR funkcia dokončí svoju prácu a procesor sa veselo vráti k prerušenej činnosti - pokračuje vo vykonávaní kódu tam, kde skončil. Toto všetko sa deje automaticky, takže našou úlohou je iba napísať obsluhu prerušenia bez toho, aby sme niečo porušili alebo aby sme procesor príliš často rozptyľovali. Budete potrebovať pochopenie obvodu, princípov fungovania pripojených zariadení a predstavu o tom, ako často môže dôjsť k prerušeniu a aké sú vlastnosti jeho výskytu. To všetko predstavuje hlavný problém práce s prerušeniami.

Hardvérové ​​a softvérové ​​prerušenia

Prerušenia v Arduine možno rozdeliť do niekoľkých typov:

  • Hardvérové ​​prerušenia. Prerušenie na úrovni architektúry mikroprocesora. Samotná udalosť môže nastať v produktívnom momente z externého zariadenia – napríklad stlačenie tlačidla na klávesnici, pohyb počítačová myš a tak ďalej.
  • Softvérové ​​prerušenia. Spúšťajú sa vo vnútri programu pomocou špeciálnych pokynov. Používa sa na volanie obsluhy prerušení.
  • Interné (synchrónne) prerušenia. K internému prerušeniu dochádza v dôsledku zmeny alebo narušenia vykonávania programu (napríklad pri prístupe na neplatnú adresu, neplatný operačný kód atď.).

Prečo sú potrebné hardvérové ​​prerušenia?

Hardvérové ​​prerušenia sa vyskytujú v reakcii na externú udalosť a pochádzajú z externého hardvérového zariadenia. Arduino poskytuje 4 typy hardvérových prerušení. Všetky sa líšia signálom na prerušovacom kolíku:

  • Kontakt je pritiahnutý k zemi. Obsluha prerušenia sa vykonáva, pokiaľ je na kolíku prerušenia signál LOW.
  • Zmena signálu na kontakte. V tomto prípade Arduino vykoná obsluhu prerušenia, keď dôjde k zmene signálu na prerušovacom kolíku.
  • Zmena signálu z LOW na HIGH na kolíku - pri zmene z nízkeho signálu na vysoký sa vykoná obsluha prerušenia.
  • Zmena signálu z HIGH na LOW na kolíku - pri zmene z vysokého signálu na nízky sa vykoná obsluha prerušenia.

Prerušenia sú užitočné v programoch Arduino, pretože pomáhajú riešiť problémy s načasovaním. Napríklad pri práci s UART vám prerušenia umožňujú vyhnúť sa tomu, aby ste museli sledovať príchod každej postavy. Externé hardvérové ​​zariadenie vydá signál prerušenia, procesor okamžite zavolá obsluhu prerušenia, ktorá znak včas zachytí. To šetrí čas CPU, ktorý by sa inak strávil kontrolou stavu UART bez prerušenia, namiesto toho všetky potrebné akcie vykoná obsluha prerušení bez ovplyvnenia hlavný program. Od hardvérového zariadenia sa nevyžadujú žiadne špeciálne schopnosti.

Hlavné dôvody, prečo je potrebné zavolať prerušenie, sú:

  • Detekcia zmeny stavu výstupu;
  • Prerušenie časovača;
  • Dátové prerušenia cez SPI, I2C, USART;
  • Analógovo-digitálny prevod;
  • Ochota používať EEPROM, flash pamäť.

Ako sa v Arduine implementujú prerušenia

Keď je prijatý signál prerušenia, prevádzka sa preruší. Začne sa vykonávanie funkcie, ktorá je deklarovaná ako vykonaná pri prerušení. Deklarovaná funkcia nemôže akceptovať vstupné hodnoty a vrátiť hodnoty pri ukončení. Prerušenie neovplyvňuje samotný kód v hlavnej programovej slučke. Na prácu s prerušeniami v Arduine sa používa štandardná funkcia pripojiťPrerušenie().

Rozdiely v implementácii prerušenia v rôznych doskách Arduino

V závislosti od hardvérovej implementácie konkrétny model Mikrokontrolér má niekoľko prerušení. zaplatiť Arduino Uno má 2 prerušenia na druhom a treťom pine, ale ak sú potrebné viac ako dva výstupy, doska podporuje špeciálny režim„výmena pinov“. Tento režim funguje zmenou vstupu pre všetky piny. Rozdiel medzi režimom prerušenia zmeny vstupu je v tom, že prerušenia môžu byť generované na ktoromkoľvek z ôsmich kolíkov. V tomto prípade bude spracovanie zložitejšie a dlhšie, pretože budete musieť sledovať najnovší stav každého z kontaktov.

Na iných doskách je počet prerušení vyšší. Napríklad doska má 6 pinov, ktoré zvládajú externé prerušenia. Pre všetky dosky Arduino, pri práci s funkciou attachmentInterrupt (prerušenie, funkcia, režim), je argument Inerrupt 0 spojený s digitálnym pinom 2.

Prerušenia v jazyku Arduino

Teraz poďme do praxe a porozprávajme sa o tom, ako používať prerušenia vo svojich projektoch.

Syntax connectInterrupt()

Na prácu s prerušeniami slúži funkcia attachmentInterrupt. Slúži na pripojenie externého prerušenia k handleru.

Syntax volania: attachmentInterrupt(prerušenie, funkcia, režim)

Argumenty funkcie:

  • prerušenie – číslo volaného prerušenia (štandard 0 – pre 2. pin, pre dosku Arduino Uno 1 – pre 3. pin),
  • funkcia – názov funkcie, ktorá sa má zavolať pri prerušení (dôležité – funkcia by nemala prijímať ani vracať žiadne hodnoty),
  • režim – podmienka pre spustenie prerušenia.

Je možné nastaviť nasledujúce spúšťacie podmienky:

  • LOW – vykonáva sa pri nízkej úrovni signálu, keď má kontakt nulovú hodnotu. Prerušenie sa môže cyklicky opakovať – napríklad pri stlačení tlačidla.
  • ZMENA – na hrane dochádza k prerušeniu pri zmene signálu z vysokého na nízky alebo naopak. Spustí sa raz pri akejkoľvek zmene signálu.
  • RISING – vykoná prerušenie raz, keď sa signál zmení z LOW na HIGH.
  • FALLING – vykoná prerušenie raz, keď sa signál zmení z HIGH na LOW.4

Dôležité poznámky

Pri práci s prerušeniami je potrebné vziať do úvahy nasledujúce dôležité obmedzenia:

  • Funkcia handlera by nemala bežať príliš dlho. Ide o to, že Arduino nedokáže zvládnuť niekoľko prerušení súčasne. Kým je spustená funkcia obsluhy, všetky ostatné prerušenia budú ignorované a môžete zmeškať dôležité udalosti. Ak potrebujete urobiť niečo veľké, stačí preniesť spracovanie udalostí do hlavnej slučky loop(). V obslužnom programe môžete nastaviť iba príznak udalosti a v slučke môžete príznak skontrolovať a spracovať.
  • S premennými musíte byť veľmi opatrní. Inteligentný kompilátor C++ dokáže „znovu optimalizovať“ váš program – odstrániť premenné, ktoré sú podľa neho zbytočné. Kompilátor jednoducho neuvidí, že v jednej časti nastavíte nejaké premenné a použijete ich v inej. Na odstránenie tejto možnosti v prípade základných dátových typov môžete použiť kľúčové slovo volatile, napríklad takto: volatile boolean state = 0. Táto metóda však nebude fungovať so zložitými dátovými štruktúrami. Preto musíte byť vždy v strehu.
  • Neodporúča sa používať veľké množstvo prerušení (snažte sa nepoužívať viac ako 6-8). Veľké množstvo spracovanie rôznych udalostí vyžaduje vážnu komplikáciu kódu, a preto vedie k chybám. Okrem toho je potrebné pochopiť, že neexistuje časová presnosť vykonávania v systémoch s veľké množstvo V reči nemôžu byť žiadne prerušenia - nikdy presne nepochopíte, aký je interval medzi volaniami príkazov, ktoré sú pre vás dôležité.
  • Je prísne zakázané používať delay() v handleroch. Mechanizmus na určenie intervalu oneskorenia používa časovače a fungujú aj na prerušeniach, ktoré váš obslužný program zablokuje. Tým pádom si každý na každého počká a program zamrzne. Z rovnakého dôvodu nemožno použiť komunikačné protokoly založené na prerušení (napríklad i2c).

Príklady použitia attachmentInterrupt

Poďme si precvičiť a pozrieť sa na jednoduchý príklad použitia prerušení. V príklade definujeme funkciu handlera, ktorá pri zmene signálu na pine 2 Arduino Uno prepne stav pinu 13, na ktorý tradične pripájame LED.

#define PIN_LED 13 volatile boolean actionState = LOW; void setup() ( pinMode(PIN_LED, OUTPUT); // Nastavenie prerušenia // Funkcia myEventListener sa zavolá, keď // na kolíku 2 (prerušenie 0 je pripojené na kolík 2) // sa zmení signál (bez ohľadu na to, v akom direction) attachmentInterrupt (0, myEventListener, CHANGE void loop() ( // Vo funkcii loop nerobíme nič, pretože celý kód spracovania udalostí bude vo funkcii myEventListener) void myEventListener() ( actionState != actionState); ; // / / Vykonajte ďalšie akcie, napríklad zapnite alebo vypnite LED diódu digitalWrite(PIN_LED, actionState )

Pozrime sa na niekoľko príkladov zložitejších prerušení a ich obsluhy: pre časovače a tlačidlá.

Preruší sa stlačením tlačidla s ochranou proti odskoku

Pri prerušení nastáva - predtým, ako sa kontakty po stlačení tlačidla dostanú do tesného kontaktu, budú oscilovať a vygenerovať niekoľko operácií. Existujú dva spôsoby, ako sa vysporiadať s odskokom: hardvér, to znamená prispájkovanie kondenzátora na tlačidlo, a softvér.

Vŕzgania sa môžete zbaviť pomocou funkcie – umožňuje vám merať čas, ktorý uplynul od prvého stlačenia tlačidla.

If(digitalRead(2)==HIGH) ( //keď sa stlačí tlačidlo //Ak od predchádzajúceho stlačenia uplynulo viac ako 100 milisekúnd if (millis() - previousMillis >= 100) ( //Čas prvého operácia je zapamätaná predošláMillis = millis( if (led==oldled) ( //skontroluje, či sa stav tlačidla nezmenil led=!led; )

Tento kód vám umožňuje odstrániť debounce a neblokuje vykonávanie programu, ako je to v prípade funkcie delay, ktorá nie je povolená pri prerušeniach.

Časovač preruší

Časovač je počítadlo, ktoré počíta na určitej frekvencii, získanej z procesora 16 MHz. Delič frekvencie je možné nakonfigurovať tak, aby získal požadovaný režim počítania. Môžete tiež nakonfigurovať počítadlo na generovanie prerušení pri dosiahnutí nastavenej hodnoty.

A prerušenie časovačom vám umožňuje prerušiť raz za milisekundu. Arduino má 3 časovače – Timer0, Timer1 a Timer2. Timer0 sa používa na generovanie prerušení raz za milisekundu, ktoré aktualizuje počítadlo a odovzdáva ho funkcii millis(). Tento časovač je osembitový a počíta od 0 do 255. Prerušenie sa vygeneruje, keď hodnota dosiahne 255. Štandardne sa na získanie frekvencie blízkej 1 kHz používa hodinový delič 65.

Porovnávacie registre slúžia na porovnanie stavu na časovači a uložených dát. IN v tomto príklade kód vygeneruje prerušenie, keď počítadlo dosiahne 0xAF.

TIMSK0 |= _BV(OCIE0A);

Musíte definovať obsluhu prerušenia pre vektor prerušenia časovača. Vektor prerušenia je ukazovateľ na adresu miesta príkazu, ktorý sa vykoná pri volaní prerušenia. Viaceré vektory prerušení sa kombinujú do tabuľky vektorov prerušení. Časovač sa v tomto prípade bude volať TIMER0_COMPA_vect. Tento handler vykoná rovnaké akcie ako v loop().

SIGNAL(TIMER0_COMPA_vect) ( bez znamienka dlhý currentMillis = millis(); sweeper1.Update(currentMillis); if(digitalRead(2) == HIGH) ( sweeper2.Update(currentMillis); led1.Update(currentMillis); ) led2.Update( currentMillis led3.Update(currentMillis) //Funkcia loop() zostane prázdna. void loop() ( )

Zhrnutie

Prerušenie v Arduine je pomerne zložitá téma, pretože musíte premýšľať o celej architektúre projektu naraz, predstaviť si, ako sa kód vykonáva, aké udalosti sú možné, čo sa stane, keď sa preruší hlavný kód. Nemali sme v úmysle odhaliť všetky vlastnosti práce s týmto jazykovým konštruktom, hlavným cieľom bolo predstaviť hlavné prípady použitia. V budúcich článkoch budeme v rozhovore o prerušeniach pokračovať podrobnejšie.