Effektlicht


Der Systemtakt beträgt 4Mhz. Bei Betätigung der Taste T0 soll ein Leuchtpunkt von L0 bis L7 wandern, Verweildauer bei der jeweiligen LED 200ms und dann von L6 zurück nach L0, Verweildauer 50ms. Am Ende sind alle LED wieder aus. Die nicht entprellten Taster T0..T2 sind gegen GND verschaltet, interne PullUp-Widerstände sind notwendig. Die LED sind als Matrix verschaltet.
Erstellen Sie eine PAP-Skizze um Ihre Gedanken zu ordnen.
Ist ein explizites Entprellen von T0 notwendig? Begründen Sie.
Erstellen Sie ein Programm. Initialisieren Sie die notwendigen PORT-Ausgänge und PullUp-Widerstände für T0..T2. Lösung anzeigen..
Raffinierte Lösung
Hier wird auch während des LED-Laufs ein Betätigen der Taste T0 registriert:
Raffinierte Lösung anzeigen..
Erstellen Sie ein Struktogramm und ein Zustandsdiagramm für die raffiniertere Lösung.
Lösungen


Ist bei dieser Lösung ein Entprellen notwendig? Begründen Sie.
Pulsweitenmodulation einer LED mit Software
Verwenden Sie ZylonEffektlicht2.ino für weitere Bearbeitung. LED15 soll pulsweitenmoduliert entsprechend i heller und dunkler in einer ISR geschaltet werden. Der Helligkeitswert wird mit einer globalen Variablen helligkeit mit dem Maximalwert MAXLED an die ISR übergeben.
Erstellen Sie Programmcode für Initialisierung des CTC-Timer0 Aufruf mit 1000 Hz (1ms).
Lösungsweg
Taktzahl = 4 MHz / 1000 Hz = 4000Vorteiler: Timer0: 8Bit Maximum 255: 4000/256 = 15,6 gewählt 64
Komparatrorwert: 4000/64= 62,5 runden 63 gewählt 62 weil 0..62 sind 63 Durchläufe
Interruptzeit = (1+62)*64/4MHz=1008µs
TCCR0B = 3; // Systemtakt / 64
TCCR0A= 1<<WGM01; // CTC Mode mit OCR0A als TOP
TIMSK = 1<<OCIE0A;// Interrupt frei geben
OCR0A=62; // bei 62 Interrupt ausloesen
sei(); // globale Interruptfreigabe
Erstellen Sie die ISR bei der LED15 entsprechend helligkeit gesteuert wird. Lösung anzeigen..
Zeiten messen
Wie lange benötigt die ISR zur Ausführung, wie wird dadurch die Genauigkeit der _delay_ms()-Funktion beeinflusst?
Modifizieren Sie das Programm:
- Die Timer0-ISR soll jetzt alle 100µs aufgerufen werden.
- Die Variable helligkeit bekommt den festen Wert 3.
- Beim Eintritt in die ISR soll auf PD0 1 und am Ende 0 ausgegeben werden.
- Im Hauptprogramm wird endlos PD1 umgeschaltet und mit _delay_ms(DZEIT) 10ms gewartet.
- Verwenden Sie hierzu Inline-Assembler:
asm volatile ("sbi 0x12,0"); // sbi PORTD,0 PD0<-1 asm volatile ("cbi 0x12,0"); // cbi PORTD,0 PD0<-0 asm volatile ("sbi 0x10,1"); // sbi PIND,1 PD1 toggeln
Messen Sie mit Hilfe eines Oszilloskops oder eines Frequenzzählers die Signalzeiten für PD0 und PD1.
Machen Sie eine zweite Messung an PD1 mit deaktiviertem Timer-Interrupt.
Typische Ergebnisse
- ISR-Zeiten an PD0:
- Periodendauer 100µs
- Zeit PD0 = 1: 5µs
- _delay_ms()-Dauer an PD1:
- Mit ISR: 11,56ms
- Ohne ISR: 10ms
Entwickeln Sie eine Funktion my_delay_ms(int n) unter Verwendung der CTC-ISR, deren Genauigkeit unabhängig von zwischenzeitlichen Interrupts ist.
Zwei Musterfolgen ausgeben

Zwei Musterfolgen sollen ausgegeben werden. Die Umschaltung der Muster erfolgt über Taste T0. Im weiteren Verlauf kann die Ausgabegeschwindigkeit mit Taste an T1 verändert werden.
- Zylonenauge: ATtiny 2313 @ 4Mhz
- Musterwechsel zunächst alle 400ms mit _delay_ms(400)
- Zur prellfreien Tastenabfrage keycheck() verwenden
- Die Länge der Musterfolgen wird als erstes Array-Element gespeichert
- Quellcode anständig formatieren und kommentieren!
#include <avr/io.h> #include <avr/interrupt.h> #include <util/delay.h> /* * Aufgabe 1 muster-Array */ unsigned char musterzaehler=1,musternummer=0; #define KEYREADER (~PIND)>>4 // Einlesen, invertieren, maskieren zurecht schieben unsigned char keyOld = 0; // alter Tasten-Zustand unsigned char keyEnter, keyExit; // gedrueckte und losgelassene Tasten void keyCheck() { // Tastaturabfrage mit Flankendedektion unsigned char keyTest, tmp; keyEnter = 0, keyExit = 0; keyTest = KEYREADER; // Einlesen if (keyOld != keyTest) { // hat sich was getan _delay_ms(20); // Prellen abwarten tmp = KEYREADER; // noch mal Einlesen if (tmp == keyTest) { // ist es stabil? keyEnter = (~keyOld) & keyTest; // steigende Flanke !alt und neu keyExit = keyOld & (~keyTest); // fallende Flanke alt und !neu keyOld = keyTest; } } } void wechsleMuster(){ /* * Aufgabe 2 */ } int main(){ // Hauptprogramm DDRB = 0xFF; // PORTB als Ausgang PORTB = 0xE0; // Leds L3..L0 aktiv PORTD = 0x70; // PullUps an while(1){ // Endlosschleife keyCheck(); if(keyEnter&1){ // Taste gedrueckt? musternummer++; if(musternummer>=sizeof(muster)/sizeof(muster[0])) musternummer=0; } wechsleMuster(); _delay_ms(400); } return 0; }
Erstellen Sie ein Array muster[][] für die zwei Muster.
Das Feld muster beginnt bei Adresse 0x60. Wie sieht die Speicherbelegung für das Feld aus?
Erstellen Sie ein Unterprogramm wechsleMuster(), das bei jedem Aufruf das nächste Muster ausgibt.
Lösung anzeigen..Erstellen Sie ein Struktogramm für main().
Duplizieren Sie das Projekt als zylon-muster2. Der Musterwechsel soll nun mittels Timer0 CTC-Interrupt erfolgen, in dessen ISR wechsleMuster() aufgerufen wird. Modifizieren Sie den Quelltext entsprechend.
Was passiert, wenn bei Muster 0011 die Taste gedückt wird, wie sieht der Übergang aus?
Duplizieren Sie das Projekt als zylon-muster3. Die Ausgabegeschwindigkeit soll nun mittels Taste T1 umgeschaltet werden können. Ein Array mit verschiedenen Geschwindigkeitsstufen ist gegeben:
musterAnzeigendauer[]={400,200,100,50}; // Zeiten des Wartens in ms bis naechstes Muster
Welchen Datentyp muss das Feld haben?
Die Wartezeiten sollen mit einer eigenem Delay my_delay_ms(unsigned int n) realisiert werden, das einen Tick-Zähler in der ISR verwendet.
Ändern Sie die Einstellungen des Timers für einen Aufruf jede ms. Im Timer wird eine Variable ticks hochgezählt, die in my_delay_ms(..) verwendet wird.
Wie und wo muss ticks definiert werden, Begründen Sie.
Im Hauptprogramm wird bei Druck auf Taste T1 eine Variable anzeigendauerIndex hochgezählt bzw. wieder auf 0 gesetzt.
In der TimerISR wird wechsleMuster() entsprechend des anzeigedauerIndex aufgerufen.
Modifizieren Sie den Quellcode entsprechend.
Hörtest (angelehnt an Abi-Aufgabe HP 2014 A2)
Für einen Hörtest sollen Töne mit einem Mikrocontroller erzeugt werden. Hier wird das Z-Eye verwendet, ich habe die ursprüngliche Aufgabenstellung modifiziert, einen C-Quelltext vorgegeben.
- Der Systemtakt beträgt 1MHz.
- Die Erzeugung des Rechtecksignals bzw. Tons erfolgt mittels Timer-Interrupt durch zyklisches Invertieren von PB4 an dem ein Lautsprecher angeschlossen ist.
- Mit jedem Tastendruck am zunächst prellfreien Taster T0 kann einer von 10 Zeitwerten für den Timer gewählt werden.
- Die Zeitwerte sind in einem zusammenhängenden Speicherbereich zeitwert hinterlegt.
Hauptprogramm (Main-Loop)
In der Main-Loop des Hauptprogramms wird der Taster T0 zyklisch abgefragt (Polling). Jedes Mal, wenn der Taster losgelassen wird, soll eine Variable (ein Register) ton inkrementiert werden. Mit Hilfe dieser Variablen wird später durch das Unterprogramm (die Funktion) zeitwerte ein Zeitwert aus Tabelle zeitwert gelesen. Nach Erreichen des Wertes 9 soll die Variable ton wieder mit 0 geladen werden.
#include <util/delay.h> unsigned int zeitwert[]={2000,1000,500,250,125,63,50,40,35,30}; unsigned char ledausgabe[]={0xe1,0xe2,0xe4,0xe8,0xd1,0xd2, 0xd4,0xd8,0xb1,0xb2,0xb4,0xb8,0x71,0x72,0x74,0x78}; unsigned char ton=0; void zeitwerte(){ // Aufgabe } // ISR Aufgabe int main(){ // Hauptprogramm DDRB = 0xFF; // PORTB als Ausgang PORTB = 0xE0; // Leds L3..L0 aktiv PORTD = 0x70; // PullUps an // Timerinit Aufgabe zeitwerte(); sei(); while(1){ // Endlosschleife // Main-Loop Aufgabe } }
Entwerfen Sie den Programmablaufplan (PAP) oder ein Struktogramm für die Main-Loop.
Lösung

Schreiben Sie das Assemblerprogramm für den PAP bzw. C-Programm entsprechend des Struktogramms für die Main-Loop. (Lösung unten)
Erklären Sie, weshalb ein prellfreier Taster verwendet werden sollte. Wie kann das Problem softwaremäßig gelöst werden, wenn kein prellfreier Taster zur Verfügung steht.
Lösung
Durch das Prellen entstehen beim Tastendurck mehrere Impulse, daduch wird ton um mehr als einen Wert verändert. Abhilfe schafft eine Verzögerung, länger als die Prellzeit, z.B. mittels Delay-Funktion nach dem Tastendruck.Unterprogramm Zeitwerte
Über die Variable (Register) ton können 10 verschiedene Werte aus einer Tabelle zeitwert im Speicher gelesen werden. Der Wert stellt Interruptzeit des Timers dar. Ausserdem soll eine LED Ln (n=15-ton) leuchten. Eine Tabelle ledausgabe für die Ausgabe auf der LED-Matrix ist vorgegeben.
Schreiben Sie den Programmcode. (Lösung unten)
Timer-Interrupt
Der 16-Bit Timer wird mit Systemtakt betrieben. Die ISR soll nach zeitwert vielen Takten aufgerufen werden.
Schreiben Sie den Code für die Timerinitalisierung und die Interruptserviceroutine in Assembler oder C. (Lösung unten)
Berechnung der Zeitwerte
Berechnen sie die Ausgangsfrequenz für ton = 0 und ton = 9.
Lösung
Die Zeitwerte geben die halbe Periodendauer in µs der Frequenzen vor.
ton = 0: 1/(2000µs*2) = 250 Hz
ton = 9: 1/(30µs*2) = 16667 Hz
Vor allem bei der höchsten Frequenz konnte im Oszillogramm ein Jitter festgestellt werden, die Frequenz ist nicht stabil. Erläutern Sie wodurch dieses Problem entstehen kann und schlagen Sie eine Lösung vor.
Lösung
Mögliche Ursachen
- Die ISR braucht zu lange. Bei der höchsten Frequenz wird die ISR alle 30µs aufgerufen. Ein wiederholter ISR-Aufruf mit Rücksprung benötigt mindestens 1+3+2+4 = 10 Takte. In der ISR wird lediglich PB4 invertiert der entsprechende Maschinenbefehl sollte 2 Takte benötigen. Die 30µs müssten ausreichen.
- Ein weiterere ISR verzögert den Aufruf der Timer-ISR. Entfällt hier.
- Der beim Eintreten der ISR unterbrochene Befehl braucht unterschiedlich lange bis er fertig ist (1..4 Takte) und in die ISR verzweigt werden kann.
Ein Blick auf den von der Arduino-Software erzeugen Assembler-Code offenbart:
ISR(TIMER1_COMPA_vect){
84: 1f 92 push r1
86: 0f 92 push r0
88: 0f b6 in r0, 0x3f ; 63
8a: 0f 92 push r0
8c: 11 24 eor r1, r1
8e: 8f 93 push r24
PINB=1<<PB4; // Ausgang PB4 invertieren
90: 80 e1 ldi r24, 0x10 ; 16
92: 86 bb out 0x16, r24 ; 22
}
94: 8f 91 pop r24
96: 0f 90 pop r0
98: 0f be out 0x3f, r0 ; 63
9a: 0f 90 pop r0
9c: 1f 90 pop r1
9e: 18 95 reti
Dieser Code-Unfug braucht sicher mehr als 30µs, somit kommt die ISR nicht mehr hinter her!
- Lösung: Mit Inline-Assembler eine bessere (schnellere) ISR programmieren. Der Jitter durch unterschiedliche Beendignungszeit des unterbrochenen Befehls wird dabei jedoch nicht behoben.
- Lösung: Die PWM-Ausgabemöglichkeit des µC nutzen, dabei ist keine ISR zur Erzeugung des Signals notwendig.
Ergänzende Aufgaben (höherer Schwierigkeitsgrad)
Die Tabellen zeitwert und ledausgabe werden bei der C-Lösung im SRAM angelegt, modifizieren Sie den Code so, dass sie sich im Programmspeicher befinden.
Modifizieren Sie die C-Lösung so, dass die PWM-Möglichkeiten des µC für die Tonausabe genutzt werden.
Erstellen Sie eine Assembler-Lösung für den Hörtest.
Zur Info: Korrekturhinweis zur damaligen Aufgabe