Skript
Bildschirmlupe an!
SW2 Hello Display World - fast Counter
 Wdh. Hello Blinking World: 
 DDRx, PORTx, _Delay_ms() --> R steht fast immer für Register
 
 --> 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
 
 
 Heute „Hello World“ in echt! Timer + Displayausgabe
 
 „Kapitel 2 Sound und Timer bitte nachträglich anschauen“
 
 Frage an Studis „Wer weiß nicht was PWM ist?“
 
In MC Studio
 neues Projekt 02_timer
 
 jetzt neu: mit Display! 
 --> Bibliothek aus wiki herunterladen!
 
 Project --> Add --> existing Item (NICHT drag & drop)
 
 bei mir --> F2 Namen ändern auf lcd_lib_de.h
 
 Split Screen
 
 Was tun, um Lib in main einzufügen?
 
 #include! 
 #inc + <Tab>
 
 --> Unterschied <lib.h> vs "lib.h"
 
 
 
 Durchsicht der lcd_lib_de.h
- 
 --> 
CPU Frequenz, wichting für genaues Timing der delays
 
 
 hier 
18,432 MHz --> Minimexle Frequenz
 
 
 Warum 18'432'000 Hz?
 ILIAS --> Elektronik Labor --> MiniMEXLE Schaltbild 
 
 „Schreck!“ sowiel Krams auf dem Schaltplan!
 
 Wo ist der Quarz? Quarz schwingt mechanisch im E-Feld --> Schaut im Bild aus wie ein Kondensator
 
 
 
 defines --> keine Magic numbers
 
 Funktionsprotoypen --> bitte immer am anfan angeben --> gut für eine Übersicht
 
 
 als erstes immer Initialisierung (anlegen der Variablen, verschiedene Konfigurationen etc.)
 lcd_i + <tab> 
 
 schon mal kompilieren (immer mal kompilieren zum test, ob noch alles klappt)
 
 noch nicht lauffähig, da nichts angezeigt !
 
 
 einen String ausgeben!
 welche Unterfunktion wohl geeignet?
 
 Hinweis auf Inkonsistenz bei Namensgebung  
 
 Eingabe lcd_displayMessage("Hallo!", 0,0 ) --> Hinweis auf Zählanfang 0 nicht 1!
 
 
 Flashen auf Minimexle
 Add Target --> STK500 --> ersten COM Port auswählen (und - falls es nicht passt - den nächsten)
 
 Tools --> Device Programming 
 Apply --> Device Signature sichtbar?
 
 --> Memories --> Program
 
 
 
 Ausgabe von Hallo! Zähler: 
 kann ä nicht schreiben , sondern schreibt µ, warum? 
 --> Datasheet lesen! 
 
 Am besten in der Schaltung den Namen suchen
 
 Googeln nach DEM16216 Datasheet --> Datenblatt etwas kurz? Blockdiagramm (immer schön Bilder in eigene Dokus machen!) --> ST7066U!
 
 Googeln nach ST7066U Datasheet 
 
 Kurzes darüberscrollen über das Datasheet
 
 --> 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? --> % !
 
 
 Vergleich in Simulide:
 Aufbau der Schaltung: mega88 + Hd44780 (ist kompatibel zu ST7066U)
 
 Wie verbinden? Siehe lib (wenn gut beschrieben) oder MEXLE Schaltung 
 
 In lib: Port-Bits. PIN_EN, PIN_RS --> wo in Simulide? 
 
 Für was steht EN? --> Enable. RS --> Register Select
 
 PORT_DATA: von PORTC nur die ersten 4 bits (0…3) 
 
 in Simulide 
18,432 MHz eingeben! 
 
 
 hex file Flashen
 
 --> animation einschalten (High/Low wird angezeigt)
 
 es wird noch nichts ausgegeben?? --> im Code schauen oder im Schaltplan!
 
 PC0 auf D4, PC1 auf D5, PC2 auf D6, PC3 auf D7
 
 jetzt klappts, aber ä an falscher Position
 
 
 lcd_gotoxy einfügen
 In Simulide autoload einschalten!
 
 
 
Jetzt: aufsteigende Zahlen ausgeben
Was tun?
 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)
 
 sprintf(output_str, "i:%03u", i); 3 --> drei Dezimalstellen, u --> unsigned
 
 output_str deklarieren
 
 lcd_displayMessageoutput_str, 1,0);
 
 kompilieren --> #include <stdio.h> vergessen 
 
 Simulation herunterdrehen
 
aktuell zählt er nicht so schnell wie die CPU kann, sondern so schnell wie er es ausgeben kann.
Die CPU kann aber schneller!
 Blick ins Datenblatt des atmega88
 
 Blockbild des atmega
 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
 TCNTn 
 --> timer Counter ; für was steht 
n? in Mathe? --> Schaubild gilbt für alle 3 Zähler. Hier ist n=1, da 
TC1!
 
 
 das ist der eigentliche aufsteigende Zählwert
 
 kann auch beschrieben werden
 
 
 OCRnA --> Output Compare („Wert zum gegen-checken“)
 
 TCCRnA --> für was steht TC? Timer Counter! Für was R? Register! --> hier neu: C für control
 
 
 Zeilen mit sprintf und lcd_displayMessage kopieren#
 
 bei Ausgabe Position ändern: lcd_displayMessageoutput_str, 1,**7**);
 
 diesmal: sprintf(output_str, "TC:%03u", TCNT1); -->  an zweiter Pos ausgeben. --> wichtig: 3 in 5 ändern!
 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 generator“
 
 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 zweitenlcd_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
 
- 
 
 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 

 
 
 
 
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:
- 
 
 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