Hallo zusammen! Ich benötige zum Auslesen eines Speicherbereichs eine 16-Bit-Schleife, die aus den Registern R0 (Highbyte) und R1 (Lowbyte) bestehen soll. Der Befehl "DJNZ Loop" scheint gewisse Probleme mit dem Überlauf zu haben, wenn der Startwert "00h" ist. Da wird dann 00h zu FFh und die Schleife wird 255 mal durchlaufen obwohl eigentlich Null mal durchlaufen werden müsste. Und wie sieht es eigentlich aus mit den Werten 255/256 ? Wenn der 16-Bit-Zähler auf 0100h steht, müsste der da die Schleife 256 mal durchlaufen oder 255 mal ? Wie würde ich die 256 erreichen wenn das Lowbyte der Schleife nur maximal 255 mal durchlaufen wird ? Weiss da jemand etwas dazu ?
H-G S. schrieb: > Wenn der 16-Bit-Zähler auf 0100h steht, müsste der da die Schleife 256 > mal durchlaufen oder 255 mal ? Reine Definitionssache. Gegenfrage dazu: wie oft müsste dein Zähler die Schleife durchlaufen, wenn da z.B. der Wert 0004h drin steht? Mit der Antwort auf diese Frage lässt sich dann leicht sagen, wie oft er die Schleife durchlaufen muss, wenn 256 = 0100h drin steht... > Weiss da jemand etwas dazu ? Bitte nicht Plenken!
:
Bearbeitet durch Moderator
H-G S. schrieb: > Der Befehl "DJNZ Loop" scheint gewisse Probleme mit dem Überlauf zu > haben, wenn der Startwert "00h" ist. Das ist kein Problem, das ist genauso (wie der Name schon sagt) dokumentiert! http://www.self8051.de/djnz_____%3Cbyte%3E.%3Crel%3E,13474.html?PHPSESSID=b781dc8aa3e09a9ce83ee08e47b3a1a4
Es gibt nen Haufen Lösungen, z.B.:
1 | weiter: |
2 | nop ; Code |
3 | anfang: ; hier Einsprung in die Funktion |
4 | dec r0 |
5 | cjne r0, #0ffh, weiter |
6 | dec r1 |
7 | cjne r1, #0ffh, weiter |
8 | ret |
Man könnte aber auch DPTR auf die Endadresse testen.
Ralf G. schrieb: > Das ist kein Problem, das ist genauso (wie der Name schon sagt) > dokumentiert! Und noch dazu ist es logisch: Decrement: 00h -> ffh Jump not zero: ja, ist ja nicht 0 Das Missverständnis liegt nicht daran, dass der Prozessor nicht logisch denken kann, sondern der Programmierer. H-G S. schrieb: > obwohl eigentlich Null mal durchlaufen > werden müsste. Noch ein kapitaler Denkfehler: 1mal wird die Schleife IN JEDEM FALL ausgeführt, DJNZ kommt ja erst am Schluss. Es gibt noch sooo viel zu lernen... Georg
Ich werde die alte Holzhammer-Methode verwenden: :-) - Den 16-Bit-Zähler laden mit Wertebereich 0001h-XXXXh. - Am Schleifenende eine volle 16-Bit-Subtraktion (minus 0001h) durchführen. - Wenn beide Bytes des Zählers 0 sind dann Schleife verlassen. Das Beispielprogramm von Peter Danegger zählt scheinbar durch Null, das ist auch nicht so günstig scheint mir - da kommt wieder das 255/256-Problem hoch. Doch die Vergleichs-Befehle scheinen nützlich - die habe ich komplett vergessen! Und hört auf mich als dumm hinzustellen :-) Ich weiss genau was der DJNZ-Befehl macht und dass die Schleife mindestens einmal durchlaufen wird.
H-G S. schrieb: > Ich weiss genau was der DJNZ-Befehl macht und dass die Schleife > mindestens einmal durchlaufen wird. H-G S. schrieb: > obwohl eigentlich Null mal durchlaufen > werden müsste. Ganz offensichtlich weisst du das nicht. Als Anfänger dumm sein ist durchaus möglich, dumm bleiben wollen und jeden anmeckern, der auf Fehler aufmerksam macht, ist aber keine gute Idee. Georg
Lese doch bitte endlich mal das original Intel Handbuch zur MCS51 Familie: http://datasheets.chipdb.org/Intel/MCS51/MANUALS/27238302.PDF Dort wird jeder Befehl haarklein beschrieben (bis auf 0A5h :-P). Dann wüsstest du auch, das bei DJNZ erst dekrementiert und dann auf Null geprüft wird - wie der Befehl ja auch aussagt.
:
Bearbeitet durch User
Sinnvoll ist der Befehl (djnz) ja auch am ENDE eines Stücks Code. Wo zum Anfang gesprungen wird, wenn noch nicht auf 0 dekrementiert ist. Wenn er einmal durchlaufen werden soll, stellt man zu Anfang 1 ein. Wenn er zweimal durchlaufen werden soll, 2. Bei DJNZ ist die Variable auch kaum als Indexzähler geeignet, so dass ein Decrement am Anfang blöd wäre. Und wenn man mehr als 255 Bytes kopieren will, man also ggf. eine äußere Schleife braucht, ist es auch schöner, wenn man mit Startwert 0 dann auch 256 Durchläufe am Stück hinkriegt. Bei 0 Durchläufen wird der ganze kladeradatsch garnicht aufgerufen.
H-G S. schrieb: > Hallo zusammen! > > Ich benötige zum Auslesen eines Speicherbereichs eine 16-Bit-Schleife, > die aus den Registern R0 (Highbyte) und R1 (Lowbyte) bestehen soll. > > Der Befehl "DJNZ Loop" scheint gewisse Probleme mit dem Überlauf zu > haben, wenn der Startwert "00h" ist. Da wird dann 00h zu FFh und die > Schleife wird 255 mal durchlaufen obwohl eigentlich Null mal durchlaufen > werden müsste. > > Und wie sieht es eigentlich aus mit den Werten 255/256 ? > Wenn der 16-Bit-Zähler auf 0100h steht, müsste der da die Schleife 256 > mal durchlaufen oder 255 mal ? Wie würde ich die 256 erreichen wenn das > Lowbyte der Schleife nur maximal 255 mal durchlaufen wird ? > > Weiss da jemand etwas dazu ? Lass dich nicht drausbringen. Wenn ein Register R1 mit dem Wert s1 = 1...255 oder 0 initialisiert ist, kannst du beim 8051 mit DJNZ zunächst eine Schleife genau 1...256 mal durchlaufen. Das gilt auch für andere 8-bit-µC mit DJNZ. Also:
1 | MOV R1,#s1 |
2 | loop1: |
3 | Befehlsfolge |
4 | DJNZ R1,loop1 |
Der Wert Null ist bei DJNZ also äqivalent mit 256 Schleifendurchgängen. Bei anderen µC nimmt man statt DJNZ einfach zwei Befehle, nämlich DEC und BRNE (je nach µC). Falls man mehr als 256 Schleifendurchgänge für eine Befehlsfolge benötigt, nimmt man entweder zwei verschachtelte Schleifen, also:
1 | MOV R0,#s2 |
2 | loop2: |
3 | MOV R1,#s1 |
4 | loop1: |
5 | Befehlsfolge |
6 | DJNZ R1,loop1 |
7 | DJNZ R0,loop2 |
Die Zahl der Schleifendurchgänge ist dann s1 * s2, reicht also von 1 bis 2^16. Oder falls sich die Zahl der Schleifendurchgänge nicht als Produkt von zwei 8-Bit-Werten (jeweils 1...256) darstellen lässt, kommt man an einer 16-Bit-Arithmetik für den Schleifenzähler nicht vorbei, die aber kaum aufwendiger ist.
Eberhard H. schrieb: > Oder falls sich die Zahl der Schleifendurchgänge nicht als Produkt von > zwei 8-Bit-Werten (jeweils 1...256) darstellen lässt, kommt man an einer > 16-Bit-Arithmetik für den Schleifenzähler nicht vorbei, Das stimmt nicht ganz! Wenn man dein Beispiel etwas umstellt: Eberhard H. schrieb: > MOV R0,#s2 > MOV R1,#s1 > loop1: > Befehlsfolge > DJNZ R1,loop1 > DJNZ R0,loop1 Jetzt wird zuerst die "Befehlsfolge" s1-mal durchlaufen und danach (s2 mal 256). So kann man jeden Wert von 1 bis 65536 erreichen. Du hast aber Recht, es läuft auf eine einfache 16-Bit Arithmetik hinaus.
:
Bearbeitet durch User
Route 6. schrieb: > Eberhard H. schrieb: >> Oder falls sich die Zahl der Schleifendurchgänge nicht als Produkt von >> zwei 8-Bit-Werten (jeweils 1...256) darstellen lässt, kommt man an einer >> 16-Bit-Arithmetik für den Schleifenzähler nicht vorbei, > > Das stimmt nicht ganz! > Wenn man dein Beispiel etwas umstellt: > > Eberhard H. schrieb: >> MOV R0,#s2 >> MOV R1,#s1 >> loop1: >> Befehlsfolge >> DJNZ R1,loop1 >> DJNZ R0,loop1 > > Jetzt wird zuerst die "Befehlsfolge" s1-mal durchlaufen und danach (s2 > mal 256). > So kann man jeden Wert von 1 bis 65536 erreichen. Sehr wohl stimmt mein Programm, und zwar in Unterschied zu deiner Korrektur ganz allgemein für alle s1, s2 = 1 bis 255 und Null:
1 | MOV R0,#s2 |
2 | loop2: |
3 | MOV R1,#s1 |
4 | loop1: |
5 | Befehlsfolge |
6 | DJNZ R1,loop1 |
7 | DJNZ R0,loop2 |
Dein Sonderfall gilt nur für s1 = 0, denn er verwendet die Tatsache, dass R1 nach DJNZ R0,loop1 bereits Null ist. Dieser Sonderfall s1 = 0 ist bei meiner allgemeinen Version wegen MOV R1,#s1 enthalten und somit lassen sich mit s1 = s2 = 0 auch 2^16 = 65536 Durchläufe erreichen. Falls s1 <> 0, wird bei dir die innere Schleife mit DJNZ R1,loop1 nur einmal richtig durchlaufen (nämlich s1 mal, danach immer 256 mal), da R1 nicht neu geladen wird. Die Gesamtzahl der Durchläufe ist bei deiner Version also (s2-1)*256 + s1, was vielleicht für andere Schleifenzahlen praktisch ist, die nicht per s1*s2 dargestellt werden können.
Beitrag #5133496 wurde vom Autor gelöscht.
Die aktuelle Schleifenversion ist auf dem angehängten Scan - den Kontrast konnte ich nicht verbessern ... Es wäre natürlich gut, wenn Eberhard H.´s Schleife funktionieren würde. Das würde ein paar Programmzeilen sparen. Edit: aber vielleicht gibt es Probleme mit dem kurzen/relativen Rücksprung, da in der Schleife recht viele Befehle stehen werden.
:
Bearbeitet durch User
H-G S. schrieb: > Die aktuelle Schleifenversion ist auf dem angehängten Scan - den > Kontrast konnte ich nicht verbessern ... Du musst nach der Befehlsfolge erst 1 subtrahieren und dann abfragen/springen (sinngemäß wie bei DJNZ) und nicht umgekehrt, denn sonst stimmt die Zahl der Durchläufe nicht wie erwartet. > > Es wäre natürlich gut, wenn Eberhard H.´s Schleife funktionieren würde. Sie funktioniert wie beschrieben. > > Edit: aber vielleicht gibt es Probleme mit dem kurzen/relativen > Rücksprung, da in der Schleife recht viele Befehle stehen werden. Das ist dann ein 8051-Problem.
H-G S. schrieb: > Die aktuelle Schleifenversion ist auf dem angehängten Scan Warum so umständlich? Ist Dir diese Lösung zu einfach: Beitrag "Re: 16-Bit-Schleifen mit 8051"
H-G S. schrieb: > aber vielleicht gibt es Probleme mit dem kurzen/relativen > Rücksprung, da in der Schleife recht viele Befehle stehen werden. 1. Es gibt auch lange Sprünge 2. Es gibt Unterprogramme Vielleicht wäre es doch ganz gut, erst mal die einfachsten Grundlagen zu lernen. Georg
Eberhard H. schrieb: > Du musst nach der Befehlsfolge erst 1 subtrahieren und dann > abfragen/springen (sinngemäß wie bei DJNZ) und nicht umgekehrt, denn > sonst stimmt die Zahl der Durchläufe nicht wie erwartet. Stimmt! Erst kommt die Subtraktion von 0001h, dann die Abfrage ob der Zähler auf 0000h steht. Danke dir! Edit: Der Wertebereich der Schleife geht ja von 0001h bis xxxxh, also größer 0000h als Starwert. Daher kann sofort 0001h subtrahiert werden ohne einen Überlauf zu provozieren. Peter D. schrieb: > Warum so umständlich? > Ist Dir diese Lösung zu einfach: > Beitrag "Re: 16-Bit-Schleifen mit 8051" Die ist mit im Moment zu relativ wegen der kurzen Sprungdistanz ... mal schauen wieviele Byte in der Schleifenmitte stehen dann kann ich sie mir nochmal anschauen.
:
Bearbeitet durch User
Peter D. schrieb: > H-G S. schrieb: >> Die aktuelle Schleifenversion ist auf dem angehängten Scan > > Warum so umständlich? > Ist Dir diese Lösung zu einfach: > Beitrag "Re: 16-Bit-Schleifen mit 8051" Ich bin zwar kein 8051-Programmierer, aber wie willst du damit 2^16 Durchläufe von "Code" erreichen?
1 | weiter: |
2 | nop ; Code |
3 | anfang: ; hier Einsprung in die Funktion |
4 | dec r0 |
5 | cjne r0, #0ffh, weiter |
6 | dec r1 |
7 | cjne r1, #0ffh, weiter |
8 | ret |
2^16 Durchläufe klappen damit nur, wenn man in "weiter" statt in "anfang" einspringt und r0, r1 vorher mit 0ffh lädt. Ansonsten hat man wenigstens einen Durchlauf vergeigt. Oder man vergleicht mit 0 statt mit 0ffh und löscht r0 und r1 vor dem Einsprung in "weiter". Das wäre dann die "lange" Version von Route 66, die man mit zwei verschiedenen Rücksprungadressen und erneutem Laden von r0 wiederum verallgemeinern kann. Der geringste Aufwand sind vermutlich zwei verschachtelte DJNZ-Schleifen mit den Anfangswerten Null und bei Bedarf eine zu lange Befehlsfolge ("Code") in ein Unterprogramm zu verlagern. Edit: Sehe gerade, dass CJNE auch nur einen relativen Sprung mit 8 Bit schafft, also ist es keine "lange" Version und benötigt sogar noch 2 Bytes mehr (2x DEC) als mit DJNZ.
:
Bearbeitet durch User
Eberhard H. schrieb: > Ich bin zwar kein 8051-Programmierer, aber wie willst du damit 2^16 > Durchläufe von "Code" erreichen? Wo steht, daß er das will? Entweder 0..65535 oder 1..65536, einen Tod muß man sterben. Ich hatte 0..65535 angenommen, ein Umstellen ist aber leicht möglich. Oder man nimmt 3 Bytes, dann geht auch 0..65536.
Streitet euch nicht :-) Ich muss eh mit Registern als Zähler arbeiten - die CJNE-Befehle unterstützen aber nur direkte Wert-Angaben im Opcode (soweit ich sah).
H-G S. schrieb: > Ich muss eh mit Registern als Zähler arbeiten - die CJNE-Befehle > unterstützen aber nur direkte Wert-Angaben im Opcode (soweit ich sah). Hä? R0, R1 sind doch Register. Der Abbruchtest ist immer gleich, auch bei Deinem Code. Laß einfach mal den Code im Simulator laufen mit verschiedenen Startwerten für R0, R1.
Peter D. schrieb: > Hä? > R0, R1 sind doch Register. Der Abbruchtest ist immer gleich, auch bei > Deinem Code. Ups ... ich dachte die ffh seien Platzhalter für den Start-/Zählwert. Doch es sind Überlauf-Testwerte. Der Zählwert steht ja in R0/R1. Sieht sehr kompakt aus die Methode - sollte man sich merken und mal durchrechnen. Die 256 als Wert scheint ja wichtig weil die 1. Bitstelle im High-Wort den Wert 256 hat und somit muss wohl die innere Schleife wirklich 256 mal durchlaufen werden :-)
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.