Pfad: Home => AVR-Übersicht => Tutorium => Teil 2    (This page in English: Flag EN) Logo

; Test 2: Board kennenlernen: Eingabe von einem Port


; Was man hier dazu lernen kann:

; - Eingabe von einem Port lesen
; - Das Aufrufen von Unterprogrammen, Stack- (Stapel-Befehle)
; - Binäre Rechen Operationen (AND, OR, ROL, etc.)
; - Bedingte Verzweigungen (SBIx, BRxx)

.NOLIST
.INCLUDE "8515def.inc"
.LIST

; Ein Universalregister definieren:


.DEF   mp = R16

; Der Reset-Sprungbefehl an Adresse 0:


   RJMP   main

; Hier beginnt das Hauptprogramm


main:   LDI   mp,LOW(RAMEND) ;Initiate Stackpointer
   OUT   SPL,mp
   LDI   mp,HIGH(RAMEND)
   OUT   SPH,mp

; Diese Befehle richten einen Stack im SRAM ein. Ein Stack wird immer
; dann gebraucht, wenn Unterprogramme oder Interrupts aufgerufen werden.
; Beim Aufruf muss der aktuelle Zählerstand der Bearbeitung in einem
; Speicher abgelegt werden, damit nach Beendigung wieder an die auf-
; rufende Stelle zurückgekehrt werden kann. Der Stack wird immer am
; oberen Speicherende des SRAM angelegt. Das Speicherende des jeweili-
; gen Chips ist in der Datei "xxxxdef.inc" mit dem Namen RAMEND defi-
; niert.
; Wird ein Byte auf dem Stapel oder Stack abgelegt, dann wird ein
; Byte in das SRAM geschrieben (an die Adresse SPH:SPL), dann wird der
; Stapelzähler SPH:SPL um Eins verringert. Weiteres Ablegen bringt den
; Zeiger näher an den Anfang des SRAMs. Wird ein Wert aus dem Stapel
; entnommen, dann wird der Zeiger um eins erhöht und der Wert ausgelesen.
; Der jeweils zuletzt im SRAM abgelegte Wert kommt bei der Entnahme
; wieder als erster heraus (Last-in-First-Out-Struktur).
; Da der Programmzähler und die weitere Adressverwaltung 16 Bits
; Breite hat, alle Register und das SRAM aber 8 Bits, muss man jeweils
; mit 2 Bytes zu 8 Bits oder einem Wort zu 16 Bits arbeiten. Der
; SRAM-Speicher hat 16 Bit Adresse, also gibt es den Port SPL für die
; unteren 8 Bits, den Port SPH für die oberen 8 Bits der Adresse. Zusam-
; men ist SPH:SPL ein 16-Bit-Zeiger in das SRAM.
; Die Rechenoperationen LOW und HIGH veranlassen den Assembler, das
; 16-Bit-Wort RAMEND jeweils zu je 8-Bits aufzuteilen, damit es in die
; Ports SPL und SPH übertragen werden kann.

; An Port D sind die Schalter angeschlossen. Der Port D wird gelesen.
; Sein Datenrichtungsregister muss also acht Nullen kriegen:

   LDI   mp,0x00 ; 8 Nullen in Universalregister
   OUT   DDRD,mp ; an Datenrichtungsregister

; Die Schalter verbinden die Eingänge des Ports D mit GND. Damit bei
; offenem Schalter ein definierter Pegel herrscht, können die Pull-Up-
; Widerstände eingeschaltet werden. Das ist an sich auf dem STK200 nicht
; erforderlich, da diese extern vorhanden sind. Aus pädagischen Gründen
; schalten wir sie trotzdem ein. Das erreicht man durch das Schreiben
; von Einsen in das Datenausgangsregister:

   LDI   mp,0xFF ; 8 Einsen in Universalregister
   OUT   PORTD,mp ; und an Port D (das sind jetzt die Pull-Ups!)

; Port B mit den LEDs soll wieder als Ausgang dienen (siehe TEST1), die
; Lampen sollen zu Beginn alle aus sein.

   LDI   mp,0xFF ; 8 Einsen in Universalregister
   OUT   DDRB,mp ; und in Datenrichtungsregister
   OUT   PORTB,mp ; und an alle Ausgänge

; Das Drücken der Schalter 0 und 1 soll die Lampen 0 und 1 anmachen,
; das Drücken eines der Schalter 2 bis 6 die anderen Lampen. Das
; Drücken von Schalter 7 macht alle Lampen aus.
; Im Hauptloop erfolgt die Abfrage der Schalter, wenn die Bedingungen
; erfüllt sind, wird zu den jeweiligen Unterprogrammen verzeigt.

loop:

; Abfrage von Schalter 0 (einfachst)
; Überspringe den nächsten Befehl (Aufruf des Unterprogrammes zum
; Anmachen der Lampe 0), wenn das 0-te Bit im Port D eine Eins ist
; (Schalter aus, Pull-Up erzeugt eine Eins). Das Unterprogramm
; Lampe 0 steht weiter hinten. Beim RCALL wird die aktuelle Adresse
; auf den Stapel abgelegt, beim Rücksprung wird diese wieder vom Stapel
; genommen und das Programm mit dem Befehl nach RCALL fortgesetzt. Weil
; mit dem SBIS-Befehl immer nur ein Befehl übersprungen wird, muss es
; sich um einen Ein-Byte-Befehl handeln. RCALL ist ein solcher, der
; absolute CALL-Befehl wäre ein 2-Byte-Befehl und ginge nicht!

   SBIS   PIND,0
; Springe, wenn Bit 0 im Port D Eins ist
   RCALL   Lampe0 ; Führe ein relativ angegebenes Unterprogramm aus

; Nach Abarbeiten des Unterprogrammes und beim Überspringen des Unter-
; programmes wird nun nächste Befehl durchgeführt.

; Abfrage von Schalter 1 (exotisch)
; Die Ports sind in den Adressraum des SRAMs gespiegelt, die Adresse
; ergibt sich durch Addition von 20 hex. Anstelle der IN/OUT-Befehle
; können auch die Lade-/Speicher-Befehle mit Zeigern verwendet werden.

.EQU   d_in_spiegel=PIND + $20

; Mit dem Registerpaar R27:R26 kann ein Zeiger X in das SRAM konstruiert
; werden, mit dem LD-Befehl kann der Inhalt des Ports gelesen werden, als
; wenn es ein SRAM-Byte wäre.

   LDI   R26,LOW(d_in_spiegel) ; unteres Byte Zeiger
   LDI   R27,HIGH(d_in_spiegel) ; oberes Byte Zeiger
   LD   mp,X ; Lade Register aus Zeiger auf PIND

; Isoliere Pin1 mit AND-Befehl und teste auf Null

   ANDI   mp,0b00000010 ; AND Bit 1

; Überspringe die folgenden Befehle, wenn nicht Null bei AND herauskam
; (= Bit 1 war Eins, Schalter 1 ist aus). Der Sprungbefehl ist für eine
; größere Anzahl Befehle geeignet, da zu einem Label verzweigt wird.

   BRNE   weiter ; Verzweige nach weiter, falls nicht Null
   RCALL   Lampe1 ; Führe Unterprogramm Lampe1 aus

; Schalter 2 bis 6
; Einlesen des Ports D in ein Register, Maskieren der anderen Schalter,
; Vergleich mit Schalter 2 bis 6,
; Vergleich mit LEDs 2 bis 7 anmachen

weiter:   IN   mp,PIND ; Lese Port D
   ORI   mp,0b10000011 ; Maskiere Schalter 0, 1 und 7
   CPI   mp,0b11111111 ; Irgendein Schalter gedrückt?
   BREQ   sw7 ; Verzweige nach sw7, falls = $FF
   IN   mp,PINB ; Lese LED-Port
   ANDI   mp,0b00000011 ; Lampen 2 bis 7 anmachen
   OUT   PORTB,mp ; an LED-Port

sw7:   IN   mp,PIND ; Lese Schalter-Port
   ROL   mp ; Schiebe siebtes Bit in das Carry-Flag
   BRCS   endloop ; Siebtes Bit ist 1 (BRanch if Carry is Set)
   LDI   mp,0xFF ; Alle Lampen aus
   OUT   PORTB,mp

endloop:
   RJMP   loop

;

; Unterprogramm Lampe 0

; schaltet Lampe 0 ein.

Lampe0:   IN   mp,PINB ; Lese aktuellen Zustand von Port B
   ANDI   mp,0b11111110 ; Lösche Bit 0 mit UND-Befehl
   OUT   PORTB,mp ; Schreibe Ergebnis zurück
   RET ; Hole Rücksprungadresse vom Stapel und kehre zurück

; Unterprogramm Lampe 1

; schaltet Lampe 1 ein

Lampe1:   IN   mp,PINB ; Lese Zustand von Port B
   CBR   mp,0b00000010 ; Lösche Bit 1 mit CBR-Befehl
   OUT   PORTB,mp ; Schreibe Ergebnis zurück
   RET ; Adresse vom Stapel und zurück


©2002 by http://www.avr-asm-tutorial.net