This is an old revision of the document!


Viite tekitamine

Vajalikud teadmised: [HW] controller, [AVR] AVR arhitektuur, [AVR] AVR taimerid, [LIB] Viikude teek, [LIB] Viite teek

Teooria

Tihti on mikrokontrollerite programmis vaja tekitada viiteid - näiteks mingi tegevuse ajastamiseks või tegevuse lõpu ootamiseks. Kontroller on aga enamasti palju “kiirem” kui nii mõnigi tegevus ja seepärast on vaja kontrollerit kuidagi “aeglustada”. Selleks on mitmeid võimalusi.

Tarkvaraline viide

Üks idee poolest lihtsamaid meetodeid kontrolleri tegevuses paus tekitada on see mingi muu tegevusega üle koormata - näiteks panna see lugema suuri numbreid. Kontrolleri taktsagedusest saab välja arvutata mitmeni see numbreid loendama peaks, et kindlat ajalist viidet tekitada. Teoreetiliselt tekitaks siis mingi arvu (muutuja) loendamine nullist kontrolleri taktsageduse väärtuseni viite üks sekund. Praktikas see aga nii lihtne ei ole ning põhjuseid on mitmed:

Kui kontroller arvutab arvudega mis on sama suure ühikuga kui selle sisemine siin (AVR puhul 8-bitti) siis enamustel kontrolleritel võtab üks arvutus, näiteks liitmine ühega, aega 1 kontrolleri töötakt. Selleks, et arvutada tuhandete või miljonitega peab arvu muutuja olema 16- või 32-bitine ja nende arvutamiseks kulub 8-bitistel kontrolleril rohkem kui 1 töötakt. Niisiis, suurte arvude puhul peab tundma kontrolleri sisemust - täpsemalt selle käsustikku.

Kuna kõrgtaseme keeles (C) programmeerides ei kirjutata programm otse käsustiku baasil peab tarkvaras viite tekitamisel tundma ka kompilaatorit, mis programmi masinkoodi teisendab. Just sellest sõltub mitu instruktsiooni (ja sellest tulenevalt takti) kulub millegi arvutamiseks. Keerukust lisab veel asjaolu, et kompilaator võib programmi masinkoodi teisendada mitut moodi - näiteks tehes masinkoodi võimalikult lühidaks või võimalikult kiiresti täidetavaks. Neid valikuid nimetatakse optimeerimiseks. Erinevate optimeerise režiimidega tuleb ka tarkvaralise viite masinkood ja selle ajaline kestvus erinev.

Lihtsustatud näide

Järgnevalt on toodud näide tarkvaralise viite tekitamisest AVR mikrokontrolleriga. Kirjutatud on C keele programmilõik mis loendab for tsüklis muutujat x nullist sajani. Iga tsükli sees toimub ühe mitte-midagi tegeva tühi-instruktsiooni täitmine. Seda on seal vaja, kuna tsükli sisu tühjaks jättes kompilaator optimeerib tsükli programmist üldse välja kuna see on tema arvates kasutu.

unsigned char x;
 
// Tsükkel seni kuni x on 100
for (x = 0; x < 100; x++)
{
	// Tühi-instruktsiooniga nop
	asm volatile ("nop");
}

Siinkohal on aga toodud sama C-keele programmilõigu esitus masinkoodis (vasakpoolsed 2 heksadetsimaalarvu) ja assembler-keeles (sõna koos operandidega). Masinkood ja assembler-keel on üks-üheselt seotud, teine neist on lihtsalt inimesele loetaval kujul. Kompileerimisel on kasutatud programmi pikkuse optimeerimist (-Os).

80 e0       	ldi	r24, 0x00	; r24 registrisse arvu 0 laadimine
00 00       	nop			; Tühioperatsioon
8f 5f       	subi	r24, 0xFF	; r24 registrist 255 lahutamine, ehk siis +1 liitmine
84 36       	cpi	r24, 0x64	; r24 registri võrdlemine arvuga 100
e1 f7       	brne	.-8      	; kui võrdlus polnud tõene siis siire 8 baiti tagasi

Kompileeritud kujul on näha mis täpselt C-keele tsüklist saab ja selle järgi saab arvutada mitu instruktsiooni ühe tsükli perioodi puhul täidetakse. Antud näites kulub ühes tsükli perioodis 4 instruktsiooni täitmiseks 4 takti. Oletades, et kontrolleri töötakt on 14,7456MHz võib välja arvutada kogu tsükli tekitatud ajalise viite:

100 * 4 / 14745600 = 27.13us

====

Näites tekitatud viide on mikrosekundites ja muutujat mida kasutatakse on 8-bitine, seega on ka masinkood üsna lihtne. Selleks, et tekitada pausi millisekundites on vaja palju suuremaid arve loendada ja siis läheb masinkood pikemaks. Võib kasutada ka üksteise sees töötavaid tsükleid kuid selle meetodi puhul pole kogu viide lineaarses sõltuvuses tsüklite arvust, sest iga tsükli tasemega tekivad pisikesed lisaviited.

Siiski, käesoleva harjutuse eesmärk ei ole ise täpset tarkvaralist viidet tekitada, sest see on üsna keeruline ja pealegi on selleks avr-libc ja kodulabori teegis juba funktsioonid olemas. Need tulevad ka näites kasutusele.

Tarkvaralise viite puhul on aga oluline teada, et hoolimata oma põhimõttelisest lihtsusest on see äärmiselt eba-efektiivne meetod energiatarbe seisukohast. Kõigil neil taktidel mil mikrokontroller tegeleb kasutu loendamisega kulub energiat. Patareidega rakenduses ei ole seega soovitatav pikkasid tarkvaralisi viiteid teha, vaid kasutada taimereid mis töötavad iseseisvalt ning äratavad protsessor uneolekust üles kui aeg on täis.

Viide taimeriga

Taimer on riistvaraline kindla sagedusega suurenev või vähenev loendur. Loenduri taktsignaali saab tekitada mikrokontrolleri töötaktist või seda läbi jagades. Seega fikseeritud taktsignaaliga loenduri väärtus on lineaarses sõltuvuses ajast. Aja saab välja arvutada korrutades loenduri taktisignaali perioodi loenduri väärtusega.

Loenduri töö

AVR loendurid saab panna loenduri ületäitumisest (inglise keeles overflow) või kindla väärtuse saavutamisest (inglise keeles compare match) teavitama. Ületäitumine tekib hetkel kui loendur on omistanud maksimaalse võimaliku väärtuse ja alustab uuesti nullist loendamist. Väärtuse saavutamise kontroll toimub loenduri suurendamise hetkel selle uut väärtust kasutaja poolt määratud väärtusega võrreldes. Mõlema sündmuse tekkimist kujutab kõrvalolev graafik. Sündmuse tekkimise korral seatakse AVR olekuregistrites vastavad bitid automaatselt kõrgeks.

Kui nüüd loendur õigesti ära seadistada, siis piisabki vaid olekubiti kõrgeks minemise ootamisest, et soovitud viide saada. Erinevalt tarkvaralisest viitest ei sõltu taimerite töö kompilaatorist, mis teeb nende kasutamise töökindlamaks. Samas, AVR-i loendurite mitmekesisuse (või ka segasuse) tõttu võib nende seadistamine esimestel kordadel ü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.

AVR loendurite tööpõhimõttega lähemalt tutvumiseks on soovitatav lugeda vastavat peatükki AVR tutvustuses.

Mõlemat liigi viite puhul peaks

Praktika

Järgnevas praktilises näites on kasutatud nii tarkvaralist kui riistvaralist viite tekitamise meetodit. Mõlema meetodi jaoks on kodulabori teeki loodud vastavad funktsioonid, mis tähendab, et ise neid kirjutama ei pea kuid kasulik on teada nende tööpõhimõtet.

Järgnev programmikood käib tarkvaralise viite funktsiooni sw_delay_ms kohta mis tekitab parameetriga count etteantud viite millisekundites. Funktsioon kasutab omakorda avr-libc teegi poolenisti assembler keeles kirjutatud funktsiooni _delay_ms. Üks põhjus miks harjutuses pole kohe _delay_ms kasutatud on selles, et _delay_ms pole tegelikult alamprogramm (funktsioon) vaid programmilõik mis kirjutatakse sisse igasse kohta kus seda välja kutsutakse ja sedasi läheb programmimälu raisku. Teine põhjus on selles, et _delay_ms puhul on maksimaalne täpne viide piiratud. sw_delay_ms funktsioon hoiab programmimälu kokku ja võimaldab kuni 65535 ms viidet teha, miinuseks on natuke ebatäpsem viite aeg.

//
// Tarkvaraline viide millisekundites
//
void sw_delay_ms(unsigned short count)
{
	// Viite muutuja nullini loendamine
	while (count-- > 0)
	{
		// 1ms viide spetsiaalse funktsiooniga
		_delay_ms(1);
	}
}

Allpool olev programmikood on aga taimeril põhinev viite-funktsioon, mida on natuke lihtsustatud. Loendamise põhimõte on sama mis tarkvaralise viite funktsioonilgi - tekitatakse soovitud arv 1 ms 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 korda 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. Nimetatud taktsageduse puhul peaks siis 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 mikrosekundit).

Tsüklis toimub loenduri algväärtustamine ja ületäitumise lipukese nullimine (sellesse 1 kirjutades). Seejärel oodatakse kuni loendur loeb algväärtusest kuni 256-ni, ehk nullimiseni. Nullimise hetkel läheb ületäitumise lipuke kõrgeks ja 1 ms viide ongi toimunud. Funktsiooni lõpus taimer peatatakse.

//
// Riistvaraline viide millisekundites
//
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();	
}

Järgnevalt on toodud aga mõlemat liiki viite tekitamise funktsiooni praktiline harjutus, milles toimub kontrollerimooduli LED-i vilgutamine 1 sekundilise poolperioodiga. Ühel poolperioodil tekitab viidet tarkvaraline funktsioon, teisel taimer.

//
// Kodulabori viite tekitamiste meetodite
// demonstratsioon-programm.
//
#include <homelab/pin.h>
#include <homelab/delay.h>
 
//
// Test LED-i 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);
 
		// Tarkvaraline paus 1000 millisekundit
		sw_delay_ms(1000);
 
		// LED-i kustutamine
		pin_set(debug_led);
 
		// Riistvaraline paus 1000 millisekundit
		hw_delay_ms(1000);
	}
}
et/examples/timer/delay.1259147576.txt.gz · Last modified: 2020/07/20 09:00 (external edit)
CC Attribution-Share Alike 4.0 International
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0