moin,
möchte ein einfaches Multitasking Programm für den msp430 schreiben,
welches per Interrupt zwischen den Tasks wechselt. Es geht hier eher um
erlernen von Dingen als um möglichst praktisch und effizient zu fahren.
Ich weiss ungefähr was ich machen muss, nur die Assembler Kenntnisse
sind kaum bis garnicht vorhanden.
Ich bin jetzt soweit, dass ich während ich Tasks a la
Ich bin jetzt soweit, dass ich zumindest den Task wechseln kann, aber
eben immer am Anfang beginne, nicht dort wo ich rausgesprungen bin. Auch
das Löschen des stacks bekomm ich nicht so ganz hin
1
asm(" mov.w r0, reg_buff\n");// Program Counter
2
stack[task_nr].reg[0]=reg_buff;
3
asm(" mov.w r1, reg_buff\n");// Stack Pointer
4
stack[task_nr].reg[1]=reg_buff;
5
asm(" mov.w r2, reg_buff\n");// Status Register
6
stack[task_nr].reg[2]=reg_buff;
7
asm(" mov.w r3, reg_buff\n");// CG Register
8
stack[task_nr].reg[3]=reg_buff;
9
asm(" mov.w r4, reg_buff\n");// GP Register
10
stack[task_nr].reg[4]=reg_buff;
11
//... usw bis r15
12
13
/*
14
* Stackeinträge löschen wegen überlauf undso
15
*/
16
17
reg_buffer=(unsignedshort)task_list[task_nr];
18
reg_buff=stack[task_nr].reg[2];
19
asm(" push.w reg_buff\n");
20
asm(" reti\n");
Er speichert mir eben die Register die er gerade hat, nicht die die auf
dem Stack liegen (denke ich mal), denn beim Debuggen springt er mir,
wenn ich
1
reg_buff=stack[task_nr].reg[0];
2
asm(" push.w reg_buff\n");
schreibe, in die ISR zurück, was schwachsinn ist.
Mir fehlt also eine Funktion "read_stack()" die mir wirklich die auf dem
Stack befindlichen PC und SR liest.
Und noch eine art "pop()" die mir PC und SR vom Stack entfernt.
Die Register SP, R4 bis R15 interargieren im Interrupt ja nicht wirklich
mit dem Stack oder? hörte sich im Datenblatt mal so an.
Danke im Voraus für jede Hilfe :)
andi schrieb:>> möchte ein einfaches Multitasking Programm für den msp430 schreiben,> welches per Interrupt zwischen den Tasks wechselt. Es geht hier eher um> erlernen von Dingen als um möglichst praktisch und effizient zu fahren.> Ich weiss ungefähr was ich machen muss, nur die Assembler Kenntnisse> sind kaum bis garnicht vorhanden.
Dann würde ich vorschlagen, daß du deinen Plan damit umzusetzen
beginnst, daß du erstmal Assembler lernst. Erstens geht das damit viel
einfacher umzusetzen als mit C und zweitens kannst du C ja
offensichtlich auch nicht wirklich.
Und mit zwei Sprachen, von denen man nicht und die andere kaum
beherrscht, kann man nunmal einfach nicht sinnvoll programmieren.
beim eintritt in die isr werden doch eh schon alle register gesichert.
es genügt, die rücksprungadresse auf dem stack gegen die des
fortzusetzenden threads auszutauschen.
rangi schrieb:> es genügt, die rücksprungadresse auf dem stack gegen die des> fortzusetzenden threads auszutauschen.
Nein, denn was soll Thread B mit den Registerinhalten von Thread A
anfangen?
rangi schrieb:> beim eintritt in die isr werden doch eh schon alle register> gesichert.> es genügt, die rücksprungadresse auf dem stack gegen die des> fortzusetzenden threads auszutauschen.
Das genügt nicht, da dann ja der fortzustzenden Thread mit den
Registerwerten des gerade unterbrochenen Threads weiterarbeiten würde.
Also nicht nur PC sondern auch alle Register austauschen!
naja, also das war die quick n dirty variante. wenn in der isr eine
funktion gerufen wird die in einem anderen modul liegt, müssen alle
register gesichert werden. je nach hardware gibt es hier auch
hw-unterstützung. demzufolge werden beim verlassen der isr alle register
aus dem zustartenden thread wieder hergestellt.
rangi schrieb:> beim eintritt in die isr werden doch eh schon alle register> gesichert.> es genügt, die rücksprungadresse auf dem stack gegen die des> fortzusetzenden threads auszutauschen.
Hm, kommt für mich aus dem Datenblatt nicht so rüber. Aber falls dem so
währe, eventuell das der Fehler, warum ich in der ISR mit
Tatsächlich ist das ganze noch etwas komplizierter - da jeder Thread
bzw. Task einen eigenen Stack haben muss. Also muss beim Verlassen der
ISR je nach zu aktivierendem Thread nicht nur dessen vollständiger
Registersatz, sondern auch der zugehörige Stackpointer restauriert
werden.
Rufus Τ. Firefly schrieb:> Tatsächlich ist das ganze noch etwas komplizierter - da jeder> Thread bzw. Task einen eigenen Stack haben muss. Also muss beim> Verlassen der ISR je nach zu aktivierendem Thread nicht nur dessen> vollständiger Registersatz, sondern auch der zugehörige Stackpointer> restauriert werden.
Ich hab des jetzt halt schnell in ein struct gespeichert, und die
register mit mov.w schreiben funktioniert ja
Nur eben pop r5 oder r15 was auch immer liefert mit immer 0
ich hab das mal für einen M16C und einen HC08 gemacht. Ich finde aber
meinen damaligen code nicht mehr wieder.
1. für jeden thread, der gestartet werden soll muss ein eigener stack
angelegt werden (array of char). die größe so nach gefühl fg.
2. dann muss jeder dieser stacks mit bestimmten werten vorbelegt werden.
das guckst du dir am besten im listing und im debugging ab. dazu musst
du eine isr schreiben und dort einen funktionsruf in ein anderes modul
machen (oder optimierung aus). im breakpoint schaust du dir an, was
alles auf den stack gesichert wurde. ganz oben sollte die
rücksprungadresse sein, also dort, wo die abarbeitung unterbrochen
wurde.
3. jetzt füllst du initial die stacks mit dem fake-kontext und als
rückspungadresse die "main" des threads, der gestartet werden soll.
4. wenn der thread gestartet werden soll, also in einer isr (z.b.
timer-isr) merkst du dir den aktuellen stackpointer und ersetzt ihn
durch den stackpointer der in den stack deines threads zeigt. mehr muss
man hier nicht sichern.
beim verlassen der isr (assembler: iret) wird jetzt nicht der kontext
des zuvor laufenden programms (z.b. main) wiederhergestellt, sondern der
kontext des zustartenden theads. zuletzt wird der pc vom stack geholt.
das ist der zeiger auf die zu startende funktion wie unter 3.
beschrieben. und schwups läuft den thread an.
5. bei zukünftigen isr-aufrufen wird dann der laufende kontext gesichert
und nur durch austausch der stackpointer erfolgt der switch zum neuen.
Ich hab die Tage nochmal dran gesessen, das ding zum laufen zu bringen.
Eigentlich habe ich fast nichts vom alten code behalten und alles neu
gemacht. zB speichere ich meinen Stack nicht in einer Struktur sonder
gebe jedem Task, so wie hier schon oft angedeutet wurde, einen eigenen
Bereich des (Hardware)Stacks.
Da ja beim Interrupt nicht nur PC und SR, sondern auch diverse Variablen
auf dem Stack landen, speichere ich einfach den Wert des SP nach dem
Betreten der ISR. Um wieder in die Funktion zu springen muss ich ja
eigentlich nur den SP auf diesen Wert setzen und ein "return;"
durchführen a la
1
...
2
structtask
3
{
4
structtask// Task struct
5
{
6
fnt_ptrinit_address;
7
unsignedshortsp_return;
8
unsignedshortsp;
9
unsignedcharstate;
10
unsignedshortdelay;
11
unsignedcharfirst_call;
12
unsignedshortstack_size;
13
}task[MAXTASKS];
14
...
15
t_idle();
16
...
17
// --->interrupt (momentan Tastendruck)
18
task[x].sp_return=_get_SP_register();
19
...
20
_set_SP_register(task[x].sp_return);
21
return;
Tasks starten ist auch kein Problem
1
if(task[task_nr].first_call==0)
2
{
3
task[task_nr].state=RUNNING;
4
task[task_nr].first_call=1;
5
6
buff=(unsignedshort)task[task_nr].init_address;// PC
7
buff2=0x0008;// IEN SR
8
9
asm(
10
" push &buff\n"
11
" push &buff2\n"
12
" reti\n"
13
);
14
}
Einen Task restoren kann ich jedoch nur 1 mal, beim zweiten restore (den
ersten restorten task kann ich noch abspeichern), lande ich nach dem
return; immer wieder in der ISR in Zeile 171
(_set_SP_register(task[x].sp) ).
ich habe pro Task immer 2 SP, einen .sp_return und einen .sp, letzterer
Zeigt auf R15 der CPU Register. Ich reserviere für jeden
Task einen gewissen Speicher für seine Variable, momentan 16 unsigned
short's beziehungsweise 32 Adressen:
Stack: --][PC][SR][v1][v2][x]...[x][x][r4][r5]...[r15][--
.......................^sp_return..................^sp...
Irgendjemand eine Idee woran das liege könnte?