This is an old revision of the document!


Table of Contents

Software delay

Necessary knowledge: [HW] Controller module, [AVR] Architecture, [LIB] Pins, [LIB] Delay

Theory

There is quite often a need to create delays in programs of microcontrollers, it is necessary to time the actions or wait them to end. By idea one of the easiest of the methods of creating a break in the work of microcontroller is overloading its processor with some marginal action – for example order it to count big numbers. From the frequency of the processor can be calculated up to what number it should count to get certain time delay. Counting some number from zero up to the value of the frequency of the processor in hertz-s, should theoretically create a delay for one second. In practice it is not so simple because of different reasons.

If the processor of the microcontroller calculates using numbers which’s binary form is as wide as its inner bus (AVR has 8 bits), then it takes one töötakt?`? of the processor to perform one arithmetical operation for example adding 1 to some value. In order to operate with thousands or millions the number has to be 16 bit or 32 bit and for an 8 bit processor it takes more than one töötakt?? to calculate them. Hence when dealing with large numbers, one has to be familiar with the inside of the processor – exactly its command.

Since when programming in advanced languages (C-language for example), the programs are not written directly on the basis of the command, in order to create a software delay, one needs to know also its compiler which converts the program to the machine code. Exactly form this depends how many instructions (and phases) it takes to perform one arithmetical operation. Complexity is added by the fact that the compiler may convert the program into the machine code in several ways – for example it can make the machine code as memory saving as possible or as easy to execute. Such compiler’s action is called optimization. Different modes of optimization mean different software delay’s machine codes and different duration of them.

Practice

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 mittemidagitegeva tühiinstruktsiooni täitmine. Seda on seal vaja, kuna tsükli sisu tühjaks jättes optimeerib kompilaator tsükli programmist üldse välja, sest see on tema arvates kasutu.

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

Siinkohal on aga toodud sama C-keele programmilõik pärast kompileerimist. Vasakpoolsed 2 heksadetsimaalarvu on masinkood ja paremal on assemblerkeeles käsk koos operandi(de)ga. Masinkood ja assemblerkeel on üks-üheselt seotud, assembler on lihtsalt masinkoodi inimesele loetaval kujul esitamiseks. Kompileerimisel on kasutatud programmi pikkuse optimeerimist (kompilaatori parameeter -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 +1 liitmine
84 36     cpi  r24, 0x64   ; r24 registri võrdlemine arvuga 100
e1 f7     brne .-8         ; Kui võrdlus oli väär, siis siire 8 baiti tagasi

Kompileeritud kujul on näha, mis tegelikult C-keele tsüklist saab, ja selle järgi saab arvutada, mitu takti ühe tsükli perioodi täitmiseks kulub. Infot instruktsioonide toime ja tööaja kohta leiab AVR käsustiku andmelehest. Antud näites kulub ühe tsükli perioodis 4 instruktsiooni täitmiseks 4 takti, sest kõik instruktsioonid võtavad ühe töötakti. Lisaks kulub enne tsüklit 1 takt laadimisinstruktsiooni jaoks ja tsüklist väljudes 1 lisatakt. Oletades, et kontrolleri töötakt on 14,7456 MHz, võib välja arvutada kogu programmilõigu tekitatud ajalise viite:

(1 + 100 ⋅ 4 + 1) / 14745600 = 27,26 μs

Näites tekitatud viide on mikrosekundites ja kasutatav muutuja on 8-bitine, seega on ka masinkood üsna lihtne. Selleks, et tekitada pausi millisekundites, on vaja loendada palju suuremaid arve ja siis läheb ka 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.

Käesoleva harjutuse eesmärk ei ole siiski masinkoodi tasandil täpset tarkvaralist viidet tekitada, sest see on üsna peen töö ja pealegi on viite tekitamiseks avr-libc ja Kodulabori teegis juba funktsioonid olemas. Need kasutatakse ka järgmistes näidetes.

Tarkvaralise viite puhul on aga oluline teada, et hoolimata oma põhimõttelisest lihtsusest on see äärmiselt ebaefektiivne meetod energiatarbe seisukohast. Kõigil neil taktidel, mil mikrokontroller tegeleb kasutu loendamisega, kulub energiat. Patareidega rakenduses ei ole seega soovitatav pikki tarkvaralisi viiteid teha, vaid tuleks kasutada raudvaralisi taimereid, mis töötavad iseseisvalt ning äratavad protsessori uneolekust üles, kui on vaja tööd jätkata.

Praktika

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 assemblerkeeles kirjutatud funktsiooni _delay_ms. Põhjus, miks harjutuses pole kohe _delay_ms kasutatud, on selles, et _delay_ms puhul võivad pikkade viidetega probleemid tekkida. sw_delay_ms funktsioon võimaldab aga probleemideta kuni 65535 ms viidet.

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

Toodud funktsiooni kasutamiseks on järgnev programm, mis tekitab lõputus tsüklis kaks viidet: 100 ms ja 900 ms. Lühema viite jooksul LED põleb ja pikema ajal on kustunud - tulemusena LED perioodiliselt vilgatab.

//
// Kodulabori tarkvaralise viite demonstratsioonprogramm.
// Programm vilgutab ~1 sekundi järel hetkeks LED-i.
//
#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 süütamine
		pin_clear(debug_led);
 
		// Tarkvaraline paus 100 millisekundit
		sw_delay_ms(100);
 
		// LED kustutamine
		pin_set(debug_led);
 
		// Tarkvaraline paus 900 millisekundit
		sw_delay_ms(900);
	}
}

Kuigi näib, et LED vilgatab tõesti 1 sekundi järel, on aeg tegelikult siiski natuke pikem, sest LED-i ja viite funktsioonide väljakutsumised võtavad ka mõned mikrokontrolleri taktid aega.

en/examples/timer/software_delay.1267565820.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