Ez nem furcsa kinézetű. Így néz ki valójában a normál MCU-kód.
Amit itt talál, az példa a memória-leképezett perifériák fogalmára. Alapvetően az MCU hardver speciális helyekkel rendelkezik az ahhoz rendelt MCU SRAM címterében. Ha ezekre a címekre ír, a címre írt bájt bitjei n vezérlik a periférikus m viselkedését.
Alapvetően bizonyos memóriabankokban szó szerint kevés vezeték fut az SRAM cellától a hardverig. Ha "1" -et ír erre a bitre abban a bájtban, akkor ez az SRAM cellát logikai magasra állítja, majd bekapcsolja a hardver egy részét.
Ha megnézi az MCU fejlécét , nagy nagy táblázatok vannak a keyword<-> cím leképezésekről. Így oldják meg a fordítás idején az olyan dolgokat, mint a TCCR1B
stb. ...
Ezt a memória-leképezési mechanizmust rendkívül széles körben használják az MCU-k. Az arduino ATmega MCU-ját használja, csakúgy, mint a PIC, ARM, MSP430, STM32 és STM8 MCU sorozatokat, valamint sok olyan MCU-t, amelyeket nem ismerek azonnal.
Az Arduino kód a furcsa dolog, olyan funkciókkal, amelyek közvetett módon hozzáférnek az MCU vezérlő regiszterekhez. Bár ez némileg "szebb" kinézetű, ugyanakkor sokkal lassabb és sokkal több programterületet is igénybe vesz.
A titokzatos állandókat mind nagyon részletesen leírja az ATmega328P adatlap, amit akkor érdemes elolvasnia, ha bármi másra kíváncsi, mint az arduino csapok általi váltása.
Válasszon ki részleteket a fent linkelt adatlapból:
Tehát például a TIMSK1 | = (1 << TOIE1);
beállítja a TOIE1
bitet a TIMSK1
mezőben. Ezt úgy érhetjük el, hogy az 1-es bináris kódot ( 0b00000001
) TOIE1
bitekkel balra toljuk, a fejlécfájlban pedig a TOIE1
-ot 0-ként definiáljuk. ezután bitenként OR OR beillesztésre kerül a TIMSK1
aktuális értékébe, amely gyakorlatilag ezt az egy bitet magasra állítja.
A TIMSK1
0. bitjének dokumentációja, láthatjuk, hogy le van írva:
Ha ezt a bitet egyre írják, és az állapotregiszterben az I-flag be van állítva (a megszakítások globálisan engedélyezve vannak), akkor engedélyezve van az Időzítő / Számláló1 túlcsordulás megszakítása . A megfelelő megszakítási vektort (lásd: „Megszakítások”, 57. oldal) akkor hajtjuk végre, amikor a TIFR1-ben található TOV1 jelző be van állítva.
Az összes többi sort ugyanúgy kell értelmezni.
Néhány megjegyzés:
Olyanokat is láthat, mint a TIMSK1 | = _BV (TOIE1);
. A _BV ()
egy általánosan használt makró, amely eredetileg az AVR libc implementációból származik. A _BV (TOIE1)
funkcionálisan megegyezik a (1 << TOIE1)
-val, a jobb olvashatóság érdekében.
Emellett sorokat is láthat, például mint: TIMSK1 & = ~ (1 << TOIE1);
vagy TIMSK1 & = ~ _BV (TOIE1);
. Ennek ellentétes funkciója van: TIMSK1 | = _BV (TOIE1);
, mivel leteszi a TOIE1
bitet a TIMSK1 kód>. Ezt úgy érhetjük el, hogy a _BV (TOIE1)
által előállított bitmaszkot veszünk, bitenként NEM műveletet hajtunk végre rajta ( ~
), majd ANDingeljük a TIMSK1
ezzel a NOTED értékkel (ami 0b11111110).
Ne feledje, hogy ezekben az esetekben a (1 << TOIE1)
vagy a _BV (TOIE1)
értékek teljes mértékben feloldódnak a fordítási időpontban em>, így funkcionálisan egyszerű konstanssá redukálódnak, és ezért nincs szükség végrehajtási időre a futás közbeni kiszámításhoz. kód, amely részletezi a hozzárendelt regiszterek működését. Itt van egy meglehetősen egyszerű soft-SPI rutin, amelyet nemrég írtam:
uint8_t transactByteADC (uint8_t outByte) {// Egy bájtot visz át az ADC-hez, és egy bájtot kap egyszerre // nem semmi a chip-select // MSB-vel először, az adatok az emelkedő élen vannak ütemezve uint8_t loopCnt; uint8_t retDat = 0; for (loopCnt = 0; loopCnt < 8; loopCnt ++) {if (outByte & 0x80) // ha az aktuális bit magas PORTC | = _BV (ADC_MOSI); // adatsor beállítása else PORTC & = ~ (_BV (ADC_MOSI)); // másképp állítsa ki aztByte << = 1; // és helyezze át a kimeneti adatokat a következő iterációra retDat << = 1; // váltás a visszaolvasott adatok fölé PORTC | = _BV (ADC_SCK); // Állítsa magasra az órát, ha (PINC & _BV (ADC_MISO)) // a retDat | = 0x01 beviteli sor mintája; // és állítsa be a bitet a retval-ban, ha a bemenet magas PORTC & = ~ (_BV (ADC_SCK)); // alacsony óra beállítása} return retDat;}
A
PORTC
az a regiszter, amely a kimeneti csapok értékét szabályozza az ATmega328P PORTC
-ján belül. A PINC
az a regisztráció, ahol a PORTC
input értéke elérhető. Alapvetően ilyen dolgok történnek belsőleg, amikor a digitalWrite
vagy a digitalRead
függvényeket használja. Van azonban egy felkutatási művelet, amely az arduino "pin-számokat" tényleges hardver-pin-számokká alakítja át, ami valahol 50 órás ciklus tartományába esik. Ahogy valószínűleg sejteni is lehet, ha gyorsan akarsz haladni, kissé nevetséges egy 50 órás ciklus pazarlása egy olyan műveletre, amelynek csak 1 szükséges.
A fenti funkció valószínűleg valahol a 100 birodalmába kerül. -200 óra ciklus 8 bit átviteléhez. Ez 24 pin-írást és 8 olvasást jelent. Ez sok-sokszor gyorsabb, mint a digital {stuff}
függvények használata.