This is an old revision of the document!


Software Verzögerungen

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

Theorie

Oft ist es wichtig in einem Programm eines Microcontrollers Verzögerungen einzubauen, es ist notwendig einige Aktionen zu timen, oder zu warten bis sie abgeschlossen sind. Die einfachste Methode die Arbeit eines Microcontrollers zu pausieren ist, ihn mit einer alternativen Operation zu überladen - z.B. einen Befehl um große Nummern zu zählen. Von der Taktfrequenz des Prozessors kann man ableiten wie weit das Programm in einer bestimmten Zeit zählt. Eine Zahl von Null bis zum Wert der Taktfrequenz des Prozessors in Hz, sollte theoretisch eine Verzögerung von einer Sekunde erzeugen. Aus einer Nummer von Gründen ist es leider praktisch nicht ganz so einfach.

Wenn der Prozessor des Microcontrollers mit Nummern rechnet, welche Binäreform so groß wie der innere Bus sind (AVR hat 8 Bits), dann brauch es einen Taktzyklus des Prozessors eine arithmetische Operation, wie z.B. 1 zu einem Wert zu addieren, durchzuführen. Aber wenn der Prozessor mit größeren Werten rechnet, und die Nummer 16 oder 32 Bit sein muss, brauch der 8 Bit Prozessor mehr als eine Taktfrequenz, diese zu berechnen. Daher sollte man sich mit der Arbeitsweise des Prozessors auskennen, wenn man mit großen Werten arbeitet.

Wenn man in fortgeschrittenen Sprachen programmiert ( z.B. C), werden die Programme nicht auf Basis des Befehls geschrieben, um eine Softwareverzögerung zu erzeugen, muss man auch den Compiler kennen, welcher das Programm zu Maschinencode konvertiert. Darauf basiert, wie viele Befehle (und Phasen) es braucht um eine arithmetische Operation durchzuführen. Das wird durch mehrere Gründe komplexer, durch die Fähigkeit des Compilers das Programm in Maschinencode umzuwandeln - z.B. dadurch, dass der Maschinencode für Speicherverbrauch optimiert wird, oder einfacher auszuführen wird. Diesen Compilervorgang nennt man Optimierung. Mit verschieden Optimierungsmodi werden die Maschinencodes und die verursachte Verzögerung der Softwareverzögerung anders.

Beispiel

Das folgende Beispiel generiert eine Softwareverzögerung im AVR Microcontroller. Ein Teil des in C geschriebenen Programms, zählt eine Variable x in einem for-Zyklus von 0 bis 100. In jedem Zyklus wird ein “no-action” leerer Befehl ausgeführt. Dies wird benötigt, da wenn der Inhalt des Zyklus leer ist, der Compiler ihn auf Grund des leeren Inhalts aus dem Programm optimiert.

unsigned char x;
 
// Cycle until x is 100
for (x = 0; x < 100; x++)
{
	// With empty instruction nop
	asm volatile ("nop");
}

Dies ist der gleiche Teil des Programms nach dem kompilieren. Zwei hexadezimale Nummern links geben den Maschinencode an, und rechts den Befehl mit Operanden in Assembler Sprache. Der Maschinencode und die Assemblersprache sind konform; Assembler ist nur da um den Maschinencode für Menschen lesbarer zu machen. Kompiliert wurde mit der Optimierungsmethode für die Länge des Programms (Parameter -Os).

80 e0     ldi  r24, 0x00   ; r24 loading number 0 to the index
00 00     nop              ; Empty operation
8f 5f     subi r24, 0xFF   ; subtracting 255 form the r24 index, that means adding +1
84 36     cpi  r24, 0x64   ; comparing r24 index with number 100
e1 f7     brne .-8         ; If the comparison was wrong, then transfer 8-baits back

In der kompilierten Form kann man sehen, was mit dem Zyklus der C-Sprache passiert und man kann berechnen wie viele Taktfrequenzen benötigt werden, um den Zyklus einer Periode zu beenden. Die Information über die Effekte der Befehle und die Laufzeit kann im AVR Datenblatt gefunden werden. Im gegebenen Beispiel, benötigt es 4 Taktzyklen um 4 Befehle in einer Periode auszuführen, da jeder Befehl nur einen Takt benötigt. Zusätzlich wird ein Takt vor dem Zyklus benötigt um die Befehle zu laden, und einen Takt um den Zyklus zu beenden. Wenn man nun den Arbeitstakt von 14,7456 MHz annimmt, kann man die Verzögerung die von dem Programm verursacht wird berechnen.

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

Die Verzögerung dieses Beispiel ist in Microsekunden und die Variable nutzt 8-Bit, daher ist der Maschinencode recht einfach. Um eine Pause in Millisekunden zu verursachen, muss man viel größere Nummern zählen, und dann wird der Maschinencode auch länger. Zyklen die ineinander arbeiten können auch genutzt werden, aber mit dieser Methode ist die Verzögerung nicht in linearer Relation mit der Nummer des Zyklus, da mit jedem Level des Zyklus eine kleine zusätzliche Verzögerung verursacht wird.

Das Ziel dieser Übung ist es nicht präzise Softwareverzögerungen in Maschinencode zu erstellen, da diese eine sehr aufwendige und genaue Arbeit ist und wir die Funktionen um Verzögerungen zu erzeugen schon in der avr-libc und der Library von HomeLab haben. Diese werden auch im folgenden Beispiel genutzt.

Wenn man mit Softwareverzögerungen arbeitet, ist es wichtig zu wissen, dass trotz der Einfachheit, eine extrem ineffiziente Methode ist, wenn man den Stromverbrauch betrachtet. Während jedes Taktes des Microcontrollers der unnötigen Zahlen zählt wird Energie verbraucht. Wenn also Applikationen mit Batterien genutzt werden, ist es nicht sinnvoll lange Softwareverzögerungen zu schreiben. Für diesen Fall sollte man lieber Hardwaretimer nutzen, welche unabhängig arbeiten und den Prozessor aus dem Ruhezustand holen wenn es an der Zeit ist die Arbeit fortzuführen.

Übung

Der folgende Code eines Programms ist über die Softwareverzögerunsfunktion sw_delay_ms, welche einen gegebene Verzögerung in ms mit dem Parameter count verursacht. Die Funktionen nutzt die avr-libc Library Funktion _delay_ms welche z.T. in Assembler geschrieben ist. Der Grund, warum _delay_ms in dieser Übung nicht genutzt wird ist, dass mit _delay_ms Probleme auftreten können wenn man lange Verzögerungen nutzt. sw_delay_ms erlaubt es aber bis zu 65535ms lange Verzögerungen ohne Komplikationen zu nutzen.

//
// Software delay in milliseconds.
//
void sw_delay_ms(unsigned short count)
{
	// Counting the variable of the delay to 0
	while (count-- > 0)
	{
		// 1ms delay with a special function.
		_delay_ms(1);
	}
}

Das folgende Programm ist um die gegebenen Funktion zu nutzen, es erzeugt zwei Verzögerungen in der Endlosschleife: 100 ms und 900ms. Während der kürzeren Verzögerung wird LED angeschaltetm und während der längern ausgeschaltet. Das Resultat: die LED blinkt periodisch.

//
// The demonstration program of the software delay of the HomeLab.
// The program is blinking a LED for a moment after ~1 second.
//
#include <homelab/pin.h>
#include <homelab/delay.h>
 
//
// Determining the pin of the test LED
//
pin debug_led = PIN(B, 7);
 
//
// Main program
//
int main(void)
{
	// Setting the pin of the LED as output.
	pin_setup_output(debug_led);
 
	// Endless loop	
	while (true)
	{
		// Lighting the LED
		pin_clear(debug_led);
 
		// Software delay for 100 ms
		sw_delay_ms(100);
 
		// Switching off the LED
		pin_set(debug_led);
 
		// Software delay for 900 milliseconds.
		sw_delay_ms(900);
	}
}

Auch wenn es scheint, dass die LED jede Sekunde blinkt, dauert es tatsächlich etwas länger, da das ansprechen der LED und die Verzögerungsfunktionen ein paar Taktfrequenzen des Microcontrollers nutzen.

de/examples/timer/software_delay.1289347131.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