Arduino INT0, INT1 megszakítás kezelése

 

2021-02-10

Elnézést az ikon képért, de semmi nem jutott eszembe, egy kismegszakítón, vagy egy gépkocsi gyújtásmegszakítón kívül, ami valamennyire is illeszkedne a témához :). Valaki egy olyan problémával keresett meg, hogy Arduinóval szeretné figyelni, hogy egy másodpercen belül érkezett-e egy bemenetre impulzus, vagy sem. Nekem egyből az ugrott be, hogy ezt megszakítás kezeléssel lehetne megoldani, de mivel ilyesmit még én sem programoztam, utána kellett olvasnom. Azt gondolom, hogy érdemes közreadni ezt a kis programot és kissé körüljárni, hogyan is működik a megszakítás.

Remélem a legtöbb kedves olvasónak nem ez lesz az első Arduino programja, hanem általában tisztában vannak azzal, hogy az Arduino úgy működik, hogy az általunk írt program sorait a processzor szép egymás után értelmezi/végrehajtja, ez ugye a normál futás. A processzor tervezők beépítettek egy megszakításnak nevezett megoldást, aminek hatására a processzor abbahagyja a normál programjának a futtatását, menti ahol éppen tart, majd átlép egy előre megírt megszakítás kezelő programágra, ha azzal végzett, visszatölti a mentett adatokat, és folytatja a normál programjának futtatását, mintha mi sem történt volna. Ha valaki programozott már Atmel AVR mikrovezérlőt (Arduino is erre épül) C-ben vagy Assemblyben, gondolom találkozott már a megszakításokkal. Egy AVR, típustól függően, számos esetben kaphat megszakítás jelet. Ilyenek lehetnek például hogy soros porton bejött adat, egy számláló túlcsordulása stb. De nézzük meg egy Arduino UNO sematikus rajzát.

Nézzük nézzük, de mit kell néznünk? Fókuszáljunk az Arduino 2. és 3. lábára, amelyek az AVR INT0 és INT1 lábára csatlakoznak. (A megszakítás angolul interrupt.) (Ha több külső megszakításra lenne szükségünk, választhatunk mondjuk egy Arduino MEGA-t.) Az AVR belső áramkörei számos megszakítás forrása, de két bemenetet kivezették, amikre köthetünk mondjuk olyan áramkört, ami akkor jelez az Arduinonak, ha mondjuk elment a betáp, és valamit csinálni kellene, amíg van még egy kis maradék energia a táp kondikban. Vagy túlhevült az atomreaktorunk, és azonnali le kell állítani, vagy ... találjatok ki valamit. Esetünkben egy jeladót kell figyelni, hogy legalább másodpercenként adjon egy impulzust. Ezt most helyettesíthetjük egy nyomógombbal, amit ugye a 2. lábra (és a GND közé) kötünk. Az Arduino beépített LED-jét fogjuk használni, ami a 13. lábra (és a VCC közé) van kötve. Megszakítás kezelés esetén nem lehet használni a delay()-t, ezért az eltelt időt a millis() függvény segítségével fogjuk megállapítani. A millis() fgv. miliszekundumban adja meg az időt, ami az Arduino bekapcsolása óta eltelt, egy jó nagy long típusú adatot ad vissza. Ha mondjuk valamilyen folyamatos üzemű berendezést tervezünk, figyelembe kell venni, hogy ez negyven valahány napra elegendő, akkor ugye a számláló túlcsordul, és 0-tól újraindul. Két globális változót használunk, az egyik a volt_felfuto_el, a másik az idopont. Az idopont long típusú változó lesz. A volt_felfuto_el deklarálása egy kicsit érdekesebb. Alapvetően a legkisebb adattípust, a byte-ot választottam, viszont a deklarációt ki kellett egészíteni a volatile kulcsszóval. Ez azt mondja meg a C++ fordítónak, hogy ne aggódjon, hogy látszólag nem módosítjuk a változót (mintha nem használnánk) és ne optimalizálja (törölje) ki a programunkból. A normál progarmunk futása közben tényleg nem változik az értéke, hanem majd a megszakítás kezelő függvény fogja azt állítani.

A setup()-ban beállítjuk a ki/bementeket. Az az attachInterr... sor szerintem ha jobban utánaolvasok kiderül, hogy nem egy fgv. hívás, hanem egy makró. Mindegy, fogadjuk el, ha Arduinót programozunk, így lehet megadni, 2./3. megszakítások kezelésére melyik függvényt hívja meg a program, és azt is, hogy milyen esemény váltsa ki a megszakítást (RISING/FALLING/CHANGE/LOW). Letároljuk az idopont-ot.

A loop()-ban ciklikusan megvizsgáljuk, hogy idopont-tol eltelt-e már 1000 msec. Ha eltelt, akkor megvizsgáljuk a volt_felfuto_el változónkat. Alapból ugye 0-ra állítottuk, a LED kimenetet 1-be kapcsoljuk, vagyis a LED nem fog világítani. Ha a változóban 1 van, akkor ugye a LED kimenetet 0-ba kapcsoljuk, vagyis világítani fog. Dolgunkat azzal fejezzük be, alapba állítjuk a volt_felfuto_el változónkat, és eltároljuk az új időpontot, amihez képest megint figyeljük majd az 1000 msec elteltét.

A lekezel() fgv. akkor hajtódik végre, amikor a 2. lábra érkező felfutó él kiváltja a megszakítást. Látjuk, hogy a lekezel() a volt_felfuto_el változót 1-be állítja. Így ha az 1000 msec-ként lefutó főprogramunk azt megvizsgálja, azt fogja látni, hogy az utolsó periódusban volt bejövő impulzus.

Reményeim szerint sikerült bemutatnom a megszakítás kezelést Arduinon. Annyi azonban még ide kívánkozik, ha valaki majd komolyabban foglalkozik mikroprocesszoros rendszerek fejlesztésével, azért ezt a témát is lehet bonyolítani a megszakítások maszkolásával, letiltásával, vagy éppen a megszakítás kezelés időszükségletének tervezésével, nehogy úgy túlterheljük a vezérlőnket, mint modjuk egy DDOS támadás a szerverünket :).



/******************************************************************************
*   Author       -  Kiraly Tibor
*                   http://www.tkiraaly.hu
*   Date         -  2021.02.08.
*   Chip         -  Arduino UNO
*   Compiler     -  Arduino IDE
*
*   Felfuto el figyelese
*   
*******************************************************************************/

#define BEMENET           2            // 2 vagy 3 labon tud IT-t figyelni
#define LED              13            // beepitett LED


volatile byte volt_felfuto_el= 0;      // IT kezeles eseten fontos a volatile
unsigned long idopont= 0;


void setup()
{
   pinMode( LED, OUTPUT);
   pinMode( BEMENET, INPUT_PULLUP);
   attachInterrupt( digitalPinToInterrupt( BEMENET), lekezel, RISING);
   // RISING   - felfuto el
   // FALLING  - lefuto el
   // CHANGE   - barmely iranyu valtozas
   // LOW      - alacsony szint
   idopont= millis();                  // kezdes idopontja
}


void loop()
{
   if ( ( millis()- idopont) >= 1000)                 // ido figyelese     
   {
      if ( volt_felfuto_el) digitalWrite( LED, 0);    // LED vilagit
      else                  digitalWrite( LED, 1);    // LED sotet
      volt_felfuto_el= 0;
      idopont= millis();                              // figyeles ujrainditasa
   }
}


void lekezel()
{
   volt_felfuto_el= 1;
}

Itt a vége, fuss el véle.