Wie kann ich am besten eine 240fache Verzweigung realisieren? Abhängig von einem Wert zwischen 10 und 250 soll jeweils an eine andere Stelle gesprungen werden. Ich habe das jetzt mit CJNE gelöst. Der uC Prüft also im schlimmsten Fall 239 Mal, ob der Wert mit einer Konstante übereinstimmt und springt gegebenenfalls. Das geht doch sicher effizienter, oder?
OMG! Wer tipt sowas alles ein o.O Nimm ne Sprungtabelle...
1 | ldi ZL, LOW(jumptable*2) ; Tabellenindex laden |
2 | ldi ZH, HIGH(jumptable*2) ; Tabellenindex laden |
3 | ( subi wert, 10 ) ;"Startindex" anpassen |
4 | add ZL, wert ;Wert aufadieren |
5 | sbc ZH, -1 ;Carry auf High |
6 | ijmp |
7 | back: |
8 | |
9 | label1: |
10 | code |
11 | rjmp back |
12 | label2: |
13 | code |
14 | rjmp back |
15 | . |
16 | . |
17 | . |
18 | label250: |
19 | code |
20 | rjmp back |
21 | |
22 | |
23 | jumptable: |
24 | rjmp label1 |
25 | rjmp label2 |
26 | rjmap label3 |
27 | . |
28 | . |
29 | . |
30 | rjmp label250 |
ist sehr viel schneller und braucht wahrscheinlich noch weniger code. Wenn dein wert nicht bei 0 anfängt mußt du vorher noch 10 abziehen (hab ich mal in klammern angedeutet.) Vorteil ist das du immer konstante Zeit brauchst für den Vergleich. Nachteil: Der Prozessor muß inderekten Sprung unterstützen.
Moin moin, schmunzel... wusste gar nicht das eine Sptungtabelle so aussieht. >jumptable: >rjmp label1 >rjmp label2 >rjmap label3 >. Mal ein Beispiel mit dem MSP430: In: R4 clr R5 ParCmd cmp.b #0,CmdTbl(R5) jz NoCmd cmp.b CmdTbl(R5),R4 jz FndCmd inc R5 jmp ParCmd FndCmd rla R5 mov CmdAdr(ACC),PC NoCmd bla bla CmdTbl DB 08h,0dh,1bh,'A','C','+','-','0',0 EVEN CmdAdr DW CmdBS,CmdCR,CmdESC,LtrA,LtrC,CmdPl,CmdMi,Cmd0 EVEN CmdBS bla bla CmdCR bla bla und so weiter... Und wech...
zonendoedel wrote: > Moin moin, > > schmunzel... > wusste gar nicht das eine Sptungtabelle so aussieht. > >>jumptable: >>rjmp label1 >>rjmp label2 >>rjmap label3 >>. Tjoar hm... wieso nicht? Ich mein ich habs jezt so ausm Gedächtnis hinprogrammiert und ist halt AVR ASM aber im Prinzip funktioniert das schon so... Man kann natürlich auch eine Tabelle der Label anlegen, von der man die Adresse lädt, und dann mit IJMP... jeder wie er es mag. Stand ja nicht dabei was nun genau er will welche Architektur, welcher proz... Auf diese weise kann man aber auch in grenzen nen Fallthroug realisieren rcall XYZ rcall fallthrough rjmp blabla
Hab eben mal geschaut Wikipedia sagt z.B.: "Dazu sind die Aufrufe (oder manchmal auch nur die nackten Funktionsadressen) mit konstanter Länge hintereinander wie in einer Tabelle im Speicher angeordnet. Sie bestehen typischerweise aus einfachen Sprungbefehlen an die Stelle im ROM oder innerhalb der Funktionsbibliothek" Und die Interuptvektoren im AVR sind auch ne "Sprungtabelle"... also seh ich jezt grad nicht warum mein Ansatz so verkehrt ist?
Moin moin @Läubi, jojo, wie zu guten alten CP/M-C64-Zeiten. Die Tabellenlösung ist halt ausgesprochen flexibel. Die Interruptvektortabellen aktueller CPUs/MCUs arbeiten ja ähnlich. Nur steinalte Design arbeiten da z.B. mit JMPs (8051...) Aber jeder wie er mag...
Hallo Läubi, an deiner Lösung ist nichts falsch. Die Auswertung mit einem berechneten Sprungziel finde ich für diese Aufgabenstellung optimal. Man kann auch die Rückkehradresse auf dem Stack ablegen, die Zieladressen ohne Jmp untereinanderschreiben, Tabellenadresse berechnen und den Inhalt ebenfalls auf dem Stack ablegen. Jetzt das Zielprogramm mit einem ret anspringen und mit einem ret abschliessen. Dann stimmt der Stack wieder und das Programm wird an der vorgegebenen Rückkehradresse fortgesetzt. Grüße Allu
Wahrscheinlich geht dieser Thread hier weiter ;-)) Beitrag "Indirekte Sprünge mit 8051" er meint eine 8051 Typ und da kann man es z.B. so machen. CMD: mov a,command ; rl a ;ajmp = 2 Bytes mov dptr,#commands ;pointer @ cmd jmp @a+dptr ;execute commands commands: ajmp command_1 ; ajmp command_2 ; ajmp command_3 ; ajmp command_4 ; ..... ..... Allerdings muß das Sprungziel innerhalb eines 2k Blocks liegen.
Danke euch für die vielen Tipps! Habe leider vergessen zu erwähnen, dass ich einen 8252 und Assembler nutze, aber das Prinzip ist ja das selbe. Statt einer Sprungtabelle oder einer 240fachen Verzweigung könnte ich in meinem Programm den Wert auch durch "Timer = 1 / (3 * 10^-6 * R0)" berechnen, aber eine Sprungtabelle mit vorberechneten Werten ist in diesem Fall einfacher, oder?
Noch einfacher ist ein Array, also das Lesen von einer berechneten Adresse. Die Sprungtabelle ist unnötiger Aufwand.
Aber der Timer ist 16-bittig, kann ich ein 16-bittiges Array anlegen? Da muss ich halt immer zwei Bytes abspeichern und dann beim Sprung mit RL die Adresse anpassen?
VALUE001: DB 513d VALUE002: DB 514d VALUE003: DB 516d ist ja dasselbe wie: VALUE001H: DB 1d VALUE001L: DB 1d VALUE002H: DB 1d VALUE002L: DB 2d VALUE003H: DB 1d VALUE003L: DB 4d oder?
Genau so. Die vielen Labels kannst du dir sparen, schreib einfach deine 240 Werte hintereinander in den Speicher, dann kannst du dir die Adresse so ausrechnen: adresse = (x - 10) * 2 + startadresse
Andreas Schwarz wrote: > Genau so. Die vielen Labels kannst du dir sparen, schreib einfach deine > 240 Werte hintereinander in den Speicher, dann kannst du dir die Adresse > so ausrechnen: > > adresse = (x - 10) * 2 + startadresse So wollte ich das auch machen. :-) Habe ein kleines Testprogramm geschrieben. Leider funktioniert es nicht richtig. Habe den Debugger verwendet, aber bis jetzt nicht herausgefunden, wo der Fehler liegt.
1 | INCLUDE C:\RIDE\INC\51\ATMEL\REG8252.INC |
2 | |
3 | MOV P2, #0h |
4 | MOV R0, #0h |
5 | |
6 | LOOP: JB P3.3, WAIT_FOR_Z |
7 | SJMP GO_ON |
8 | |
9 | WAIT_FOR_Z: JNB P3.3, BUTTON_OK |
10 | SJMP WAIT_FOR_Z |
11 | |
12 | BUTTON_OK: INC R0 ;Falls Taster betätigt, erhöhe R0 um 1 |
13 | |
14 | GO_ON: MOV DPTR, #TABELLE ;Lade Adresse des Arrays in den Datenzeiger |
15 | MOV A, R0 |
16 | RL A ;Verschiebe Wert um eine Stelle nach links (gleich der Multiplikation mit 2) |
17 | ADD A, #1d ;Addiere 1, um an das Low-Byte zu kommen |
18 | MOVC A, @A+DPTR ;Kopiere Inhalt aus dem Array in den Akkumulator |
19 | MOV P2, A |
20 | |
21 | RET_GO_ON: SJMP LOOP |
22 | |
23 | TABELLE: |
24 | |
25 | VALUE001: DB 513d ;Low-Byte = 1 |
26 | |
27 | VALUE002: DB 514d ;Low-Byte = 2 |
28 | |
29 | VALUE003: DB 516d ;Low-Byte = 4 |
30 | |
31 | VALUE004: DB 520d ;Low-Byte = 8 |
32 | |
33 | VALUE005: DB 528d ;Low-Byte = 16 |
34 | |
35 | END
|
Am Port P2 sind 8 LEDs angeschlossen. Mit diesem Programm sollte bei einem Tastendruck (Taster am P3.3) immer eine LED weitergeschaltet werden. Stattdessen leuchtet am Anfang die zweite, dann die vierte und bei einem weiteren Tastendruck die erste LED. Danach erscheinen alle möglichen Kombinationen, da der Datenzeiger scheinbar irgendwo auf den Code zeigt, weil das Array zu ende ist. Ich werde jetzt noch weiter mit dem Debugger rumspielen, aber vielleicht sieht jemand den Fehler auf Anhieb? update: Irgendwie werden die dezimalen Werte nur in einem Byte gespeichert. Der Assembler beachtet den High-Byte gar nicht. Das erklärt das Verhalten des uC. Ich benutze die kostenlose Version von RIDE 6.10.15. Liegt es an dem Programm oder muss ich die Schreibweise irgendwie verändern, dass er z.B. die Zahl 513 automatisch in zwei Bytes ablegt?
Für 16Bit mußt Du natürlich DW nehmen und nicht DB. Und auch 2* MOVC um beide Bytes auszulesen (INC DPTR dazwischen). Peter
Also z.B. so hier:
1 | mov dptr, #table |
2 | mov a, index |
3 | add a, acc |
4 | jnc _m1 |
5 | inc dph ;übertrag |
6 | _m1: |
7 | mov r7, a |
8 | movc a, @a+dptr |
9 | inc dptr |
10 | xch a, r7 |
11 | movc a, @a+dptr |
12 | ret ;r7 = high, a = low byte |
13 | |
14 | table: |
15 | dw 1000 |
16 | dw 2000 |
17 | usw. |
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.