DW EditSeite anzeigenÄltere VersionenLinks hierherAlles aus-/einklappenNach oben Diese Seite ist nicht editierbar. Sie können den Quelltext sehen, jedoch nicht verändern. Kontaktieren Sie den Administrator, wenn Sie glauben, dass hier ein Fehler vorliegt. ====== 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'' - ''F_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, 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 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 CKG Edit