Next revision | Previous revision |
de:examples:timer:software_delay [2010/09/07 13:02] – angelegt Wember | de:examples:timer:software_delay [2020/07/20 09:00] (current) – external edit 127.0.0.1 |
---|
====== Software Verzögerungen ====== | ====== Softwareverzögerungen ====== |
| |
//Necessary knowledge: [HW] [[en:hardware:homelab:controller]], [AVR] [[en:avr:architecture]], [LIB] [[en:software:homelab:library:pin]], [LIB] [[en:software:homelab:library:delay]]// | //Notwendiges Wissen: [HW] [[en:hardware:homelab:controller]], [AVR] [[en:avr:architecture]], [LIB] [[en:software:homelab:library:pin]], [LIB] [[en:software:homelab:library:delay]]// |
| |
===== Theorie ===== | ===== 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 Befehel 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. | Oft ist es wichtig in einem Programm eines Mikrocontrollers Verzögerungen einzubauen, einige Aktionen zu terminieren, oder zu warten bis sie abgeschlossen sind. Die einfachste Methode die Arbeit eines Mikrocontrollers zu pausieren ist, ihn mit einer alternativen Operation zu überladen - z.B. durch einen Befehl, große Nummern zu zählen. Aus der Taktfrequenz des Prozessors kann man errechnen, wie weit das Programm zählen sollte um eine Verzögerung hervorzurufen. |
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. | Eine Zahl von Null bis zum Wert der Taktfrequenz des Prozessors in Hz, sollte theoretisch eine Verzögerung von einer Sekunde erzeugen. Aus verschiedenen Gründen ist dieses jedoch praktisch nicht ganz so einfach. |
| |
Wenn der Prozessor des Microcontrollers mit Nummern rechnet, welche Binäreform so groß wie der inner 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 der Prozessor des Microcontrollers mit Nummern rechnet, deren Binärform so groß ist wie der innere Bus (AVR hat 8 Bits), dann braucht es einen Taktzyklus des Prozessors um eine arithmetische Operation, wie z.B. 1 zu einem Wert zu addieren, durchzuführen. Soll mit tausenden oder millionen gerechnet werden, muss die Nummer 16 oder 32 Bit groß sein und ein 8-Bit Prozessor benötigt zur Berechnung mehr als eine Taktfrequenz. Daher sollte man sich mit der Arbeitsweise und insbesondere den Befehlen des Prozessors auskennen, wenn man mit großen Werten arbeitet. |
| |
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. From this depends how many instructions (and phases therefore) it takes to perform one arithmetical operation. Complexity is added by compilers ability to convert the program into the machine code in several ways – for example, by making the machine code as memory saving as possible or easily executable. Such compiler’s actions are called optimization. With different modes of optimization the software delay’s machine codes and their duration come out different. | Wenn man in fortgeschrittenen Sprachen programmiert (z.B. C), werden die Programme nicht auf Basis des Befehls geschrieben, um eine Softwareverzögerung zu erzeugen. Vielmehr muss man auch den Compiler kennen, welcher das Programm zu Maschinencode konvertiert. Davon hängt ab, 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, oder einfacher auszuführen wird. Diesen Compilervorgang nennt man Optimierung. Durch verschiedene Optimierungsmodi verändern sich die Maschinencodes für die Softwareverzögerung sowie die Dauer der Verzögerung. |
| |
| ===== Beispiel ====== |
| |
===== Practice ====== | Das folgende Beispiel generiert eine Softwareverzögerung im AVR Mikrocontroller. Ein Teil des in C geschriebenen Programms zählt eine Variable x in vier Zyklen von 0 bis 100. |
| In jedem Zyklus wird ein leerer "no-action empty" Befehl ausgeführt. Dieser wird benötigt, der Compiler einen Zyklus ohne Inhalt als nutzlos betrachtet und ihn daher aus dem Programm werfen würde. |
The following is an example of generating software delay with AVR microcontroller. A part of a program in C-language is written, which counts the variable x of for-cycle from 0 to 100. Inside each cycle a no-action empty instruction is completed. It is needed, because if the content of the cycle is empty, the compiler optimizes it out of the program as it considers this to be useless. | |
| |
<code c> | <code c> |
unsigned char x; | unsigned char x; |
| |
// Cycle until x is 100 | // Zyklus bis x ist 100 |
for (x = 0; x < 100; x++) | for (x = 0; x < 100; x++) |
{ | { |
// With empty instruction nop | // Mit "empty"-Befehl nop |
asm volatile ("nop"); | asm volatile ("nop"); |
} | } |
</code> | </code> |
| |
This is the same part of a program after compiling. 2 hexadecimal numbers on the left is machine code and on the right is the command with operand(s) in assembler language. The machine code and the assembler language are conformal; the assembler is just for presenting the machine code for humans in a readable form. In compiling, the optimization of the length of the program is used (compiler’s parameter –Os). | Dies ist der gleiche Teil des Programms nach dem Kompilieren. Die beiden Hexadezimalzahlen links geben den Maschinencode an, und rechts befindet sich der Befehl mit den Operanden in Assembler. |
| Der Maschinencode und die Assemblersprache sind konform; Assembler dient nur dazu, den Maschinencode für Menschen lesbar zu machen. Kompiliert wurde mit der Optimierungsmethode für die Länge des Programms (Parameter -Os). |
| |
<code asm> | <code asm> |
80 e0 ldi r24, 0x00 ; r24 loading number 0 to the index | 80 e0 ldi r24, 0x00 ; r24 lädt Zahl 0 in den index |
00 00 nop ; Empty operation | 00 00 nop ; "Empty" Operation |
8f 5f subi r24, 0xFF ; subtracting 255 form the r24 index, that means adding +1 | 8f 5f subi r24, 0xFF ; subtrahieren von 255 aus dem r24 Index, was bedeutet +1 addieren |
84 36 cpi r24, 0x64 ; comparing r24 index with number 100 | 84 36 cpi r24, 0x64 ; r24 Index mit der Zahl 100 vergleichen |
e1 f7 brne .-8 ; If the comparison was wrong, then transfer 8-baits back | e1 f7 brne .-8 ; Falls der Vergleich nicht übereinstimmt, 8-Bits zurücktransferieren |
</code> | </code> |
| |
| In der kompilierten Form erkennt man, 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 Wirkung der Befehle und ihre Laufzeit findet man im AVR Datenblatt. Im vorliegenden Beispiel, braucht es 4 Taktzyklen um 4 Befehle in einer Periode auszuführen, da jeder Befehl einen Takt benötigt. Zusätzlich wird ein Takt vor dem Zyklus benötigt, um die Befehle zu laden und ein Takt, um den Zyklus zu beenden. |
In the compiled form can be seen, what is actually happening with the cycle of the C-language and it can be used to calculate how many clock sycles is needed to complete a cycle of one period. The information about the effect of the instructions and operating time can be found from AVR’s instructions datasheet. In the given example, it takes 4 clock cycles to complete 4 instructions in one cycle period, because all instructions demand one clock rate. In addition, one clock rate is used before the cycle for loading instruction and afterwards one extra clock rate for exiting the cycle. By assuming, that the working clock rate of the controller is 14,7456 MHz, the whole delay produced by the program can be calculated. | Wenn man nun den Arbeitstakt von 14,7456 MHz annimmt, kann man die vom Programm verursachte Verzögerung berechnen. |
| |
(1 + 100 ⋅ 4 + 1) / 14745600 = 27,26 μs | (1 + 100 ⋅ 4 + 1) / 14745600 = 27,26 μs |
| |
The delay produced in the example, is in microseconds and the variable used is 8-bit so the machine code is fairly simple. In order to produce a break in millisecond, we need to count much larger numbers and thus the machine code gets longer. Cycles working inside each other may also be used, but with this method the delay is not in linear relation with the number of the cycles because with each level of cycle a small extra delay occurs. | Die Verzögerung in diesem Beispiel ist in Mikrosekunden angegeben und die Variable nutzt 8-Bit, daher ist der Maschinencode recht einfach. Um eine Pause in Millisekunden zu verursachen, müssen weitaus größere Nummern gezählt werden, wodurch dann der Maschinencode auch länger wird. Es können ebenfalls Zyklen verwendet werden, die ineinander greifen, jedoch steht dann 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. |
| |
The goal of this exercise is not creating precise software delay on the level of machine code, because it is quite an accurate work and we already have the functions necessary to produce delays in the avr-libc and in the library of the HomeLab. Those are used also in the following examples. | |
| |
When dealing with the software delay it is important to know, that regardless its basic simplicity; it is extremely inefficient method from the power consumption point of view. During all of these clock rates when the microcontroller is counting the useless, energy is consumed. So if using applications operating on batteries, it is not wise to write long software delays. Wiser is to use hardware timers, which are working independently and wake the processor from hibernating when it is time to continue the work. | Das Ziel dieser Übung ist es nicht präzise Softwareverzögerungen in Maschinencode zu erstellen, da diese eine sehr präzise Arbeit ist und die Funktionen zur Erzeugung von Verzögerungen bereits in der avr-libc und der Bibliothek von HomeLab hinterlegt sind. Diese werden auch in den folgenden Beispielen genutzt. |
| |
| Bei der Arbeit mit Softwareverzögerungen arbeitet, ist es wichtig zu wissen, dass diese trotz ihrer Einfachheit, eine sehr ineffiziente Methode sind was den den Stromverbrauch betrifft. |
| Während jedes Taktes in dem der Mikrocontroller unnötig Zahlen zählt, wird Energie verbraucht. Wenn also batteriebetriebene Anwendungen 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. |
| |
===== Practice ====== | ===== Übung ====== |
| |
| Der folgende Programmcode beinhaltet die Softwareverzögerunsfunktion //sw_delay_ms//, welche einen gegebene Verzögerung in ms mit dem Parameter //count// verursacht. Die Funktionen nutzt die Funktion der Bibliothek avr-libc //_delay_ms// welche zum Teil in Assembler geschrieben ist. |
| //_delay_ms// wird in dieser Übung nicht genutzt, da hiermit bei langen Verzögerungen Probleme auftreten können. //sw_delay_ms// erlaubt es aber bis zu 65535ms lange Verzögerungen ohne Komplikationen zu nutzen. |
| |
The following code of a program is about software delay function //sw_delay_ms// , which makes a given delay in milliseconds using the parameter //count//. The function uses avr-libc library’s function //_delay_ms// which is partly written in assembler language. The reason why the //_delay_ms// is not used in this exercise immediately is that when using the //_delay_ms// problems may occur when the delays are long. The //sw_delay_ms// enables creating 65535 ms long delays without any complications. | |
| |
<code c> | <code c> |
// | // |
// Software delay in milliseconds. | // Softwareverzögerung in Millisekunden. |
// | // |
void sw_delay_ms(unsigned short count) | void sw_delay_ms(unsigned short count) |
{ | { |
// Counting the variable of the delay to 0 | // Zählen der Variable der Verzögerung auf 0 |
while (count-- > 0) | while (count-- > 0) |
{ | { |
// 1ms delay with a special function. | // 1ms Verzögerung mit spezieller Funktion. |
_delay_ms(1); | _delay_ms(1); |
} | } |
</code> | </code> |
| |
The following program is for using the given function, this creates two delays in the endless loop: 100 ms and 900 ms. During the shorter delay LED is lit and during the longer it is switched off, the result – the LED is blinking periodically | Das folgende Programm dient dazu, die gegebene Funktion zu nutzen, es erzeugt zwei Verzögerungen in der Endlosschleife: 100 ms und 900 ms. Während der kürzeren Verzögerung wird eine LED eingeschaltet und während der längern ausgeschaltet. Das Resultat: die LED blinkt periodisch. |
| |
<code c> | <code c> |
// | // |
// The demonstration program of the software delay of the HomeLab. | // Das Vorführprogramm des HomeLab für Softwareverzögerungen. |
// The program is blinking a LED for a moment after ~1 second. | // Es lässt eine LED für ~1 Sekunde aufleuchten. |
// | // |
#include <homelab/pin.h> | #include <homelab/pin.h> |
| |
// | // |
// Determining the pin of the test LED | // Festlegung des Pins zum Testen der LED |
// | // |
pin debug_led = PIN(B, 7); | pin debug_led = PIN(B, 7); |
| |
// | // |
// Main program | // Hauptprogramm |
// | // |
int main(void) | int main(void) |
{ | { |
// Setting the pin of the LED as output. | // Setzt LED Pin als Output. |
pin_setup_output(debug_led); | pin_setup_output(debug_led); |
| |
// Endless loop | // Endlosschleife |
while (true) | while (true) |
{ | { |
// Lighting the LED | // Aufleuchten der LED |
pin_clear(debug_led); | pin_clear(debug_led); |
| |
// Software delay for 100 ms | // Softwareverzögerung für 100 ms |
sw_delay_ms(100); | sw_delay_ms(100); |
| |
// Switching off the LED | // Ausschalten der LED |
pin_set(debug_led); | pin_set(debug_led); |
| |
// Software delay for 900 milliseconds. | // Softwareverzögerung für 900 ms. |
sw_delay_ms(900); | sw_delay_ms(900); |
} | } |
</code> | </code> |
| |
Although it seems that the LED blinks in every 1 second, the time is actually a little bit longer, because the callouts of LED’s and delay functions are taking a couple of clock rates of the microcontroller | Auch wenn es so aussieht, als blinke die LED jede Sekunde, dauert es tatsächlich etwas länger, da das ansprechen der LED und die Verzögerungsfunktionen ein paar Taktfrequenzen des Mikrocontrollers benötigen. |