Ich habe zwei Register. In beiden befindet sich ein Wert zwischen null und drei. Es sollen bestimmte Kombinationen erkannt werden: 1) R1 = 0 und R2 = 2 --> Unterprogramm A 2) R1 = 0 und R2 = 1 --> Unterprogramm B 3) R1 = 2 und R2 = 3 --> Unterprogramm A 4) R1 = 2 und R2 = 0 --> Unterprogramm B . . . Wie kann man das am besten Lösen? Der einfachste Weg wäre, eine Abfrage mit ganz vielen Sprüngen zu erstellen. Gibt es eine elegantere und vor allem platzsparendere Lösung?
soweit ich weiß nicht, bei x86 glaub ich gibt jump-tables oder so. So gesehen ists halb so tragisch, weil r1 ja entweder 0 oder 2 ist. Mfg Clemens
So: Funktionspointertabelle mit 16 Einträgen (denn soviele Kombinationen kann es ja geben), als Index in die Tabelle R2 << 2 + R1 verwenden. Der erste Eintrag entspricht R1 = 0 und R2 = 0, der zweite entspricht R1 = 1 und R2 = 0 etc. Für jede Bitkombination von R1 und R2 wird in die Funktionspointertabelle die Adresse der aufzurufenden Funktion ("Unterprogramm") eingetragen.
An Adresse X kommt eine lange Liste mit Sprüngen: rjmp ProgA ; r1 = 0, r2= 0 rjmp ProgB ; r1 = 0, r2= 1 ... (16 Einträge insgesamt) Dann ein jump an die Adresse (X + r1<<2 + r2)
Wie geht die Reihe weiter? Im Moment sieht es so aus: Wenn Bit 1 in R2 gesetzt ist, dann kommt A zum Zug ansonsten B
Theoretisch gibt es nur 8 mögliche Kombinationen. Bezieht man alle möglichen Störfälle ein, gibt es 16 mögliche. Kann man bei dem 8051 wirklich so eine Tabelle anlegen? Das heißt doch look up table oder?
Ich denke, Du suchst eine Sprungtabelle. Falls es sich um einen (nicht zu kleinen und zu alten) AVR handelt, dann bietet sich der indirekte Sprung mittels 'ijmp' an. Dazu wird der Z-Pointer auf die Adresse mit der Sprungtabelle positioniert, dann der Index draufaddiert, dann mit ijmp die Adresse angesprungen, auf die der Z-Pointer zeigt. Dort steht dann die oben bereits erwähnte Liste mit rjmp xyz - Einträgen. Geht gut und recht schnell, nutze ich gerne bei größeren Menüdialogen. ...
Ist ein AT89S8252, so weit ich weiß, kennt er kein IJMP. Aber indirekte Sprünge sollen mit z.B. JMP @A+DATAPOINTER Ich bin leider noch nicht so weit, um das alles 100% zu verstehen. Also ich stelle mir das so vor: Man hat im Speicher z.B. 5 Adressen mit bestimmten Werten, welche man zuvor auf den uC geladen hat. Das Programm spricht nacheinander diese Adresse an und vergleicht sie mit einem Wert (z.B. im ACCU), wenn der Wert übereinstimmt, wird eben ein Unterprogramm aufgerufen. Man kann das entweder nacheinander für jeden Wert in der Tabelle machen oder eine Art Schleife durch indirekte Sprünge Programmieren. Stimmt das so? Könnte mir bitte jemand ein kleines Beispiel mit einer Tabelle im Flash-Speicher zeigen?
1 | mov a, r1 |
2 | add a, acc |
3 | add a, acc ; * 4 |
4 | orl a, r2 |
5 | anl a, #0Fh ; table = 16 entries |
6 | rl a ; * 2 (AJMP = 2 byte) |
7 | mov dptr, #tabelle |
8 | jmp @a+dptr |
9 | tabelle: |
10 | ajmp ... |
11 | ajmp ... |
12 | ... |
Peter
Peters Beispiel ist ok allerdings mußt du bei ajmp berücksichtigen das dass Sprungziel innerhalb eines 2k Segmentes liegt.
Danke euch soweit! Das Programm verstehe ich noch nicht ganz. mov a, r1 add a, acc add a, acc ; * 4 orl a, r2 anl a, #0Fh ; table = 16 entries Hier werden Werte aus R1 und R2 in ein register kopiert und nebeneinander platziert: 0 | 0 | 0 | 0 | R1 | R1 | R2 | R2 ------------------------------------------------------------------------ -- rl a ; * 2 (AJMP = 2 byte) Hier werden die Bits gedreht: R2 | R2 | R1 | R1 | 0 | 0 | 0 | 0 ------------------------------------------------------------------------ -- mov dptr, #tabelle Die Adresse der Tabelle wird in den Datapointer geladen. Irgendwie erkennt mein Assembler dptr nicht, ist wohl nicht in der Bibliothek vorhanden. Welche Adresse hat der Datapointer? ------------------------------------------------------------------------ -- jmp @a+dptr Relativsprung an die Adresse: R2 | R2 | R1 | R1 | 0 | 0 | 0 | 0 | T | T | T | T | T | T | T | T Oder sind die zwei Bytes vertauscht? ------------------------------------------------------------------------ -- tabelle: ajmp ... ajmp ... Das ist ein Label im Speicher, wo die 16 Kombinationen in 16 Adressen gespeichert sind. Warum kommt drunter ajmp? ------------------------------------------------------------------------ -- Klärt mich bitte auf.
Peter Dannegger wrote:
Ganz Einfach
>
1 | > mov a, r1 |
2 | |
3 | R1 in den Akku |
4 | |
5 | > add a, acc |
6 | |
7 | Mit sich selbst addieren heiß also x 2 |
8 | |
9 | > add a, acc ; * 4 |
10 | |
11 | Ergebnis noch mal mit sich addieren also nochmal x 2 |
12 | Beiden Add Befehle ersetzen ein Multiplikation mit 4 |
13 | |
14 | > orl a, r2 |
15 | |
16 | den Wert aus R2 dazu ODERN (wie "addieren") |
17 | Vorraussetzung hier ist, daß der Wert aus R1 nie größer als 3 |
18 | ist sonst gehts nicht |
19 | |
20 | > anl a, #0Fh ; table = 16 entries |
21 | |
22 | die unteren 4 Bit ausfiltern, sodaß 16 Kombinationen übrig blieben |
23 | |
24 | > rl a ; * 2 (AJMP = 2 byte) |
25 | |
26 | einmal nach links um auf eine 32Byte Tabelle zu erweitern |
27 | Wie oben geschrieben sind AJMp Befehle im auf eine 2KB Page beschränkt |
28 | Wenn Du Programmerweiterungen machst und den Sourcecode verschiebst |
29 | kann es Probleme geben |
30 | Um mit LJMP statt AJMP Befehlen zu arbeiten schreibst du statt RL A golgendes |
31 | |
32 | mov B,A |
33 | rl A |
34 | add A,B |
35 | |
36 | dann hast du eine 3x16 Byte Tabelle |
37 | |
38 | > mov dptr, #tabelle |
39 | |
40 | Datenpointer an Tabellenanfang |
41 | |
42 | > jmp @a+dptr |
43 | |
44 | Zur Funktion springen |
45 | |
46 | > tabelle: |
47 | > ajmp ... |
48 | > ajmp ... |
49 | > ... |
50 | > |
> > > Peter Gruß Dirk
Ach so, ich habe gedacht, der Befehl RL dreht das Register um die eigene Achse. In Wirklichkeit wird es ja nur um eine Stelle verschoben. Jetzt sollte es klappen, danke noch Mal.
Ja so ein Befehl wäre auch schön, sowas hätte ein paar Leute hier im Forum echt glücklich gemacht
Hm, ich sitze hier jetzt seit einer Stunde und kann den Fehler nicht finden. Wahrscheinlich läuft irgendwo ein Register über, der uC hängt sich auf, wenn SIG_A = 0 und SIG_B = 1 ist.
1 | ;Zweiter Versuch mit dem Drehimpulsencoder |
2 | |
3 | include REG8252.INC |
4 | |
5 | ;Zuweisungen |
6 | SIG_A EQU P3.3 |
7 | SIG_B EQU P3.2 |
8 | LAST_STATE EQU R6 |
9 | |
10 | ;Resets |
11 | MOV P2, #0 |
12 | MOV LAST_STATE, #0 |
13 | |
14 | ;*************************************************************************************************** |
15 | |
16 | ;Die Hauptschleife |
17 | LOOP: ACALL CHECK_DIG_STATE |
18 | SJMP LOOP |
19 | |
20 | ;*************************************************************************************************** |
21 | |
22 | CHECK_DIG_STATE: ACALL READOUT_DIG |
23 | MOV A, LAST_STATE |
24 | MOV B, #04h |
25 | MUL AB |
26 | |
27 | ACALL READOUT_DIG |
28 | ORL A, LAST_STATE |
29 | ANL A, #0Fh |
30 | |
31 | MOV DPTR, #TABLE |
32 | JMP @A+DPTR |
33 | |
34 | ;*************************************************************************************************** |
35 | |
36 | ;Liest den Drehimpulsgeber aus und speichert den Zustand im Register LAST_STATE |
37 | |
38 | READOUT_DIG: JB SIG_A, A_TRUE |
39 | SJMP A_FALSE |
40 | |
41 | A_TRUE: JB SIG_B, STATE_2 |
42 | SJMP STATE_1 |
43 | |
44 | A_FALSE: JB SIG_B, STATE_3 |
45 | SJMP STATE_0 |
46 | |
47 | STATE_0: MOV LAST_STATE, #0 ;SIG_A=0 and SIG_B=0 |
48 | RET |
49 | |
50 | STATE_1: MOV LAST_STATE, #1 ;SIG_A=1 and SIG_B=0 |
51 | RET |
52 | |
53 | STATE_2: MOV LAST_STATE, #2 ;SIG_A=1 and SIG_B=1 |
54 | RET |
55 | |
56 | STATE_3: MOV LAST_STATE, #3 ;SIG_A=0 and SIG_B=1 |
57 | RET |
58 | |
59 | ;*************************************************************************************************** |
60 | |
61 | TABLE: SJMP NOA |
62 | SJMP CW |
63 | SJMP ERR |
64 | SJMP CCW |
65 | SJMP CCW |
66 | SJMP NOA |
67 | SJMP CW |
68 | SJMP ERR |
69 | SJMP ERR |
70 | SJMP CCW |
71 | SJMP NOA |
72 | SJMP CW |
73 | SJMP CW |
74 | SJMP ERR |
75 | SJMP CCW |
76 | SJMP NOA |
77 | |
78 | ;*************************************************************************************************** |
79 | |
80 | NOA: NOP |
81 | RET |
82 | |
83 | ERR: NOP |
84 | RET |
85 | |
86 | CW: SETB P2.7 |
87 | RET |
88 | |
89 | CCW: CLR P2.7 |
90 | RET |
91 | |
92 | END |
Hallo Maxim, hänge den Sourcecode mal als Textdatei an, ich korrigiere Ihn Dir. Dirk
Vor Freude wuße ich erstmal gar nicht, wo ich die Datei suchen soll ...
Maxim wrote:
> Vor Freude wuße ich erstmal gar nicht, wo ich die Datei suchen soll ...
Hallo Maxim,
habs mir durchgesehen, tut mir leid aber das wird und kann nie
funktionieren.
Wenn Du möchtest, poste ich hier ne Routine um nen Encoder auszulesen.
Die funktioniert schon in viele Geräten!
?????
So, ich hab mal in den Anhang gelegt. Viel Spaß damit. Wenn es Fragen gibt, dann melde Dich. Gruß Dirk
Schade, dass mein Programm nicht funktioniert. Aber da habe ich mir wohl zu viel für einen Einstieg vorgenommen. Ich habe dein Programm durchgelesen und einen PAP erstellt. Mit der Logik habe ich mich aber noch nicht beschäftigt, jedoch erahne ich, wie es funktioniert. Bin heute zu müde, um da noch etwas zu machen. Der PAP ist angehängt. Danke für deine Hilfe.
Hier mal die Assemblerversion meines Encoderprogramms. Ich taste ihn im Timerinterrupt ab. Peter
Hallo Peter, das funktioniert schon. Aus leidlicher Erfahrung weiß ich aber, das wenn sich zwischen den Auswertungen der beiden Signale der Phasenzustand ändert, eine Fehlauswertung stattfindet. Dies kann auch passieren wenn ein höher priorisierter Ínterrupt zwischen der Auswertung der beiden Signale ausgeführt wird und die Distanz der Phasenauswertung verschiebt. Deswegen sollte man, beide Signal zeitsynchron latchen.
Dirk Hofmann wrote: > Aus leidlicher Erfahrung weiß ich aber, das wenn sich zwischen den > Auswertungen der beiden Signale der Phasenzustand ändert, eine > Fehlauswertung stattfindet. Dies kann auch passieren wenn ein höher > priorisierter Ínterrupt zwischen der Auswertung der beiden Signale > ausgeführt wird und die Distanz der Phasenauswertung verschiebt. Ein nicht korrigierbarer Fehler kann erst dann auftreten, wenn dazwischen 2 Phasenübergänge liegen, d.h. Du hast nen exorbitant langen Interrupt dazwischen oder das Intervall des Timerinterrupts ist zu groß. Man sollte also immer dem Encoder-Interrupt die höchste Priorität geben und das Timerintervall kurz genug wählen. Idealer Weise sollte die Timerinterruptfrequenz mindestens doppelt so hoch wie die maximale Schrittfrequenz des Encoders sein, damit man auch noch Zeit hat, Preller zu korrigieren. Peter P.S.: Du hast das warscheinlich damit verwechselt, den gleichen Pin mehrmals in der Routine abzufragen. Dann kann in der Tat sonstwas passieren, wenn er sich genau zwischen 2 Abfragen ändert. Man muß immer darauf achten, innerhalb eines Auswertezyklus asynchrone Eingänge nur einmalig einzulesen und dann zwischenzuspeichern, wenn deren Zustand nochmal benötigt wird.
Habe mich gerade hingesetzt und den PAP (da hat sich ein kleiner Fehler eingeschliechen auf dem Bild ...) manuell abgelaufen. Habe den Algorithmus weitgehend verstanden. Ich muss sagen, der ist ziemlich raffiniert, im vergleich zum meinem ... ;-) Wie kommt man nur auf solche "einfachen" Lösungen?
Hallo Peter, mir ist das schon klar, mit Interruptpriorisierung. Da Maxim gerade einsteigt, wollte ich auf diese Problematik hinweisen, das daß nicht zur Frustration führt und zur Veranschaulichung. Hab mal ne Frage an Dich. Hattest Du schon mal was mit Software JPEG decodierung zu tun? Hab noch keine gute Beschreibung gefunden, hab zwar das Buch von John Miano, aber der File Aufbau geht nicht direkt klar. Maxim, wenn man ne Weile programmiert, erarbeitet man sich Viele Problemlösungsstrategien systematisch, also immer weiter machen und dran bleiben. Learning by doing sozusagen
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.