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


von Stefan S. (elektronik-freaks)


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

von Stefan S. (elektronik-freaks)


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?

von Mars (Gast)


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.

von Stefan S. (elektronik-freaks)


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?
1
;Projekt: Uhrzeit - Ausgabe an Porterweiterung 7-Segment
2
;Mikrocontroller AT89S8253
3
4
5
;Belegung: 
6
; Port 3 -->
7
; Port 3.4 --> DATA
8
; Port 3.5 --> CLOCK
9
; Port 3.6 --> LATCH
10
11
;  2EH    -->  Byte Ausgabe - Wert vom datenpointer welcher durchrotiert wird und jede einzelne Bit seriell Ausgegeben wird mit Serielle_Ausgabe_1byte
12
;  22H    --> 12H --> Zeit-Flag - Timer0
13
14
;Registerbank 2 - Uhr
15
;  R0      -->  Zählregister Ausgabe - 8 Bits ausgeben deshalb 8 mal schleife
16
;  R1      -->  Zählregister 1.Segment - Stunden 10er
17
;  R2       -->  Zählregister 2.Segment - Stunden 1er
18
;  R3       -->  Zählregister 3.Segment - Minuten 10er
19
;  R4       -->  Zählregister 4.Segment - Minuten 1er
20
;  R5      --> Zählregister Zeitbasis 1 Sekunde
21
;  R6     -->  Zählregister Zeitbasis 1 Sekunde
22
;  R7      -->  Zählregister Zeitbasis 1 Minute
23
24
Include 51/reg51.inc
25
ORG   0000H            ;Speicheradressse 0 
26
SJMP Beginn
27
28
;####Interruptroutine Timer 1 - TF1 - 001BH
29
ORG  001BH            ;Einsprungadresse Timer 1 TF1 alle 50ms ein Einsprung
30
SETB  12H                  ;1MZ
31
RETI                ;
32
;#### ENDE Interruptroutine Timer 1 - TF1 - 001BH
33
34
;#### Startwerte setzten
35
Beginn:
36
;*****Allgemeine Startwerte
37
MOV   TMOD,#00100000B      ;Timer 1 Modus = 2
38
SETB  EA               ;Interruptbehandlung aktivieren
39
40
;*****Startwerte Uhr
41
CLR  12H
42
CLR  P3.4  ;DATA
43
CLR  P3.5  ;Clock
44
CLR  P3.6  ;Latch leitung
45
CLR  RS0  ;Registerbank 2 auswählen
46
SETB  RS1  ;Registerbank 2 auswählen
47
MOV   R0,#8  ;Datenlänge zur Ausgabe an die Porterweiterung 8Bit
48
MOV  R1,#0  ;1.Segment = 0 
49
MOV  R2,#0
50
MOV  R3,#0
51
MOV  R4,#0
52
MOV  R5,#0 ;Zaehler
53
MOV  R6,#0
54
MOV  R7,#0
55
CLR  RS1  ;Registerbank 0 auswählen
56
CLR  RS0  ;Registerbank 0 auswählen
57
MOV   TH1,#56  ;          ; 255-56 = 199 + 1MZ von der Interruptroutine = 200uS
58
MOV   TL1,#56   ;
59
SETB   ET1               ;Interrupt für Timer 1 aktivieren
60
SETB   TR1               ;Timer 1 Start
61
62
sjmp   Ausgabe    ;am anfang 0 anzeigen
63
;#### ENDE Startwerte setzten
64
65
Start:
66
JB    12H,Zeitbasis_1Sec        ;Zeit-Flag vom Timer 1 gesetzt -> Prüfen ob eine Minute schon erreicht wurde
67
SJMP  Start
68
69
;####Zeitbasis 1 Sekunde
70
Zeitbasis_1Sec:
71
CLR  RS0  ;Registerbank 2 auswählen
72
SETB  RS1  ;Registerbank 2 auswählen
73
CLR  12H
74
INC  R5                    ;Alle 200us ein Einsprung
75
CJNE  R5,#200,Registerbank0_start  ;200 - 200us vom Timer x 200 = 0,04s = 40ms
76
MOV  R5,#0                       ;
77
INC  R6                  ;
78
CJNE  R6,#25,Registerbank0_start    ;ist 25 schon erreicht? (25*0,04s) = 1s
79
MOV  R6,#0
80
SJMP  Zeitbasis_1min
81
82
Registerbank0_start:
83
CLR  RS0
84
CLR  RS1
85
SJMP  Start
86
;#### ENDE Zeitbasis 1 Sekunde
87
88
;#### Zeitbasis 1 Minute ( 60x 1Sekunde)
89
Zeitbasis_1min:            ;ein Einsprung pro Sekunde
90
INC  R7
91
CJNE  R7,#60,Registerbank0_start  ;wenn noch keine Minute vorbei ist wieder zum Start springen
92
MOV  R7,#0
93
SJMP  Uhrzeit              ;Uhrzeit Aktuallisieren
94
;#### ENDE Zeitbasis 1 Minute
95
96
97
;#### Uhrzeit
98
Uhrzeit:
99
CJNE  R4,#9,FF4
100
MOV  R4,#0
101
CJNE  R3,#5,FF3
102
MOV  R3,#0
103
CJNE  R1,#2,Std1
104
CJNE  R2,#3,FF2
105
MOV  R2,#0
106
MOV  R1,#0
107
SJMP  Ausgabe
108
109
Std1:
110
CJNE  R2,#9,FF2
111
MOV  R2,#0
112
SJMP  FF1
113
114
FF1:
115
INC  R1            ;Übertarg von der dritten Stellen
116
SJMP  Ausgabe
117
FF2:
118
INC  R2            ;Übertrag von der zweiten Stelle
119
SJMP  Ausgabe
120
FF3:
121
INC   R3            ;Übertrag von der Ersten Stelle
122
SJMP  Ausgabe        ;Die zweite Stelle wurde ohne weiteren Übertrag eins hochgezählt
123
FF4:
124
INC  R4            ;Es wurde ohne übertrag gezählt
125
SJMP  Ausgabe        ;Also wert Ausgeben
126
;#### ENDE Sekundenzaehl
127
128
129
;#### Ausgabe an die 7-Segmentplatine über die Porterweiterung
130
Ausgabe:
131
CLR  RS0  ;Registerbank 2 auswählen
132
SETB  RS1  ;Registerbank 2 auswählen
133
Datenpointer_Seg4:        ;Als erstes letztes Segment ausgeben - Daten werden Durchgeschoben
134
MOV   A,R4              ;Zählstand der Minuten 1er in Akku schreiben
135
MOV   dptr,#Seg4          ;wähle Tabelle Seg1 aus
136
ACALL  Serielle_Ausgabe_1byte  ;2EH seriell ausgeben
137
138
Datenpointer_Seg3:
139
MOV   A,R3              ;Zählstand der Minuten 10er in Akku schreiben
140
MOV   dptr,#Seg3          ;wähle Tabelle Seg1 aus
141
ACALL  Serielle_Ausgabe_1byte  ;2EH seriell ausgeben
142
143
Datenpointer_Seg2:
144
MOV   A,R2              ;Zählstand der Stunden 1er in Akku schreiben
145
MOV   dptr,#Seg2          ;wähle Tabelle Seg1 aus
146
ACALL  Serielle_Ausgabe_1byte  ;2EH seriell ausgeben
147
148
Datenpointer_Seg1:
149
MOV   A,R1              ;Zählstand der Stunden 10ner in Akku schreiben
150
MOV   dptr,#Seg1          ;wähle Tabelle Seg1 aus
151
ACALL  Serielle_Ausgabe_1byte  ;2EH seriell ausgeben
152
153
SETB  P3.6              ;Latch des 74HC595 neu laden mit der negativen Signalflanke an Port 3.6
154
CLR  P3.6              ;Der seriell übertragene Wert wird nun Ausgegeben
155
SJMP  Registerbank0_start
156
;#### ENDE Ausgabe an die 7-Segmentplatine über die Porterweiterung
157
158
159
;#### Unterprogramme
160
Serielle_Ausgabe_1byte:
161
MOVC   a,@a+dptr          ;Hohle Wert aus dem Datenpointer, relative zum Zählerstand(Akkku) und schreibe ihn in Akku
162
MOV  2EH,A              ;Schreibe den Wert aus dem Datenpointer nach 2EH
163
Wiederholung:
164
JNB  77H,Ausgabe_0        ;Wenn die Auszugebende erste Stelle von 2EH 0 ist (Wert vom Datenpointer + evtl. durchrotiert)
165
SETB  P3.4              ;Erste Stelle ist 1 also Dataleitung = 1
166
SJMP  Takt
167
Ausgabe_0:
168
CLR  P3.4              ;Erste Stelle = 0
169
Takt:
170
SETB  P3.5              ;Takt vollziehen
171
CLR  P3.5
172
MOV  A,2EH              ;2EH in Akku schreiben
173
RL    A                ;Akku nach links verschieben
174
MOV  2EH,A              ;Wert vom Akku in 2EH schreiben
175
DJNZ  R0,Wiederholung      ;Prüfen wie oft die Ausgabeschleife sich wiederholt hat --> wenn nötig noch ein weiteres Bit ausgeben
176
MOV  R0,#8
177
RET
178
;#### ENDE Unterprogramme
179
180
char_tab: ;Anzeige:  0        1        2      3        4        5      6        7        8      9        F
181
Seg1:       DB    00000101B, 11010111B, 10001001B, 10010001B, 01010011B, 00110001B, 00100001B, 10010111B, 00000001B, 00010001B, 00101011B
182
Seg2:      DB    00000101B, 11110101B, 10001100B, 11000100B, 01110100B, 01000110B, 00000110B, 11100101B, 00000100B, 01000100B, 00101110B
183
Seg3:      DB    00000101B, 11010111B, 10001001B, 11000001B, 01010011B, 01100001B, 00100001B, 11000111B, 00000001B, 01000001B, 00101011B
184
Seg4:      DB    00000101B, 11110101B, 10001100B, 11000100B, 01110100B, 01000110B, 00000110B, 11100101B, 00000100B, 01000100B, 00101110B
185
186
187
END

von Mars (Gast)


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.

von Klaus (Gast)


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.

von Klaus (Gast)


Lesenswert?

Sorry! So sollte es sein.

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



 POP   PSW
RET

von Stefan S. (elektronik-freaks)


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?

von AndreasH (Gast)


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

von Ralf (Gast)


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

von Pieter (Gast)


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

von Bernd (Gast)


Angehängte Dateien:

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 :-)

von Stefan S. (elektronik-freaks)


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?

von Mars (Gast)


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

von Mars (Gast)


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.

von Peter D. (peda)


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

von Stefan S. (elektronik-freaks)


Angehängte Dateien:

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.

von Ralf (Gast)


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

von Peter D. (peda)


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:
1
; input: A = Zahl (0..99)
2
; output: A = Zehner, B = Einer
3
dezimal_out:
4
  mov b, #10
5
  div ab


Peter

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.