![]() |
MikrocomputertechnikProf. Jürgen Plate |
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:
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:
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:

...
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
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.
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:
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:
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 0 | Zeichenmuster 1 | Zeichenmuster 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).
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.
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 loDer 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.
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
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:
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)
org data
...
dlycnt ds.b 1
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