volatile

[Változók hatóköre & Minősítők]

Leírás

A volatile egy változó minősítőként ismert kulcsszó, általában egy változó adattípusa előtt használatos, hogy módosítsa azt a módot, ahogyan a fordító és az azt követő program kezeli a változót.

Egy változó volatile-nak nyilvánítása egy direktíva a fordító számára. A fordító egy olyan szoftver, amely lefordítja a C/C++ kódot gépi kódra, ami az Arduino Atmega chipjének valós utasításai.

Konkrétan arra utasítja a fordítót, hogy a változót a RAM-ból töltse be, és ne egy tárolóregiszterből, amely egy ideiglenes memóriahely, ahol a programváltozókat tárolják és kezelik. Bizonyos feltételek mellett a regiszterekben tárolt változó értéke pontatlan lehet.

Egy változót volatile-nak kell nyilvánítani, ha az értékét olyan dolog módosíthatja, amely kívül esik azon kódrészen, amelyben megjelenik, például egy párhuzamosan futó szál. Az Arduinóban ez az egyetlen hely, ahol ez valószínűleg megtörténik, a megszakításokhoz kapcsolódó kódszakaszok, amelyeket megszakítási szolgáltatási rutinnak neveznek.

int vagy long volatile-k

Ha a volatile változó egy bájtnál nagyobb (pl. 16 bites int vagy 32 bites long), akkor a mikrokontroller nem tudja egy lépésben beolvasni, mert egy 8 bites mikrokontrollerről van szó. Ez azt jelenti, hogy míg a fő kódszakasz (például a ciklusod) a változó első 8 bitjét olvassa, a megszakítás már megváltoztathatja a második 8 bitet. Ez véletlenszerű értékeket fog előállítani a változó számára.

Orvoslás:

A változó olvasása közben a megszakításokat le kell tiltani, hogy ne vacakolhassanak a bitekkel olvasás közben. Ennek többféle módja van:

  1. NYELV noInterrupts

  2. az ATOMIC_BLOCK makró használata. Az Atomic műveletek egyszerű MCU-műveletek – a lehető legkisebb egység.

Példa

A volatile módosító biztosítja, hogy a state változó változásai azonnal láthatóak legyenek a loop()-ban. A volatile módosító nélkül a state változó a függvény beírásakor bekerülhet egy regiszterbe, és a függvény lejártáig nem frissül.

// 1 másodpercig villog a LED, ha a bemenet
// megváltozott az előző másodpercben.

volatile byte changed = 0;

void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
  attachInterrupt(digitalPinToInterrupt(2), toggle, CHANGE);
}

void loop() {
  if (changed == 1) {
    // toggle() megszakításból hívták!

    // visszaállítás 0-ra
    changed = 0;

    // A LED felvillan 200 ms-ig
    digitalWrite(LED_BUILTIN, HIGH);
    delay(200);
    digitalWrite(LED_BUILTIN, LOW);
  }
}

void toggle() {
  changed = 1;
}

A mikrokontroller 8 bites adatbuszánál nagyobb méretű változó eléréséhez használja az ATOMIC_BLOCK makrót. A makró biztosítja, hogy a változó Atomic műveletben kerüljön beolvasásra, azaz a tartalma ne változzon olvasás közben.

#include <util/atomic.h> // ez a könyvtár tartalmazza az ATOMIC_BLOCK makrót.
volatile int input_from_interrupt;

// Valahol a kódban, pl. a loop()-on belül
  ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
    // kód blokkolt megszakításokkal (az egymást követő Atomic műveletek nem szakadnak meg)
    int result = input_from_interrupt;
  }

Lásd még