====== Riistvaraline viide ======
//Vajalikud teadmised: [HW] [[et:hardware:homelab:controller]], [AVR] [[et:avr:timers]], [LIB] [[et:software:homelab:library:pin]], [LIB] [[et:software:homelab:library:delay]], [LIB] [[et:software:homelab:library:timer]], [PRT] [[et:examples:timer:software_delay]]//
===== Teooria =====
Tarkvaraline viide pole ainus meetod pausi tekitamiseks. Sama asja saab teha ka taimeriga. Taimer on riistvaraline kindla sagedusega suurenev või vähenev loendur. Loenduri taktsignaali saab enamasti tekitada mikrokontrolleri töötaktist või mingist välisest taktist. Taktsignaali sagedust saab üldjuhul ka mingi teguriga jagada väiksem taktsageduse saavutamiseks - seda tehakse taktijaguriga, mida inglise keeles nimetatakse //prescaler//. Oluline on siinkohal siiski fakt, et fikseeritud taktsagedusega loenduri väärtus on lineaarses sõltuvuses ajast. Aja saab välja arvutada, korrutades loenduri taktisignaali perioodi loenduri väärtusega.
[{{ :examples:timer:timer_counter.png?300|AVR loenduri väärtuse muutusega kaasnevad sündmused}}]
AVR loendurit saab panna teavitama loenduri ületäitumisest (inglise keeles //overflow//) või kindla väärtuse saavutamisest (inglise keeles //compare match//). Ületäitumine tekib hetkel, kui loendur on omistanud maksimaalse võimaliku väärtuse ja alustab uuesti nullist loendamist. Kindla väärtuse saavutamise puhul aga toimub loenduri suurendamise hetkel selle uue väärtuse võrdlemine kasutaja poolt määratud väärtusega. Sündmuse tekkimise korral seatakse AVR olekuregistrites vastavad bitid automaatselt kõrgeks.
Selleks, et taimeriga viide tekitada, piisabki vaid loenduri seadistamisest ja olekubiti kõrgeks minemise ootamisest. Erinevalt tarkvaralisest viitest ei sõltu taimerite töö kompilaatorist, mis teeb nende kasutamise töökindlamaks. Samas võib AVR loendurite mitmekesisuse (või ka segasuse) tõttu nende seadistamine üsna tülikas tunduda. Olenevalt mikrokontrolleri taktsignaalist võib ka juhtuda, et see ei jagu täpselt soovitud viite perioodiga ja viide ei ole täpne.
===== Praktika =====
Allpool olev programmikood on taimeril põhinev viitefunktsioon, mida on natuke lihtsustatud. Loendamise põhimõte on sama, mis tarkvaralise viite funktsioonilgi - tekitatakse soovitud arv 1 ms pikkuseid viiteid. Viite tekitamiseks on kasutusel ATmega128 8-bitine loendur 0. Eelnevalt on juba välja arvutatud, et 14,7456 Mhz taktsageduse puhul peab loenduri taktsignaal olema vähemalt 64-ga jagatud, et 1 ms jooksul 8-bitine loendur üle ei täituks. See, mis väärtust loendur omama peab, et ületäitumine toimuks 1 ms järel, on esitatud avaldise kujul ja omistatud muutujale //timer_start//. //F_CPU// on makro-keele konstant, mis näitab taktsagedust hertsides. Nimetatud taktsageduse puhul peaks loenduri väärtus 25,6 olema, kuid kuna murdarve kasutada ei saa, siis loenduri algväärtuseks saab 26. Siin tekib paraku ka viga viite ajas, kuid see on üsna väike (-1,7 μs).
Tsüklis toimub loenduri algväärtustamine ja ületäitumise lipukese nullimine (sellesse 1 kirjutades). Seejärel oodatakse, kuni loendur loendab algväärtusest 256-ni, ehk ületäitumiseni. Ületäitumise hetkel läheb ületäitumise lipuke kõrgeks ja 1 ms viide ongi toimunud. Funktsiooni lõpus taimer peatatakse.
//
// Riistvaraline viide millisekundites Atmega kontrolleril
//
void hw_delay_ms(unsigned short count)
{
// Taimeri algväärtuse arvutamine
register unsigned char timer_start = 256 - F_CPU / 1000 / 64;
// Taimeri käivitamine
timer0_init_normal(TIMER0_PRESCALE_64);
// Viite muutuja nullini loendamine
while (count-- > 0)
{
// Taimeri algväärtustamine
timer0_set_value(timer_start);
// Ületäitumise lipukese nullimine
timer0_overflow_flag_clear();
// Ületäitumise ootamine
while (!timer0_overflow_flag_is_set())
{
asm volatile ("nop");
}
}
// Ületäitumise lipukese nullimine
timer0_overflow_flag_clear();
// Taimeri peatamine
timer0_stop();
}
Esitatud viite funktsioon kasutab aga taimerite teeki, mille lähtekood Atmega kontrollerile näeb välja järgmine:
//
// Taimer 0 taktijaguri valiku tüüp
//
typedef enum
{
TIMER0_NO_PRESCALE = 0x01,
TIMER0_PRESCALE_8 = 0x02,
TIMER0_PRESCALE_32 = 0x03,
TIMER0_PRESCALE_64 = 0x04,
TIMER0_PRESCALE_128 = 0x05,
TIMER0_PRESCALE_256 = 0x06,
TIMER0_PRESCALE_1024 = 0x07
}
timer0_prescale;
//
// Taimer 0 normaalrežiimi seadistamine
//
inline void timer0_init_normal(timer0_prescale prescale)
{
TCCR0 = prescale & 0x07;
}
//
// Taimer 0 peatamine
//
inline void timer0_stop()
{
TCCR0 = 0x00;
}
//
// Taimer 0 loenduri väärtuse määramine
//
inline void timer0_set_value(unsigned char value)
{
TCNT0 = value;
}
//
// Taimer 0 ületäitumise lipukese nullimine
//
inline void timer0_overflow_flag_clear(void)
{
bit_set(TIFR, TOV0);
}
//
// Taimer 0 ületäitumise lipukese oleku lugemine
//
inline bool timer0_overflow_flag_is_set(void)
{
return (bit_is_set(TIFR, TOV0) ? true : false);
}
Järgnevalt on toodud samasugune programm nagu tarkvaralise viite näiteski. Lühemal 100 ms poolperioodil LED süüdatakse, pikemal 900 ms poolperioodil kustutatakse. Tulemusena vilgatab LED iga sekundi järel. Paraku pole ka selles näites periood täpselt 1 sekund, sest programmi muude funktsioonide täitmine igas tsüklis võtab samuti aega. Täpseks ajastuseks tuleb kasutada 16-bitist taimerit koos katkestustega.
//
// Kodulabori riistvaralise viite demonstratsioonprogramm.
// Programm vilgutab ~1 sekundi järel hetkeks LED-i.
//
#include
#include
//
// Test LED viigu määramine
//
pin debug_led = PIN(B, 7);
//
// Põhiprogramm
//
int main(void)
{
// LED-i viigu väljundiks seadmine
pin_setup_output(debug_led);
// Lõputu tsükkel
while (true)
{
// LED-i põlema panek
pin_clear(debug_led);
// Riistvaraline paus 100 millisekundit
hw_delay_ms(100);
// LED-i kustutamine
pin_set(debug_led);
// Riistvaraline paus 900 millisekundit
hw_delay_ms(900);
}
}