Skript
Bildschirmlupe an!
SW2 Hello Display World - fast Counter
- Wdh. Hello Blinking World:
DDRx,PORTx,_Delay_ms()-->Rsteht 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.hF_CPU- --> 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, oderldc_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: vonPORTCnur die ersten 4 bits (0…3)- in Simulide
18,432 MHzeingeben! - hex file Flashen
 - --> animation einschalten (High/Low wird angezeigt)
 - es wird noch nichts ausgegeben?? --> im Code schauen oder im Schaltplan!
 PC0aufD4,PC1aufD5,PC2aufD6,PC3aufD7- jetzt klappts, aber
äan falscher Position 
 lcd_gotoxyeinfügen- In Simulide autoload einschalten!
 
 
Jetzt: aufsteigende Zahlen ausgeben Was tun?
- Laufvariable anlegen und nutzen:
uint8_t i=0;undi++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--> unsignedoutput_strdeklarierenlcd_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
sprintfundlcd_displayMessagekopieren# - bei Ausgabe Position ändern:
lcd_displayMessageoutput_str, 1,**7**); - diesmal:
sprintf(output_str, "TC:%03u", TCNT1);--> an zweiter Pos ausgeben. --> wichtig:3in5ä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
0normal 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
CS10setzen --> schnellster Zähler - Wo ist
CS10? im RegisterTCCR1B - 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:
 TCNTnwird 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
OC1Adurchsuchen!- 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?
 11z.B. delay für dauerhaftes Leuchten nach ein paar Millisekunden- wir brauchen
COM1B1=1undCOM1B0=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
WGM12setzen, das ist aber inTCCR1B! 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
strinitialisieren:char str[3]=„“; - Umbenennen der Variablen über
Refactoring»Rename--> z.B.SwCounterundOutputStr - 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
SwCountersuchen - RAM Table über
MCU Monitor - auf Byte PC umschalten
 - Suchen der Speicherzelle
 - Geschwindigkeit reduzieren
 
 - Tipp 4: Logic Analyzer
PORTB|=1«PB3;nach erstemlcd_putstrPORTB&=~1«PB3;nach zweitemlcd_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(Tbietet schonTIMER1_OVF_vectan 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 VariableIntFlag=1;in die Interrupt Routineif(IntFlag==1)
{
IntFlag=0;
…
}
 - Geschwindigkeit zu langsam?
- statt
TCCR1B |= 1«CS12;besserTCCR1B |= 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