Mikrocomputertechnik


Prof. Jürgen Plate

Praktikum Mikrocomputer

Versuch 1: Parallel-Ports, Unterprogramme, Zeitverzögerung, Tabellenzugriff

Bei diesem Versuch soll unter Verwendung der Ports B und C des M68HC11 Datenverkehr mit externen Komponenten durchgeführt werden. Es soll grundsätzlich im polling mode gearbeitet werden, d.h. ohne Unterbrechungen. Geeignete Programmteile sind als Unterprogramm zu formulieren. Zeichnen Sie immer zuerst ein Flussdiagramm und programmieren Sie dann danach.

Die Stellung der Schalter S7 - S4 soll andauernd über Port C [3:0] eingelesen und über Port C [7:4] auf die LEDs L7 - L4 ausgegeben werden. Die Initialisierung des Ports C soll als Unterprogramm (Name: PINI) formuliert werden.

Zusätzlich soll ein 8-bit-Lauflicht mit einem Takt von 10 Hz programmiert werden. Seine aktuelle Stellung soll über Port B [7:0] ausgegeben und mittels der LEDs L15 - L8 angezeigt werden. Dabei soll zuerst die LED L8 eingeschaltet werden, im zweiten Schritt zusätzlich LED L9, dann zusätzlich LED L10 usw. bis alle acht LEDs leuchten. Nun wird im folgenden Schritt LED L8 ausgeschaltet, dann L9 usw. bis alle LEDs wieder dunkel sind - worauf das Spiel von vorne beginnt. Tipp: Der Effekt ist ganz einfach zu erreichen, wenn der aktuelle 8-Bit-Wert, der die LEDs repräsentiert, immer nach links geschoben wird und in der ersten Phase zusätzlich das niederwertige Bit auf 1 gesetzt wird. In der zweiten Phase ist nichts zu tun, da sowieso eine 0 nachgeschoben wird.

Der zeitbestimmende Programmteil sowie das Weiterschalten und Anzeigen des Lauflichts sollen jeweils als Unterprogramme (Namen: DELAY und DISPLAY) formuliert werden.

Nun wird das Programm erweitert: Es soll sich der Takt verändern lassen. Der über die Schalter S7 - S4 des Experimentier-Boards eingestellte und über Port C [3:0] eingelesene Wert (Pull-Up-Widerstände nicht vergessen) stellt die Lauflichtfrequenz in Hz dar, wenn er grösser als 0 ist. Der Wert 0 führt zu einem Anhalten der Anzeige (Lauflicht bleibt stehen und läuft erst weiter, wenn der Wert grösser als 0 ist). Siehe Anmerkung unten.

Als Letztes soll noch mittels der Taste T0, die am Eingang STRA des M68HC11 anzuschließen ist, das Lauflicht auch bei Frequenzen größer 0 angehalten werden und umgekehrt (toggle).

Anmerkung: Es gilt bekanntlich, dass die Frequenz ein Kehrwert der Zeit (Periodendauer) ist (f = 1/t). Für eine Warteschleife, wie sie in der Vorlesung behandelt wurde, muss die Frequenz in eine Periodendauer umgerechnet werden (1 Hz = 1000 ms, 2 Hz = 500 ms, 3 Hz = 333 ms, 4 Hz = 250 ms usw.). Das Umrechnen der Frequenz in die Wartezeit kann programmtechnisch auf zweierlei Weise geschehen, durch (Integer-)Division oder durch eine Tabelle. Verwenden Sie in diesem Fall die Division (IDIV-Befehl). Schematisch stellt sich die Programmierung wie folgt dar:

  1. Entwurf einer Warteschleife für 1 Hz = 1000 ms mit einem möglichst großen Startwert I des Schleifenzählers.
  2. Einlesen der Schalterstellung und Expandieren des Wertes auf eine 16-Bit-Variable K.
  3. Division von J = I/K. J ist nun der aktuelle Startwert für die Zählschleife.

Versuch 2: Parallel-Ports, 7-Segmentanzeige, State-Machine

Bei diesem Versuch soll unter Verwendung der Ports B und C des M68HC11 Datenverkehr mit externen Komponenten durchgeführt werden. Es soll grundsätzlich im polling mode gearbeitet werden, d.h. ohne Unterbrechungen. Geeignete Programmteile sind als Unterprogramm zu formulieren. Zeichnen Sie immer zuerst ein Flussdiagramm und programmieren Sie dann danach.

Die Initialisierung der Ports sowie alle anderen Initialisierungen und die Vorbelegung von Variablen soll als Unterprogramm (Name: INIT) formuliert werden.

Wir wollen mit unserem System eine Toaster steuern, dessen Funktionen hier leider nur durch LEDs repräsentiert werden. Der Toastvorgang läuft folgendermaßen ab:

Für alle Abläufe wird, wie schon im ersten Versuch, einem Warteroutine DELAY benötigt, die hier eine Periodendauer von 0,5 s hat (für den Test kann man diese Zeit ja verkürzen). Das bedeutet auch, dass alle

Hinweis zur Programmierung

Eine sehr häufig anzutreffende Methode derartige Abläufe vernünftig zu programmieren, ist die sogenannte State-Machine (Zustandsfolge-Maschine, Automat). Das Prinzip kennen Sie hinreichend aus der Vorlesung "Digitale Schaltwerke" und aus den Beispielen der Mikrocomputer-Vorlesung. Der Unterschied ist hier die Realisierung als Software. Der Toaster kennt fünf Zustände, die als Konstanten definiert werden: <

STANDBY    EQU 1  ; Ruhezustand
START      EQU 2  ; Toast einziehen
ROAST      EQU 3  ; Toast rösten
EXT_ROAST  EQU 4  ; Röstzeit erweitern (Nachrösten)
FINI       EQU 5  ; Toast auswerfen

Die State-Machine gestaltet sich folgendermassen:

Die folgende Abbildung zeigt das grundlegende Zustandsfolge-Diagramm. Notieren Sie alle Aktionen, die für jeden Zustand auszuführen sind und eventuell benötigte Variablen. Setzen Sie diese Informationen dann in ein Programm um. Es kann bei umfangreicheren Aufgaben durchaus sinnvoll sein, alle Aktionen eines jeden Zustands in einem Unterprogramm zusammenzufassen. So können Die Aktions-Unterprogramme unabhängig von der eigentlichen State-Machine entwickelt und getestet werden. Das folgende Bild zeigt nur die Zustandsübergänge bzw. dei Bedingungen, nicht jedoch die Aktionen (z. B. das Runterzählen).

Grndsätzlich folgt das Programm dem Schema aus der Vorlesung, das hier nochmals kurz skizziert wird. Übernehmen Sie das unten stehende Schema in Ihr Hauptprogramm und formulieren Sie die notwendigen Unterprogramme für die einzelnen Aktionen.
   ...
   Definitionen, Initialisierung
   ...

; mainloop = statemachine
loop  jsr       display    ; ggf. Anzeige der Restzeit
      jsr       delay      ; Warteschleife
                           ; ggf. weitere allgemeine Aktionen

      ldaa      state      ; aktueller Status      
S0    cmpa      #STANDBY   ; Idle-Zustand, waten auf T0?
      bne       S1         ; nein, weiter
      bsr       DoStandby  ; Aktion und Statuswechsel ausfuehren
      bra       loop       
      
S1    cmpa      #WAIT      ; warten bis T0 losgelassen?
      bne       S2         ; nein, weiter
      bsr       DoWait     ; Aktion und Statuswechsel ausfuehren
      bra       loop
      
S2    cmpa      #ROAST     ; Roesten?
      bne       S3         ; nein, weiter
      bsr       DoRoast    ; Aktion und Statuswechsel ausfuehren
      bra       loop

S3    cmpa      #DO_ROAST  ; Extra braun?
      bne       S4         ; nein, zu loop (keine Fehlerbehandlung)
      bsr       DoExtend   ; Aktion und Statuswechsel ausfuehren
      bra       loop

S4    cmpa      #FINI      ; Auswerfen?
      bne       loop       ; Ops! Unbekannter Zustand!
      bsr       DoEject    ; Aktion und Statuswechsel ausfuehren
      bra       loop

Versuch 3: Serielle Schnittstelle, ASCII-Zeichenketten

Bei diesem Versuch soll unter Verwendung des SCI (Asynchronous Serial Communications Interface) des M68HC11 Datenverkehr zwischen Prozessor M68HC11 und PC als Terminal durchgeführt werden. Es soll grundsätzlich im polling mode gearbeitet werden, d.h. ohne Unterbrechungen. Als String-Terminator dient wie bei C das Nullbyte ($00). Geeignete Programmteile sind als Unterprogramm zu formulieren. Zeichnen Sie immer zuerst ein Flussdiagramm und programmieren Sie dann danach.


Wichtig: Zum Terminalbetrieb ist nach dem Terminalbetrieb der Schalter "PC-MAN" am Praktikumsrechner in Stellung MAN zu bringen. Zum Laden des Programms muss er dann wieder auf PC gestellt werden. Entsprechend muss beim Hyperterminal zum Programm-Download "aufgelegt" (Telefon-Icon in der oberen Werkzeugleiste) und zum Betrieb wieder "abgehoben" werden.


Aufgabenstellung

Zum Einstieg soll das ASCII-Zeichen 'R' mit einer Wiederholfrequenz von etwa 5 Hz andauernd auf das Terminalfenster des Hyperterminal-Programms ausgegeben werden. Die Initialisierung des SCI ist als Unterprogramm (Name: SINIT) zu formulieren. Das Senden des Zeiches soll durch ein Unterprogramm namens CHOU erfolgen, das das im Akku A übergebene Zeichen (bei freier Schnittstelle) sendet.

Als Nächstes ist ein vorbereiteter Text (nullterminierter ASCII-String) mittels des neu zu schreibenden Unterprogramms STOU dauernd auf das Terminalfenster auszugeben. Register X enthält die Adresse des ersten Zeichens des Textes. Das Unterprogramm STOU gibt die ab (X) stehenden ASCII-Zeichen unter Verwendung des Unterprogramms CHOU aus, bis das Textendezeichen (\0) gefunden wird, das selbst nicht mit ausgegeben wird. Stattdessen werden die Steuerzeichen CR (Carriage Return) und LF (Line Feed) mittels des Unterprogramms CRLF ausgegeben.

Ein über die Tastatur eingegebenes Zeichen ist mittels des Unterprogramms CHIN einzulesen und mittels des Unterprogramms CHOU als Echo auszugeben (Endlos-Schleife). Das Unterprogramm CHIN übergibt das gelesene Zeichen im Akku A.

Nun soll statt eines einzelnen Zeichens ein String über die Tastatur eingegeben, mittels des Unterprogramms STIN eingelesen und im Speicher abgelegt werden. STIN verwendet seinerseits CHIN, um ein Zeichen zu lesen. Zur Kontrolle wird der String mithilfe des Unterprogramms STOU wieder ausgegeben (Endlos-Schleife). Das Unterprogramm STIN soll folgende Eigenschaften besitzen:

  1. STIN echot die mittels CHIN eingelesenen Zeichen und legt sie im Speicher ab. Die Adresse des Eingabepuffers wird im Indexregister X übergeben.
  2. Nicht darstellbare Zeichen (alle ASCII-Zeichen < " ") werden bei der Eingabe ignoriert.
  3. Das ASCII-Zeichen CR beendet die Eingabe, es wird ohne Echo als Nullbyte abgelegt.
  4. STIN wird durch die Ausgabe von CR und LF abgeschlossen (Unterprogramm CRLF).

Das folgende Programm läuft als Schleife ab. Zuerst wird mittels STOU der Text "Passwort: " ausgegeben (ohne CRLF). Mit dem Unterprogramm STIN soll nun eine Zeichenkette eingelesen und mit einem im Programm abgelegten, konstanten String zeichenweise verglichen werden. (typische Passwortabfrage). Das Ergebnis des Vergleichs soll über Port B [6:0] ausgegeben und mittels der 7-Segment-Anzeige A5 (ohne Decoder) dargestellt werden:

  1. Sind die Zeichenketten unterschiedlich, wird das Zeichenmuster 0 (siehe Tabelle unten) ausgegeben (für "falsch"). Gleichzeitig wird ein Fehlerzähler inkrementiert.
  2. Stimmen beide Zeichenketten überein, wird eine das Zeichenmuster 1 ausgegeben. Der Fehlerzähler wird in diesem Fall zurückgesetzt.
  3. Erfolgt dreimal hintereinander eine falsche Eingabe (Fehlerzähler = = 3), wird das Zeichenmuster 2 ausgegeben, das im Rhythmus von 2 Hz im Wechsel mit Zeichenmuster 0 blinkt. In diesem Fall wird auch keine weitere Eingabe mehr angenommen und der Zustand kann nur mit der Reset-Taste beendet werden.

Die Zeichenmuster werden, wie in der Vorlesung gezeigt, in einer Tabelle mit drei Elementen abgelegt. Zur Umcodierung der Werte 0 bis 2 schreiben Sie ein Unterprogramm UMCO, das den Eingangswert in Register B erwartet, diesen prüft bzw. normiert, und den Anzeigecode ebenfalls in Register B zurückgibt.

Zeichenmuster 0Zeichenmuster 1Zeichenmuster 2

Programmieren Sie für den Vergleich ein eigenes Unterprogramm, dem die Anfangsadressen der beiden zu vergleichenden Zeichenketten in den Registern X und Y übergeben werden.

Modifizieren Sie die Eingaberoutine STIN so, dass nicht das eingegebene Zeichen, sondern stattdessen ein '*' geechot wird (es genügt in der Regel, nur einen Assemblerbefehl an der richtigen Stelle einzufügen).

Versuch 4: Zähler, bedingte Unterbrechungen

Bei diesem Versuch sollen ein einfacher Zähler betrieben und verschiedene bedingte Unterbrechungen aktiviert und bearbeitet werden. Geeignete Programmteile sind als Unterprogramm zu formulieren. Zeichnen Sie immer zuerst ein Flussdiagramm und programmieren Sie dann danach.

Aufgabenstellung

Ein 2-stelliger Sedezimalzähler ist mit einer Frequenz von etwa 10 Hz zu betreiben. Seine aktuelle Stellung soll über Port C [7:0] ausgegeben und mittels der 7-Segment-Anzeigen A1 und A2 (mit Decoder) dargestellt werden. Der zeitbestimmende Programmteil soll als Unterprogramm (D100) unter Verwendung des Registers Y formuliert werden. Orientieren Sie sich an den Programmen aus den Versuchen 1 und 2.

Modifizieren Sie das Programm nun so, dass beim Starten des Programms der Text "M68HC11 gestartet" auf das Terminalfenster ausgegeben wird. Verwenden Sie dazu die beim Versuch 3 entwickelten Unterprogramme zur Initialisierung der SCI, sowie zur Zeichen- und Stringausgabe (CHOU und STOU).

Zusätzlich sind nun bedingte Unterbrechungen zu bearbeiten. Mittels der Tasten T0 und T1, die an den Eingängen STRA und PA7 des M68HC11 anzuschließen sind (Ohne Pullup-Widerstände!), können bedingte Unterbrechungen ausgelöst werden. Als Folge darauf sollen vom Hauptprogramm aus die unten beschriebenen Aktionen erfolgen.

Die Unterprogramme zur Unterbrechungsbearbeitung sind möglichst kurz zu halten. Zur Signalisierung von der Unterbrechungsbearbeitung zum Hauptprogramm sollen zwei Flag-Variablen PFLAG (PA7) und SFLAG (STRA) verwendet werden. Die zum Starten der Unterbrechungsbearbeitungen nötigen Vektoren nicht vergessen:

    org     $ffda
    dc.w    pirq    ; pulse accu input (PA7)
    org     $fff2
    dc.w    sirq    ; IRQ/STRA
    org     $fffe
    dc.w    main    ; reset

Das Unterprogramme hx4a können Sie von dieser Angabe per Cut-and-Paste übernehmen. Dabei ist zu beachten, dass hx4a den entstehenden String nach fallenden Adressen hin aufbaut (also von hinten) und dass damit die Übergabeparameter entsprechend gewählt werden müssen. Achten Sie darauf, dass auch der durch hx4a erzeugte String mit einem Nullbyte abgeschlossen werden muss. Sehen Sie sich dazu auch den Quelltext zu den Unterprogrammen am Ende dieses Dokuments an.

* Frage: Wo befindet sich der Stand des Program Counters, wenn die Interrupt Service Routine läuft?
Antwort: auf dem Stack.
Frage: Wie komme ich da dran?
Antwort:In der Befehlsreferenz nachsehen: Stacking-Order. Dort sieht man, dass der Stackpointer auf das erste freie Byte zeigt, dann folgen:

   CC
   B
   A
   X hi
   X lo
   Y hi
   Y lo
   PC hi
   PC lo
Der PC liegt also 8 Bytes höher. Da der SP nicht zum Adressieren verwendet werden kann, nehmen wir die Befehle TSX (Transfer S nach X), TSY (Transfer S nach Y) oder den Befehl STS (Store Stackpointer). TSX und TSY erhöhen den Wert schon um 1, damit X/Y gleich auf das CC-Register zeigen. Also liegt der PC da bei X+7 bzw. Y+7.

Anhang

Binär zu ASCII-Decodierung (hx4a)

Diese Unterprogramme werden beim vierten Praktikumstermin benötigt. Im Speicher liegen die Werte bekanntermaßen binär vor. Um einen Speicherinhalt oder eine Adresse auf der seriellen Schnittstelle (Terminal) oder einem LC-Display ausgeben zu können, müsen die Werte als ASCII-Zeichen dargestellt werden.
Die folgenden vier miteinander verbundenen Unterprogramme wandeln einen 16-Bit-Wert, einen 8-Bit-Wert oder einen 4-Bit-Wert in eine Hexadezimalzahl in ASCII-Darstellung um (4, 2, oder 1 Stelle):
hx4a    psha              ; Akku A auf dem Stack ablegen
        bsr     hx2a      ; Akku B umwandeln (Stelle 3 und 4)
        pulb              ; Akku A vom Stack holen, in Akku B ablegen
                          ; Nun einfach in hx2a fallen und Akku B
                          ; umwandeln (Stelle 1 und 2)
hx2a    pshb              ; Akku B auf dem Stack sichern
        bsr     hx1a      ; Bits 0-3 von Akku B umwandeln
        pulb              ; Original-Akku B wieder holen
        lsrb              ; viermal schieben (Bits 4-7 --> 0-3)
        lsrb
        lsrb              ; und nun die obere Haelfte umwandeln
        lsrb              ; indem einfach in hx1a gefallen wird
                          ; Nun wird die eigentliche Arbeit gemacht:
hx1a    andb    #$0F      ; die oberen 4 Bits ausmaskieren
        addb    #48       ; ASCII-Wert von "0" addieren
        cmpb    #57       ; groesser als "9"?
        bls     hx1b      ; nein, dann speichern
        addb    #7        ; sonst die Distanz zwischen "9" und "a" addieren
hx1b    stab    0,y       ; im Speicher ablegen, auf den Y zeigt
        dey               ; Y zeigt jetzt auf die Stelle davor
        rts               ; und weg
Beispielaufruf:
   
        ldd     #$AFFE    ; Hexzahl in Akku D laden
        ldy     #PUFFER+3 ; Position auf letzte Stelle im Puffer
                          ; der Puffer muss im Datenbereich reserviert sein
        bsr     hx4a      ; do it!
        ldy     #PUFFER   ; y --> Pufferanfang
        bsr     STOU      ; z.B. seriell ausgeben

Alternative Warteschleifenprogrammierung

Optional kann anstelle einer Warteschleife mittels X- oder Y-Register auch der frei laufende Zähler für die Zeitverzögerung verwendet werden (ohne Interrupt). Dazu sind folgende Vereinbarungen notwendig:

  1. Bei den Konstantendefinitionen
    tflag   equ   $1025   ; Timer Flag Register
    tmask   equ   $1024   ; Timer Mask Register
    faktor  equ   3       ; Multiplikationsfaktor fuer Timer
                          ; 3 ~ 0,1 s:
                          ; 32,77 * 3 = 98,31 (fast 0,1 s)
                          ; 15 ~ 0,5 s:
                          ; 32,77 * 15 = 491,6 (fast 0,5 s)
                          ; 30 ~ 1 s:
                          ; 32,77 * 30 = 983,1 ms (fast 1 s)
    
  2. Bei den Veriablendefinitionen
            org   data
            ...
    dlycnt  ds.b  1
    
  3. Bei den Unterprogrammen (tinit wird beim Programm-Init aufgerufen)
    tinit   psha          ; Timer Init
            clr   tmask   ; Timer-Prescaler / 1 --> 32,77 ms
            ldaa  #faktor ; Zeit festlegen
            staa  dlycnt
            pula
            rts
    
    
    delay   psha          ; Delay-Routine
    delay1  ldaa  tflag   ; warten auf Timer-Ueberlauf
            bpl   delay1
            ldaa  #$80    ; TOFL-Flag zuruecksetzen
            staa  tflag
            dec   dlycnt  ; delay-counter dekrementieren
            tst   dlycnt
            bne   delay1  ; warten auf dlycnt == 0
            ldaa  #faktor ; dann dlycnt neu setzen
            staa  dlycnt   
            pula          ; und fertig
            rts
    



[ Praktikumsanleitung ] [ Skript ]

Copyright © FH München, FB 04, Prof. Jürgen Plate
Letzte Aktualisierung: