AVR C++ Lib põhimõtted
Eeldused juhendist arusaamiseks
- C++'s koodi kirjutamise kogemus.
- Algteadmised riistvaralähedasest programmeerimisest.
Hierarhilisus
Kogu AVR C++ Lib kood on jagatud namespace'deks. Võrdlemisi suurel hulgal namespace'sid on vajalik nimekonfliktide ärahoidmiseks ja tarkvarateegile üldise loogilise struktuuri andmiseks.
UML diagramm AVR C++ Lib ülesehitusest:
- AVRCpp - baasklassid ja -funktsioonid
- AVRCpp::AnalogToDigital - analog/digital muundur
- AVRCpp::Assembler - masinakoodi lisamiseks
- AVRCpp::Collection - klassid erinevat tüüpi massiividele, järjekordadele ja stringidele
- AVRCpp::EEPROM - EEPROM mälu kasutamiseks
- AVRCpp::ExternalInterrupt - välised katkestusted
- AVRCpp::GlobalInterrupts - globaalselt katkestuste lubamine ja keelamine
- AVRCpp::Sleeping - toite kasutuse seaded
- AVRCpp::Timer - taimer-loendurid
- AVRCpp::USART - USART järjestikpordiga andmeside pidamine
- CppDelegate C++ delegaadid (.NET Framework näitel), peamiselt mõeldud katkestuste alamprogrammide väljakutsumiseks
- std - delete ja new operaatorite tugi
Selleks, et ligi pääseda näiteks järjestikport USART0'le on mitmeid võimalusi:
void UsingUSARTInHere() { using namespace AVRCpp::USART; USART0::EnableReceiver(); }
… või:
using namespace AVRCpp; void UsingUSARTInHere() { using namespace USART; USART0::EnableReceiver(); }
… või:
using namespace AVRCpp::USART; void UsingUSARTInHere() { USART0::EnableReceiver(); }
… või:
void UsingUSARTInHere() { AVRCpp::USART::USART0::EnableReceiver(); }
jne.
Kasutades WinAVR C++ Extension Project Wizard't on vaikimisi kasutuses järgmised osad:
using namespace AVRCpp; using namespace AVRCpp::Collection; using namespace CppDelegate;
Pea igasse namespace'i kuulub namespace „Internal“, mis sisaldab vaid teegi sisemisi osi ja ei ole mõeldud lõppkasutajale.
Bitimaskid
Bitimaskid on AVR C++ Lib's defineeritud makrodena, mille nimed on läbivalt suurtähtedega ja algavad ühe alakriipsuga. avr-libc's on defineeritud bittide numbrid ja need on ilma alakriipsuta:
// koodi fragment avr-libc'st #define OCF1B 3
… aga AVR C++ Lib's on lisaks bitinumbrile defineeritud ka bitimask:
#define _OCF1B 0x08
_OCF1B on _BV(OCF1B) lühem kirjaviis ehk _OCF1B võrdub (1 « OCF1B). Alakriipsu juurdelisamise või ärajätmise tagajärjeks võib olla vigaselt töötav programm!
Registrid
Registrid on AVR C++ Lib's tüüpidena. Registri nimi on läbinisti suurtähtedega ja algab alakriipsuga.
Iga register on defineeritud järgnevalt (põhimõtteline lihtsustatud kood 8-bitise registri TCNT0 näitel):
class _TCNT0 { protected: static inline reg8_t GetRegister() { return TCNT0; } public: typedef uint8_t Type; typedef reg8_t RegType; static inline reg8_t Get() { return GetRegister(); } static inline void Set(uint8_t flags) { GetRegister() = flags; } }; // class _TCNT0
Registri nimi suurtähtedega on defineeritud avr-libc's ja on keelelises mõttes kasutuses kui muutuja.
Baasfunktsioonid
Kogu teek on üles ehitatud neile põhifunktsioonidele:
template <class Register> inline void SetBits(typename Register::Type flags); template <class Register> inline void ClearBits(typename Register::Type flags); template <class Register> inline void SetBitsTo(typename Register::Type flags, bool value); template <class Register> inline void ToggleBits(typename Register::Type flags); template <class Register> inline void ChangeBits ( typename Register::Type selection, typename Register::Type value ); template <class Register> inline typename Register::Type SelectBits ( typename Register::Type selection ); template <class Register> inline bool IsAnyBitSet(typename Register::Type flags); template <class Register> inline bool IsBitsSet(typename Register::Type flags);
Näited
Kahe biti määramine üheks:
// avr-libc variant PORTB |= _BV(PB0) | _BV(PB5); // ... on samaväärne kui: SetBits<_PORTB>(_PB0 | _PB5);
Ühe biti nullimine:
// avr-libc variant PINC &= ~_BV(PC2); // ... identne AVR C++ Lib'i lahendus on: ClearBits<_PINC>(_PC2);
Kolme biti oleku vastupidiseks pööramine:
// avr-libc variant TIMSK1 ^= _BV(ICIE1) | _BV(OCIE1B) | _BV(OCIE1A); // ... on ekvivalentne järgnevaga: ToggleBits<_TIMSK1>(_ICIE1 | _OCIE1B | _OCIE1A);
Veel erinevaid variante:
// Muudab valitud bitid (ISC11 ja _ISC10) määratud väärtusele - // antud juhul üks bitt kõrgele teine madalale. ChangeBits<_EICRA>(_ISC11 | _ISC10, 0x08); // Biti _PCINT7 väärtus seada vastavalt muutujale 'value' // (antud juhul üheks) bool value = true; SetBitsTo<_PCMSK0>(_PCINT7, value); // Kas kõik TWAR registri bitid on ühed? if (IsBitsSet<_TWAR>(0xFF) ); // Kas vähemalt üks nimetatud kahest TWAR registri bitist on üks? if (IsAnyBitSet<_TWAR>(_TWA6 | _TWA2) );
Üldjuhul kasutajakoodis neid baasfunktsioone ei kasutata. Nende tundmine on ülivajalik, et AVR C++ Lib'i lähtekoodist aru saada.
Klasside ümbernimetamine
Põhiline võte kasutajakoodis on riistvaraliste osade ümbernimetamine. Sellega tagab universaalse koodi, mis on võimeline töötama ka teise AVR C++ Lib'i poolt toetatud mikrokontrolleri peal nii, et see ei vaja koodi põhiosas ühtegi muudatust.
Iga riistvaraline osa on klass. Klassi ümbernimetamiseks kasutame C++'i võtmesõna typedef:
using namespace AVRCpp; using namespace AVRCpp::ExternalInterrupt; typedef Interrupt0 MyInterrupt; typedef MyInterrupt::Pin MyInterruptPin; typedef OutputPin2<PortC> LED; int main() { // Sisend ja väljund algväärtustada MyInterruptPin::InitInput(); LED::InitOutput(); // Katkestus antakse, kui MyInterruptPin sisendis on langev front MyInterrupt::SetEvent(Fall); // Katkestus sisse lülitada MyInterrupt::Enable(); while (true) { // Kas on olnud langevat fronti? if (MyInterrupt::IsTriggered() ) { // LED'i väljund vastupidiseks LED::Toggle(); // Katkestuse bitt nullida MyInterrupt::ClearFlag(); } } return 0; }
Ilma ümbernimetamiseta käiks see nii (ei soovita taolist koodi kasutada!):
using namespace AVRCpp; using namespace AVRCpp::ExternalInterrupt; int main() { // Sisend ja väljund algväärtustada Interrupt0::Pin::InitInput(); OutputPin2<PortC>::InitOutput(); // Katkestus antakse, kui MyInterruptPin sisendis on langev front Interrupt0::SetEvent(Fall); // Katkestus sisse lülitada Interrupt0::Enable(); while (true) { // Kas on olnud langevat fronti? if (Interrupt0::IsTriggered() ) { // LED'i väljund vastupidiseks OutputPin2<PortC>::Toggle(); // Katkestuse bitt nullida Interrupt0::ClearFlag(); } } return 0; }
Esimese näite puhul tuleks välise katkestuse väljaviigu vahetamise korral muuta üks rida koodi, teisel juhul on aga vaja teha seitse muudatust!
Ümber võib nimetada peaegu ükskõik millist riistvaralist ühikut: ühte registrit, ühte bitti, ühte sisend/väljund porti, ühte väljaviiku, mitut väljaviiku, taimerit, ühte katkestust jne.
Soovitav on alati projektile lisada Config.h fail (Project Wizard lisab selle automaatselt). Sellesse faili kirjutada ainult typedef lauseid, mis kirjeldavad ära riistvaralise seadistuse. Hästi kirjutatud programmi korral ainult Config.h fail vajab muudatusi, kui teha on vaja mõni riistvaraline muudatus (näiteks: anduri ühendamine teiste väljaviikude külge, teise mikrokontrolleri peale tööle panemine või teise taimeri kasutusele võtmine).