USART - järjestikühenduse liides

USART'i (Universal Synchronous/Asynchronous Receiver Transmitter) kasutatakse mikrokontrollerite omavaheliseks andmesideks ja ühenduseks muude väliste seadmetega (näiteks arvutiga). Side võib olla sünkroonne (USRT - Universal Synchronous Receiver Transmitter) või asünkroonne (UART - Universal Asynchronous Receiver Transmitter). Kogu USART'i funktsionaalsus on kogutud namespace'i AVRCpp::USART, mis asub päisefailis USART.h“. Mõnel mikrokontrolleril on mitu USART porti - nendele vastavad staatiliste liikmetega klassid on nimetatud USART0, USART1, USART2 jne. Juhul, kui seadmel on üks USART'i port, siis on see seotud klassiga USART0.

Ühenduse ülesseadmine

Seaded

  • Paarsusbitt - paarsuskontroll tegemaks kindlaks, kas andmed on jõudnud kohale rikkumata kujul. Soovitav on alati paarsusbitti kasutada. Paarsusbitt ei taga, et viga alati kindlaks tehakse: töötab alati vaid juhul, kui vaid üks bitt kaadrist võetakse vastu valesti.
    enum   ParityCheck
    {
      // Paarsusbitt kaadris puudub.
      NoParityCheck,
      // Paarsusbitt on kaadris olemas. Väärtusega üks, kui andmebittide summa
      // annab tulemuseks paaris arvu.
      EvenParity,
      // Paarsusbitt on kaadris olemas. Väärtusega üks, kui andmebittide summa
      // annab tulemuseks paaritu arvu.
      OddParity
    };
  • Stopbitt - määrab ära lõpubiti pikkuse. Võib olla saatjal ja vastuvõtjal erinevalt seadistatud. Sümbolite edastuskiirus väheneb, kui valida DoubledStopBit, aga see tagab usaldusväärsema andmeühenduse.
    enum   StopBit
    {
       // Kaadris on ühekordne lõpubitt
       NormalStopBit,
       // Kaadris on kahekordne lõpubitt
       DoubledStopBit
    };
  • Sümboli pikkus - Selleks, et ühe kaadriga terve bait üle kanda (binary mode), peab sümboli pikkus olema 8 bitti. ASCII teksti ülekandmiseks (ASCII mode) on mõistlik kasutada 7-bitilist andmeosa. Üheksabitine andmeosa on mõeldud esmajoones mitme mikrokontrolleri ühendamisks ühe liini peale (andmesiin - data bus).
    enum   CharacterSize
    {
      // Kaadri andmeosa on 5 bitti.
      CharacterSize5,
      // Kaadri andmeosa on 6 bitti.
      CharacterSize6,
      // Kaadri andmeosa on 7 bitti.
      CharacterSize7,
      // Kaadri andmeosa on 8 bitti.
      CharacterSize8,
      // Kaadri andmeosa on 9 bitti.
      CharacterSize9
    };
  • Sünkronisatsiooni front - Määrab ära, kas biti väärtus loetakse sisse sünkronisatsioonisignaali (clock) tõusval või langeval küljel.
    enum   SynchroEdge
    {
      // Vastuvõtt tagafrondil
      ReceiveOnFall,
      // Vastuvõtt esifrondil
      ReceiveOnRise
    };
  • Andmesiin või lihtne andmeliin - andmesiini kasutatakse juhul, kui ühe ja sama liini peale on ühendatud mitu kontrollerit. Lihtsa andmeliini korral ühendusel on alati täpselt kaks poolt.
    enum   CommunicationMode
    {
      // Lihtne andmeliin
      SingleProcessor,
      // Andmesiin
      MultiProcessor
    };
  • Andmeliin kasutuses või mitte - saab määrata kommunikatsiooni suunda: simpleks ehk andmeliiklus käib vaid ühtepidi (üks pool saadab ja teine võtab vastu) või täis/pool-dupleks ehk andmeliiklus on mõlemasuunaline. Andmeliini väljalülitamisel saab kasutada vastavat väljaviiku alternatiivseteks tegevuseks (näiteks teha tavaliseks digitaalseks sisendiks).
    • Saatjaliin
      enum   Transmitter
      {
        // Saatjaliin on kasutuses
        TransmitterEnable,
        // Saatjaliini väljaviik on muuks otstarbeks
        TransmitterDisable
      };
    • Vastuvõtuliin
      enum   Receiver
      {
        // Vastuvõtuliin on kasutuses
        ReceiverEnable,
        // Vastuvõtuliini väljaviik on muuks otstarbeks
        ReceiverDisable
      };
  • Andmeedastuskiirus (baud rate) - bitikiirus (bitti sekundis), kusjuures sümboli edastuse kiirus = andmeedastuskiirus / bittide arv kaadris. Andmeedastuskiiruste seadmseks on mõistlik kasutada ühte järgnevatest makrodest (failist USART.h“):
    // Asünkroonse ühekordse kiirusega andmeühenduse korral
    #define AsyncNormBaudCalc(BaudRate)    F_CPU / 16 / BaudRate - 1
    // Asünkroonse kahekordse kiirusega andmeühenduse korral
    #define AsyncDblBaudCalc(BaudRate)     F_CPU / 8 / BaudRate - 1
    // Sünkroonse andmeühenduse korral halduri poolel
    #define SyncMasterBaudCalc(BaudRate)   F_CPU / 2 / BaudRate - 1

    Järgnevates koodinäidetes on ära näidatud, millal ja kus neid makrosid kasutatakse.

Asünkroonne ühendus

Asünkroonsel ühendusel on kasutuses väljaviigud TXn ja RXn. Mõlemad ühenduse pooled peavad olema ühesuguselt seadistatud.

Klassi USARTn'i staatiline funktsioon asünkroonse ühenduse seadistamiseks on:

void  SetupAsynchronous (
  uint16_t baudRate,
  Receiver,
  Transmitter,
  ParityCheck,
  StopBit,
  CharacterSize,
  Speed,
  CommunicationMode
);

Näide:

#include "USART.h"
 
using namespace USART;
 
typedef USART0 MyUART;
 
void Initialize()
{
    MyUART::SetupAsynchronous (
        AsyncDblBaudCalc(9600), /* baudRate */
        ReceiverEnable,        /* Receiver */
        TransmitterEnable,     /* Transmitter */
        EvenParity,            /* ParitiCheck */
        NormalStopBit,         /* StopBit */
        CharacterSize8,        /* CharacterSize */
        DoubleSpeed,           /* Speed */
        SingleProcessor        /* CommunicationMode */
    );
}

Kui parameeter speed on määratud väärtusele NormalSpeed, siis tuleb andmeedastuskiiruse parameetri edastamisel kasutada makrot AsyncNormBaudCalc, vastasel korral AsyncDblBaudCalc.

Sünkroonne ühendus

Sünkroonse ühenduse korral on üheks pooleks haldur (master) ja teiseks pooleks alluv (slave). Alluvaid võib olla seejuures mitu, haldureid aga alati üks. Sünkroonne ühendus kasutab kolme liini: TXn, RXn ja XCKn.

Halduri seadistamine

SetupMasterSync (
  uint16_t  baudRate,
  Receiver,
  Transmitter,
  ParityCheck,
  StopBit,
  CharacterSize,
  SynchroEdge,
  CommunicationMode
);

Näide:

#include "USART.h"
 
using namespace USART;
 
typedef USART0 MyMasterUSRT;
 
void Initialize()
{
    MyMasterUSRT::SetupMasterSync (
        SyncMasterBaudCalc(9600) /* baudRate */
        ReceiverEnable,          /* Receiver */
        TransmitterEnable,       /* Transmitter */
        EvenParity,              /* ParitiCheck */
        NormalStopBit,           /* StopBit */
        CharacterSize8,          /* CharacterSize */
        ReceiveOnFall,           /* SynchroEdge */
        SingleProcessor          /* CommunicationMode */
    );
}

Alluva seadistamine

Klassi USARTn'i staatiline funktsioon alluva seadistamiseks on:

void SetupSlaveSync (
    Receiver,
    Transmitter,
    ParityCheck,
    StopBit,
    CharacterSize,
    SynchroEdge,
    CommunicationMode
);

Näide:

#include "USART.h"
 
using namespace USART;
 
typedef USART0 MySlaveUSRT;
 
void Initialize()
{
    MySlaveUSRT::SetupSlaveSync (
        ReceiverEnable,    /* Receiver */
        TransmitterEnable, /* Transmitter */
        EvenParity,        /* ParitiCheck */
        NormalStopBit,     /* StopBit */
        CharacterSize8,    /* CharacterSize */
        ReceiveOnFall,     /* SynchroEdge */
        SingleProcessor    /* CommunicationMode */
    );
}

Andmeside pidamine

Saatmisfunktsioonid

static void  Write (uint8_t data)
static void  Write (uint8_t data, bool ninth)
static void  WriteNinthSet (uint8_t data)
static void  WriteNinthCleared (uint8_t data)

Kõik need funktsioonid täidavad riistvaralise saatmispuhvri baidi. Kui see bait on juba eelnevalt täidetud, siis programmijärg on funktsioonis kinni senikaua, kuni eelmine bait on ära saadetud.

Teksti saatmiseks on mõistlik kasutada:

void  SendText (char *text, uint16_t size);

SendText saadab parameetriga size määratud arv baite sõltumata parameetriga text viidatud nullsümboliga lõppeva sõne pikkusest. „Puudu jäävad baidid“ täidetakse nullsümbolitega. Sobib ideaalselt konstantse pikkusega pakettide saatmiseks.

Universaalsed funktsioonid:

template <typename Type> void  Send (Type &data);
template <typename Type> void  SendArray (Type *data, uint16_t size);

Funktsioonid Send ja SendArray pöörduvad tagasi alles siis, kui kogu andmehulk on saadetud.

Näide:

#include "USART."
 
using namespace AVRCpp::USART;
 
typedef USART0 MyUSART;
 
struct Packet
{
    uint8_t version : 2;
    uint8_t function : 2;
    uint8_t length : 4;
    uint16_t address;
    int32_t data[16];
 
}; // struct Packet
 
void InitializeUSART();
void FillBuffer(uint8_t *buffer);
 
int main()
{
    Packet packet;
    uint8_t additionalData[32];
 
    InitializeUSART();
 
    packet.version = 1;
    packet.function = 0;
    packet.length = 0;
    packet.address = 43;
    packet.data[0] = 0;
    FillBuffer(additionalData);
 
    MyUSART::Send(packet);
    MyUSART::SendArray(additionalData, 32);
 
    /* ... */
 
} // main

Vastuvõtufunktsioonid

bool  Read (uint8_t &data);
bool  Read (uint8_t &data, bool &ninth);
bool  SimpleRead (uint8_t &data);
bool  SimpleRead (uint8_t &data, bool &ninth);
ReadResult  DetailedRead (uint8_t &data);
ReadResult  DetailedRead (uint8_t &data, bool &ninth);
ReadResult  DetailedSimpleRead (uint8_t &data);
ReadResult  DetailedSimpleRead (uint8_t &data, bool &ninth);
  • Eesliide Simple tähistab kõige optimaalsemat võimalikku lugemist - tarkvaraline lugemise katkestamine ei ole võimaldatud, kuid see-eest programmi maht on väikseim võimalik.
  • Eesliide Detailed tähistab võimalust funktsiooni tagastatava väärtuse abil teada saada lugemise õnnestumise kohta üksikasjalikku infot, kusjuures ReadResult on defineeritud:
    enum   ReadResult
    {
      // Vastuvõtt oli edukas
      Success,
      // Tarkvaraliselt katkestatud
      Canceled,
      // Vähemalt üks kaader jäi vahele - osa sümboleid jäi õigel ajal
      // tarkvara poolt välja lugemata
      DataOverRun,
      // Vastuvõetud kaadri formaat ei vasta seadistustele või on tugevasti moondunud
      FrameError,
      // Paarsuskontroll andis negatiivse tulemuse
      ParityError
    };
  • Ilma eeslii(de)teta funktsioon Read võimaldab tarkvaralist vastuvõtu katkestamist ja tagastatava väärtuse põhjal saab programmis kindlaks teha, kas vastuvõtt õnnestus (true) või mitte (false).

Universaalsed funktsioonid:

template <typename Type> bool  Receive (Type &data);
template <typename Type> bool  ReceiveArray (Type *data, uint16_t size);
template <typename Type> ReadResult  DetailedReceive (Type &data);
template <typename Type> ReadResult  DetailedReceiveArray (Type *data, uint16_t size);

Näide:

#include "USART."
 
using namespace AVRCpp::USART;
 
typedef USART0 MyUSART;
 
struct Packet
{
    uint8_t version : 2;
    uint8_t function : 2;
    uint8_t length : 4;
    uint16_t address;
    int32_t data[16];
 
}; // struct Packet
 
void InitializeUSART();
 
int main()
{
    Packet packet;
    uint8_t additionalData[32];
 
    InitializeUSART();
 
    if (MyUSART::Receive(packet) )
    {
        if (packet.version == 1)
            switch (packet.function)
            {
            case 0:
                if (MyUSART::DetailedReceiveArray(additionalData, 32) == Success)
                {
                    /* ... */
                }
                else
                {
                    /* FAILED ! */
                }
                break;
 
            case 1:
 
            /* ... */
 
            }
    }
    else
    {
        /* FAILED ! */
    }
 
 
} // main

Vastuvõtu katkestamine

Vastuvõtu katkestamist kasutatakse juhul, kui Read või Receive funktsioon on vaja blokeerivast olekust välja tuua ilma, et peaks teiselt seadmelt andmeid saabuma. Tüüpiliseks juhtumiks on maksimaalse ooteaja (time out period) kehtestamine: seadmele, kellega suhtlus käib, antakse teatud aeg, mille vältel on tal võimalik vastus saata. Kui vastus ei jõua määratud aja vältel kohale, siis taimeri katkestuse alamprogramm katkestab lugemise ja Read funktsioon pöördub tagasi väärtusega false või Cancelled. Ehk siis C++'s:

#include "USART.h"
#include "Interrupt.h"
 
using namespace AVRCpp;
using namespace USART;
 
typedef USART0 MyUSART;
 
#define PLEASE_SEND_SOMETHING 0
#define THANK_YOU 1
#define TOO_LATE 2
 
INTERRUPT_HANDLER(TIMER0_OVF)
{
    // Maksimaalne ooteaeg on läbi
    MyUSART::CancelReading();
}
 
void InitializeMyUSART();
void InitializeTimer();
void ActivateTimerOverflowInterrupt();
 
int main()
{
    uint8_t data = 0;
 
    InitializeMyUSART();
 
    MyUSART::Write(PLEASE_SEND_SOMETHING);
 
    InitializeTimer();
    ActivateTimerOverflowInterrupt();
 
    GlobalInterrupts::Enable();
 
    if (Read(data) )
    {
       // Korras, soovitav bait sai sisseloetud
       MyUSART::Write(THANK_YOU);
    }
    else
    {
       // Ühendus on katkenud või teine seade jäi saatmisega liiga hiljaks
       // või vastuvõtmisel esines viga
       MyUSART::Write(TOO_LATE);
    }
 
    return 0;
}

USART'i kasutamine siinina

USART'i liidesega Atmeli mikrokontrolleritele on riistvaraliselt sisse ehitatud võimalus kasutada seda porti siinina. Lihtsaim viis on toimida järgnevalt:

  1. Luua sünkroonne võrk ühe halduri ja mitme alluvaga
  2. Kasutada üheksabitist sümbolit ühes kaadris, kusjuures üheksas bitt tähistab paketi algust.
  3. Kasutada mitmeprotsessorilist režiimi (CommunicationMode = Multiproccessor)
  4. Paketi esimese baidi vastuvõtul (üheksas bitt on loogiline 1):
    MyUSART::EnterSingleProcessorMode(); 
  5. Esimesel võimalusel, kui on kindlaks tehtud, et addressaat on keegi teine, või kui pakett on lõpuni loetud:
    MyUSART::EnterMultiProcessorMode();