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:
namespaces

  • 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).