Üks kõige raskemini mõistetavaid asju mikrokontrollerite juures on algajate jaoks tavaliselt “register”. Sellest mõistest ei pääse üle ega ümber, kui on soov mikrokontrolleritega tegeleda. Ka käesolev materjal eeldab, et lugeja saab tuttavaks registri mõistega ja seepärast on järgnevalt seda algajale ka võimalikult lihtsalt selgitatud.
Register on nagu mõne kodumasina nuppude paneel. Seal on lülitid, mida saab sisse vajutada või välja lükata. Üks parim näide on kassetimängija. Kes veel mäletab, siis kassetimängijatel on (oli) vasakult paremale 6 nuppu:
Iga nupp teeb midagi, kuid ainult õigel kasutamisel. Näiteks stopp-nupp ei tee midagi enne, kui kassett on mängima pandud - alles siis teeb see midagi arusaadavat ja peatab mängimise. Edasi- või tagasikerimise nuppe võib aga igal ajal vajutada, sest linti hakatakse kerima nii poole mängimise kui seismise ajal. Salvestama hakkab kassetimängija aga ainult siis, kui salvestamise nupp koos mängimise nupuga alla vajutada. Mõni on ehk proovinud mitu nuppu või kõik nupud korraga alla vajutada - sel juhul võis mängija mida iganes teha või üldse katki minna.
Mikrokontrolleri registriga on sama lugu nagu kassetimängija nuppudega - iga nupuke paneb seal õigel kasutamisel midagi käima. Valesid nuppe vajutades mikrokontroller (enamasti) küll katki ei lähe, kuid ei tööta ka. Tegelikult registris selliseid nuppe nagu kodumasinatel muidugi pole, on hoopis hulk transistore, mis elektrit sisse ja välja lülitavad. Lihtsamatel mikrokontrolleritel on registris 8 transistoridel põhinevat elektrilist lülitit. Registrit võib aga käsitleda nagu 8-bitist arvu, milles iga bitt tähistab ühe lüliti olekut. Näiteks biti väärtus 1 võib tähendada, et lüliti on sees ja 0, et lüliti on väljas.
Kuna registri lülitite olekut saab väga hästi esitada arvuna ja vastupidi, siis võib registrit võrrelda ka mäluga, mis suudab hoida ühe arvu suurust informatsiooni. Selle võrdlusega jõudis jutt selleni, et registrid ongi mälupesad. Vahe mälupesaga seisnebki üldjuhul ainult selles, et mälupesa ei tee muud, kui hoiab informatsiooni, registris see informatsioon aga juhib midagi. Ehk kui pildil kujutatud registrisse kirjutada binaarväärtus 01100001, siis kolm näilist lülitit lükatakse alla ja midagi hakkab toimuma.
Kui kassetimängijal võib igat nuppu eraldi vajutada, siis registrites on ühe “lüliti” või biti muutmine keerulisem. Üldjuhul tuleb muuta kogu registri sisu korraga. Enne kui bittide muutmisest rääkida, tuleks aga teada veel seda, et registreid on mikrokontrolleris palju. Mõnede mikrokontrolleri osade juhtimiseks võib kasutusel olla ka kümneid registreid. Registrite paljusus tähendab, et registreid peab kuidagi eristama ja seda tehakse nimega. Üks ilus registri nimi on näiteks PORTB. Tõe huvides võib mainida, et tegelikult on nimed vaid inimese välja mõeldud asendused numbrilistele aadressidele.
C-keele programmis registri väärtuse kirjutamiseks või lugemiseks tuleb selle poole pöörduda nagu muutuja poole. Järgnev näide demonstreerib väljamõeldud registrisse REG binaarväärtuse kirjutamist ja selle väärtuse muutujasse reg lugemist. Binaarväärtuse ette kirjutatakse 0b (ees on null), et kompilaator arvusüsteemist aru saaks.
REG = 0b01100001; unsigned char reg = REG;
Põhimõtteliselt registrite väärtuse kirjutamises ja lugemises midagi keerulist polegi, kuid keerulisemaks läheb lugu siis, kui soovitakse muuta registri üksikute bittide väärtust. Bittide muutmiseks tuleb enne selgeks saada binaartehted ja erinevad arvusüsteemid. Keegi ei keela tegutseda binaararvudega, kuid binaararvudega tegelemine on tülikas nende pikkuse tõttu ja tavaliselt kasutatakse nende asemel heksadetsimaalarve, mis on lühemad.
Heksadetsimaalarvus pole numbrid mitte 0 ja 1, nagu binaarsüsteemis, ega 0-st 9-ni, nagu kümnendsüsteemis, vaid 0-st F-ni. Üks heksadetsimaalnumber moodustub neljast bitist. Kõrvalolev tabel näitab heksadetsimaalnumbritele vastavaid binaararve. Binaararve teisendatakse heksadetsimaalarvuks lugedes bitte nelja kaupa, alates madalamast järgust. Järkusid loetakse paremalt vasakule ja nende nummerdamist alustatakse nullist. Näiteks binaararvu 1110 madalaima ehk 0. järgu väärtus on 0 ja kõrgeima ehk 3. järgu väärtus on 1. Eespool toodud näidisregistri binaarväärtus 01100001 on heksadetsimaalkujul 61, mis C-keeles kirjutatakse kujul 0x61 (ees on null).
Üksikute bittide muutmiseks arvus (registris, muutujas või kus iganes) tuleb kasutada binaartehteid. Binaartehe on tehe binaararvude vahel, kus nende arvude iga biti vahel toimub omaette loogikatehe. Enamasti on mikrokontrollerites kasutusel neli binaartehet, millel kõigil on mitu nimetust. Järgnevalt on toodud kõigile neljale binaartehtele vastav loogikatehe üksiku biti või bittidega.
Nüüd on lühidalt selgitatud kõik, mida läheb vaja üksikute bittide väärtuste muutmiseks. Kuid ilmselt jääb teooriast ikkagi väheks ja seepärast on järgnevalt toodud mõningad tüüpnäited registritega.
Selleks et üks või enam bitte registris kõrgeks ehk üheks seada, tuleb kasutada loogilist liitmise tehet. Liitmistehte üks operand peab olema register, teine binaararv, kus kõrge on ainult see bitt, mida ka registris soovitakse kõrgeks seada. Seda teist binaararvu nimetatakse ka bitimaskiks. Kõrvalnäites toodud tehe näeb C-keeles välja niimoodi:
// Oletame, et REG = 0x0F REG = REG | 0x11; // Üks meetod REG |= 0x11; // Teine meetod // Siinkohal REG = 0x1F
Ühe või enama biti registris madalaks ehk nulliks seadmiseks tuleb kasutada loogilise korrutamise tehet. Tehte üks operand peab olema register, teine bitimask, kus madalaks on seatud vaid see bitt, mida ka registris soovitakse madalaks seada. Kõrvalnäites toodud tehe näeb C-keeles välja nii:
// Oletame, et REG = 0x0F REG = REG & 0xFE; // Üks meetod REG &= 0xFE; // Teine meetod // Siinkohal REG = 0x0E
Ühe või enama biti registris inverteerimiseks tuleb kasutada mittesamaväärsuse tehet. Tehte üks operand peab olema register, teine bitimask, kus kõrgeks on seatud vaid see bitt, mida ka registris soovitakse inverteerida. Kõrvalnäites toodud tehe näeb C-keeles välja järgmiselt:
// Oletame, et REG = 0x0F REG = REG ^ 0x11; // Üks meetod REG ^= 0x11; // Teine meetod (rakendada tohib korraga ainult üht) // Siinkohal REG = 0x1E
Kogu registri bittide inverteerimiseks tuleb kasutada eitustehet. See on unaarne tehe ehk tal on ainult üks operand. Kõrvalnäites toodud tehe näeb C-keeles välja niimoodi:
// Oletame, et REG = 0x0F REG = ~REG; // Siinkohal REG = 0xF0
Ühe või enama biti väärtuse lugemiseks registrist tuleb kasutada sama tehet, mis biti nullimisel - loogilist korrutamist. Tehte üks operand peab olema register, teine bitimask, kus kõrgeks on seatud vaid see bitt, mille väärtust registrist lugeda soovitakse. Kõrvalnäites toodud tehe näeb C-keeles välja järgmiselt:
// Oletame, et REG = 0x0F unsigned char x = REG & 0x01; // Siinkohal x = 0x01
Tegelikult on paljudes programmeerimiskeeltes peale binaartehete veel mõned bitioperatsioonid, mis teevad programmeerija elu lihtsamaks. Need on bitinihutuse operatsioonid, mis binaararvus nihutavad bitte kas vasakule või paremale poole. Nihutusoperatsioonide põhiline väärtus seisneb registritega tegeledes nende võimes bitijärkusid bitimaskiks teisendada ja vastupidi.
Kõrvaloleval pildil on toodud näide bitinihutuse operatsioonist vasakule. Bitinihutus pole loogikaoperatsioon ja sel puudub vastav tähis, C-keeles on see aga “«”. Nihet vasakule kasutatakse bitijärgu bitimaskiks teisendamiseks. Näiteks, kui soovitakse kuuenda biti (NB! järk on 5) maski, siis tuleb arvu 1 nihutada vasakule 5 korda. Näites toodud operatsioon näeb C-keeles välja järgmiselt:
REG = 0x01 << 5; // Siinkohal REG = 0x20
Sarnaselt bitinihkega vasakule toimib ka bitinihke operatsioon paremale. Selle operatsiooni tähis C-keeles on “»”. Nihet paremale kasutatakse bitimaskist biti loogilise väärtuse leidmiseks. Eespool oli toodud näiteks üksiku biti väärtuse lugemise tehe. Oletame, et bitt, mida lugeda, pole aga madalaima järguga, vaid näiteks järguga 5. Sel juhul oleks lugemisel vastus kas 0x20 või 0x00, kuid vahel läheb vaja vastust 1 või 0 ja siis tulebki appi nihe paremale. Kõrvalnäites toodud operatsioon näeb C-keeles välja nii:
// Oletame, et REG väärtus on 0x20 unsigned char x = REG >> 5; // Siinkohal on x väärtus 0x01 (ehk lihtsalt 1)
Kui bitinihke operatsioonidega nihkub bitt madalaimast järgust paremale või kõrgeimast järgust vasakule, siis see bitt kaob. Mõnedes programmeerimiskeeltes on olemas ka roteeruvad bitinihke operatsioonid, kus “servast” välja minev bitt tuleb teiselt poolt tagasi. C-keeles roteeruvad bitinihke operatsioonid puuduvad, kuid vajadusel saab need ise kirjutada.
Kõik toodud bitioperatsioonide näited toimivad peale registrite ka muutujatega ja konstantidega. Viimased saavad muidugi ainult operandideks, mitte vastusteks olla.
Selleks, et midagi reaalselt mikrokontrolleri registritega teha saaks, tuleb osata selle mikrokontrolleriga läbi saada. Kõigi mikrokontrolleritega käib kaasas üks või mitu andmelehte, kus on dokumenteeritud kogu mikrokontrolleri struktuur ja funktsionaalsus. Andmelehes on kirjeldatud ka registrid. Järgnevalt uurime, kuidas saada aru AVR-i andmelehe registrite kirjeldusest.
Pildil on toodud ATmega128 mikrokontrolleri register UCSRnA, mille pikem nimetus on “USART Control and Status Register A”. See on register, millega sätitakse AVR-i USART moodulit ja kust saab lugeda selle mooduli olekuid. Kõik AVR-i registrite nimed kirjutatakse suurte tähtedega, kuid tähelepanelik lugeja ilmselt märkab, et selles registris on väike n-täht. Väikese n-tähega tähistatakse nimelt mõne mooduli indeksit. Kuna ATmega128-s on 2 üsna sarnast USART moodulit, siis ei kirjeldata nende registreid topelt, vaid ühe korra ja n-tähe asemele peab lugeja arvestama kas “0” või “1”. Seega ATmega128-s on registrid UCSR0A ja UCSR1A.
Registri sisu tähistab paksu piirjoonega 8 lahtriga kast. Iga lahter tähistab üht bitti. Kasti kohal on toodud biti järgud - suurenevad paremalt vasakule. Kuna AVR on 8-bitine mikrokontroller, on ka enamik registreid 8-bitised. Mõningad erandid on 16-bitised registrid, mis koosnevad tegelikult kahest 8-bitisest registrist. Lisaks registritele on nimi ka igal registri bitil - täpselt nagu kassetimängija nuppudelgi. Iga biti kohta on andmelehes olemas selle selgitus. Biti nimed on samuti lühendid ja n-täht neis tuleb samuti asendada mooduli indeksiga. Mõnes registris pole kõiki 8 bitti kasutatud ja sel juhul tähistatakse biti lahter sidekriipsuga.
Registri bittide all on toodud kaks rida, kus on kirjas, kas bitt on loetav (R), kirjutatav (W) või mõlemat (R/W). Näiteks olekubitte ei saa üle kirjutada ja isegi siis, kui seda programmis üritada, ei omanda bitt lihtsalt talle omistatavat väärtust. Biti puhul, mida saab ainult kirjutada, on öeldud üks kindel väärtus, mis selle lugemisel alati tuleb. Bittide all teises reas on toodud vaikeväärtus, mis on bitil pärast mikrokontrolleri käivitamist (inglise keeles reset).
Kui AVR-i registrite nimed viitavad tegelikult mälupesade aadressidele, siis biti nimede taga peitub selle biti järgu number. Seega registris bittidega manipuleerimiseks tuleb bitinimed nihutusoperatsiooni abil bitimaskiks teisendada. Järgnevalt on toodud mõned C-keele näitelaused eeltoodud USART 0 mooduli registri kasutamiseks.
// TXC0 biti kõrgeks seadmine UCSR0A |= (1 << TXC0); // U2X0 biti madalaks seadmine UCSR0A &= ~(1 << U2X0); // UDRE0 biti(maski) väärtuse lugemine unsigned char u = (UCSR0A & (1 << UDRE0)); // Siinkohal on u väärtus kas 0 või 32,
Atmeli xmega seeria kontrolleritel, mis on kasutusel Kodulabori III seeria plaatidel tuleb lisaks moodulile ära märkida ka port, mille moodulit kasutada soovitakse. Lisaks on defineeritud registri bittide maskid ning bittide positsioonid _bm(bit mask) ja _bp(bit position) lõppudega.
// TXEN biti kõrgeks seadmine USARTD0.CTRLB |= (USART_TXEN_bm); // CLK2X biti madalaks seadmine USARTD0.CTRLB &= ~(USART_CLK2X_bm); // DREIF biti(maski) väärtuse lugemine unsigned char u = (USARTD0.STATUS & (USART_DREIF_bm)); // Siinkohal on u väärtus kas 0 või 32, u >>= USART_DREIF_bp; // Siinkohal on u väärtus kas 0 või 1,