hallo,
aus eine Tabelle lese ich bis zu 255 8Bit Werte, die ich dann in einer
switch / case Anweisung weiter verarbeite. Da der Befehlscode
(Assembler) von switch immer größer wird, je mehr case Anweisungen
existieren, suche ich hierfür eine alternative.
Gruß Siegfried
Hängt davon ab was bei den einzelnen Werten passieren soll. Wenn du
wirklich 255 unterschiedliche Fälle hast, ist - vom Speicherbedarf
gesehen - die switch dein kleinstes Problem.
Was steht denn in den einzelnen cases deiner Switch drin?
Hallo,
Die Anweisung der einzelnen cases meiner Switch ist sehr
unterschiedlich.
Habe mal im Disassembly nachgerechnet. jede nachfolgende Case Anweisung
benötigt beim C30 Compiler ca 8 bis 10 * 2 Byte.
jenachdem, welche case Anweisung bearbeitet werden soll, ergibt auch
eine unterschiedliche Laufzeit,
Gruß
bei den PICs gibt es diesen Trick. Den Wert des Switch steht im W
Register. Der wird zum Programmcounter hinzugezählt (ADDWF). DT
(Data-Table o. ä.) gibt dann den Wert ins W Register und springt ans
Ende der Tabelle.
HTH,
Wolfgang
Beispiel:
TABLE_H_CW
addwf PCL, F
dt 0x0
dt 0x0
dt 0x1
dt 0x2
dt 0x3
dt 0x3
dt 0x4
dt 0x5
dt 0x5
dt 0x6
dt 0x6
dt 0x6
dt 0x7
Hallo,
vielen Dank für die Hilfe.
Werde alles in einem Array ablegen.
Muß nur noch sehen, wie ich einen "unbekannten" Befehl abfangen kann.
Gruß Siegfried
Siegfried Saueressig schrieb:> Muß nur noch sehen, wie ich einen "unbekannten" Befehl abfangen kann.
Mit einem Pointer auf eine allgemeine "unbekannt"-Routine. Der wird an
allen Stellen ins Array eingetragen, für die der Array-Index keine
definierte Bedeutung hat.
Solche Klimmzüge sind unnötig.
Ein optimierender Compiler erkennt selber, ab wann eine Tabelle
günstiger ist
Schreib einfach mal 20 aufeinanderfolgende Case, die verschiedenes
machen und guck ins Listing, dann sollte da ne Sprungtabelle stehen.
Peter
>bei den PICs gibt es diesen Trick. Den Wert des Switch steht im W>Register. Der wird zum Programmcounter hinzugezählt (ADDWF). DT>(Data-Table o. ä.) gibt dann den Wert ins W Register und springt ans>Ende der Tabelle.
Vorsicht: Je nach Paging erkennt diese Routine den Pagerand nicht, der
addwf PC, F führt dann zum Überrollen.
Beim PIC braucht man die Werte nicht mal mit dt einzubinden. Dafür gibt
es den Befehl retlw:
TABLE_H_CW
addwf PCL, F
retlw 0x0
retlw 0x0
retlw 0x1
>Muß nur noch sehen, wie ich einen "unbekannten" Befehl abfangen kann.>Gruß Siegfried
Mit orwf vor der addwf-routine.
In C kannst Du switch case mit else if ersetzen. Je nach Compiler kann
das zu besseren Ergebnissen führen.
if (....)
{ ...
}
else if(...)
{
...
}
else if(...)
{
...
}
else (...)
{
...
}
MPLAP 8.40 C30 3.21
Hallo,
@ Peter Dannegger
leider keine Tabelle gefunden.
Habe eine Tabelle mit 256 Einträge im Flash angelegt. Entsprechend auch
notwendige Unterprogramme. Ist etwas Arbeit, aber nicht schlimm.
call Aufruf
1
215:p_ablauf_list[table_codex]();
2
06416BFD64Emov.b0x164e,0x0000
3
06418FB8000ze0x0000,0x0000
4
0641A400080add.w0x0000,0x0000,0x0002
5
0641C2CD720mov.w#0xcd72,0x0000
6
0641E408000add.w0x0002,0x0000,0x0000
7
06420780010mov.w[0x0000],0x0000
8
06422010000call0x0000
9
=^ca30Byte
das Unterprogramm
1
379:{
2
065E2FA0000lnk#0x0
3
//
4
// ---->>>> hier der Code im Unterprogramm
5
6
392:return(0);
7
0660EEB0000clr.w0x0000
8
393:}
9
06610FA8000ulnk
10
06612060000return=^ca10Byte
wenn ich die switch Routine ansehe, komme ich schnell 100 Byte und mehr.
am liebsten wäre es mir, wenn ich eine Sprung-Make in eine Tabelle
bekommen würde. Somit wären die Unterprogramm Code nicht notwendig.
Wäre gut, wenn man dabei hilft.
Gruß Siegfried
MPLAP 8.40 C30 3.21
Hallo,
gibt es noch eine bessere Lösung als switch / case bzw. Tabelle mit
Zeiger(call) ?
wenn ich richtg gerechnet habe, benötig das Programm ca 21 Takt mit der
Tabelle Lösung
Gruß Siegfried
Also, grob beschrieben...
Über das Z-Register adressierst du die Tabelle und lädst dir den Wert
heraus, multiplizierst mit 2, lädst die Spungmarke in ein
Doppelregister, addierst den angepassten Wert aus der Tabelle und Pusht
diesen Wert auf den Stack. Anschließend leitest du einen Rücksprung mit
"RET" ein. Der Controller sollte dann bei irgen deiner Marke landen und
dort das gewünschte Programm aufrufen. Allerdings ist dies nur eine
Überlegung, ich hab sowas noch nicht gemacht und kanns hier nicht
prüfen. Sollte aber von der Überlegung her funktionieren.
Gruß oldmax
Hallo,
@ oldmax
Danke für deine Antwort. Ich programmier unter "C" mit dem C30 Compiler.
Wie man dort Assembler einbindet, ist mir nicht bekannt.
"Sprungmarken" in einer Tabelle wäre eine Alternative, aber wie ?
Gruß Siegfried
Wenn die Tabelle immer vollständig ist, also keine Lücken aufweist, die
über einen default-Handler behandelt werden müssen, dann kannst Du die
Tabelle so ausführen, dass sie einem vielfachen einer 2er Potenz
entspricht.
Dann wandelst Du das Byte in dem Du einen Left-Shift machst und addierst
die Anfangsadresse der Tabelle dazu und springst.
Braucht das 21 Cycles? Wäre ein weiterer Grund nicht mit PIC zu arbeiten
:)
Ulrich
Hallo,
@ Ulrich
wäre dankbar, wenn du deine vorgehenweise mir erklärst. Habe dieses so
noch nicht gemacht.
derzeitig arbeite ich mit Unterprogramme. Die Adressen stehen in einer
Tabelle.
hier kleinen Ausschnitt aus meinem Programm
CALL Tabelle:
1
P_Ptr_Auftragconstp_Auftrag_list[]=
2
{
3
//
4
auftrag_FALSE,// C_FALSE, // 000
5
auftrag_TRUE,// C_TRUE, // 001
6
auftrag_INPUT,// C_Input, // 002
7
auftrag_OUTPUT,// C_Output, // 003
8
auftrag_DEFAULT,// C_GLEIS_LANGE,// 004
9
auftrag_TIMER,// C_TIMER,// 005
10
......
11
insgesamt256Einträge
12
}
Hauptprogramm:
Der Code für die Auswahl des Unterprogramm, steht in einem großem
Speicher.
Hi
Leider kann ich dir bei C nicht helfen und den von dir verwendeten µC
kenne ich auch nicht. Allerdings schreibst du, das du die Adressen der
Unterprogramme aus einer Tabelle aufrufst, nichts anderes sollte der von
mir geschriebene Code machen. Wenn du weißt, wie du eine Adresse
berechnen und anspringen kannst, dürfte die Umsetzung doch kein Problem
mehr sein. Wichtig zu wissen, wie die Befehle adressiert sind. Beim
Atmega8 hat jeder Befehl eine Breite von 16 Bit, also reicht es, wenn
der Programmcounter nur gerade Adressen aufruft. Somit ist das 0-Bit
irrelevant. Deshalb die Multiplikation mit 2, bzw. die Schiebeoperation.
In C braucht dich dies doch aber nicht zu kümmern. Du mußt eben nur
sehen, wie du die entsprechenden Adressen berechnen mußt.
Gruß oldmax
Hi!
Es war mir aus den vorangegangenen Aussagen nicht klar, ob Du in
Assembler oder C arbeitest.
Aber meine idee lässt sich in beiden Sprachen umsetzen.
1
typedefvoid(*fkt)(void);
2
3
constfktFktTabelle[255]={
4
...ListemitFunktionen...
5
};
6
7
voidsprung(uint8_twert)
8
{
9
fkt*myFkt=&FktTabelle[wert];/* Funktion zum Anspringen */
10
myFkt();
11
}
Das war jetzt minimalistisch. Das kann man auch erweitern:
1
typedefint(*fkt)(uint8_t);
2
...
3
4
intsprung(uint8_twert)
5
{
6
fkt*myFkt=&FktTabelle[wert];/* Funktion zum Anspringen */
7
returnmyFkt(wert);
8
}
C hat aber den Nachteil, dass jede Funktion erst einmal einen Function
Header durchläuft in dem einige Register auf dem Stack gesichert werden.
Nach dem Durchlaufen der Funktion werden diese wieder zurück geholt.
Das benötigt Zeit und ist in diesem Fall überflüssig, wenn uns ein
Compiler-Spezialist verraten kann, wie man das verhindert.
Eventuell reicht es, wenn man alle in der Tabelle genannten Funktionen
'inline' deklariert. Eventuell kann man auch den typedef mit inline
versehen, das habe ich noch nie probiert. Meine CPUs haben immer ein
klein wenig mehr Rechenleistung als ich brauche.
Wenn Du es noch viel schneller haben willst, wirst Du auf Assembler
zurückgreifen müssen, oder Dir ein Linker-Script bauen müssen, dass für
alle aufzurufenden Funktionen eine geradzahlige Startadresse vergibt.
Nehmen wir an, dass Du die Funktionen ab 0x4000 im Speicher hast und
alle Funktionen jeweils max. 0x100 Bytes lang sind. Dann würde man sie
also im Speicher unter 0x4000, 0x4100, 0x4200... finden.
Also 'Wert' um 8bit links shiften und 0x4000 auf-odern. Dann call auf
diese Adresse.
das wiederum kann man noch einmal optimieren, in Assembler, denn ein
call speichert die Rücksprungadresse auf dem Stack. Stattdessen kann ich
einen JMP auf die Funktion machen und per JMP wieder zurück hinter meine
Tabellen-Funktion.
Gruß, Ulrich