mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik 8051 Assembler - Registerbänke


Autor: Stefan S. (elektronik-freaks)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo zusammen,

Ich versuche schon den ganzen morgen diese Ansteuerung der Registerbänke 
in Assembler hinzubekommen aber es funktioniert nicht.

Infos:
- Mikrocontroller AT89S8253
- Compiler Ride 51

Ich schaffe es nicht z.b. Registerbank 1 zu verwenden anstatt 
Registerbank0

Nach meiner Auffassung muss ich zum verwenden einer anderen Registerbank 
nur RS0 und RS1 im PSW zu setzten/löschen. Jedoch funktioniert dann mein 
ganzes Programm nicht mehr. (Register sind beliebig zugeordnet)

RS1  RS0  Registerbank   Adressen im RAM
 0    0         0        00 bis 07
 0    1         1        08 bis 0F
 1    0         2        10 bis 17
 1    1         3        18 bis 7F


Beispielprogramm:

Include 51/reg51.inc
ORG  0000H    ;Speicheradressse 0
Start:
SETB  RS0    ;Registerbank 1 auswählen
CLR  RS1
MOV  R1,#125

CLR  RS0    ;Registerbank 2 auswählen
SETB  RS1
MOV  R1,#50

SJMP  Start

Autor: Stefan S. (elektronik-freaks)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Bin nun schon weiter gekommen..

Dieses Problem der Registerbankauswahl tritt bei mir nur auf bei der 
Registerbank 1:

SETB  RS0
CLR  RS1

Wieso??? Ist die Registerbank 1 etwas besonderes?

Autor: Mars (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Der Stackpointer vom 8051 wird mit 0x07 initialisiert, wenn du nun die 
Registerbank 1 auswählst, überschreibst du dir den Stack.
Du solltest vielleicht mal posten was nicht mehr funktioniert, den an 
deinem Codefragment sieht man nicht viel.

Autor: Stefan S. (elektronik-freaks)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Der Komplette Code ist im Anhang.
Sorry wegen der Formatierung Ride macht dies ein wenig wie er will.

Es soll eine Uhr werden die im Hintergrund eines anderen Programms 
läuft.
Hier im Code verwende ich schon die Registerbank 2. Doch mit der 
Registerbank 1 funktioniert einfach nichts mehr bzw. es werden 
irgendwelche Werte seriell ausgegeben.
Die Ausgabe geht seriell raus an Schieberegister welche dann 4x 7Segment 
Anzeigen treiben.
nähere Infos zur Porterweiterungsplatine kann man auf meiner Beta Seite 
sehen http://elektronik-freaks.de/wordpress/

Aber wie kann es sein das die Adressen 0x07 zweimal vom Controller 
verwendet werden?
Und wie kann ich das umgehen?
;Projekt: Uhrzeit - Ausgabe an Porterweiterung 7-Segment
;Mikrocontroller AT89S8253


;Belegung: 
; Port 3 -->
; Port 3.4 --> DATA
; Port 3.5 --> CLOCK
; Port 3.6 --> LATCH

;  2EH    -->  Byte Ausgabe - Wert vom datenpointer welcher durchrotiert wird und jede einzelne Bit seriell Ausgegeben wird mit Serielle_Ausgabe_1byte
;  22H    --> 12H --> Zeit-Flag - Timer0

;Registerbank 2 - Uhr
;  R0      -->  Zählregister Ausgabe - 8 Bits ausgeben deshalb 8 mal schleife
;  R1      -->  Zählregister 1.Segment - Stunden 10er
;  R2       -->  Zählregister 2.Segment - Stunden 1er
;  R3       -->  Zählregister 3.Segment - Minuten 10er
;  R4       -->  Zählregister 4.Segment - Minuten 1er
;  R5      --> Zählregister Zeitbasis 1 Sekunde
;  R6     -->  Zählregister Zeitbasis 1 Sekunde
;  R7      -->  Zählregister Zeitbasis 1 Minute

Include 51/reg51.inc
ORG   0000H            ;Speicheradressse 0 
SJMP Beginn

;####Interruptroutine Timer 1 - TF1 - 001BH
ORG  001BH            ;Einsprungadresse Timer 1 TF1 alle 50ms ein Einsprung
SETB  12H                  ;1MZ
RETI                ;
;#### ENDE Interruptroutine Timer 1 - TF1 - 001BH

;#### Startwerte setzten
Beginn:
;*****Allgemeine Startwerte
MOV   TMOD,#00100000B      ;Timer 1 Modus = 2
SETB  EA               ;Interruptbehandlung aktivieren

;*****Startwerte Uhr
CLR  12H
CLR  P3.4  ;DATA
CLR  P3.5  ;Clock
CLR  P3.6  ;Latch leitung
CLR  RS0  ;Registerbank 2 auswählen
SETB  RS1  ;Registerbank 2 auswählen
MOV   R0,#8  ;Datenlänge zur Ausgabe an die Porterweiterung 8Bit
MOV  R1,#0  ;1.Segment = 0 
MOV  R2,#0
MOV  R3,#0
MOV  R4,#0
MOV  R5,#0 ;Zaehler
MOV  R6,#0
MOV  R7,#0
CLR  RS1  ;Registerbank 0 auswählen
CLR  RS0  ;Registerbank 0 auswählen
MOV   TH1,#56  ;          ; 255-56 = 199 + 1MZ von der Interruptroutine = 200uS
MOV   TL1,#56   ;
SETB   ET1               ;Interrupt für Timer 1 aktivieren
SETB   TR1               ;Timer 1 Start

sjmp   Ausgabe    ;am anfang 0 anzeigen
;#### ENDE Startwerte setzten

Start:
JB    12H,Zeitbasis_1Sec        ;Zeit-Flag vom Timer 1 gesetzt -> Prüfen ob eine Minute schon erreicht wurde
SJMP  Start

;####Zeitbasis 1 Sekunde
Zeitbasis_1Sec:
CLR  RS0  ;Registerbank 2 auswählen
SETB  RS1  ;Registerbank 2 auswählen
CLR  12H
INC  R5                    ;Alle 200us ein Einsprung
CJNE  R5,#200,Registerbank0_start  ;200 - 200us vom Timer x 200 = 0,04s = 40ms
MOV  R5,#0                       ;
INC  R6                  ;
CJNE  R6,#25,Registerbank0_start    ;ist 25 schon erreicht? (25*0,04s) = 1s
MOV  R6,#0
SJMP  Zeitbasis_1min

Registerbank0_start:
CLR  RS0
CLR  RS1
SJMP  Start
;#### ENDE Zeitbasis 1 Sekunde

;#### Zeitbasis 1 Minute ( 60x 1Sekunde)
Zeitbasis_1min:            ;ein Einsprung pro Sekunde
INC  R7
CJNE  R7,#60,Registerbank0_start  ;wenn noch keine Minute vorbei ist wieder zum Start springen
MOV  R7,#0
SJMP  Uhrzeit              ;Uhrzeit Aktuallisieren
;#### ENDE Zeitbasis 1 Minute


;#### Uhrzeit
Uhrzeit:
CJNE  R4,#9,FF4
MOV  R4,#0
CJNE  R3,#5,FF3
MOV  R3,#0
CJNE  R1,#2,Std1
CJNE  R2,#3,FF2
MOV  R2,#0
MOV  R1,#0
SJMP  Ausgabe

Std1:
CJNE  R2,#9,FF2
MOV  R2,#0
SJMP  FF1

FF1:
INC  R1            ;Übertarg von der dritten Stellen
SJMP  Ausgabe
FF2:
INC  R2            ;Übertrag von der zweiten Stelle
SJMP  Ausgabe
FF3:
INC   R3            ;Übertrag von der Ersten Stelle
SJMP  Ausgabe        ;Die zweite Stelle wurde ohne weiteren Übertrag eins hochgezählt
FF4:
INC  R4            ;Es wurde ohne übertrag gezählt
SJMP  Ausgabe        ;Also wert Ausgeben
;#### ENDE Sekundenzaehl


;#### Ausgabe an die 7-Segmentplatine über die Porterweiterung
Ausgabe:
CLR  RS0  ;Registerbank 2 auswählen
SETB  RS1  ;Registerbank 2 auswählen
Datenpointer_Seg4:        ;Als erstes letztes Segment ausgeben - Daten werden Durchgeschoben
MOV   A,R4              ;Zählstand der Minuten 1er in Akku schreiben
MOV   dptr,#Seg4          ;wähle Tabelle Seg1 aus
ACALL  Serielle_Ausgabe_1byte  ;2EH seriell ausgeben

Datenpointer_Seg3:
MOV   A,R3              ;Zählstand der Minuten 10er in Akku schreiben
MOV   dptr,#Seg3          ;wähle Tabelle Seg1 aus
ACALL  Serielle_Ausgabe_1byte  ;2EH seriell ausgeben

Datenpointer_Seg2:
MOV   A,R2              ;Zählstand der Stunden 1er in Akku schreiben
MOV   dptr,#Seg2          ;wähle Tabelle Seg1 aus
ACALL  Serielle_Ausgabe_1byte  ;2EH seriell ausgeben

Datenpointer_Seg1:
MOV   A,R1              ;Zählstand der Stunden 10ner in Akku schreiben
MOV   dptr,#Seg1          ;wähle Tabelle Seg1 aus
ACALL  Serielle_Ausgabe_1byte  ;2EH seriell ausgeben

SETB  P3.6              ;Latch des 74HC595 neu laden mit der negativen Signalflanke an Port 3.6
CLR  P3.6              ;Der seriell übertragene Wert wird nun Ausgegeben
SJMP  Registerbank0_start
;#### ENDE Ausgabe an die 7-Segmentplatine über die Porterweiterung


;#### Unterprogramme
Serielle_Ausgabe_1byte:
MOVC   a,@a+dptr          ;Hohle Wert aus dem Datenpointer, relative zum Zählerstand(Akkku) und schreibe ihn in Akku
MOV  2EH,A              ;Schreibe den Wert aus dem Datenpointer nach 2EH
Wiederholung:
JNB  77H,Ausgabe_0        ;Wenn die Auszugebende erste Stelle von 2EH 0 ist (Wert vom Datenpointer + evtl. durchrotiert)
SETB  P3.4              ;Erste Stelle ist 1 also Dataleitung = 1
SJMP  Takt
Ausgabe_0:
CLR  P3.4              ;Erste Stelle = 0
Takt:
SETB  P3.5              ;Takt vollziehen
CLR  P3.5
MOV  A,2EH              ;2EH in Akku schreiben
RL    A                ;Akku nach links verschieben
MOV  2EH,A              ;Wert vom Akku in 2EH schreiben
DJNZ  R0,Wiederholung      ;Prüfen wie oft die Ausgabeschleife sich wiederholt hat --> wenn nötig noch ein weiteres Bit ausgeben
MOV  R0,#8
RET
;#### ENDE Unterprogramme

char_tab: ;Anzeige:  0        1        2      3        4        5      6        7        8      9        F
Seg1:       DB    00000101B, 11010111B, 10001001B, 10010001B, 01010011B, 00110001B, 00100001B, 10010111B, 00000001B, 00010001B, 00101011B
Seg2:      DB    00000101B, 11110101B, 10001100B, 11000100B, 01110100B, 01000110B, 00000110B, 11100101B, 00000100B, 01000100B, 00101110B
Seg3:      DB    00000101B, 11010111B, 10001001B, 11000001B, 01010011B, 01100001B, 00100001B, 11000111B, 00000001B, 01000001B, 00101011B
Seg4:      DB    00000101B, 11110101B, 10001100B, 11000100B, 01110100B, 01000110B, 00000110B, 11100101B, 00000100B, 01000100B, 00101110B


END 

Autor: Mars (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Aber wie kann es sein das die Adressen 0x07 zweimal vom Controller
>verwendet werden?
>Und wie kann ich das umgehen?

In dem du den SP mit einem anderen Wert initialisierst. Oder du steigst 
auf C um, dann must du dich um den Stack & Register nicht mehr selber 
kümmern.

Autor: Klaus (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Da RSO und RS1 ein bestandteil des PSW Registers ist solltest Du auch 
das PSW sichern.

Also z.B.:

Testroutine:
 PUSH   PSW
 MOV    PSW,#0001000b  ; Umschaltung Registerbank 1



 POP   PSW
RET

Auf jedenfall vorher das PSW sichern in den Unteroutinen wo Du die 
Registerbankumschaltung machen willst. Wenn nicht kommt da nur heiloses 
durcheinander dabei raus.

Autor: Klaus (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Sorry! So sollte es sein.

Testroutine:
 PUSH   PSW
 ORL    PSW,#0001000b  ; Umschaltung Registerbank 1



 POP   PSW
RET

Autor: Stefan S. (elektronik-freaks)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Klaus wrote:
> Auf jedenfall vorher das PSW sichern in den Unteroutinen wo Du die
> Registerbankumschaltung machen willst. Wenn nicht kommt da nur heiloses
> durcheinander dabei raus.

Ne das PSW lasse ich ja so wie es ist und manipuliere nur die zwei Bits 
RS0 und RS1. Der Rest bleibt ja so wie es war.

Mars wrote:
>In dem du den SP mit einem anderen Wert initialisierst.

Wie soll ich das machen?? Ich benutze den Stack in meinem Programm ja 
garnicht. (nur die Interrupts und Unterprogramme) und diese können den 
oberen RAM Speicher niemals ausnutzten.
Der Stack ist doch normal im oberen RAM oder irre ich mich da?

Autor: AndreasH (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo Stefan,

Du benutzt sehr wohl den Stack, oder wo meinst Du werden die 
Rücksrungadressen von ACALL und der Interruptroutine gespeicher ?

Der Stack beim 8051 wird von unten nach oben gefüllt. Also wird die 
Registerbank 1 überschrieben.

Andreas

Autor: Ralf (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Ne das PSW lasse ich ja so wie es ist und manipuliere nur die zwei Bits
> RS0 und RS1. Der Rest bleibt ja so wie es war.
Die Aussage ist in sich nicht lösbar, weil RS0 und RS1 Bits im PSW sind 
:)

Ralf

Autor: Pieter (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
moin moin,

nicht vergessen:
Auch das CarrayFlag liegt im PSW!
Daher, so wie Klaus beschrieben hat, immer mit Push/Pop PSW arbeiten.
Alles andere ist ULK!

mfg
Pieter

Autor: Bernd (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Hallo Stefan,

die Speicheraufteilung beim 8051 ist gewöhnungsbedürftig und Du solltest 
Dir das genau ansehen.

mov sp,#60h

lagert Dir den Stack z.B. nach 60h aus.

Im Anhang ein kleines Testprogramm das den Speicher mit Dummydaten 
beschreibt. Ich hab mal nen kleinen ICE für den 8051 gebaut und damit 
konntest Du dann den internen Speicher auch zu sehen bekommen. Der 
Screenshot zeigt das Ergebnis des Programms.

Ab "start" kannst Du das Programm verwenden und Dir das Ergebnis auch in 
einem Simulator ansehen... vielleicht hilft ja ein Bild :-)

Autor: Stefan S. (elektronik-freaks)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>die Speicheraufteilung beim 8051 ist gewöhnungsbedürftig und Du solltest
>Dir das genau ansehen.

Alles klar. Der Stack wird von von 08h standardmässig aufgefüllt. Das 
bedeutet er überschreibt als erstes Registerbank 1.
Programm funktioniert, wenn ich den Stack mit
mov sp,#30h
auslager, da er dann oberhalb der vier Registerbänke und oberhalb der 
von mir verwendeten Direkt Bytes anfängt aufzufüllen.

>Auch das CarrayFlag liegt im PSW!
>Daher, so wie Klaus beschrieben hat, immer mit Push/Pop PSW arbeiten.
>Alles andere ist ULK!

Das Carrybit steht unter anderem im PSW.
Aber wieso muss ich das ganze PSW sichern, wenn ich doch nur die Bits 
RS0, RS1 verändere?

Autor: Mars (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo Stefan!

Zumindest in der ISR solltest du alle Register, die du veränderst(PSW, 
ACC, DPT, ...) sichern, sonst kommt es zu unerklärlichen Fehlern.
Warum das so ist kannst du dir ja selber überlegen.

Grüße

Autor: Mars (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Nachtrag:
Für Subroutinen kannst du dir ja selbst Konventionen, wie wie werden die 
Parameter übergeben, wo der Rückgabewert, welche Register dürfen 
verändert, welche müssen gesichert werden.

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Stellt sich die Frage, ob man mit dieser Art der Banknutzung für globale 
Variablen überhaupt etwas gewinnt.
Verlieren tut man in jedem Fall erstmal die Übersicht.

Wo verschiedene Registerbänke sinnvoll sind, ist z.B. in Interrupts, 
dann spart man einiges an PUSH/POP.
Im Main würde ich immer nur eine Bank benutzen und globale Variablen im 
Data-Bereich ablegen.

Du könntest schonmal Speicher sparen, indem Du die Einer und Zehner 
zusammen in einem Byte speicherst. Dann wird auch der 24-Stunden 
Überlauf einfacher.

Eine Beipiel-Uhrenroutine findest Du hier:

http://www.mikrocontroller.net/attachment/49155/UHR.A51


Peter

Autor: Stefan S. (elektronik-freaks)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
>Zumindest in der ISR solltest du alle Register, die du veränderst(PSW,
>ACC, DPT, ...) sichern, sonst kommt es zu unerklärlichen Fehlern.

Sehr guter Tipp :D
War bisher bei den einfachen Programmen nicht nötig doch es wird gerade 
sehr komplex THX

>Stellt sich die Frage, ob man mit dieser Art der Banknutzung für globale
>Variablen überhaupt etwas gewinnt.

Ich denke auch weniger, doch habe ich bis heute noch nie wirklich mit 
Globalen Variablen gearbeitet bzw. noch nie mit dem RAM-Bereich überhalb 
30h ^^
aber es würde vieles einfacher machen und mir vieleicht ein wenig 
Übersicht bringen.
(wen's interessiert im Anhang die noch nicht laufende DCF77 Uhr, aber 
nicht erschrecken)

>Du könntest schonmal Speicher sparen, indem Du die Einer und Zehner
>zusammen in einem Byte speicherst. Dann wird auch der 24-Stunden
>Überlauf einfacher.

Der Überlauf schon aber um die Ziffern einzelnd an der 7-Segment Anzeige 
auszugeben müsste ich sie wieder auseinanderpfriemeln.

Autor: Ralf (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Der Überlauf schon aber um die Ziffern einzelnd an der 7-Segment Anzeige
> auszugeben müsste ich sie wieder auseinanderpfriemeln.
Nana, stell dir das nicht so schwer vor. Du löschst die oberen vier Bits 
weg, und konvertierst das Ergebnis in den Wert für die Anzeige. Für das 
obere Nibble machst du genau das gleiche, nur führst du vorher noch ein 
"swap a" aus, das vertauscht die Nibbles des Akkus.
Wenn du dann noch den Wert für die Anzeige aus einer Tabelle holst, hast 
du doch schon alles.

Ralf

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Stefan S. wrote:

> Der Überlauf schon aber um die Ziffern einzelnd an der 7-Segment Anzeige
> auszugeben müsste ich sie wieder auseinanderpfriemeln.

Im Prinzip richtig, aber bei großen Programmen wird das Leben bedeutend 
einfacher, wenn alle Rechnungen im Binärsystem (8, 16, 32 Bit) gemacht 
werden, das ist nämlich das Lieblingsformat der CPU.

Und nur zur Ausgabe an den nicht binären Menschen gibt es eine einzige 
Umwandlungsroutine.

Für 8-Bit Zahlen kann man einfach den DIV-Befehl benutzen, z.B. bis 99:
; input: A = Zahl (0..99)
; output: A = Zehner, B = Einer
dezimal_out:
  mov b, #10
  div ab


Peter

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.