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
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?
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.
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 |
>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.
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.
Sorry! So sollte es sein. Testroutine: PUSH PSW ORL PSW,#0001000b ; Umschaltung Registerbank 1 POP PSW RET
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?
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
> 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
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
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 :-)
>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?
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
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.
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
>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.
> 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
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.