MezData-Logo

Zylonenauge Übungen

Effektlicht

Schema
Schema der Platine
Matrixanordnung
Matrixanordnung der LED

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ösungenStruktogrammZustandsdiagramm

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 = 4000
Vorteiler: 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
Lösung anzeigen..

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

Muster 1
Ausgabemuster

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.

Quellcode Vorgabe [zylon-muster1/zylon-muster1-aufg.h]
#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.

Lösung anzeigen..

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.

Lösung anzeigen..

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.

Quellcode Vorgabe [hoertest/hoertest_aufg.h]
#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 Struktogramm

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

  1. 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.
  2. Ein weiterere ISR verzögert den Aufruf der Timer-ISR. Entfällt hier.
  3. 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!

  1. Lösung: Mit Inline-Assembler eine bessere (schnellere) ISR programmieren. Der Jitter durch unterschiedliche Beendignungszeit des unterbrochenen Befehls wird dabei jedoch nicht behoben.
  2. Lösung: Die PWM-Ausgabemöglichkeit des µC nutzen, dabei ist keine ISR zur Erzeugung des Signals notwendig.

Lösung anzeigen..

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