Unterschiede

Hier werden die Unterschiede zwischen zwei Versionen angezeigt.

Link zu dieser Vergleichsansicht

Beide Seiten der vorigen Revision Vorhergehende Überarbeitung
Nächste Überarbeitung
Vorhergehende Überarbeitung
microcontrollertechnik:skript [2024/10/07 00:00] – angelegt mexleadminmicrocontrollertechnik:skript [2024/10/28 05:04] (aktuell) mexleadmin
Zeile 6: Zeile 6:
  
   - Wdh. Hello Blinking World:    - Wdh. Hello Blinking World: 
-    - DDRx, PORTx, _Delay_ms() --> R steht fast immer für Register+    - ''DDRx''''PORTx''''_Delay_ms()'' --> ''R'' steht fast immer für Register
     - --> einmal kompilieren und in Simulide aufbauen     - --> einmal kompilieren und in Simulide aufbauen
     - Welche "Vorgaben für die SW-Entwicklung" wurden verletzt? --> Keine magic numbers, sondern #defines ! \\ siehe Weiterführende Fragen und Infos     - Welche "Vorgaben für die SW-Entwicklung" wurden verletzt? --> Keine magic numbers, sondern #defines ! \\ siehe Weiterführende Fragen und Infos
Zeile 12: Zeile 12:
   - "Kapitel 2 Sound und Timer bitte nachträglich anschauen"   - "Kapitel 2 Sound und Timer bitte nachträglich anschauen"
   - Frage an Studis "Wer weiß nicht was PWM ist?"   - Frage an Studis "Wer weiß nicht was PWM ist?"
- 
  
 In MC Studio In MC Studio
-  - neues Projekt "02_timer"+  - neues Projekt ''02_timer''
   - jetzt neu: mit Display!    - jetzt neu: mit Display! 
     - --> Bibliothek aus wiki herunterladen!     - --> Bibliothek aus wiki herunterladen!
     - Project --> Add --> existing Item (NICHT drag & drop)     - Project --> Add --> existing Item (NICHT drag & drop)
-    - bei mir --> F2 Namen ändern auf "lcd_lib_de.h"+    - bei mir --> F2 Namen ändern auf ''lcd_lib_de.h''
     - Split Screen     - Split Screen
     - Was tun, um Lib in main einzufügen?     - Was tun, um Lib in main einzufügen?
-    - #include!  +    - ''#include''!  
-      - #inc + <Tab> +      - ''#inc'' + <Tab> 
-      - --> Unterschied <lib.h> vs "lib.h" +      - --> Unterschied ''<lib.h>'' vs ''%%"lib.h"%%'' 
-  - Durchsicht der lcd_lib_de.h +  - Durchsicht der ''lcd_lib_de.h'' 
-    - F_CPU +    - ''F_CPU'' 
       - --> CPU Frequenz, wichting für genaues Timing der delays       - --> CPU Frequenz, wichting für genaues Timing der delays
-      - hier 18,432 MHz --> Minimexle Frequenz +      - hier ''18,432 MHz'' --> Minimexle Frequenz 
-      - Warum 18,432?+      - Warum ''18'432'000 Hz''?
         - ILIAS --> Elektronik Labor --> MiniMEXLE Schaltbild          - ILIAS --> Elektronik Labor --> MiniMEXLE Schaltbild 
         - "Schreck!" sowiel Krams auf dem Schaltplan!         - "Schreck!" sowiel Krams auf dem Schaltplan!
Zeile 36: Zeile 35:
     - Funktionsprotoypen --> bitte immer am anfan angeben --> gut für eine Übersicht     - Funktionsprotoypen --> bitte immer am anfan angeben --> gut für eine Übersicht
   - als erstes immer Initialisierung (anlegen der Variablen, verschiedene Konfigurationen etc.)   - als erstes immer Initialisierung (anlegen der Variablen, verschiedene Konfigurationen etc.)
-    - lcd_i + <tab> +    - ''lcd_i'' + <tab> 
     - schon mal kompilieren (immer mal kompilieren zum test, ob noch alles klappt)     - schon mal kompilieren (immer mal kompilieren zum test, ob noch alles klappt)
     - noch nicht lauffähig, da nichts angezeigt !     - noch nicht lauffähig, da nichts angezeigt !
Zeile 42: Zeile 41:
     - welche Unterfunktion wohl geeignet?     - welche Unterfunktion wohl geeignet?
     - Hinweis auf Inkonsistenz bei Namensgebung       - Hinweis auf Inkonsistenz bei Namensgebung  
-    - Eingabe lcd_displayMessage("Hallo!", 0,0 ) --> Hinweis auf Zählanfang 0 nicht 1!+    - Eingabe ''%%lcd_displayMessage("Hallo!", 0,0 )%%'' --> Hinweis auf Zählanfang 0 nicht 1!
   - Flashen auf Minimexle   - Flashen auf Minimexle
     - Add Target --> STK500 --> ersten COM Port auswählen (und - falls es nicht passt - den nächsten)     - Add Target --> STK500 --> ersten COM Port auswählen (und - falls es nicht passt - den nächsten)
Zeile 48: Zeile 47:
       - Apply --> Device Signature sichtbar?       - Apply --> Device Signature sichtbar?
       - --> Memories --> Program       - --> Memories --> Program
-  - Ausgabe von "Hallo! Zähler: " +  - Ausgabe von ''Hallo! Zähler: '' 
-    - kann "änicht schreiben , sondern schreibt "µ", warum? +    - kann ''ä'' nicht schreiben , sondern schreibt ''µ'', warum? 
       - --> Datasheet lesen!        - --> Datasheet lesen! 
       - Am besten in der Schaltung den Namen suchen       - Am besten in der Schaltung den Namen suchen
Zeile 55: Zeile 54:
       - Googeln nach ST7066U Datasheet        - Googeln nach ST7066U Datasheet 
       - Kurzes darüberscrollen über das Datasheet       - Kurzes darüberscrollen über das Datasheet
-      - --> Character code Table! --> ist da "ädrin? In einer schon... In der anderen is "µbeim gleichen Bitmuster +      - --> Character code Table! --> ist da ''ä'' drin? In einer schon... In der anderen is ''µ'' beim gleichen Bitmuster 
-      - Also: was tun? entweder "ánutzen, oder ldc_putc(11100001); --> was wurde vergessen? --> % !+      - Also: was tun? entweder ''á'' nutzen, oder ''ldc_putc(11100001);'' --> was wurde vergessen? --> ''%'' !
     - Vergleich in Simulide:     - Vergleich in Simulide:
       - Aufbau der Schaltung: mega88 + Hd44780 (ist kompatibel zu ST7066U)       - Aufbau der Schaltung: mega88 + Hd44780 (ist kompatibel zu ST7066U)
       - Wie verbinden? Siehe lib (wenn gut beschrieben) oder MEXLE Schaltung        - Wie verbinden? Siehe lib (wenn gut beschrieben) oder MEXLE Schaltung 
-      - In lib: Port-Bits. PIN_EN, PIN_RS --> wo in Simulide?  +      - In lib: Port-Bits. ''PIN_EN''''PIN_RS'' --> wo in Simulide?  
-      - Für was steht EN? --> Enable. RS --> Register Select +      - Für was steht ''EN''? --> Enable. ''RS'' --> Register Select 
-      - PORT_DATA: von PORTC nur die ersten 4 bits (0...3)  +      - ''PORT_DATA'': von ''PORTC'' nur die ersten 4 bits (0...3)  
-      - 18,432 MHz eingeben! +      - in Simulide ''18,432 MHz'' eingeben! 
       - hex file Flashen       - hex file Flashen
       - --> animation einschalten (High/Low wird angezeigt)       - --> animation einschalten (High/Low wird angezeigt)
       - es wird noch nichts ausgegeben?? --> im Code schauen oder im Schaltplan!       - es wird noch nichts ausgegeben?? --> im Code schauen oder im Schaltplan!
-      - PC0 auf D4, PC1 auf D5, PC2 auf D6, PC3 auf D7 +      - ''PC0'' auf ''D4''''PC1'' auf ''D5''''PC2'' auf ''D6''''PC3'' auf ''D7'' 
-      - jetzt klappts , aber ä an falscher Position +      - jetzt klappts, aber ''ä'' an falscher Position 
-    - lcd_gotoxy einfügen+    - ''lcd_gotoxy'' einfügen
       - In Simulide autoload einschalten!       - In Simulide autoload einschalten!
  
 Jetzt: aufsteigende Zahlen ausgeben Jetzt: aufsteigende Zahlen ausgeben
 Was tun? Was tun?
-  - Laufvariable anlegen und nutzen: uint8_t i=0; und i++ in der Schleife +  - Laufvariable anlegen und nutzen: ''uint8_t i=0;'' und ''i++'' in der Schleife 
-  - wie gibt man Zahlen aus? sprintf? (kann in String einen Zahlenwert ausgeben) +  - wie gibt man Zahlen aus? ''sprintf''? (kann in String einen Zahlenwert ausgeben) 
-  - sprintf(output_str, "i:%03u", i); // 03 --> drei Stellen, u --> unsigned +  - ''%%sprintf(output_str, "i:%03u", i);%%'' ''3'' --> drei Dezimalstellen''u'' --> unsigned 
-  - output_str deklarieren +  - ''output_str'' deklarieren 
-  - kompilieren --> #include <stdio.h> vergessen +  - ''%%lcd_displayMessageoutput_str, 1,0);%%'' 
 +  - kompilieren --> ''#include <stdio.h>'' vergessen 
   - Simulation herunterdrehen   - Simulation herunterdrehen
  
Zeile 84: Zeile 84:
 Die CPU kann aber schneller! Die CPU kann aber schneller!
   - Blick ins Datenblatt des atmega88   - Blick ins Datenblatt des atmega88
-  - Blockbild +  - Blockbild des atmega 
-  - T/C im Inhaltsverzeichnis --16 bit Timer/Counter+    - Vergleich mit Zahnarztpraxis  
 +    - "Zahnarzt" macht nicht alles, sondern nur komplexere Dinge 
 +    - viele Helfer (Servants) die der CPU zuarbeiten 
 +    - PORT's unten kennen wir schon. Sind die Türsteher (doorman) für die Anschlüsse 
 +    - Aber auch Analog Digital Wandler, nicht flüchtiger Speicher EEPROM (non-volatile memory) und mehr 
 +    - Wir werden Timer und Counter nutzen 
 +  - T/C im Inhaltsverzeichnis  
 +    gibts mehrere: 8 bit TC0, 16 bit TC1 und 8 bit TC2 
 +    wir nehmen 16 bit Timer/Counter und gehen zu diesem Kapitel 
 +  -  **16 bit Timer/Counter** 
 +    - wieder Blockbild, diesmal vom Timer / Counter 
 +    - wieder echt kompliziert auf den ersten Blick
   - wichtig sind immer die Register   - wichtig sind immer die Register
-    - TCNTn --> timer Counter ; für was steht n? in Mathe? +    - ''TCNTn''  
-    - OCRnA --> Output Compare +      - --> timer Counter ; für was steht ''n''? in Mathe? --> Schaubild gilbt für alle 3 Zähler. Hier ist n=1, da TC1! 
-    - TCCRnA  +      - das ist der eigentliche aufsteigende Zählwert 
-  - sprintf(output_str, "TC:%03u", TCNT1); // an zweiter Pos ausgeben. --> wichtig: 3 in 5 ändern! +      - kann auch beschrieben werden 
-    - kommt noch nix heraus+    - ''OCRnA'' --> Output Compare ("Wert zum gegen-checken") 
-    - Control logic --> steht im folgenden in der Register Description +    - ''TCCRnA'' --> für was steht TC? Timer Counter! Für was R? Register! --> hier neu: C für control 
-    - Blick in die Tabellen, was bei initialisierung mit 0 passiert +  - Zeilen mit ''sprintf'' und ''lcd_displayMessage'' kopieren# 
-    - --> CS auf 000 hat keine Clock! +  - bei Ausgabe Position ändern: ''%%lcd_displayMessageoutput_str, 1,**7**);%%'' 
-    - also CS10 setzen für schnellsten Zähler +  - diesmal: ''%%sprintf(output_str, "TC:%03u"%%**TCNT1**);'' -->  an zweiter Pos ausgeben. --> wichtig: ''3'' in ''5'' ändern! 
-    - TCCR1B |= 1<<CS10;+    - kompilieren und in Simulide starten 
 +    - TC zählt noch nicht
 +    - siehe Blockdiagramm: Control logic --> steht im folgenden in der Register Description 
 +    - Blick in die Tabellen, was bei Initialisierung des uC mit 0 passiert \\ **Speziell die __Register description__!** 
 +    - bei vielen ergibt ''0'' normal operation  
 +    aber bei CS (clock select) bedeutet ''000''keine Clock. Da kommt nix raus
 +    - mit CS kann der Vorteiler (prescaler) gesetzt werden, welcher für die CPU Clk zum zählen herunterteilt 
 +    - gut wäre ''CS10'' setzen --> schnellster Zähler 
 +    - Wo ist ''CS10''? im Register ''TCCR1B'' 
 +    - Eingabe von ''TCCR1B |= 1%%<<%%CS10;'' vor der main loop 
 +    - jetzt zählt der Zähle echt schnell. Schneller als die Anzeige!  
 +  - Neue Challenge: Blinken ohne ''_delay_ms()'' 
 +    - Mit Zähler möglich! Wie? mal selbst überlegen! 
 +      - z.B. ''if(TCNT1>x){doBlink();TCNT1=0;};'' 
 +    - geht auch besser! Denn der Knecht kann noch mehr: 
 +      - Blick ins Datenblatt --> viele Seiten! 
 +      - Blick auf Block Diagramm des TC: 
 +      - ''TCNTn'' wird doch verglichen! 
 +      - wo geht das Vergleichsergebnis hin?  
 +        - zu "Waveform generation" und ''OC1A (Int.req.)'' 
 +        - leider etwas irreführend, weil 2x ''OC1A'' (OC1A (Int.req.) müsste OCF1A heißen für Flag --> siehe Text) 
 +      - jetzt erstmal Datenblatt nach ''OC1A'' durchsuchen! 
 +        - Oh! Kommt als Pin am Chip vor. 
 +        - Warum wohl? 
 +      - was könnte das "Waveform generation" machen? 
 +        - suchen im Datenblatt nach **__Waveform generat__** (ohne **__ion__**!) 
 +        - (zunächst im 8-Bit Timer gefunden) Output compare unit, block diagram  
 +        - hier als "Waveform generat**__or__**" 
 +        - Der Generator wird über die Register bits WGM und COM geändert 
 +        - (besser im 16-bit TC1 das Block diagramm nachsehen) 
 +      - wieder Register description ansehen  
 +        - erste Bits im TCCR1A sind COM.. 
 +        - Blick in die Tabelle: was muss eingestellt werden, um eine Ausgabe für die blinkende LED zu bekommen? 
 +        - ''11'' z.B. delay für dauerhaftes Leuchten nach ein paar Millisekunden 
 +        - wir brauchen ''COM1B1=1'' und ''COM1B0=0'' 
 +        - ''TCCR1A |= 1<<COM1B0;'' einfügen 
 +        - UND: ''DDRB = 255;'' 
 +      - Output-Pin togglt! Yeah :-) 
 +    - Problem: wir können die Geschwnindigkeit nich richtig einstellen :-( 
 +  - Also Challenge: zeitlich einstellbares toggeln 
 +    - Bisher: timer zählt stur von 0 ... 65535, also er verwendet OCR1A gar nicht! 
 +    - Wäre gut den MaxWert einstellen zu können, also nach erreichen von OCR1A wieder zurück auf 0. 
 +    - Lösung über "Clear Timer on Compare": CTC im Datenblatt suchen 
 +    - timing diagramm erklären 
 +    - Mal alle Wave Generation Modes ansehen: 
 +      - Normal: 0 ... 65535, und dann wieder Sprung auf 0 --> OC1A ändert sich nur bei max Wert 
 +      - Fast PWM:  
 +        - 0 ... max Wert, und dann wieder Sprung auf 0 --> OC1A bei TCNT>= OCR1A gleich 0, sonst 1 
 +        - Fast PWM kann verschiedene max Werte haben: 255, 511, 1023, ICR1 und sogar OCR1A selbst 
 +        - Ansteigende Flanke immer bei 0 --> links bündig! 
 +      - PWM, phase correct 
 +        - 0 ... max Wert, und dann: max Wert ... 0 --> OC1A bei TCNT>= OCR1A gleich 0, sonst 1  
 +        - halb so schnell weil doppelte Rampe 
 +        - mitten zentriert 
 +    - Für uns wichtig Mode 4: da denn OCR1A der Maxwert, also die Dauer für Ein (und Dauer für Aus) beim PWM 
 +    - Wir müssen ''WGM12'' setzen, das ist aber in ''TCCR1B''
 +    - ''TCCR1B |= 1<<WGM12;'' 
 +    - Und OCR1A setzen: '' OCR1AH = 0xFF;'', '' OCR1AL = 0xFF;'' 
 +    - Wichtig immer LED an Port bei Simulide! nicht nur auf die Animation vertrauen! 
 +  - Mit CS Clock select: kann es auch langsamer ausgegeben werden 
 +  - Ggf. auch Sound Code ansehen. 
 + 
 + 
 +--> Ergebnisse: 
 +  - bitte immer eindeutige und konistente Namen in Ihrer Doku nutzen! 
 +  - Sonst werden nachfolgende Leser Probleme beim verstehen bekommen..   
 + 
 + 
 +===== Einschub - Debugging ===== 
 + 
 +Debugging Beispiel: DisplayAndTimer_v02 in ILIAS  
 + 
 +kleine Änderungen: 
 +  *  Variable ''str'' initialisieren: ''char str[3]="";'' 
 +  *  Umbenennen der Variablen über ''Refactoring'' >> ''Rename'' --> z.B. ''SwCounter'' und ''OutputStr'' 
 +  *  Beispielhaft: statt Deklaration in Funktion nun als globale Variablen und umgekehrter Reihenfolge \\ ''char OutputStr[3]="";'' \\ ''char SwCounter;'' (also keine Initialisierung!) 
 +  *  Kompilieren und am Bildschirm den Output ansehen 
 +  *  Problem: Zähler scheint nur zwischen 48 und 57 zu zählen! 
 + 
 +Tipps 
 +  - **Tipp 1**: Verwenden von nicht genutzten Registern 
 +    - nach erstem ''lcd_putstr'': ''TWDR=SwCounter;'' 
 +    - nach zweiten''lcd_putstr'': ''SPDR=SwCounter;'' 
 +    - In Simulide: MCU Monitor >> Blick auf die beiden Register 
 +    - Komisch: OutputStr verändert sich zwischen beiden! 
 +  - **Tipp 2**: Nur absolut notwendigen Code betrachten 
 +    - Code soweit auskommentieren wie es nur geht.  
 +  - **Tipp 3**: Im Fall von Deklarationen: map Datei 
 +    - Nach ''SwCounter'' suchen 
 +    - RAM Table über ''MCU Monitor'' 
 +    - auf Byte PC umschalten 
 +    - Suchen der Speicherzelle 
 +    - Geschwindigkeit reduzieren 
 +  - **Tipp 4**: Logic Analyzer 
 +    - ''PORTB|=1<<PB3;''  nach erstem  ''lcd_putstr'' 
 +    - ''PORTB&=~1<<PB3;'' nach zweitem ''lcd_putstr''  
 +    - Logic analyzer beschreiben: wichtig Time Pos korrekt einstellen 
 +    - Trigger in Logic Analyzer 
 +  - **Tipp 5**: Hyper-V  
 +    - Windows Eingabe: ''Hyper-V-Schnellerstellung'' 
 +    - siehe: https://learn.microsoft.com/de-de/virtualization/hyper-v-on-windows/quick-start/quick-create-virtual-machine 
 +    - Installation dauert einige Minuten 
 +    - Simulide in VM ist stabiler 
 +  - **Tipp 6**: Scope  
 +    - Tracks: Aufteilen des Bildschirms  
 +    - Trigger 
 +    - Auto 
 +  - **Tipp 7**: Scripted Modules 
 +    - Beispiel DAC 
 +    - im Komponentenexplorer: Scripted >> DAC 
 +    - im Dateiexplorer: Data >> scripted >> DAC  
 +    - Alle Dateien anklicken --> nix passiert 
 +    - rechten Editorbereich in Simulide öffnen ("hereinschieben"
 +    -  
 +  - Known Problems  
 +    - Ampmeter only for DC! (about 10 Hz) 
 +    - FETs 
 +      - use Relays instead \\ Change 
 +        - Inductance: 1 nH 
 +        - Resistance to 1 mOhm 
 +        - IOn 100 mA 
 +        - IOff 50 mA 
 +      - Beispiel: Halbbrücke (auch als FET Variante ok) 
 +      - Alternative mit Relays  
 +      - Hier besser als Umschalter umzusetzen, da Kurzschluss im Zwischenstadium {{drawio>microcontrollertechnik:RelayBeispiel.svg}} 
 + 
 +===== Interrupts und Zeitslots ===== 
 + 
 +Wdh: 
 +  - Bisher:  
 +    - Ausgabe auf dem Display in while-Schleife "so schnell wie's geht" 
 +    - Counter TC1 läuft autonom und unabhängig vom Programmcode (nach Config) 
 +    - Counter kann unterschiedlich schnell laufen (prescaler) 
 +    - Counter kann auch Ausgabe antriggern 
 +  - Nun: 
 +    - welche Modi gibts? Was kann man noch mit dem Timer/Counter starten? 
 +    - Anschauen der verschiedenen Modi: 
 +      - Normal: 0 ... 65535, und dann wieder Sprung auf 0 --> OC1A ändert sich nur bei max Wert 
 +      - Fast PWM:  
 +        - 0 ... max Wert, und dann wieder Sprung auf 0 --> OC1A bei TCNT>= OCR1A gleich 0, sonst 1 
 +        - Fast PWM kann verschiedene max Werte haben: 255, 511, 1023, ICR1 und sogar OCR1A selbst 
 +        - Ansteigende Flanke immer bei 0 --> links bündig! 
 +      - PWM, phase correct 
 +        - 0 ... max Wert, und dann: max Wert ... 0 --> OC1A bei TCNT>= OCR1A gleich 0, sonst 1  
 +        - halb so schnell weil doppelte Rampe 
 +        - mitten zentriert 
 +    - weitere Register des TC anschauen 
 +      - TCNT1H/TCNT1L: ist der eigentliche Counter Wert 
 +      - OCR1AH/OCR1AL: ist Vergleichswert für den ersten Vergleich 
 +      - OCR1BH/OCR1BL: ist Vergleichswert für den zweiten Vergleich 
 +      - ICR1H/ICR1L: ist "Zwischenspeicher" der mit dem Counter-Wert gesetzt wird, sobald Pin ICP1 sich ändert 
 +      - erst TIFR1:  
 +        - zeigt Ereignisse an (über Flags): z.B. Vergleichswert ist erreicht, oder Maximalwert ist erreicht 
 +        - wenn ein Ereignis eintritt, dann kann ein Interrupt ausgelöst werden 
 +      - TIMSK1 : Ist eine Maske, die angibt, welcher Interrupt aktiv ist 
 +  - Mal Overflow Interrupt testen: 
 +    - bei Initialisierung: ''TIMSK2 |= (1<<TOIE1);'' 
 +    - außerhalb von main: 
 +      - ''ISR()'' --> suchen nach ISR (Goto implementation) liefert keine praktikable Antwort was das tut (Interrupt Service routine erklären) 
 +      - wir brauchen zumindest einen "vector" (Zeiger auf die Sprungadresse welche im Interruptfall abgearbeitet werden soll) 
 +      - woher bekommen?  
 +        - am besten da nachschauen, wo auch PORTB und PB1 definiert ist 
 +        - suchen nach vector 
 +        - ''TIMER1_OVF_vect''
 +      - Eingeben von ''ISR(T'' bietet schon ''TIMER1_OVF_vect'' an 
 +      - ''ISR(TIMER1_OVF_vect)'' \\ ''{'' \\ ''}'' 
 +      - was machen wir da drin? am besten z.B. Port B3 toggeln 
 +      - ''PORTB ^= (1 << PB3);'' einfügen 
 +    - testen --> toggelt! 
 +    - Wie könnte man nun die Ausgabe nur alle paar Zentelsekunden ausführen lassen? 
 +      - Alle Zeilen in den Interrupt? --> bloß nicht! 
 +      - SW_Flag in ISR setzen und in main auswerten 
 +        - ''uint8_t IntFlag=0;'' als globale Variable 
 +        - ''IntFlag=1;'' in die Interrupt Routine 
 +        - ''if(IntFlag==1)'' \\ ''{'' \\ ''IntFlag=0;'' \\ ... \\ ''}'' 
 +      - Geschwindigkeit zu langsam? 
 +        - statt ''TCCR1B |= 1<<CS12;'' besser ''TCCR1B |= 1<<CS11;'' 
 +      - Aber wie kommt man z.b. genau auf eine Millisekunde? 
 +      - Man nehme: 
 +        - Takt: 18.432 MHz  
 +        - 8-Bit Counter: zählt bis 256 (16 Bit geht nicht genau auf...) 
 +        - --> zählt 72'000x pro Sekunde bis 256 und löst Interrupt aus 
 +        - Prescaler von 8: zählt 8x so langsam, also nur 9'000 pro Sekunde und löst interrupt aus 
 +        - Im Interrupt von 9 herunterzählen: bei jeder 0 wäre es eine Millisekunde 
 +    - up-Down-Counter ansehen