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