VIDI Project X #50:
ESP32 Prekidi (engl. Interrupts)

Prekidi su funkcije koje se izvršavaju odmah nakon što je okidač (pritisak gumba ili drugi događaj) signalizirao da se funkcija prekida mora izvršiti. Najčešće se koriste za pokretanje debug procedure, no mogu služiti i u druge svrhe.

Prekide ima gotovo svaki mikroprocesor. Oni služe kako bi se za određenu akciju program, ili neki njegov dio, pokrenuo, zaustavio ili preusmjerio. Primjer prekida kod vašeg mobitela je čekanje akcije pritiska gumba kako bi se upalio ekran mobitela. Sve dok gumb nije stisnut, mobitel je u „sleep modu“ kako bi uštedio bateriju. Njegovo buđenje i paljenje ekrana uzrokovano je hardverskim prekidom pritiska gumba.

 

 

Kod VIDI X mikroračunala, osim hardverskih, postoje i softverski prekidi. Softverski prekidi mogu biti vremenski te se njihovom uporabom dio koda može izvršavati periodički.

VIDI X pruža maksimalno 32 utora za prekid za svaku od dvije procesorske jezgre. Svaki prekid ima određenu razinu prioriteta i može se svrstati u dvije kategorije.

Hardverski prekid – javlja se kao posljedica vanjskog događaja. Na primjer, GPIO prekid (kada pritisnete tipku) ili prekid dodira (kada se otkrije dodir). GPIO prekid može biti aktiviran i nekim senzorima koji kao rezultat mogu vratiti stanje High (logička jedinica). Primjer takvih senzora su oni koji koriste LM393 komparator uz pomoć kojeg se neka postavljena analogna vrijednost pretvara u logičku jedinicu dok se ostale pretvaraju u logičku nulu. Jednostavan senzor svjetlosti s takvim komparatorom potražite na linku https://e-radionica.com/hr/jednostavni-senzor-svjetlosti.html.

Isto tako, senzor s LM393 komparatorom koristili smo u radionici „Senzor ugljičnog monoksida“ na linku https://vidi-x.org/radionice/vidi-project-x-68-mq-7-analogni-senzor-ugljicnog-monoksida/ te u radionici „VIDI Project X #67: Pametni vrt“ koristeći senzor osvjetljenja, ali i senzor vlažnosti tla. Radionicu pogledajte na linku https://vidi-x.org/radionice/vidi-project-x-67-pametni-vrt/.

VIDI Project X #67: Pametni vrt

Softverski prekid – javlja se kao odgovor na softverske upute. Na primjer, jednostavan prekid mjerača vremena (simple timer) ili prekid mjerača vremena čuvara (watchdog timer) kada mjerač istekne.

Watchdog timer je vrsta čuvara koji pazi na vaš softver te on obavlja neku funkciju (najčešće je to resetiranje) kad god se sustav zamrzne. Pojam „watchdog“ zapravo potječe od čuvara i njegovog psa stražara. Odgovornost čuvara kao nadzornog tijela i njegovog psa je zaštititi okolinu od bilo kakvih nedozvoljenih tj. ilegalnih aktivnosti i ako postoji bilo kakva nedozvoljena aktivnost, uključuje alarm.

Dok koristite watchdog timer, prije nego što prođe definirano vrijeme se brojač programa mora povećati ili promijeniti. On se koristi u mobilnom uređaju za isključivanje zaslona u slučaju da se zaslon ne dotakne, recimo unutar 10 minuta. Dakle, on odbrojava od 10 minuta prema nuli, te ako dođe do nule, gasi zaslon. Dotaknete li zaslon unutar tih 10 minuta, recimo nakon pet minuta, watchdog timer se vraća na vrijednost od 10 minuta i ponovno broji unatrag do nule.

Timer je specijalizirana vrsta sata koja se koristi za mjerenje određenih vremenskih intervala. Timeri se mogu kategorizirati u dvije glavne vrste. Mjerač vremena koji broji prema gore od nule radi mjerenja proteklog vremena često se naziva štoperica ili simple timer, dok se uređaj koji odbrojava prema nuli od postavljenog vremenskog intervala obično naziva watchdog timer.

 

ESP32 GPIO prekidi

U ESP32 možemo definirati rutinsku funkciju prekida koja će se izvršiti kada GPIO pin promijeni svoju logičku razinu. Svi GPIO pinovi mogu se konfigurirati tako da služe kao tipke tj. okidači zahtjeva za prekidom.
U razvojnom okruženju Arduino IDE koristimo funkciju koja se koristi za postavljanje prekida na pin.

Sintaksa izgleda ovako:

attachInterrupt(GPIOPin, ISR, Mode);

 

Ova funkcija prihvaća tri argumenta:

GPIOPin - postavlja GPIO pin kao pin za prekid, koji VIDI X mikroračunalu govori koji pin nadzire.
ISR – naziv funkcije koja će se pokrenuti svaki put kada dođe do prekida (npr. kada je stisnut gumb).
Mode – definira kada bi se prekid trebao aktivirati. Unaprijed je definirano pet konstanti kao vrijednosti koje možemo koristiti:
LOW - Pokreće prekid kad god je GPIO pin LOW
HIGH - Pokreće prekid kad god je GPIO pin HIGH
CHANGE - Pokreće prekid kad god GPIO pin promijeni vrijednost, od HIGH do LOW ili od LOW do HIGH
FALLING - Pokreće prekid kada GPIO pin prijeđe s HIGH na LOW
RISING - Pokreće prekid kada GPIO pin prijeđe s LOW na HIGH

 

Ako želite da VIDI X više ne nadzire određeni GPIO pin, možete pozvati funkciju čija sintaksa izgleda ovako:

detachInterrupt(GPIOPin);

 

Interrupt Service Routine (ISR)

Rutina prekida servisa (ISR) funkcija je ona koja se poziva svaki put kada dođe do prekida na GPIO pinu.
Sintaksa ISR funkcije izgleda poput obične funkcije, ovako:

void IRAM_ATTR ISR() {
  Statements;
}

 

No ISR funkcija je posebna vrsta funkcije koja ima neka jedinstvena pravila koja većina drugih funkcija nema.

ISR funkcija ne može imati nikakve parametre i ne bi trebala ništa vraćati.

ISR funkcija bi trebala biti što kraća i što brža jer u suprotnom blokira normalno izvršavanje programa.

Ona bi trebala imati atribut IRAM_ATTR, prema dokumentaciji ESP32 procesora koju detaljno možete pogledati na linku https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/memory-types.html#iram-instruction-ram.

Kada označimo dio koda s atributom IRAM_ATTR, kompilirani kod smješta se u interni RAM (IRAM) ESP32 procesora. Ostatak koda se čuva u flash memoriji. Flash na ESP32 je sporiji od internog RAM-a (IRAM_ATTR). Pretpostavimo da je kod koji želimo pokrenuti funkcija prekida usluge tj. ISR – u tom slučaju ga želimo izvršiti što je prije moguće. Ako bismo morali čekati da se ISR učita iz flash memorije, stvari bi mogle poći po zlu.

 

Primjer koda: Jednostavan prekid

Ovaj kod pokazuje uporabu prekida i ispravan način pisanja rutine prekida usluge. Program nadzire GPIO32 (BTN_A) za padajuću vrijednost. Drugim riječima, traži promjenu napona koja prelazi iz logičkog HIGH u logički LOW, a koja se događa kada se pritisne gumb. Kada se to dogodi, poziva se funkcija isr(). Kod unutar ove funkcije broji koliko je puta pritisnut gumb.

struct Button {
  const uint8_t PIN;
  uint32_t numberKeyPresses;
  bool pressed;
};

Button button1 = {32, 0, false};

void IRAM_ATTR isr() {
  button1.numberKeyPresses++;
  button1.pressed = true;
}

void setup() {
  Serial.begin(115200);
  pinMode(button1.PIN, INPUT_PULLUP);
  attachInterrupt(button1.PIN, isr, FALLING);
}

void loop() {
  if (button1.pressed) {
    Serial.printf(“Button has been pressed %u times\n”, button1.numberKeyPresses);
    button1.pressed = false;
  }
}

 

Nakon što prenesete skicu, pritisnite gumb BTN_A na VIDI X mikroračunalu te otvorite serijski monitor s postavljenom brzinom prijenosa podataka od 115.200 bauda. Pritiskom na gumb dobit ćete brojku koliko puta se gumb pritisnuo.

Primijetite da na početku skice stvaramo strukturu tj. objekt zvan Button. Ova struktura ima tri člana (atributa) – PIN, numberKeyPresses (broj pritisaka tipke) i pressed stanje. Struktura je skup varijabli različitih vrsta, ali logički povezanih u smislenu cjelinu pod jednim nazivom.

struct Button {
  const uint8_t PIN;
  uint32_t numberKeyPresses;
  bool pressed;
};

 

Nakon toga stvaramo instancu strukture gumba i inicijaliziramo redom: broj pina na GPIO32, broj pritisaka tipke na nulu i zadano pritisnuto stanje na false.

Button button1 = {32, 0, false};

 

 

Kod u nastavku je funkcija prekida. Kao što je ranije spomenuto, ISR kod VIDI X-a mora imati atribut IRAM_ATTR.
U ISR-u jednostavno povećavamo brojač numberKeyPresses za 1 i postavljamo pritisnuto stanje gumba na true.

void IRAM_ATTR isr() {
  button1.numberKeyPresses += 1;
  button1.pressed = true;
}

 

U funkciji za postavljanje koda setup() prvo inicijaliziramo serijsku komunikaciju s računalom, a zatim omogućujemo interni pull_up otpornik za GPIO pin 32.
Zatim postavljamo prekid da nadgleda pin 32 i pozove ISR funkciju ukoliko pin ode u padajuće stanje, odnosno s HIGH u LOW.

Serial.begin(115200);
pinMode(button1.PIN, INPUT_PULLUP);
attachInterrupt(button1.PIN, isr, FALLING);

 

U dijelu koda s petljom provjeravamo je li gumb pritisnut, te ukoliko jest, ispisujemo koliko je puta tipka do sada pritisnuta i postavljamo pritisnuto stanje gumba na false kako bismo zaustavili ispisivanje broja pritisaka gumba sve dok se gumb ponovno ne pritisne. Takvo ponašanje dobili smo korištenjem if uvjeta.

if (button1.pressed) {
  Serial.printf(“Button 1 has been pressed %u times\n”, button1.numberKeyPresses);
  button1.pressed = false;
}

 

Funkcija prekida će se pokrenuti svaki put kada je gumb pritisnut. No tu možemo naići na problem skakutanja prekidača koji će time ovako napisanu funkciju prekida pokrenuti više puta uzastopno, što može predstavljati problem za izvršavanje koda ukoliko nam nije bila namjera funkciju pokrenuti nekoliko puta uzastopno u kratkom vremenskom periodu.

 

Odskakivanje prekidača (engl. Bouncing)

Pokrenuvši ranije navedeni programski kod mogli ste primijetiti da povremeno VIDI X izbroji više pritisaka, iako ste prekidač stisnuli samo jednom. To se događa zbog odskakivanja prekidača te je prekidač zaista imao nekoliko spajanja i prekidanja signala, kako je naš kod i zabilježio.

Sigurno ste katkada mogli primijetiti da prekidači za paljenje žarulje i sami malo zasvijetle. Najčešće plavkastom bojom. Taj efekt dogodi se radi iskrenja električne energije uzrokovanog efektom odskakivanja prekidača, recimo pri paljenju svjetla.

Pojava se najčešće događa zato što je prekidač napravljen od savitljivog materijala koji se pri promjeni oblika pokušava vratiti u prvobitni položaj te odskoči pomalo, dok se ne stabilizira u novom položaju. No može se događati i radi drugih fizičkih čimbenika. Postoje razne vrste prekidača poput kliznih, na feder, klik-klak i drugih.

Kod nekih prekidača dva komada metala dolaze u kontakt jedan s drugim. Ako te dvije sićušne pločice metala nisu savršeno ravne i poravnate, a u pravilu nikada nisu, tada mogu uspostaviti i prekinuti kontakt nekoliko puta prije negoli budu dovoljno čvrsto spojene jedna s drugom. VIDI X mikroračunalu, koje može testirati stanje gumba nekoliko milijuna puta u sekundi, to uopće nije uključeno ili isključeno nego mu se čini da je gumb pritisnut mnogo puta u iznimno kratkom vremenu.

 

Princip rada prekidača s tri nožice. Poluga prekidača mijenja nagib mehanizma koji se tako odvaja od jedne strane i spaja s drugom stranom, dok je njegova sredina konstantno spojena.

 

Možda je lakše shvatiti što se događa na prekidaču s tri nožice koji radi na principu klackalice. Ukoliko ste klackalicu u parku lagano gurnuli rukom prema dolje, primijetili ste da je ta strana odskočila od zemlje nekoliko puta sve dok se nije stabilizirala u novom položaju. Ista stvar se događa i s prekidačima. Nekoliko puta se spoji i odspoji dok naposljetku ne zauzme novi položaj. Kod nekih skupljih prekidača ovaj problem može biti riješen njihovim dizajnom. No takvi prekidači bez efekta odskakivanja imaju znatno višu cijenu od „običnih“.

Problem odskakivanja možemo riješiti i programski, tako da pričekamo neko kraće vrijeme prije nego što stanje prekidača proglasimo konačnim. Ta metoda kao manu ima sporiji odziv koda za definirano vrijeme čekanja.
Kako bismo ubrzali izvođenje koda, umjesto da čekamo proglašenje konačnog stanja, možemo odmah proglasiti promjenu stanja te jedno kraće vrijeme ignorirati promjene koje uslijede.

To možemo izvesti na način da dodamo nekoliko varijabli neposredno prije funkcije isr(), te dodamo uvjet koji će ispitivati prije koliko vremena je pritisnut gumb.

//variables to keep track of the timing of recent interrupts
unsigned long button_time = 0;  
unsigned long last_button_time = 0; 
unsigned long ignore_time = 250; //time in miliseconds to ignore new button state

void IRAM_ATTR isr() {
    button_time = millis();
if (button_time - last_button_time > ignore_time)
  {
        button1.numberKeyPresses++;
        button1.pressed = true;
       last_button_time = button_time;
  }
}

 

Ovaj kod radi samo kada brzo stisnete i otpustite gumb, jer svaki put kada se ISR izvrši, uspoređuje trenutno vrijeme koje vraća funkcija millis() s vremenom prijašnjeg poziva ISR-a.

Kako smo vrijednost postavili na 250 milisekundi, znači da se za ovako postavljeni kod funkcija isr() maksimalno može pokrenuti 4 puta unutar jedne sekunde.

Ako se pritisak i otpuštanje gumba nalaze unutar 250 milisekundi, VIDI X ignorira prekid i odmah se vraća na ono što je radio. Ako je ipak prošlo više od 250 milisekundi, izvršava se kod unutar if uvjeta koji povećava brojač i ažurira varijablu last_button_time, tako da funkcija ima novu vrijednost za usporedbu kada bude ponovno pokrenuta u budućnosti.

Ukoliko držite gumb pritisnut duže od 250 milisekundi koliko iznosi varijabla ignore_time, funkcija prekida isr() mogla bi biti pokrenuta kada otpustite gumb, jer se i tada može dogoditi odskakivanje gumba.

 

RC Filtri (R – otpor, C – kapacitivnost)

Dodatno poboljšanje rješenja krije se u korištenju hardverskog RC filtra.

RC filtar ili RC filtarski element u elektrotehnici se odnosi na krug s otporom R i kapacitetom C. Dvije komponente mogu se povezati paralelno ili serijski. Moguća je kombinacija nekoliko otpornika i kondenzatora.
Zbog svojstava komponenti otpornika i kondenzatora, postiže se odnos između frekvencije na ulazu i napona na izlazu. Taj se učinak može smisleno koristiti u elektroničkim krugovima.

Otpor R uvijek ostaje konstantan te ne mijenja svoju vrijednost s promjenom napona i struje. Promjene frekvencije isto tako nemaju utjecaja na otpor R.

Kondenzator C ponaša se poput baterije vrlo malog kapaciteta koja se puni i prazni u intervalima ovisnima o dizajnu električne sheme. S istosmjernim naponom će se napuniti te predstavljati prekid u krugu kada se potpuno napuni.

Pri računanju vrijednosti RC filtra, najvažnije su vrijednosti otpora i kapacitivnosti kondenzatora. Interakcija tih dvaju elemenata rezultira željenim efektom filtra. Ovisno o međusobnoj povezanosti, formule za izračun se mijenjaju, ali te dvije varijable uvijek igraju ulogu.

Uloga kondenzatora čini vremensku konstantu RC filtra važnom. Ona se izračunava na temelju otpora i kapaciteta te označava potrebno vrijeme punjenja. Ovisno o strujnom krugu, RC filtar može se izračunati prema različitim formulama, ali se vremenska konstanta RC filtra računa identično za svaku od njih.

 

Razlikujemo mnoge filtre među kojima su High pass i Low pass najpoznatiji

 

High pass

RC visokopropusni filtar (eng. High pass) nastaje serijskim spajanjem dviju komponenti.

Visokopropusni filtar koristi se u svrhu slabljenja ili blokiranja niskih frekvencija. Visoke frekvencije, međutim, trebaju biti što je više moguće neometanije. Pojam „filtar visokog prolaza“ također je čest. Visokopropusni filtar je pasivan ako se ne koristi element za pojačavanje signala. Inače se smatra aktivnim. On se koristi tamo gdje su niske frekvencije nepoželjne i stoga ih treba filtrirati.

 

Low pass

Struktura RC niskopropusnog (eng. Low pass) filtra i visokopropusnog RC filtra je identična, ali se uzima u obzir izlazni napon preko kondenzatora. To nam daje potpuno suprotan učinak od visokopropusnog filtra. Otpornost kondenzatora povećava se s frekvencijom. Što je veći otpor, to je veći pad napona kao i izlazni napon.

 

Primjeri primjene RC filtra

Filtriranje frekvencija najčešće se koristi u zvučnicima za poboljšanje kvalitete zvuka. Zvučnici mogu reproducirati samo signale u predviđenom frekvencijskom rasponu. Zvukovi na drugačijim frekvencijama od onih za koje je zvučnik namijenjen su iskrivljeni, te se čuju kao škripanje ili grebanje. Visokotonac dakel koristi RC visokopropusni filtar, a woofer koristi RC niskopropusni filtar. Zvučnik srednjih tonova koristi bandpass filtar.

Na isti način ćemo RC filtrom eliminirati šumove gumba koji se dobiju prilikom pritiskanja gumba koji nema unaprijed ugrađen debounce mehanizam.

 

Low pass primjer

Praktično rješenje je sljedeće: odabirete R i C tako da umnožak R*C (u ohmima, odnosno faradima) bude u skladu s vremenom za koje želite filtrirati odbijanje. Ovdje koristite unutarnji pull_up otpornik mikrokontrolera, koji je oko 10 kOhma i kondenzator od 100 nF, koji daje 0,001 sekundu tj. jednu milisekundu pri otpuštanju gumba.

 

Ovdje gumb koristi otpornik od jednog kOhma i on mora biti znatno manji od pull_up otpornika kako bi se osiguralo da se prekidač postavi na LOW. To je neophodno jer izbacivanje kondenzatora izravno kroz sklopku proizvodi neželjeni visokofrekventni naponski šum.

 

R = 10 kOhma = 10.000 ohma
C = 100 nF = 0,0000001 F
10.000 * 0,0000001 = 0,001 s

Ovakav jednostavan RC filtar ovdje je ipak samo djelomično rješenje. Ako VIDI X radi na 3,3 V, mikrokontroler će prebacivati ​​između logičkog niskog i logičkog visokog na 1,65 V. Iako filtar prilično dobro izglađuje stvari, ne garantira da nećete dobiti poneke odskoke koji se šeću uokolo baš kada napon na kondenzatoru dosegne 1,65 V.

 

Na VIDI X osciloskopu vidimo krivulju ponekog odskoka koji se šeće uokolo.

 

Korisno je imati osciloskop pri ruci kako biste mogli promatrati što se događa s odskocima te kako oni djeluju pri različitim konfiguracijama RC filtra.

VIDI X mikroračunalo možete pretvoriti u osciloskop uz pomoć radionice na linku https://vidi-x.org/radionice/vidi-project-x-52-vidi-x-kao-osciloskop/.

VIDI Project X #52: VIDI X kao Osciloskop

 

 

 

Konkretna: primjena VIDI X osciloskopa i njegovih analognih ulaza koje u ovom slučaju koristimo za mjerenje učinkovitosti RC filtra te graf prikazujemo na ekranu.

 

Idealno hardversko rješenje

Idealno hardversko rješenje vrlo je jednostavno. Potreban vam je kondenzator za izglađivanje valova, dva otpornika od 10 kOhma i integrirani krug 74HC14 za dobivanje histereze. Ovaj čip može upravljati s do šest tipki, a pri tome ne morate uopće razmišljati o softverskoj strani priče.

Histereza znači da umjesto jednog napona, točno u sredini, postoje dvije granice napona. Uobičajena donja granica za LOW signal je jedna trećina do nule, te dvije trećine do maksimalnog logičkog napona za HIGH stanje. Prekidač s histerezom koristi gornji prag kada je posljednji put imao LOW stanje, a donji prag kada je u HIGH stanju. Ovo ostavlja prostor između dvaju pragova kao „mrtvu zonu“ u kojoj se nikad ne odvija prebacivanje. Da biste pobliže shvatili kako prebacivanje funkcionira, proučite priloženi grafikon prebacivanja.

 

Grafikon prebacivanja Schmittovog okidača s histerezom.

 

Najčešće se koristi logički čip s prethodnim kondicioniranjem i histerezom za ispravljanje prebacivanja. Logički čip poput 74HC14 koji radi za voltaže od 2 V do 6 V ili čip oznake 40106 za signale od 5 V učinit će upravo to.
74HC14 ima tipično vrijeme tranzicije od 125 milisekundi, no u pravilu je znatno brži od toga pri sobnoj temperaturi i djelovanju logičkog signala na 3,3 V.

 

Shema potpuno otklonjenog efekta odbijanja.

 

Kako biste mogli eksperimentirati s vrijednostima otpornika, ali i kondenzatora te promatrati stanje prebacivanja prekidača osciloskopom, možete na breadboard pločici postaviti gumb s kondenzatorom i potenciometrima umjesto otpornika kako biste mogli potenciometrima (trimerima u ovom slučaju) mijenjati vrijednosti.

 

Shema spajanje RF filtra uz pomoć otpornika i kondenzatora. Mi smo otpornike zamijenili potenciometrima kako bismo lakše eksperimentirali s njihovim vrijednostima i time dobili mogućnost postavljanja njihove vrijednosti na 10 kOhma, kao i na sve vrijednosti manje od toga.

 

Breadboard bi izgledao ovako prema navedenoj shemi spajanja. Zeleni vod preko gumba ovdje se nalazi samo zato jer neki gumbi već imaju takvu konekciju dok drugi nemaju, pa ju napravite da biste bili sigurni da ona postoji. Po jedna krajnja nožica svakog potenciometra nije spojena u strujni krug. Pripazite da gornji potenciometar nikada ne postavite u položaj u kojem je njegov otpor nula ohma jer time radite kratki spoj strujnog kruga te će VIDI X pokušati aktivirati mehanizam zaštite od kratkog spoja kako bi izbjegao kvar.

 

Nikako ne smijemo: zaboraviti postaviti prekidače u poziciju za korištenje utora za proširenje. Kao što je na slici prikazano, prekidač kod oznake TOUCH_IRQ i BTN_A postavite u poziciju USE EXP kako biste odvojili konekciju od već ugađanog gumba A i tako mogli eksperimentirati na breadboard pločici.

 

Naposljetku, rješenje koje smo dobili gotovo je idealno u većini situacija, a graf krivulje gumba na osciloskopu izgleda ovako.

 

Za vrijednosti: otpora RL1 = 10 kΩ te RL2 = 10 kΩ te kondenzatora C = 100 nF dobili smo najbolje rezultate

Mi nismo koristili logički čip 74HC14, no u raznim projektima on može biti jednostavan spasitelj u situaciji odbijanja signala gumba.

Popravljate li gumbe softverski, postoji jedna odlična rutina koju možete proučiti na poveznici https://www.kennethkuhn.com/electronics/debounce.c.

Za one koji žele znati više, posjetite link https://hackaday.com/2010/11/09/debounce-code-one-post-to-rule-them-all/.

Za sve one koji nemaju strpljenja postavljati elektroničke komponente ili ih jednostavno nemaju pri ruci, u online emulatoru koji je sposoban emulirati gumb s odbijanjem kao i onaj bez odbijanja, pripremili smo vam simulaciju za proučavanje. Nadamo se da vam je ona dovoljna za shvaćanje što se u biti događa, iako nema mogućnost postavljanja kondenzatora te samim time i isprobavanja strujnoga kruga.

 

Emulator

Iako je znatno sporiji od VIDI X-a uživo, može biti od velike koristi, kako za učenje, tako i za isprobavanje raznih rješenja. Koristite ga na linku https://bit.ly/de-bounce

 

Vremenski prekidi

Osim hardverskih prekida, VIDI X posjeduje i prekide koji su određeni nekim vremenskim intervalom te se mogu periodički ponavljati neovisno o ostatku koda koji se izvršava. Vremenski prekidi mogu se konfigurirati tako da odbrojavaju prema gore ili prema nuli. Na taj način mogu generirati alarme kada dosegnu određenu vrijednost definiranu kodom.

Postavimo varijablu brojača koju možemo dijeliti između glavne petlje i ISR-a. Treba je deklarirati kao nestabilnu (eng. volatile), čime se izbjegava njezino slučajno uklanjanje od strane kompajlera radi optimizacije koda.

volatile int interruptCounter;

 

Imat ćemo dodatni brojač kako bismo pratili koliko je prekida već izvršeno od početka programa. Taj brojač će koristiti samo glavna petlja i zato ga ne treba proglašavati nestabilnim.

int totalInterruptCounter;

 

Da bismo konfigurirali timer, trebat će nam pokazivač na varijablu tipa hw_timer_t, koju ćemo kasnije koristiti u funkciji setup.

hw_timer_t * timer = NULL;

 

Inicijalizirat ćemo naš timer pozivom funkcije timerBegin, koja vraća pokazivač na strukturu tipa hw_timer_t. Ta struktura je globalna „varijabla“ timera.

timer = timerBegin(0, 80, true);

 

Kao ulaz, ova funkcija prima redni broj timera koji želimo koristiti (od 0 do 3, budući da imamo 4 hardverska timera), vrijednost pred skale i zastavicu koja pokazuje treba li brojač brojati prema naprijed (true) ili odbrojavati prema nuli (false).

Što se tiče pred skale, obično je frekvencija osnovnog signala koji koristi ESP32 timer oko 80 MHz. Ta je vrijednost jednaka 80.000.000 Hz, što znači da bi brojač vremena rastao 80.000.000 puta u sekundi.

 

 

Iako bismo mogli napraviti izračune s ovom vrijednošću, kako bismo postavili timer brojača za generiranje prekida, iskoristit ćemo pred skalu da ga pojednostavimo. Dakle, ako podijelimo ovu vrijednost s 80 (koristeći 80 kao vrijednost predračuna), dobit ćemo signal s frekvencijom od 1 MHz koji će povećati brojač vremena 1.000.000 puta u sekundi.

 

Prije nego omogućite timer, morate ga dodijeliti funkciji prekida koja će se izvršiti kada se generira prekid. To se radi pozivom funkcije timerAttachInterrupt.

timerAttachInterrupt(timer, &onTimer, true);

 

Zatim ćemo koristiti funkciju timerAlarmWrite za određivanje protuvrijednosti u kojoj će se generirati prekid mjerača vremena. Dakle, ova funkcija prima kao prvi ulaz pokazivač na timer, kao drugi vrijednost brojača u kojem bi se trebao generirati prekid, a kao treći zastavicu koja označava treba li se timer automatski ponovno učitati nakon generiranja prekida.

timerAlarmWrite(timer, 1000000, true);

 

Što se tiče drugog argumenta, sjetite se, postavili smo pred skalu kako bi ona označavala broj mikrosekundi nakon kojih bi se trebao izvršiti prekid. Dakle, za konkretni primjer pretpostavljamo da želimo generirati prekid svake sekunde, i tako imamo vrijednost od 1.000.000 mikrosekundi, što je jednako jednoj sekundi. Upamtite: ta je vrijednost navedena u mikrosekundama samo ako navedemo vrijednost 80 za pred skalu. Možemo koristiti različite vrijednosti pred skale i u tom slučaju moramo napraviti izračune kako bismo znali kada će brojač doseći određenu vrijednost.

Funkciju postavljanja završavamo omogućavanjem timera pozivom funkcije timerAlarmEnable, prosljeđujući kao ulaz varijablu mjerača vremena.

timerAlarmEnable(timer);

 

Dakle, obratite pažnju na to kako izgleda kompletna funkcija setup, a kako funkcija interrupt, te na deklariranje potrebnih varijabli i naposljetku na glavnu funkciju loop koja ustvari ostaje prazna u ovom primjeru:

volatile int interruptCounter;
int totalInterruptCounter;
 
hw_timer_t * timer = NULL;

void IRAM_ATTR onTimer1() {
  interruptCounter++;
  Serial.print(“An TIMER interrupt has occurred. Total number of times: “);
  Serial.println(totalInterruptCounter);
}

void setup() {
  Serial.begin(115200);
  timer = timerBegin(0, 80, true);
  timerAttachInterrupt(timer, &onTimer, true);
  timerAlarmWrite(timer, 1000000, true);
  timerAlarmEnable(timer);
}

void loop() {}

 

Obratite pažnju na to da ISR funkcija ne smije vraćati ništa te je deklarirana kao void i ne prima argumente. Ova funkcija je jednostavna te samo prikazuje stanje brojača, odnosno povećava vrijednost varijable koju ispisujemo na serijsku konzolu. U realnoj uporabi interrupt funkcija, nemojte im zadavati ispis na serijsku konzolu (kao u našem primjeru) jer je to jako spora naredba te u kompleksnim aplikacijama može stvoriti neočekivano usporavanje izvođenja kompletnog koda.

Opet napomenimo, ISR funkcija trebala bi biti optimizirana za brzo izvođenje, koliko god je to u moći programera.

Atribut IRAM_ATTR obavezan je kako bi kompajler postavio kod u IRAM memoriju. ISR funkcija bi trebala pozivati samo funkcije koje se isto tako nalaze u IRAM-u, radi izbjegavanja „smrzavanja“ programa ukoliko glavna petlja „zapne“ u nekoj petlji ili slično.

 

 

Kompletan kod radionice pronađite na GitHub-u: https://github.com/VIDI-X/Vidi-X-Interrupts

PRIJTELJI PROJEKTA

bez kojih sve ovo ne bi bilo moguće.

SVA PRAVA PRIDRŽANA – VIDI TO 2020.

Niti jedan dio ovog web site-a ne smije se u bilo kojem obliku ili radi bilo koje namjene reproducirati bez prethodne pismene suglasnosti izdavača, Svi tekstovi objavljeni na www.vidi-x.com pripremljeni su s osobitom pažnjom i kontrolirani na više razina.

Redakcija www.vidi-x.com međutim, ni u kojem slučaju ne može odgovarati za moguće štete bilo kakve vrste nastale na osnovu savjeta, tekstova, slika ili drugog redakcijskog ili oglašivačkog materijala objavljenog na www.vidi-x.com ili na drugi način datog od strane zaposlenika ili suradnika izdavača.