Lekce 21 - Arduino a využití přerušení (interrupt) a ošetření záchvěvů při stisku tlačítka

22.06.2013 21:25

Translate to English

V dnešní lekci si řekneme něco o přerušení neboli anglicky"interrupt". Určitě jste si říkali někdy při psaní programu do Arduina, že program nebo cyklus programu musíte napsat tak, aby v cyklech např zjišťoval, zda nebylo stisknuto nějaké tlačítko. V malém programu na pár řádků to není problém, ale pokud máme nějaký program, kde smyčka v "loop" trvá např. 2 sekundy, tak přidávat funkci zda uživatel stiskl nějaké tlačítko by byl nesmysl a také co když se uživatel zrovna "trefí" do času, kdy program dělá něco jiného. Řešení je právě tzv. "přerušení", které zastaví aktuální běh programu, a vykoná funkci, která je zapsaná, aby se při tomto právě vykonala. Jinak řečeno, když Arduino dostane na pin, kde je možné nastavit přerušení, nějaký určený impuls, zastaví běh programu a vykoná něco jiného.

Tabulka pinů, kde je možno nastavit přerušení:

Arduino 0 1 2 3 4 5
UNO, Ethernet 2 3 -- -- -- --
Mega 2560 2 3 21 20 19 18
Leonardo 3 2 0 1 7 --

 

Zápis funkce, která aktivuje přerušení:

attachInterrupt(přerušení, funkce, mód);

Příklad:

attachInterrupt(1, Reakce, RISING); - což v překladu znamená, že se spustí funkce Reakce(), když se na pinu 3 (Arduino UNO), změní hodnota z LOW na HIGH

V tabulce nahoře vidíme, kde naše Arduino má piny kde lze aktivovat přerušení. V případě Arduina UNO máme přerušení dvě na pinech 2 a 3 a v případě Arduino MEGA (typ 2560) je možnost nastavit přerušeních dokonce 6 na pinech, uvedených v tabulce. V případech jiných typů Arduina, byste měli přerušení nalézt v jejich dokumentaci. 

My si nyní zkusíme jednoduchý praktický příklad. Nějak zaměstnáme Arduino a budeme chtít, aby něco udělal když detekuje přerušení. Do Arduina si nahrajeme tedy tento program:

Sami můžete vidět, že je to kód jenom pro demonstraci. Nic užitečného nedělá jen v cyklech něco počítá a čísla cyklu vypisuje na sériový port. Ale tím jsme Arduino zaměstnali a můžeme vyzkoušet přerušení, které jsme si v setup() nastavili na pin 2 (přerušení 1 u UNO), vykoná funkci test (ta jenom rozsvítí nebo zhasne LED integrovanou na Arduinu k pinu 13), a bude aktivována když se na pinu 2 (přerušení) změní hodnota z LOW na HIGH.

Samozřejmně by jsem k Arduinu připojil tlačítko, aby jsme simulovali změnu honoty na přerušení tlačítkem, ale neučiním tak a to z důvodu, který zjistíme dále. Jako zdroj této změny jsem si naprogramoval jednoduchým kódem druhé Arduino (MEGA), které pouze bliká ledkou integrovanou k pinu 13 na svoji desce. 

Kód pro druhé Arduino:

Takže jedno Arduinko nám dává hezký signál, který je připojený na ke druhému Arduino. To spustí funkci, kterou má nastavenou, když v našem případě jde signál z 0 do 1. My jsme nastavili mód detekce na RISING, ale možností je více jak ukazuje následující tabulka:

   
spustí funkci při přechodu z 0 na 1
spustí funkci při přechodu z 1 na 0
spustí funkci při kterékoliv změně jak z 1 na 0, tak z 0 na 1
spustí funkci pro 0 (LOW)

Tak tento příklad funguje bezvadně a i když si Arduino ve funkci loop() hezky něco dělá, nemusíme ošetřovat v kódu a zjišťovat zda bylo přerušení aktivováno. V běžné praxi budeme, ale chtít, aby jsme třeba zjistili, zda bylo stisknuto nějaké tlačítko. No není problém, připojíme si tedy místo Arduina s naším uměle vyrobeným signálem tlačítko, které když stiskneme, tak budeme chtít, aby se rozsvítila nebo zhasla LED (digital 13) na Arduinu. Připojíme si ho tedy (samozřejmě dle zvyklostí musí být vstupní pin připojen přes odpor k 0 nebo 1 a tlačítkem měníme hodnotu).

 

Tak a co se nám děje? funguje to zcela nespolehlivě, někdy nám dioda zhasne nebo se rozsvítí, ale né podle pravidel. Funguje to náhodně. A teď otázka, čím je to způsobeno? Vysvětlení je jednoduché. Tlačítko je vlastně elektromechanický prvek, který když stiskneme tak v našem reálném čase byť je to zcela okem nezjistitelné dojde při zapnutí, nebo rozpojení tlačítka k záchvěvům (bouncing). Viz obrázek z osciloskopu:

Takže Arduino to vyhodnotí jako několik stisknutí i když jsme tlačítko stisky pouze jednou. Takže pokud náš program vyhodnotí a spustí 4x funkci k přerušení (protože zjistil 4x změnu z 0 na 1) tak spustí také 4x funkci test, která ze stavu vypnuté LED  - zapne, vypne, zapne, vypne. Takže my si vlastně můžeme myslet, že to nefunguje, ale funguje to zcela správně. Co tedy udělat. Existují 2 způsoby, buď to ošetřit v programu tím, že po zjištění prvního přerušení toto přerušení vypneme nebo deaktivujeme, vykonáme co potřebujeme, poté počkáme třeba 300 ms  a následně zase přerušení aktivujeme, tak je upraveno zde:

Má to ovšem i své nevýhody. Zastavíme tím program na 300ms a také nemáme jistotu co se stane, když stisk bude trvat déle jak 300ms. Takže moje rada používat toto jen v nejnutnějších případech a z rozvahou.

Pozn.: V mém případě nepomohlo ani nastavení prodlevy na 500ms a ledka si po stisknutí tlačítka dělala co chtěla.

Druhá možnost je ošetřit tyto záchvěvy elektronicky pomocí několika málo součástek. Bude se jednat o obvod RC složený z kondenzátoru a odporu a jednoho integrovaného obvodu 40106, který obsahuje 6x Schmittův klopný obvod. Zakladní schéma je zde:

Princip je následovný. Když stiskneme tlačítko vybijeme kondenzátor C1, tím se vstupu Schmittova klopného obvodu objeví 0. Po uvolnení tlačítka se kondenzátor opět nabije. Pro zvádavé nabití kondenzátoru C1 si můžeme vypočíst podle vzorce t = R*C (10 000 Ohm * 0,00001 F) což je 0,1sec. Pro nás nepodstatné. Využijeme vlasnosti Schmittova klopného obvodu, který upraví signál do podoby podle obrázku (vidíme tam i průběh před na vstupu klopného obvodu i na výstupu)

Schmittův KO slouží k úpravě tvaru impulzů. Jeho základní vlastností je hystereze. To znamená, že jeho výstup je závislý nejen na hodnotě vstupu, ale i na jeho původním stavu. Hystereze, která je jindy nežádoucí, má zde své opodstatnění v tom, že zabraňuje vzniku zákmitů výstupního signálu v okolí střední úrovně spínání. Citlivost obvodu se nastavuje šíří-velikostí hystereze. Obvod je možno využít i jako jednobitový analogově digitální převodník - komparátor.

Schema zapojení pinů obvodu 40106, existuje několik integrovaných obvodů, které Schmittovy klopné obvody obsahují - máte mnoho možností při nákupu.

 

Jinak mnou uvedený integrovaný obvod obsahuje celkem 6 uvedených klopných obvodů, takže k 6ti tlačítkům nám stačí 1 integráček. Až ho budete kupovat, zjistíte, že jeho cena je opravdu nízká :-). 

Poté co máme zapojeno, vyzkoušíme a zjistíme, že to funguje touto metodou opravdu skvěle. Odstranění záchvěvů při práci s tlačítky a využití přerušení v Arduinu myslím, že využijete a doufám, že Vám tento návod pomohl a posunul Vaše vědomosti a malý kousek dále. 

Zpět

Diskusní téma: Lekce 21 - Arduino a využití přerušení (interrupt) a ošetření záchvěvů při stisku tlačítka

Datum
Vložil
Titulek

odpoved na to proc nefunguje to spozdeni

Mam zde odpoved, proc ti nefungovalo to osetreni zakmitu. je to pouzitim funkce delay(), ta totis neni funkcni, sama pouziva na pozadi preruseni. Jedina funkce ktera bude fungovat je delayMicroseconds().
Je to napsano primo v popisu te funkce preruseni jako aplikacni poznamka.

Datum
Vložil
Titulek

přerušení sériovým portem

Ahoj, chci se zeptat jestli by šlo udělat aby smyčku přerušení vyvolal příjem dat na sériovém portu.
Díky

Datum
Vložil
Titulek

Preruseni

Nedávno jsem narazil na tuto stránku a když si pročítám tento tutoriál tak musím nesouhlasit s autorem. Jeho ošetření "bouncingu" není příliš šťastné. Delay je třeba v programu používat co nejméně.

Pro ošetření zákmitů bez potíží využívám toto:

void interrupt()
{
static unsigned long last_millis = 0;
unsigned long m = millis();
if (m - last_millis < 500) ; // ignoruj preruseni
else {
state = !state;
buttonTime=millis();
last_millis = m;
}


Procesor nestojí a může pokračovat v činnosti. Taktéž je to velmi spolehlivé řešení. Ve výše uvedeném případě je nastaven čas 500ms po který jsou další ipmuplzy vyvolané přerušením ignorovány.

S pozdravem Marek (aka CZ_Iceman)

Datum
Vložil
Titulek

Re: Preruseni

Já to sice nezkoušel, ale v referencích příkazu attachInterrupt() na arduino.cc se píše:
" ... and the value returned by millis() will not increment." To jsem si vysvětlil tak, že se po dobu přerušení ta hodnota nemění a tohle by tedy nemělo fungovat. Neměl autor na mysli funkci micros(), o které se tam nepíše a tedy snad funguje?

Datum
Vložil
Titulek

Re: Re: Preruseni

Řekl bych, že to na mysli neměl. Protože během přerušení se sice millis nemění, ale mimo něj se mění. A tady se na ně jen podívá a z přerušení vypadne. Než do něj zase vletí (kvůli dalšímu zákmitu), tak se buď millis stihnou zupdatovat (a mají aktuální novou hodnotu, co se během tohoto průchodu přerušením nezmění, ale s tou předchozí nijak nesouvisí), nebo se nestihnou zaktualizovat a pak to stejně pořád ještě divoce bouncuje, takže se správně usoudí, že nemá ještě cenu číst další hodnotu.

Datum
Vložil
Titulek

Re: Preruseni

Na australské stránce https://www.gammon.com.au/power
lze najít v části Sketch J (v souvislosti se spínáním):

attachInterrupt (0, wake, FALLING);
EIFR = bit (INTF0); // clear flag for interrupt 0

Mohl by někdo zdůvodnit podrobnosti výrazů EIFR, INTF0...?

ARBY

Datum
Vložil
Titulek

Re: Re: Preruseni

Zrovna jsem vyguglovat na foru N.Gammona
https://www.gammon.com.au/interrupts
v textu "How are interrupts queued?" je popsáno
EIFR = bit (INTF0) jako vynulování přerušení.

ARBY

Datum
Vložil
Titulek

Re: Preruseni

Ne vždy se zákmit projeví, např. web
https://www.arduinohobby.euweb.cz/toppage8.htm
uvádí, že se zákmit v souvislosti s přerušením neprojevil.

Datum
Vložil
Titulek

Jazyčkove rele

Dobrý den, řeším palubní počítač na motorku, otáčky kola snímám jazýčkovým relém, potřeboval bych odrušit zákmity relátka, stačí si pouze přepočítat čas (R*C) a lze použít toto zapojení s schmittovym obvodem? Děkuji za Vaši odpověď.

Datum
Vložil
Titulek

Cidlo

Zdravim, mam podobne cidlo jako je tady na dx. https://dx.com/p/k1208065-metal-detection-sensor-module-for-arduino-robot-kit-silver-black-151198 a zajimalo by me jestli mam pouzivat preruseni v pripade, ze cidlo vysila napeti(indikuje kov) nebo by se to dalo nejak jinak poresit? Mam arduino uno a 2 piny na preruseni mi nestaci :-) pry se to da nejak obejit, aby bylo vice takovych pinu, ale moc jsem se o tom nedocetl. Dekuji za odpoved

<< 1 | 2 | 3 >>

Vyhledávání

arduino8.cz © 2015 Všechna práva vyhrazena.