Arduino prekida svake sekunde. Obrada kontakata

A evo i primjera korištenja Arduino funkcije priložiteprekid().

Prekid je signal koji obavještava procesor da se dogodio događaj koji zahtijeva hitnu pozornost. Procesor mora odgovoriti na ovaj signal prekidanjem izvršenja trenutnih instrukcija i prijenosom kontrole na rukovatelja prekida (ISR, Interrupt Service Routine). Rukovatelj je redovita funkcija koju sami pišemo i tamo postavljamo kod koji bi trebao odgovoriti na događaj.

Nakon servisiranja ISR prekida, funkcija dovršava svoj posao i procesor se sretno vraća svojim prekinutim aktivnostima – nastavlja izvršavati kod tamo gdje je stao. Sve se to događa automatski, tako da je naš zadatak samo napisati rukovatelj prekidima, a da ništa ne pokvarimo ili da prečesto ometamo procesor. Trebat će vam razumijevanje kruga, načela rada povezanih uređaja i ideja o tome koliko često se prekid može pokrenuti i koje su značajke njegove pojave. Sve ovo predstavlja glavnu poteškoću rada s prekidima.

Hardverski i softverski prekidi

Prekidi u Arduinu mogu se podijeliti u nekoliko tipova:

  • Hardverski prekidi. Prekid na razini arhitekture mikroprocesora. Sam događaj može se dogoditi u produktivnom trenutku s vanjskog uređaja - na primjer, pritiskom na gumb na tipkovnici, pomicanjem računalnog miša itd.
  • Softverski prekidi. Pokreću se unutar programa pomoću posebnih uputa. Koristi se za pozivanje rukovatelja prekidom.
  • Interni (sinkroni) prekidi. Interni prekid nastaje kao rezultat promjene ili kršenja u izvršavanju programa (na primjer, kada se pristupa nevažećoj adresi, nevažećem kodu operacije itd.).

Zašto su potrebni hardverski prekidi?

Hardverski prekidi nastaju kao odgovor na vanjski događaj i potječu od vanjskog hardverskog uređaja. Arduino nudi 4 vrste hardverskih prekida. Svi se razlikuju po signalu na prekidnom pinu:

  • Kontakt je povučen na tlo. Rukovatelj prekidom se izvršava sve dok postoji LOW signal na pinu prekida.
  • Promjena signala na kontaktu. U ovom slučaju, Arduino izvršava rukovatelj prekidom kada dođe do promjene signala na pinu prekida.
  • Promjena signala iz LOW u HIGH na pinu - kod promjene s niskog signala na visoki, izvršit će se rukovatelj prekidom.
  • Promjena signala s HIGH na LOW na pinu - kada se mijenja s visokog signala na niski signal, izvršit će se rukovatelj prekidom.

Prekidi su korisni u Arduino programima jer pomažu u rješavanju problema vremena. Na primjer, kada radite s UART-om, prekidi vam omogućuju da izbjegnete praćenje dolaska svakog znaka. Vanjski hardverski uređaj izdaje signal prekida, procesor odmah poziva rukovatelja prekidom, koji na vrijeme hvata znak. Ovo štedi CPU vrijeme koje bi se inače potrošilo na provjeru statusa UART-a bez prekida; umjesto toga, sve potrebne radnje izvodi rukovatelj prekidima bez utjecaja na glavni program. Od hardverskog uređaja nisu potrebne posebne mogućnosti.

Glavni razlozi zašto je potrebno pozvati prekid su:

  • Detektiranje promjene izlaznog stanja;
  • Prekid mjerača vremena;
  • Prekidi podataka putem SPI, I2C, USART;
  • Analogno-digitalna pretvorba;
  • Spremnost na korištenje EEPROM-a, flash memorije.

Kako su prekidi implementirani u Arduinu

Kada se primi signal prekida, rad se obustavlja. Započinje izvršenje funkcije koja je deklarirana za izvršenje kada se prekine. Deklarirana funkcija ne može prihvatiti ulazne vrijednosti i vratiti vrijednosti kada izađe. Prekid ne utječe na sam kod u glavnoj programskoj petlji. Za rad s prekidima u Arduinu koristi se standardna funkcija priložiteprekid().

Razlike u implementaciji prekida u različitim Arduino pločama

Ovisno o hardverskoj izvedbi pojedinog modela mikrokontrolera, postoji nekoliko prekida. Arduino Uno ploča ima 2 prekida na drugom i trećem pinu, ali ako je potrebno više od dva izlaza, ploča podržava poseban mod "promjene pinova". Ovaj način funkcionira mijenjanjem ulaza za sve pinove. Razlika između moda prekida promjene ulaza je u tome što se prekidi mogu generirati na bilo kojem od osam pinova. U tom će slučaju obrada biti teža i duža jer ćete morati pratiti najnovije stanje na svakom od kontakata.

Na drugim pločama broj prekida je veći. Na primjer, ploča ima 6 pinova koji mogu rukovati vanjskim prekidima. Za sve Arduino ploče, kada radite s funkcijom attachInterrupt (prekid, funkcija, način), argument Inerrupt 0 povezan je s digitalnim pinom 2.

Prekidi u Arduino jeziku

Sada idemo praktično i razgovarajmo o tome kako koristiti prekide u svojim projektima.

Sintaksa attachInterrupt()

Funkcija attachInterrupt koristi se za rad s prekidima. Služi za povezivanje vanjskog prekida s rukovateljem.

Sintaksa poziva: attachInterrupt(prekid, funkcija, način)

Argumenti funkcije:

  • interrupt – broj prekida koji treba pozvati (standardno 0 – za 2. pin, za Arduino Uno ploču 1 – za 3. pin),
  • funkcija – naziv funkcije koja se poziva kada se prekine (važno - funkcija ne smije prihvaćati niti vraćati vrijednosti),
  • mode – uvjet za pokretanje prekida.

Mogu se postaviti sljedeći uvjeti okidanja:

  • LOW – izvodi se kada je razina signala niska kada kontakt ima nultu vrijednost. Prekid se može ponavljati ciklički - na primjer, kada se pritisne gumb.
  • CHANGE – na rubu se javlja prekid kada se signal promijeni iz visokog u niski ili obrnuto. Izvršava se jednom za bilo koju promjenu signala.
  • RISING – izvrši prekid jednom kada se signal promijeni iz LOW u HIGH.
  • FALLING – izvršiti prekid jednom kada se signal promijeni iz HIGH u LOW.4

Važne bilješke

Pri radu s prekidima moraju se uzeti u obzir sljedeća važna ograničenja:

  • Funkcija rukovatelja ne bi trebala raditi predugo. Stvar je u tome što Arduino ne može podnijeti nekoliko prekida u isto vrijeme. Dok vaša funkcija rukovatelja radi, svi ostali prekidi bit će zanemareni i mogli biste propustiti važne događaje. Ako trebate učiniti nešto veliko, samo prenesite obradu događaja u glavnu petlju (). U rukovatelju možete postaviti samo oznaku događaja, au petlji možete provjeriti oznaku i obraditi je.
  • Morate biti vrlo oprezni s varijablama. Inteligentni C++ prevodilac može "ponovno optimizirati" vaš program - ukloniti varijable koje su, po njegovom mišljenju, nepotrebne. Prevodilac jednostavno neće vidjeti da neke varijable postavljate u jednom dijelu, a koristite ih u drugom. Da biste eliminirali ovu mogućnost u slučaju osnovnih tipova podataka, možete koristiti ključnu riječ volatile, na primjer: volatile boolean state = 0. Ali ova metoda neće raditi sa složenim strukturama podataka. Stoga uvijek morate biti na oprezu.
  • Ne preporučuje se korištenje velikog broja prekida (pokušajte ne koristiti više od 6-8). Velik broj različitih događaja zahtijeva ozbiljno kompliciranje koda, i stoga dovodi do pogrešaka. Osim toga, morate shvatiti da se ne može govoriti o vremenskoj točnosti izvršenja u sustavima s velikim brojem prekida - nikada nećete razumjeti točno koliki je interval između poziva vama važnih naredbi.
  • Strogo je zabranjeno koristiti delay() u rukovateljima. Mehanizam za određivanje intervala kašnjenja koristi mjerače vremena, a oni također rade na prekidima koje će vaš rukovatelj blokirati. Kao rezultat toga, svatko će čekati svakoga i program će se zamrznuti. Iz istog razloga se ne mogu koristiti komunikacijski protokoli temeljeni na prekidu (na primjer, i2c).

Primjeri korištenja attachInterrupt-a

Krenimo na praksu i pogledajmo jednostavan primjer korištenja prekida. U primjeru definiramo funkciju rukovatelja koja će, kada se promijeni signal na pinu 2 Arduino Uno, promijeniti stanje pina 13, na koji tradicionalno povezujemo LED.

#define PIN_LED 13 volatile boolean actionState = LOW; void setup() ( pinMode(PIN_LED, OUTPUT); // Postavite prekid // Funkcija myEventListener bit će pozvana kada se // na pinu 2 (prekid 0 spojen je na pin 2) // promijeni signal (bez obzira na kojem smjer) attachInterrupt (0, myEventListener, CHANGE); void loop() ( // Ne radimo ništa u funkciji petlje, jer će sav kod za rukovanje događajima biti u funkciji myEventListener) void myEventListener() ( actionState != actionState ; // / / Izvršite druge radnje, na primjer, uključite ili isključite LED digitalWrite(PIN_LED, actionState)

Pogledajmo neke primjere složenijih prekida i njihovih rukovatelja: za mjerače vremena i gumbe.

Prekida pritiskom na tipku s anti-bounce funkcijom

Kada se prekine, događa se - prije nego što kontakti dođu u bliski kontakt kada se pritisne gumb, oni će oscilirati, generirajući nekoliko operacija. Postoje dva načina rješavanja problema odbijanja: hardverski, odnosno lemljenjem kondenzatora na gumb, i softverski.

Pomoću funkcije možete se riješiti brbljanja - omogućuje vam mjerenje vremena koje je prošlo od prvog pritiskanja gumba.

If(digitalRead(2)==HIGH) ( //kada je gumb pritisnut //Ako je prošlo više od 100 milisekundi od prethodnog pritiska if (millis() - previousMillis >= 100) ( //Vrijeme prvog operacija se pamti previousMillis = millis(); if (led==oldled) ( //provjerava da se stanje gumba nije promijenilo led=!led; )

Ovaj kod vam omogućuje uklanjanje odbijanja i ne blokira izvršavanje programa, kao što je slučaj s funkcijom kašnjenja, koja nije dopuštena u prekidima.

Timer prekida

Tajmer je brojač koji broji na određenoj frekvenciji, dobivenoj od procesora od 16 MHz. Razdjelnik frekvencije može se konfigurirati za postizanje željenog načina brojanja. Također možete konfigurirati brojač da generira prekide kada se postigne postavljena vrijednost.

A prekid timera omogućuje vam prekid jednom svake milisekunde. Arduino ima 3 mjerača vremena - Timer0, Timer1 i Timer2. Timer0 se koristi za generiranje prekida svake milisekunde, koji ažurira brojač i prosljeđuje ga funkciji millis(). Ovaj mjerač vremena je osmobitni i broji od 0 do 255. Prekid se generira kada vrijednost dosegne 255. Prema zadanim postavkama, djelitelj takta od 65 koristi se za dobivanje frekvencije blizu 1 kHz.

Registri usporedbe koriste se za usporedbu stanja na mjeraču vremena i pohranjenih podataka. U ovom primjeru, kod će generirati prekid kada brojač dosegne 0xAF.

TIMSK0 |= _BV(OCIE0A);

Morate definirati rukovatelja prekidom za vektor prekida vremena. Vektor prekida je pokazivač na adresu lokacije naredbe koja će se izvršiti kada se prekid pozove. Više vektora prekida kombinira se u tablicu vektora prekida. Tajmer će se u ovom slučaju zvati TIMER0_COMPA_vect. Ovaj rukovatelj izvodit će iste radnje kao u petlji().

SIGNAL(TIMER0_COMPA_vect) ( unsigned long currentMillis = millis(); sweeper1.Update(currentMillis); if(digitalRead(2) == HIGH) ( sweeper2.Update(currentMillis); led1.Update(currentMillis); ) led2.Update( currentMillis); led3.Update(currentMillis); //Funkcija loop() ostat će prazna. void petlja() ( )

Sažimajući

Prekid u Arduinu prilično je složena tema, jer morate razmišljati o cijeloj arhitekturi projekta odjednom, zamisliti kako se kod izvršava, koji su događaji mogući, što se događa kada se glavni kod prekine. Nismo namjeravali otkriti sve značajke rada s ovom jezičnom konstrukcijom; glavni cilj bio je predstaviti glavne slučajeve uporabe. U budućim člancima ćemo detaljnije nastaviti razgovor o prekidima.

Tijekom projekta može biti potrebno nekoliko prekida, ali ako svaki od njih ima najveći prioritet, tada ga zapravo niti jedna funkcija neće imati. Iz istog razloga se ne preporuča koristiti više od desetak prekida.

Rukovatelji se trebaju primjenjivati ​​samo na procese koji imaju najveću osjetljivost na vremenske intervale. Ne zaboravite da dok je program u rukovatelju prekidima, svi ostali prekidi su onemogućeni. Velik broj prekida dovodi do pogoršanja njihovog odgovora.

U trenutku kada je jedan prekid aktivan, a ostali su onemogućeni, pojavljuju se dvije važne nijanse koje dizajner sklopa mora uzeti u obzir. Prvo, vrijeme prekida treba biti što kraće.

Ovo će osigurati da ne propustite nijedan drugi planirani prekid. Drugo, kada se rukuje prekidom, programski kod ne bi trebao zahtijevati aktivnost drugih prekida. Ako se to ne spriječi, program će se jednostavno zamrznuti.

Nemojte koristiti dugotrajnu obradu petlja() , bolje je razviti kod za rukovatelja prekidima s varijablom postavljenom na volatile. Reći će programu da nije potrebna daljnja obrada.

Ako poziv funkcije Ažuriraj() još uvijek potrebno, prvo ćete morati provjeriti varijablu stanja. To će vam omogućiti da odredite je li potrebna daljnja obrada.

Prije konfiguriranja mjerača vremena, trebali biste provjeriti kod. Anduino timere treba klasificirati kao ograničene resurse, jer ih ima samo tri, a koriste se za obavljanje raznih funkcija. Ako se zbunite s upotrebom mjerača vremena, brojne operacije mogu jednostavno prestati raditi.

Koje funkcije radi ovaj ili onaj mjerač vremena?

Za Arduino Uno mikrokontroler, svaki od tri mjerača vremena ima svoje operacije.

Tako Timer0 odgovoran za PWM na petom i šestom pinu, funkcije millis() , mikros() , odgoditi() .

Drugi mjerač vremena - Timer1, koristi se s PWM-om na devetom i desetom pinu, s bibliotekama WaveHC i Servo.

Timer2 radi s PWM na pinovima 11 i 13, kao i Ton.

Projektant strujnog kruga mora osigurati sigurnu upotrebu zajedničkih podataka. Uostalom, prekid na milisekundu zaustavlja sve operacije procesora i razmjenu podataka između petlja() a rukovatelji prekidima moraju biti postojani. Može doći do situacije kada prevodilac počne optimizirati kod kako bi postigao maksimalnu izvedbu.

Rezultat ovog procesa bit će pohranjivanje kopije varijabli glavnog koda u registar, što će osigurati maksimalnu brzinu pristupa istima.

Loša strana ovog procesa može biti da se stvarne vrijednosti zamjenjuju pohranjenim kopijama, što može dovesti do gubitka funkcionalnosti.

Kako biste spriječili da se to dogodi, morate koristiti varijablu nestalan , što će pomoći u sprječavanju nepotrebnih optimizacija. Kada koristite velike nizove koji zahtijevaju cikluse za ažuriranje, trebate onemogućiti prekide tijekom tih ažuriranja.

Kada instalirate ovaj program, iznenadit ćete se koliko je sličan Arduino IDE-u. Nemojte se iznenaditi, oba programa su napravljena na istom motoru.

Aplikacija ima mnogo značajki, uključujući biblioteku Serijski, tako da možemo povezati prijenos podataka između ploče i .

Pokrenimo Arduino IDE i izaberimo najjednostavniji primjer izlaza podataka Serijski priključak:

Void setup() ( Serial.begin(9600); ) void loop() ( Serial.println("Hello Kitty!"); // pričekajte 500 milisekundi prije slanja sljedeće odgode (500); )

Pokrenimo primjer i provjerimo radi li kôd.

Primanje podataka

Sada želimo dobiti isti tekst u . Započnimo novi projekt i napišimo kod.

Prvi korak je uvoz biblioteke. Idemo Skica | Uvoz knjižnice | Serijski. Linija će se pojaviti na skici:

Import processing.serial.*; Serijski serijski; // kreiranje objekta serijskog porta String primljen; // podaci primljeni sa serijskog porta void setup() ( String port = Serial.list(); serial = new Serial(this, port, 9600); ) void draw() ( if (serial.available() > 0) ( // ako ima podataka, primljeno = serial.readStringUntil("\n"); // očitavanje podataka ) println(primljeno); // prikaz podataka u konzoli )

Kako bismo osigurali primanje podataka sa serijskog porta, potreban nam je objekt klase Serijski. Budući da s Arduinom šaljemo podatke tipa String, moramo primiti niz u Processingu.

U metodi postaviti() morate dobiti slobodan serijski port. Obično je ovo prvi dostupni priključak na popisu. Nakon toga možemo konfigurirati objekt Serijski, s naznakom porta i brzine prijenosa podataka (poželjno je da se brzine podudaraju).

Ostaje samo ponovno spojiti pločicu, pokrenuti skicu iz Processinga i promatrati pristigle podatke u aplikacijskoj konzoli.

Obrada vam omogućuje rad ne samo s konzolom, već i stvaranje standardnih prozora. Prepišimo kod.

Import processing.serial.*; Serijski serijski; // kreiranje objekta serijskog porta String primljen; // podaci primljeni sa serijskog porta void setup() ( size(320, 120); String port = Serial.list(); serial = new Serial(this, port, 9600); ) void draw() ( if (serial) .available() > 0) ( // ako postoje podaci, // pročitajte ih i upišite u primljenu varijablu receive = serial.readStringUntil("\n"); ) // Postavke za tekst textSize(24); (); if (received != null) (text(received, 10, 30); ) )

Pokrenimo ponovno primjer i vidimo prozor s natpisom koji je ponovno nacrtan na jednom mjestu.

Na taj smo način naučili kako primati podatke od Arduina. To će nam omogućiti crtanje lijepih grafikona ili izradu programa za praćenje očitanja senzora.

Slanje podataka

Ne samo da možemo primati podatke s ploče, već i slati podatke na ploču, prisiljavajući je da izvršava naredbe s računala.

Recimo da šaljemo znak "1" iz obrade. Kada ploča otkrije poslani simbol, uključite LED na portu 13 (ugrađeno).

Skica će biti slična prethodnoj. Na primjer, napravimo mali prozor. Kada kliknete u području prozora, poslat ćemo "1" i duplicirati ga u konzoli radi provjere. Ako nema klikova, šalje se naredba "0".

Import processing.serial.*; Serijski serijski; // kreiranje objekta serijskog porta String primljen; // podaci primljeni sa serijskog porta void setup() ( size(320, 120); String port = Serial.list(); serial = new Serial(this, port, 9600); ) void draw() ( if (mousePressed) == true) ( ​​​​//ako smo kliknuli mišem unutar prozora serial.write("1"); //pošalji 1 println("1"); ) else ( //ako nije bilo klika serial.write ("0" ); //pošalji 0 ))

Sada napišimo skicu za Arduino.

Char vrijednost naredbe; // podaci dolaze sa serijskog porta int ledPin = 13; // ugrađeni LED void setup() ( pinMode(ledPin, OUTPUT); // način izlaza podataka Serial.begin(9600); ) void loop() ( if (Serial.available()) ( commandValue = Serial.read ( ); if (commandValue == "1") ( digitalWrite(ledPin, HIGH); // uključi LED) else (digitalWrite(ledPin, LOW); // u suprotnom isključi) delay(10); čitanje)

Pokrenimo obje skice. Kliknemo unutar prozora i primijetimo da LED svijetli. Ne morate ni kliknuti, već držite pritisnutu tipku miša - LED će svijetliti neprestano.

Razmjena podataka

Pokušajmo sada kombinirati oba pristupa i razmijeniti poruke između ploče i aplikacije u dva smjera.

Za maksimalnu učinkovitost, dodajmo Booleovu varijablu. Kao rezultat toga, više ne moramo stalno slati 1 ili 0 iz Obrade, a serijski port je neopterećen i ne prenosi nepotrebne informacije.

Kada ploča otkrije poslanu jedinicu, mijenjamo Booleovu vrijednost u suprotnu u odnosu na trenutno stanje ( NISKO na VISOKO i obrnuto). U drugo Koristimo string “Hello Kity”, koji ćemo poslati samo ako ne detektiramo “1”.

Funkcija uspostaviKontakt()šalje niz koji očekujemo primiti u Obradi. Ako dođe odgovor, obrada može primiti podatke.

Char vrijednost naredbe; // podaci dolaze sa serijskog porta int ledPin = 13; boolean ledState = LOW; //kontrolira stanje LED-a void setup() ( pinMode(ledPin, OUTPUT); Serial.begin(9600); establishContact(); // šalje bajt za kontakt dok primatelj odgovara) void loop() ( / / ako se podaci mogu pročitati if (Serial.available() > 0) ( // read data commandValue = Serial.read(); if (commandValue == "1") ( ledState = !ledState; digitalWrite(ledPin, ledState ); ) delay(100) ;) else ( // Pošalji natrag Serial.println("Hello Kitty"); ) delay(50) void establishContact() ( while (Serial.available());<= 0) { Serial.println("A"); // отсылает заглавную A delay(300); } }

Prijeđimo na skicu obrade. Koristit ćemo metodu serijski događaj(), koji će biti pozvan svaki put kada se u međuspremniku naiđe na određeni znak.

Dodajmo novu Booleovu varijablu prvi kontakt, koji vam omogućuje da odredite postoji li veza s Arduinom.

U metodi postaviti() dodajte liniju serial.bufferUntil("\n");. To nam omogućuje pohranjivanje dolaznih podataka u međuspremnik dok ne naiđemo na određeni znak. U ovom slučaju vraćamo (\n) budući da šaljemo Serial.println() od Arduina. "\n" na kraju znači da ćemo aktivirati novi red, odnosno to će biti zadnji podatak koji ćemo vidjeti.

Budući da stalno šaljemo podatke, metoda serijski događaj() obavlja zadatke petlje crtati(), onda ga možete ostaviti praznim.

Sada pogledajmo glavnu metodu serijski događaj(). Svaki put kada unesemo novi red (\n), poziva se ova metoda. I svaki put kada se izvrši sljedeći niz radnji:

  • Čitaju se dolazni podaci;
  • Provjerava se sadrže li bilo kakve vrijednosti (to jest, je li nam proslijeđen prazan niz podataka ili "null");
  • Uklonite razmake;
  • Ako smo prvi put dobili potrebne podatke, mijenjamo vrijednost Boolean varijable prvi kontakt i reci Arduinu da smo spremni prihvatiti nove podatke;
  • Ukoliko ovo nije prvi prijem tražene vrste podataka, prikazujemo ih u konzoli i šaljemo mikrokontroleru podatke o izvršenom kliku;
  • Obavještavamo Arduino da smo spremni primiti novi paket podataka.
import processing.serial.*; Serijski serijski; // kreiranje objekta serijskog porta String primljen; // podaci primljeni sa serijskog porta // Provjera podataka primljenih od Arduina 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) ( //formira niz od pristiglih podataka // "\n" - razdjelnik - kraj primljenog podatkovnog paketa = myPort.readStringUntil("\n"); //uvjerite se da su naši podaci nije prazno prije kako nastaviti if (primljeno != null) ( //ukloni razmake primljeno = trim(primljeno); println(primljeno); //potražite naš niz "A" za početak rukovanja //ako se nađe, obrišite međuspremnik i poslati zahtjev za podacima if (firstContact == false) ( if (received.equals("A")) ( serial.clear(); firstContact = true; myPort.write("A"); println("contact "); ) ) else ( //ako je kontakt uspostavljen, primamo i analiziramo podatke println(received); if (mousePressed == true) ( ​​​​//ako smo kliknuli na prozor serial.write("1 "); //pošalji 1 println(" 1"); ) // kada budeš imao sve podatke, napravi zahtjev za novi paket serial.write("A"); ) ) )

Kada se povežete i pokrenete, fraza "Hello Kitty" trebala bi se pojaviti na konzoli. Kada kliknete mišem u prozoru Processing, LED na pinu 13 će se uključiti i isključiti.

Osim Processinga, možete koristiti PuTTy programe ili napisati vlastiti program u C# koristeći gotove klase za rad s portovima.

04.Komunikacija: Dimer

Primjer pokazuje kako se podaci mogu poslati s računala na ploču za kontrolu svjetline LED-a. Podaci dolaze u obliku pojedinačnih bajtova od 0 do 255. Podaci mogu doći iz bilo kojeg programa na računalu koji ima pristup serijskom priključku, uključujući obradu.

Na primjer, trebat će vam standardni krug s otpornikom i LED na pinu 9.

Skica za Arduino.

Const int ledPin = 9; // LED na pinu 9 void setup() ( Serial.begin(9600); // način rada postavi na pin pinMode(ledPin, OUTPUT); ) void loop() (svjetlina bajta; // provjeri ima li podataka iz računalo if (Serial.available()) ( // čita zadnje primljene bajtove od 0 do 255 svjetlina = Serial.read(); // postavlja LED svjetlinu analogWrite(ledPin, svjetlina); ) )

Kod za obradu

Import processing.serial.*; serijski port; void setup() ( size(256, 150); println("Dostupni serijski portovi:"); println(Serial.list()); // Koristi prvi port na ovom popisu (broj 0). Promijenite ovo da odaberete port // koji odgovara vašoj Arduino ploči. Posljednji parametar (npr. 9600) je // brzina komunikacije. Mora odgovarati vrijednosti // Serial.begin() u vašem Arduino sketch portu = new Serial. (this, Serial.list(), 9600); // Ako znate naziv porta koji koristi Arduino ploča, tada eksplicitno navedite //port = new Serial(this, "COM1", 9600 ) void draw() (); // nacrtaj gradijent od crne do bijele za (int i = 0; i

Pokrenite ga i pomaknite miš preko stvorenog prozora u bilo kojem smjeru. Pri pomicanju ulijevo svjetlina LED-a će se smanjiti, pri pomicanju udesno će se povećati.

04. Komunikacija: PhysicalPixel (Osvijetlite LED pomoću miša)

Promijenimo malo problem. Pomaknut ćemo miš preko kvadrata i poslati simbol "H" (Visoko) da zasvijetli LED na ploči. Kada miš napusti kvadratno područje, poslat ćemo simbol "L" (Nisko) da isključi LED.

Kod za Arduino.

Const int ledPin = 13; // pin 13 za LED int incomingByte; // varijabla za primanje podataka void setup() ( Serial.begin(9600); pinMode(ledPin, OUTPUT); ) void loop() ( // ako ima podataka if (Serial.available() > 0) ( // pročitaj bajt u međuspremniku incomingByte = Serial.read(); // ako je ovo znak H (ASCII 72), tada uključi LED if (incomingByte == "H") ( digitalWrite(ledPin, HIGH); ) / / ako je ovo znak L ( ASCII 76), tada isključite LED if (incomingByte == "L") ( digitalWrite(ledPin, LOW); ) ) )

Kod za obradu.

Import processing.serial.*; plutajuća kutijaX; plutajuća kutijaY; int boxSize = 20; boolean mouseOverBox = false; serijski port; void setup() ( size(200, 200); boxX = width / 2.0; boxY = height / 2.0; rectMode(RADIUS); println(Serial.list()); // Otvaranje porta na koji je spojena Arduino ploča (u ovom slučaju #0) // Obavezno otvorite port istom brzinom koju koristi Arduino (9600bps) port = new Serial(this, Serial.list(), 9600 ) void draw() ( background(0); ); // Ako je kursor iznad kvadrata if (mouseX > boxX - boxSize && mouseX boxY - boxSize && mouseY

04. Komunikacija: Grafikon (Crtanje grafikona)

Ako smo u prethodnom primjeru podatke s računala slali na pločicu, sada ćemo izvršiti suprotan zadatak - primiti ćemo podatke s potenciometra i prikazati ih u obliku grafikona.


Hardverski prekidi

Nisam uspio pronaći smiješnu sliku za ovu lekciju, našao sam samo neko predavanje o programiranju, a sam početak ovog predavanja savršeno nam objašnjava što prekinuti. Prekid u Arduinu može se opisati na potpuno isti način: mikrokontroler "ispušta sve", prebacuje se na izvršavanje bloka funkcija u rukovatelju prekidima, izvršava ih, a zatim se vraća točno na mjesto u glavnom kodu gdje je stao.

Postoje različite vrste prekida, odnosno ne sami prekidi, već njihovi uzroci: prekid može izazvati analogno-digitalni pretvarač, brojač vremena ili doslovno pin mikrokontrolera. Ti se prekidi nazivaju vanjski prekidi. hardver, i to je ono o čemu ćemo danas razgovarati.

Vanjski hardverski prekid je prekid uzrokovan promjenom napona na pinu mikrokontrolera. Glavna stvar je da mikrokontroler (računska jezgra) ne anketira pin I ne gubi vrijeme na to, iglom upravlja drugi dio hardvera. Čim se napon na pinu promijeni (što znači digitalni signal, +5 doveden/+5 uklonjen), mikrokontroler prima signal, odbacuje sve, obrađuje prekid i vraća se na posao. Zašto je to potrebno? Najčešće se prekidi koriste za otkrivanje kratkih događaja - impulsa, ili čak za brojanje njihovog broja, bez učitavanja glavnog koda. Hardverski prekid može uhvatiti kratki pritisak na gumb ili okidač senzora tijekom složenih dugih izračuna ili kašnjenja koda, tj. grubo govoreći, pin se anketira paralelno s glavnim kodom. Prekidi također mogu probuditi mikrokontroler iz načina rada za uštedu energije, kada su gotovo svi periferni uređaji isključeni. Pogledajmo kako raditi s hardverskim prekidima u Arduino IDE.

Prekidi u Arduinu

Počnimo s činjenicom da ne mogu svi pinovi prekinuti. Da, postoji nešto poput pinChangeInterrupts, ali o tome ćemo govoriti u naprednim lekcijama. Sada moramo shvatiti da hardverski prekidi mogu generirati samo određene pinove:

MK / broj prekida 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

Kao što razumijete iz tablice, prekidi imaju svoj broj, koji se razlikuje od broja pina. Usput, postoji prikladna funkcija digitalPinToInterrupt(pin), koji uzima pin broj i vraća broj prekida. Dodavanjem ove funkcije broju 3 na Arduino nano, dobivamo 1. Sve je prema gornjoj tablici, funkcija za lijene.

Prekid je povezan pomoću funkcije attachInterrupt(pin, rukovatelj, način):

  • pribadača– broj prekida
  • rukovatelj– naziv funkcije rukovatelja prekidom (morate ga sami izraditi)
  • način rada– “način” rada prekida:
    • NISKO(nisko) – aktivirano signalom NISKO na iglu
    • USTANJE(rast) – pokreće se kada se promijeni signal na pinu c NISKO na VISOKO
    • PADANJE(pad) – aktivira se kada se promijeni signal na pinu VISOKO na NISKO
    • PROMIJENITI(promjena) – aktivira se kada se signal promijeni (sa NISKO na VISOKO i obrnuto)

Prekid se također može onemogućiti pomoću funkcije detachInterrupt(pin), gdje pin – opet broj prekida.

Također možete globalno onemogućiti prekide pomoću ove funkcije noInterrupts() i ponovno ih razriješite s prekidi(). Oprezno s njima! noInterrupts() također će zaustaviti prekide mjerača vremena, a sve vremenske funkcije i PWM generacija će prekinuti.

Pogledajmo primjer u kojem se pritisci gumba broje u prekidu, ali u glavnoj petlji izlaze s odgodom od 1 sekunde. Radeći s gumbom u normalnom načinu rada, nemoguće je kombinirati tako grubi izlaz s odgodom:

Hlapljivi int brojač = 0; // varijabla brojača void setup() ( Serial.begin(9600); // otvorio port za komunikaciju // spojio gumb na D2 i GND pinMode(2, INPUT_PULLUP); \ // D2 je prekid 0 // rukovatelj - funkcija buttonTick // FALLING - kada se gumb pritisne, bit će signal 0, hvatamo ga attachInterrupt(0, buttonTick, FALLING) void buttonTick() ( counter++; // + pressing ) void loop() ( Serial.println (brojač); // kašnjenje izlaza (1000); // čekanje )

Dakle, naš kod broji pritiske tipki čak i tijekom odgode! Sjajno. Ali što je nepostojan? Deklarirali smo globalnu varijablu brojač, koji će pohraniti broj klikova na gumb. Ako se vrijednost varijable promijeni u prekidu, morate o tome obavijestiti mikrokontroler pomoću specifikatora nepostojan, koji je napisan prije označavanja tipa podataka varijable, inače će rad biti netočan. Ovo je samo nešto što treba zapamtiti: ako se varijabla promijeni u prekidu, učinite to nepostojan.

Još nekoliko važnih točaka:

  • Varijable modificirane u prekidu moraju biti deklarirane kao nepostojan
  • Ovakva kašnjenja ne rade u prekidima odgoditi()
  • Ne mijenja svoje značenje u prekidu millis() I mikros()
  • U prekidu izlaz na port ne radi ispravno ( Serial.print()), također ga ne biste trebali koristiti tamo - učitava kernel
  • Prilikom prekida pokušajte raditi što manje izračuna i općenito "dugih" radnji - to će usporiti rad MC-a s čestim prekidima! Što uraditi? Pročitajte u nastavku.

Ako prekid uhvati neki događaj koji ne treba odmah obraditi, tada je bolje koristiti sljedeći algoritam za rad s prekidom:

  • U rukovatelju prekidima jednostavno podižemo zastavicu
  • U glavnoj petlji programa provjeravamo zastavu, ako je podignuta, poništavamo je i izvršavamo potrebne radnje
volatile Boolean intFlag = false; // označiti void setup() ( Serial.begin(9600); // otvorio port za komunikaciju // spojio gumb na D2 i GND pinMode(2, INPUT_PULLUP); // D2 je prekid 0 // rukovatelj - funkcijski gumbTick // FALLING - kada se gumb pritisne, bit će signal 0, hvatamo ga attachInterrupt(0, buttonTick, FALLING) void buttonTick() ( intFlag = true; // podignuta zastavica prekida) void loop() ( if (intFlag) ( intFlag = false; // reset // izvrši neke akcije Serial.println("Prekid!");

Ovo je u osnovi sve što trebate znati o prekidima; pogledat ćemo konkretnije slučajeve u naprednim lekcijama.

Video

Naučimo kako raditi s prekidima timera. Napišimo jednostavan program s paralelnim procesima.

U pravom programu, mnoge radnje moraju se izvoditi istovremeno. U uvodu sam naveo primjer. Navest ću koje radnje ona izvodi:

Operacija

Vrijeme ciklusa
Ispituje 3 gumba, obrađuje njihove signale kako bi eliminirao odbijanje 2 ms
Regenerira podatke iz LED indikatora sa sedam segmenata 2 ms
Generira upravljačke signale za 2 senzora temperature DS18B20 i čita podatke s njih. Senzori imaju 1-wire serijsko sučelje. 100 µs za svaki bit,
1 s ukupni ciklus čitanja
Očitavanje analognih vrijednosti struje i napona na Peltierovom elementu, napon napajanja 100 µs
Digitalno filtriranje analognih vrijednosti struje i napona 10 ms
Proračun snage na Peltierovom elementu 10 ms
PID (proporcionalni integralni diferencijal) regulator stabilizacije struje i napona 100 µs
Regulator snage 10 ms
Regulator temperature 1 sek
Sigurnosne funkcije, praćenje cjelovitosti podataka 1 sek
Upravljanje, opća logika rada sustava 10 ms

Sve ove operacije izvode se ciklički, svaka s različitim periodima ciklusa. Nitko od njih ne može biti suspendiran. Svaka, čak i kratkotrajna, promjena u vremenu rada dovest će do problema: značajna pogreška mjerenja, neispravan rad stabilizatora, treptanje indikatora, nestabilan odgovor na pritiske gumba itd.

U programu kontrolera hladnjaka postoji nekoliko paralelnih procesa koji izvode sve ove radnje, svaki u ciklusu sa svojim vremenskim periodom. Paralelni procesi su procesi čije se akcije izvode istovremeno.

U prethodnim lekcijama stvorili smo klasu za objekt gumba. Rekli smo da je ovo klasa za obradu signala u paralelnom procesu. Da je za njegov normalan rad potrebno pozvati funkciju (metodu) za obradu signala u ciklusu s pravilnim periodom (odabrali smo vrijeme od 2 ms). I tada, bilo gdje u programu, dostupni su znakovi koji pokazuju trenutno stanje tipke ili signala.

U jednu smo petlju smjestili kod za obradu stanja tipki i upravljanje LED diodama. I na kraju petlje postavljamo funkciju kašnjenja delay(2). No, vrijeme potrebno za izvođenje programa u petlji mijenja ukupno vrijeme petlje. A razdoblje ciklusa očito nije jednako 2 ms. Osim toga, tijekom izvršavanja funkcije delay(), program se zamrzava i ne može izvoditi druge radnje. Kompleksan program rezultirat će potpunim kaosom.

Izlaz - poziva funkciju za obradu stanja gumba po prekidu od hardverskog timera. Svake 2 ms petlja glavnog programa mora se prekinuti, signal gumba se obrađuje i kontrola se vraća u glavnu petlju na kod gdje je prekinuta. Kratko vrijeme za obradu signala gumba neće značajno utjecati na izvođenje glavne petlje. Oni. Obrada gumba odvijat će se paralelno, nezapaženo od strane glavnog programa.

Prekid hardverskog mjerača vremena.

Hardverski prekid je signal koji prijavljuje neki događaj. Po njegovom dolasku, izvršavanje programa se obustavlja i kontrola prelazi na rukovatelja prekida. Nakon obrade kontrola se vraća na prekinuti programski kod.

Sa stajališta programa, prekid je poziv funkcije zbog vanjskog događaja koji nije izravno povezan s programskim kodom.

Signal prekida tajmera generira se ciklički, s određenim vremenskim periodom. Generira ga hardverski mjerač vremena - brojač s logikom koji resetira svoj kod kada se postigne određena vrijednost. Programskim postavljanjem koda za logiku resetiranja, možemo postaviti vrijeme razdoblja prekida mjerača vremena.

Podešavanje načina rada i vremena perioda Arduino timera vrši se preko hardverskih registara mikrokontrolera. Ako želite, možete shvatiti kako se to radi. Ali predlažem jednostavniju opciju - korištenje biblioteke MsTimer2. Štoviše, postavljanje mjerača vremena događa se rijetko, što znači da korištenje funkcija knjižnice neće usporiti program.

Knjižnica MsTimer2.

Biblioteka je namijenjena za konfiguriranje hardverskog prekida iz Timera 2 mikrokontrolera. Ima samo tri funkcije:

  • MsTimer2::set(unsigned long ms, void (*f)())

Ova funkcija postavlja vrijeme prekida u ms. Rukovatelj prekida f bit će pozvan s ovim periodom. Mora se proglasiti nevažećim (ne vraća ništa) i nema argumenata. * f je pokazivač funkcije. Umjesto toga, trebate napisati naziv funkcije.

  • MsTimer2::start()

Funkcija omogućuje prekide timera.

  • MsTimer2::stop()

Funkcija onemogućuje prekide mjerača vremena.

Prije imena funkcija potrebno je napisati MsTimer2::, jer Knjižnica je napisana korištenjem direktive imenskog prostora.

Da biste instalirali biblioteku, kopirajte direktorij MsTimer2 u mapu biblioteka u radnoj mapi Arduino IDE. Zatim pokrenite Arduino IDE program, otvorite Skica -> Poveži knjižnicu i vidjeti da je biblioteka MsTimer2 prisutna na popisu biblioteka.

Knjižnicu MsTimer2 možete preuzeti u zip arhivi. Da biste ga instalirali, morate ga raspakirati.

Jednostavan program s paralelnom obradom signala gumba.

Sada napišimo jednostavan program s jednim gumbom i LED-om iz lekcije 6. Jedan gumb spojen je na Arduino ploču prema dijagramu:

Ovako izgleda:

Za svaki pritisak na gumb, LED na Arduino ploči mijenja svoje stanje. Potrebno je instalirati biblioteke MsTimer2 i Button:

MsTimer2

I platiti. Samo 40 rub. mjesečno za pristup svim resursima stranice!

// sketch_10_1 lekcija 10
// Pritiskom na tipku mijenja se stanje LED-a

#uključi
#uključi

#define LED_1_PIN 13 //
#define BUTTON_1_PIN 12 // gumb je spojen na pin 12

Button button1(BUTTON_1_PIN, 15); // kreiranje objekta - gumb

void setup() (

MsTimer2::set(2, timerInterupt); // postavite razdoblje prekida timera na 2 ms
MsTimer2::start(); //
}

void petlja() (

// LED kontrola
if (button1.flagClick == true) (
// kliknuto je dugme



}
}

// rukovatelj prekidom
void timerInterrupt() (
button1.scanState(); // pozivanje metode za čekanje stabilnog stanja za gumb
}

U funkciji setup() postavljamo vrijeme ciklusa prekida timera na 2 ms i navodimo naziv rukovatelja prekidima timerInterrupt. Funkcija obrade signala gumba button1.scanState() poziva se u rukovatelju prekidom vremena svake 2 ms.

Dakle, obrađujemo stanje gumba u paralelnom procesu. A u glavnoj petlji programa provjeravamo znak klika gumba i mijenjamo stanje LED-a.

Hlapljivi kvalifikator.

Modificirajmo petlju loop() u prethodnom programu.

void petlja() (

dok (istina) (
if (button1.flagClick == true) break;
}

// kliknuto je dugme
button1.flagClick= false; // poništi znak
digitalWrite(LED_1_PIN, ! digitalRead(LED_1_PIN)); // LED inverzija
}

Logično, ništa se nije promijenilo.

  • U prvoj verziji program je prošao kroz petlju do kraja i u njoj analizirao oznaku button1.flagClick.
  • U drugoj opciji, program analizira oznaku button1.flagClick u beskonačnoj while petlji. Kada zastavica postane aktivna, izlazi iz while petlje kroz prekid i invertira stanje LED-a.

Jedina razlika je u tome koji ciklus se program izvodi u petlji ili dok.

Ali ako pokrenemo zadnju verziju programa, vidjet ćemo da LED ne reagira na pritiske gumba. Uklonimo klasu i pojednostavimo program.

#uključi
#define LED_1_PIN 13 // LED spojen na pin 13
int count=0;

void setup() (
pinMode(LED_1_PIN, IZLAZ); // definirajte LED pin kao izlaz
MsTimer2::set(500, timerInterupt); // postavite razdoblje prekida timera na 500 ms
MsTimer2::start(); // omogućiti prekid mjerača vremena
}

void petlja() (

dok (istina) (
if (count != 0) break;
}

broj= 0;
digitalWrite(LED_1_PIN, ! digitalRead(LED_1_PIN)); // LED inverzija stanja
}

// rukovatelj prekidom
void timerInterrupt() (
brojati++;
}

U ovom programu, brojač se povećava za 1 u rukovatelju prekidima svakih 500 ms. U while petlji se analizira, s breakom izlazimo iz petlje i invertiramo stanje LED-a. Niste mogli zamisliti jednostavniji program, ali ni on ne radi.

Činjenica je da prevodilac jezika C++ optimizira program prema njegovoj inteligenciji. Ponekad ovo ne uspije dobro. Prevodilac vidi da se ne izvode nikakve operacije na varijabli brojača u petlji while. Stoga smatra da je dovoljno samo jednom provjeriti stanje brojanja. Zašto provjeravati u petlji nešto što se nikada ne može promijeniti. Kompajler ispravlja kod, optimizirajući ga za vrijeme izvođenja. Jednostavno rečeno, uklanja kod za provjeru varijable iz petlje. Kompajler ne može shvatiti da varijabla brojanja mijenja svoje stanje u rukovatelju prekidima. Kao rezultat toga, zaglavili smo u while petlji.

U verzijama programa koje izvršavaju petlju do kraja, kompajler pretpostavlja da se sve varijable mogu mijenjati i ostavlja kod za provjeru. Ako umetnete poziv bilo koje sistemske funkcije u while petlju, prevodilac će također odlučiti da se varijable mogu mijenjati.

Ako, na primjer, dodate poziv funkcije delay() u while petlju, program će raditi.

dok (istina) (
if (count != 0) break;
kašnjenje(1);
}

Dobar stil je razvijati programe u kojima se petlja izvodi do kraja i program se nigdje ne zamrzava. Sljedeća lekcija sadržavat će jedini kod koji analizira zastavice u beskonačnim while petljama. Dalje, planiram izvršiti petlju do kraja u svim programima.

Ponekad to nije lako ni učinkovito. Zatim morate koristiti kvalifikator volatile. Specificira se kada je varijabla deklarirana i govori prevoditelju da ne pokušava optimizirati njezinu upotrebu. Sprječava kompajler od donošenja pretpostavki o vrijednosti varijable jer se varijabla može promijeniti u drugoj programskoj jedinici, kao što je u paralelnom procesu. Također, kompajler smješta varijablu u RAM radije nego u registre opće namjene.

Kada deklarirate count u programu, dovoljno je napisati

volatile int count=0;

i sve opcije će raditi.

Za program koji kontrolira gumb, morate deklarirati da se svojstva instance klase Button mogu mijenjati.

volatile Button button1(BUTTON_1_PIN, 15); // kreiranje objekta - gumb

Prema mojim zapažanjima, korištenje kvalifikatora volatile ni na koji način ne povećava duljinu programskog koda.

Usporedba metode obrade signala gumba s bibliotekom Bounce.

Postoji gotova biblioteka za uklanjanje odskočnih gumba. Stanje gumba provjerava se kada se pozove funkcija update(). U ovoj funkciji:

  • očitava se signal gumba;
  • u usporedbi sa stanjem tijekom prethodnog poziva update();
  • provjerava koliko je vremena prošlo od prethodnog poziva koristeći millis() funkciju;
  • donosi se odluka je li se stanje gumba promijenilo.
  • Ali ovo nije paralelna obrada signala. Funkcija update() obično se poziva u glavnoj, asinkronoj programskoj petlji. Ako se ne pozove dulje od određenog vremena, informacija o signalu tipke će se izgubiti. Nepravilni pozivi dovode do neispravnog rada algoritma.
  • Sama funkcija ima dosta koda i potrebno joj je puno više vremena da se izvrši od funkcija biblioteke Button().
  • Uopće ne postoji digitalno filtriranje signala na temelju prosječne vrijednosti.

Bolje je ne koristiti ovu biblioteku u složenim programima.

U sljedećoj lekciji ćemo napisati složeniji program s paralelnim procesima. Naučimo kako implementirati izvođenje programskih blokova u petljama s različitim vremenskim intervalima iz jednog prekida timera.

Kategorija: . Možete ga označiti.