Hallo zusammen. Ich versuche mich gerade an einem einfachen Multitasking System. Daszu habe ich zwei Funktionen, die in einer Endlosschleife sind und eine LED an unterschiedlichen Pins unterschiedlich schnell blinken lassen sollen. Aufgerufen wird am anfang keine Funktion, sondern es wird nur ein Timer gestartet. Der soll dann immer zwischen den Funktionen hin und herschalten. Ich habe mir gedacht, dass sobald die Interrupt-Routine aufgerufen wird, befindet sich ja die Rücksprungadresse ganz oben auf den stack....um jetzt zu eine Funktion zu springen, müsste man doch die Rücksprung adresse verändern so das sie auf die erste Funktion zeigt. Beim verlassen des Interrupts müsste doch dann durch den befehl Reti zur ersten Funktion gesprungen werden. Der Timer dürfte aber dann weiterlaufen, beim nächsten interrupt wird die erste funktion unterbrocken, rücksprungadresse auf die zweite funktion gesetzt usw.. Natürlich müssten alle register gesichert werden vorher. Aber ich bekomm es irgendwie nich, ich benutze CodeVision und weiss irgendwie nicht wie ich die Rücksprungadresse manipulieren kann. Hat jemand eine Idee?
Du willst dir das Leben schwer machen ? Der Standardansatz ist ein Timer Interrupt und eine Statusmaschine im Main. Diesen Ansatz durch Multitasking zu ersetzen macht erst Sinn wenn die Statusmaschine komplex und unuebersichtlich geworden ist. Falls du wirklich einen Kernel selbst schreiben willst, solltest du dir mal das ASM Manual des Prozessors anschauen. Welche Befehle was machen und so.
Also ein relativ einfacher Ansatz ist, einen Timer aufzuziehen. Jeder Task bekommt ein festes Intervall in dem er läuft. z.B. alle 20ms. In der Timer ISR reduziert man dann jedesmal den Counter für alle Tasks. Sobald ein Counter 0 ist, wird ein Flag (running flag) gesetzt. In der main lässt Du eine Endlosschleife laufen, die jede Funktion auführt deren running flag auf true steht. Das ist mal gaaanz einfach beschrieben wie man das im Prinzip machen kann. Wenn Du echtes Multitasking haben willst, musst Du bei jedem Taskwechsel sämtliche Register etc. retten. Ich empfehle freeRTOS um mal zu gucken wie sowas funktioniert. Gruss Guile
hi, danke für die schnellen antworten. Bin ich denn hier nicht im richtigen Forum?? also ich habe mir das so gedacht, das jeder Task eine bestimmte anzahl von Ticks (Timer Interrupts bekommt) somit kann man regulieren ob man ein task länger ausgeführt (höhere Priorität) oder weniger lange (niedrige Prio). Jeder Task bekommt seinen Eigenen Stackpointer bereich, in dem er seine Variablen sichern kann, die grosse des bereiches wird bestimmt durch eine minimale Anzahl von Registern (also R0 bis r31) und die Größe der lokalen Variablen. Alle Daten zu einem Task sind in einer Ringliste die aus einem entsprechenden Struct besteht gespeichert. Dort ist auch jedes mal ein pointer zum nächsten Struct für den nächsten Task gespeichert, ein ring halt... Wird nun ein Timerinterrupt ausgelöst, sollen zunächste alle Register gesichert werden, danach wird überprüft ob der aktuelle Task (ein globaler Pointer zeigt auf das Struct von diesem Task) gestartet wurde und ob die anzahl der Ticks schon auf null steht. Is dies nicht der Fall wird die Tick variable decrementiert und nix geschieht weiter... Ist sie allerdings null ist die zeit für den Task abgelaufen, und der nächste Task soll gestarten bzw. fortgesetzt werden...dazu wird der aktuelle Taskpointer auf den Next Pointer vom aktuellen Task gesetzt (Weil Ringliste).. Und jetzt haperts bei mir. Denn der nächste Stackbereich muss eingestellt werden, für den nächsten Task, und da habe ich Probleme, ich weiss nicht genau wie..... hmm vielleicht kann mir ja jetzt irgendwie jemand ein tip geben??
@ Hirbel Ha (leo) >also ich habe mir das so gedacht, das jeder Task eine bestimmte anzahl >von Ticks (Timer Interrupts bekommt) somit kann man regulieren ob man >ein task länger ausgeführt (höhere Priorität) oder weniger lange >(niedrige Prio). >Jeder Task bekommt seinen Eigenen Stackpointer bereich, in dem er seine >Variablen sichern kann, die grosse des bereiches wird bestimmt durch >eine minimale Anzahl von Registern (also R0 bis r31) und die Größe der >lokalen Variablen. Ob das auf einem kleinen uC sinnvoll ist? Warum nicht einfach kooperatives Multitasking, jeder Task ist einmal pro Timerinterupt dran. Das alles als Endloschleife im main(). Siehe dein eigenes Betreff! MG Falk
höö? naja okay, aber wie bitte schön soll das funktionieren? im main sieht es dann so aus? void main(void) { ... while (1) { task1(); task2(); } Task1 und 2 haben jeweils auch eine endlosschleife, bei start würden wir also in task1 gehen und da bleiben, wird ein interrupt ausgelöst, springen wir aus task1 in die interrupt routine...danach würden wir wieder zur endlosschleife in task1 springen und so weiter, man käme nie zu task2, wenn wir nicht die rücksprungadresse manipulieren würden. Und genau um dieses manipulieren geht es mir.
@ Hirbel Ha (leo) >naja okay, aber wie bitte schön soll das funktionieren? Das geht wunderbar. Im Prinzip wie hier dargestellt, auch wenn das Thema ein anderes ist. Sleep Mode >Task1 und 2 haben jeweils auch eine endlosschleife, bei start würden wir Falscher Ansatz. Die Task sind kooperativ! D.h. sie werden aufgerufen, prüfen ob was zu machen ist, machen was, und werden wieder beendet. >zu task2, wenn wir nicht die rücksprungadresse manipulieren würden. Und Lass den Quark. Das ist bestenfalls was für grosse CPUs. >genau um dieses manipulieren geht es mir. Falscher Ansatz. MFG Falk
Sorry du verstehst mich nicht... hab mich ein bissl gelesen und es ist auf einem avr kein problem. das was du meinst,hat ja absolut nix mit multitask bzw. scheinbarer parallelität zu tun, das ist einfach nur sequentiell und das ist was, was ich zwar auch immer gemacht habe, aber das brauche ich nicht. Also das "Verbiegen" des Stacks ist schon notwendig. Aber trotzdem danke.
>rücksprungadresse manipulieren
push & pop sind deine freunde :-)
Lad Dir doch einfach freeRTOS runter und guck Dir den portierten Code für den ATmega323 an! Was da gemacht wird, kannst Du auch nehmen um Deine Register etc. zu sichern bevor ein Taskwechsel durchgeführt wird.
ja aber, ein falsches mal "poppen" oder "pushen" und alles is dahin ;) ausserdem gibts in Codevision ein Hardware und Software Stack...das is alles nich so einfach..
Hirbel Ha wrote: > Daszu habe ich zwei Funktionen, die in einer Endlosschleife sind und > eine LED an unterschiedlichen Pins unterschiedlich schnell blinken > lassen sollen. Um viele LEDs unabhängig blinken zu lassen, ist ein Scheduler ideal: Beitrag "Wartezeiten effektiv (Scheduler)" Peter
@peda das mit den LEDs sollte nur ein test sein...das man LEDs mit einem timer unterschiedlich schnell blinken lassen kann, das is mir klar.. in wirklichkeit sind später mein task's viel komplexer...z.b. sensoren oder motoren steuerung..
>hab mich ein bissl gelesen und es ist auf einem avr kein problem. Bei jedem Taskwechsel alle 32 Register im SRAM zu sichern und wiederherzustellen, kann schneller zum Problem werden, als Dir lieb ist. Sowohl zeitlich gesehen, als auch vom Speicherplatz her (8 Tasks --> 256 Byte = schon 1/4 des SRAMs eines ATmega8). >das was du meinst,hat ja absolut nix mit multitask bzw. scheinbarer >parallelität zu tun, Offensichtlich kennst Du den Unterschied zwischen kooperativem und preemptiven Multitasking nicht. >das ist einfach nur sequentiell Nein. >und das ist was, >was ich zwar auch immer gemacht habe, aber das brauche ich nicht. Welche Aufgabe kannst Du denn mit der herkömmlichen Methode (siehe Falks Erklärungen) nicht lösen?
@ Hirbel Ha (leo) >in wirklichkeit sind später mein task's viel komplexer...z.b. sensoren >oder motoren steuerung.. Was umsomehr für kooperatives Multitasking spricht. Ohne Stackgefummel. Siehe Multitasking MfG Falk
es wird ein atmega128 benutzt mit 5 tasks das ziel ist es das die ergebnisse der task, quasi alle gleichzeit zur verfügung stehen sollen..
@ Hirbel Ha (leo) >es wird ein atmega128 benutzt mit 5 tasks >das ziel ist es das die ergebnisse der task, quasi alle gleichzeit zur >verfügung stehen sollen.. Ja und? Deine Stackfummelei bringt dich diesem Ziel nicht wirklich näher. MFG Falk
Es ist doch so: Wenn Task1 durch den Timer-IRQ unterbrochen wird, legt er die Rücksprungadresse in den Stack in seinem Stackmemory-Bereich ab. Nun ermittelt der Timer-IRQ, welcher Task als nächstes dran ist. Nehmen wir an, Task3. Der Stack wird umgeschaltet auf den Stackmemory-Bereich des Tasks3. Da Task3 ja vor einiger Zeit bereits unterbrochen worden ist, liegt ja die Rücksprungadresse immer noch auf seinem Stack (von Task3). Also braucht es nur noch ein RTS plus allenfalls vorher noch IRQ-Flags aufräumen.
hmmm also ich glaube, das was ich will is multi-threading...oder präemptives mutltitasking? also ich will das ein timer in einem intervall die funktionen unterbricht und hin und her schaltet
Hirbel Ha wrote: > @peda > das mit den LEDs sollte nur ein test sein...das man LEDs mit einem timer > unterschiedlich schnell blinken lassen kann, das is mir klar.. > > in wirklichkeit sind später mein task's viel komplexer...z.b. sensoren > oder motoren steuerung.. Mag ja alles sein. Trotzdem ist der Ansatz > Task1 und 2 haben jeweils auch eine endlosschleife auf einem µC nicht unbedingt der beste. Wenn du kein OS drunter liegen hast, dann braucht ein Task nun mal keine Endlosschleife, bzw eine Warteschleife. Genau das ist ja der Dreh beim kooperativen Multitasking: Kein Task darf die CPU längere Zeit blockieren sondern ist verpflichtet die CPU wieder abzugeben, damit der nächste Task drann kommen kann. Wie Falk schon sagte: In der Hauptschleife in main() (der einzigen Endlosschleife die im Programm ist), werden zyklisch alle Tasks aufgerufen. Jeder Task prüft ob etwas zu tun ist und wenn ja, dann macht er es. Hat er nichts zu tun (weil er zb. auf das Ablaufen einer Zeit wartet), dann darf er nicht in eine Schleife gehen, sondern kommt sofort zurück. Im Grunde macht PeDa's Scheduler (wenn ich das richtig im Kopf habe) auch nicht anders. Nur halt organisierter. Kooperatives Multitasking hat den Vorteil, dass es schön einfach ist. Der Nachteil ist, dass sich alle Tasks an die Regeln halten müssen. Dem gegenüber steht preemptive Multitasking, dass so funktioniert wie du dir das im Moment vorstellst. Der Nachteil vom kooperativen Multitasking wird dadurch behoben, dass der Scheduler einem Task die CPU mit Gewalt entziehen kann und nicht auf seine Kooperationsbereitschaft angewiesen ist. Aber dann wird alles komplizierter. Mit dem Kontextswitch ist dann nämlich noch lange nicht getan. Dann brauchst du auch Semaphoren und sontiges Zeugs um sichere Kommunikation zwischen den Tasks zu erreichen. Bei kooperativem Multitasking ist das alles wesentlich weniger aufwändig, weil u.A. ein Task sich auch darauf verlassen kann, dass er nicht unterbrochen wird, bis er die Kontrolle wieder freiwillig abgibt. Und es macht nun mal einen Unterschied, ob ich die 2 Byte eines int in einem Rutsch in den Speicher schreiben kann oder ob ich damit rechnen muss, dass mir der Scheduler die CPU nach dem Schreiben des ersten Bytes unter dem Arsch wegzieht und der momentane Zustand des Speichers nicht stimmt (auf den dann aber wiederrum ein anderer Task angewiesen ist).
@ Hirbel Ha (leo) >hmmm also ich glaube, das was ich will is multi-threading...oder >präemptives mutltitasking? >also ich will das ein timer in einem intervall die funktionen >unterbricht und hin und her schaltet Du legst dich schon vorher fest, WIE ein Problem gelöst werden soll, ohne erstmal die Alternativen zu suchen und zu bewerten. Keine gute Idee. MFG Falk
nein @Falk, versteh mich nicht falsch, ich bin dankbar über jede alternative..aber ich habe meine vorgaben und es sollte so ablaufen, das der timer die cpu mit gewalt einer funktion entreisst und einer anderen funktion zuweisst, so wie hier schon beschrieben. Also die Funktion selber kann nichts machen ausser vorsichhinlaufen, die steuerung muss jemand anderes übernehmen
@ Hirbel Ha (leo) >versteh mich nicht falsch, ich bin dankbar über jede alternative..aber >ich habe meine vorgaben und es sollte so ablaufen, das der timer die cpu >mit gewalt einer funktion entreisst und einer anderen funktion zuweisst, >so wie hier schon beschrieben. Kann man machen. Will ich dir auch nicht ausreden. Mach mal und berichte von deinen Erfahrungen. MfG Falk
ja mach ich gerne, nur da wäre ich wieder bei meiner anfänglichen Frage :)
Das ist doch genau was preemptive Multitasking macht. Das braucht man nicht selbst zu programmieren, das gibt es fix und fertig. AvrX oder FreeRTOS. Wenn man das selber macht, braucht man nur VIEL länger und funktioniert nicht so sicher. Ich habe AvrX eingesetzt am Atmega32 und es funktioniert wunderbar.
Hirbel Ha wrote: > nein @Falk, > versteh mich nicht falsch, ich bin dankbar über jede alternative..aber > ich habe meine vorgaben und es sollte so ablaufen, das der timer die cpu > mit gewalt einer funktion entreisst und einer anderen funktion zuweisst, > so wie hier schon beschrieben. Dann würde ich mal vorschlagen, du studierst wie andere das machen. Im Web gibt es einige Multitaksing Systeme zum download. Und soviel ich weiss, ist der innerste Kern des Schedulers (nicht ohne Grund) sehr oft in Assembler geschrieben.
Und ums nochmal zu sagen: Beim kooperativen Multitasking gibts innerhalb der Tasks grundsätzlich keine Warteschleifen (heißt: Controller zigtausendmal nop ausführen lassen) oder gar Endlosschleifen. Die sind strikt verboten. Die einzige Endlosschleife im gesamten Programm ist die Mainloop, in der alle Tasks z. B. alle 10 ms zyklisch aufgerufen werden. Für die Erzeugung der 10 ms-Ticks ist ein Timer zuständig. Haben alle Tasks ihre Arbeit erledigt, kann man den µC stromsparend schlafen legen, bis die aktuellen 10 ms vergangen sind.
ja ich kenne diese ganzen projekte... ich habe mir ein eigenes konzept zusammengestellt und teilweise funktioniert das schon, aber an einigen stellen komm ich nicht weiter, aus dem einen grund das codevision irgendwie komisch mit stacks umgeht und asm auch anders behandelt wird als in winavr...aber ich werds weiter versuchen :)
Zwei Anmerkungen. Es gibt auch bei praemtivem multitasking keine Warteschleifen in einer Task. Wenn die Task auf einen Wait auflaeuft wechselt der Task. Zweitens. Das Konzept sollte unabhaengig von den Tools sein. Und wenn ein Tool das Konzept versaut, war's wohl das Falsche.
>ja ich kenne diese ganzen projekte... Dann ist ja gut :-) >ich habe mir ein eigenes konzept zusammengestellt und teilweise >funktioniert das schon, aber an einigen stellen komm ich nicht weiter, >aus dem einen grund das codevision irgendwie komisch mit stacks umgeht >und asm auch anders behandelt wird als in winavr... Das ist ja nur ein compilerbedingtes Detailproblem. Du kannst es sicher schnell lösen. Auf die Gefahr, dass Du schon genervt bist, noch ein paar Worte zu Deinem Vorhaben. Zunächst kann man sagen, dass die Taskwechselei mit Registersicherung, -wiederherstellung und Stackverwaltung (die "context switches") noch einigermaßen easy zum Laufen zu bringen ist. Aber damit ist es nicht getan. Deine Prozesse müssen nämlich auch untereinander kommunizieren, d. h. es muss Speicherbereiche geben, auf die mehrere Prozesse zugreifen dürfen. Und dies ist der Punkt, an dem die Sache anfängt, teuflisch knifflig zu werden, weil jeder Prozess ja unvorhersehbar zu jedem Zeitpunkt, also auch mitten in einer Berechnung unterbrochen werden kann, und wegen der Reservierung von Ressourcen. Stichworte dazu: Race Conditions, Deadlocks, Semaphore, Mutexe, Critical Sections, Message Passing, Scheduling, Thread Synchronisation. Und glaub nicht, dass Du davon verschont bleiben wirst, weil Du Dich ja mit einem "ganz einfachen" System begnügen willst. Allgemein kann man sagen, dass das Schreiben eines funktionierenden präemptiven Multitasking-Betriebssystems eine enorm anspruchsvolle Aufgabe ist. Ein auf kooperativem Multitasking basierendes System ist viel einfacher zu verstehen und (vom Anwendungsentwickler) zu beherrschen, weil hier viele typische Probleme von Haus aus nicht auftreten. An µC-Anwendungen ("messen, steuern, regeln") stellt man auch eher die Forderung, dass bestimmte Aktionen zu bestimmten Zeiten ausgeführt werden, als dass eine vom Benutzer initiierte Berechnung, z. B. die Simulation der Bewegung einer Rakete, so schnell wie möglich abgearbeitet ist. Bei letzterer Klasse von Problemen sind präemptive Betriebssysteme sinnvoll und vorteilhaft. So, wenn ich Dir jetzt die gute Laune verdorben haben sollte, tuts mir leid ;-) Schau, wie weit Du kommst, und auf jeden Fall viel Spaß und Erfolg!
@AVRFan.. Jeder Task hat seinen eigenen Speicherbereich im SRAM, auf den kann und wird nur er zugreifen...darin werden alle laufzeit variablen für den task abgelegt...wenn ein switch erfolgt...die größe errechnet sich aus den normalen registern die gesichert werden + die anzahl der lokal benutzen Variablen dieses Tasks. Natürlich greifen sie evtl. auf I.O. oder andere system register zu, aber das muss dann ebenfalls gesichter werden, wo es nötig ist...
> auf den kann und wird nur er zugreifen
Das haben schon viele gesagt :-)
Um dann tagelang den Bug zu suchen, warum Variablen plötzlich
und unerwartet ihre Werte ändern.
Darf ich fragen was das wird, wenn du ein Multitaskingsystem
aufbaust, bei dem die Tasks nicht miteinander reden?
redet winamp etwa mit word wärend du ein text schreibst und dabei musik hörst? Die verständigen sich mit der cpu usw. aber doch nicht untereinander? warum auch? Genaus ist es bei mir auch. Z.b. soll sensoren daten erfassen (an eigenen IO pins), speichern diese Global.. ein anderer Task sendet diese globalen Daten z.b. übers UART... Natürlich könnte man sagen sie verständigen sich indirekt über eben diese globalen Var....aber das wär übertrieben..
lach... Du bist ein Knaller :-) >Natürlich könnte man sagen sie verständigen sich indirekt über eben >diese globalen Var.... Genau darum geht es!!!
Hirbel Ha wrote: > Natürlich könnte man sagen sie verständigen sich indirekt über eben > diese globalen Var....aber das wär übertrieben.. Lass mich raten: Du hast noch nie wirklich Multitasking-Programmierung betrieben. Oder? Wobei: Wenn man exakt sein will, dann gibt es tatsächlich nur einen Task und wir reden eigentlich von Multithreading Programmierung. Ist im Kontext eines µC aber meist ein und dasselbe.
häää?? aber wo ist denn das Problem eurer meinung nach?? Beide dürfen natürlich die Globalen Speicher nicht verändern, wenn er einer aber nur liest und der andere diese verändert, so wie bei meinem beispiel, das ist das doch kein problem... Das ein Task die überlebenswichtigen Variablen vom anderen Task nicht verändern DARF, das versteht sich doch von selber..
was soll das denn jetzt @Karl heinz?? natürlich nicht, sonst hätte ich diesen thread garnicht erst eröffnet... Wenn es für dich so einfach ist, dann kannst du mir ja helfen und mir meine anfangsaufgabe ganz leicht zusammenstellen: Zwei tasks, beide bestehen aus einer endlosschleifen...beider lassen eine LED unterschiedlich schnell blinken... Eine Timer soll zwischen beiden hin und her schalten... ERGEBNISS: beide LED's blinken mit einer unterschiedlich geschwindigkeit..scheinbar laufen zwei Funktionen parallel... mehr will ich erstmal garnicht...
Hirbel Ha wrote: > aber wo ist denn das Problem eurer meinung nach?? > Beide dürfen natürlich die Globalen Speicher nicht verändern, wenn er > einer aber nur liest und der andere diese verändert, so wie bei meinem > beispiel, das ist das doch kein problem... Du bist naiv. Nehmen wir mal an, da gibt es eine globale int Variable (16 Bit, also 2 Byte) int Global; Jetzt schreibt der eine Task auf diese Variable Global = 20768; Dein Prozessor kann das aber a priori nicht atomar machen. Er muss 2 Schreibzugriffe machen. Und jetzt kommt dein Scheduler daher und macht einen Taskwechsel nachdem das erste Byte geschrieben wurde aber noch bevor das 2. Byte geschrieben wurde. Was denkst du, wird ein zweiter Task, der zufällig genau jetzt die Kontrolle vom System kriegt aus dieser halb geschriebenen Variablen auslesen? > Das ein Task die überlebenswichtigen Variablen vom anderen Task nicht > verändern DARF, das versteht sich doch von selber.. Du vergisst, dass wir alle Menschen sind und dass wir alle Fehler in Programme einbauen. Das gemeine bei solchen Fehlern ist, dass sie Stunden-, Tage-, Wochen-, ja Jahrelang nicht auffallen, bis sie dann eines Tages zuschlagen, wenn sie den größtmöglichen Schaden anrichten können.
>Jeder Task hat seinen eigenen Speicherbereich im SRAM, auf den kann und >wird nur er zugreifen...darin werden alle laufzeit variablen für den >task abgelegt. Das ist eine sehr vereinfachte Version, dann sind alle Task voneinander komplett unabhaengig. Es wird etwas schwieriger, wenn die Task untereinander kommunizieren, resp gemeinsame Daten haben.
Hirbel Ha wrote: > was soll das denn jetzt @Karl heinz?? > > natürlich nicht, sonst hätte ich diesen thread garnicht erst eröffnet... > Wenn es für dich so einfach ist, dann kannst du mir ja helfen und mir > meine anfangsaufgabe ganz leicht zusammenstellen: > > Zwei tasks, beide bestehen aus einer endlosschleifen...beider lassen > eine LED unterschiedlich schnell blinken... > Eine Timer soll zwischen beiden hin und her schalten... > ERGEBNISS: beide LED's blinken mit einer unterschiedlich > geschwindigkeit..scheinbar laufen zwei Funktionen parallel... > > mehr will ich erstmal garnicht... Genau sowas und noch viel mehr würde ich überhaupt nicht mit einem premptive Multitasking System lösen, sondern mit kooperativem Multitasking. So wie es unzählige Poster vor und nach mir ebenfalls beschrieben haben. Ist viiiiieeeeel einfacher, wesentlich weniger fehleranfällig und wäre schon längst fertig. Im Moment machst du aus deiner Übungsaufgabe sehr viel mehr als dazu notwendig ist: Du konstruierst ein preemptive Multitasking System, ohne zu wissen worauf du dich da im Endeffekt einlässt. Noch schlimmer: Anstatt ein fertiges System zu verwenden, baust du das auch noch selber auf. Fazit: Viel Zeit in etwas investiert, was du geprüft und getestet auch für lau haben kannst.
okay @karl-heinz das ist korrekt, obwohl es nur ein beispiel war und für meine anwendung nicht notwendig...aber das wäre mit einem kurzen ausschalten des interrupts gelöst... aber bevor ich mich hier noch um kopf un kragen rede, werd ich einfach mal machen :)
Hirbel Ha wrote: > Beide dürfen natürlich die Globalen Speicher nicht verändern, wenn er > einer aber nur liest und der andere diese verändert, so wie bei meinem > beispiel, das ist das doch kein problem... Ganz genau das ist ein ganz großes Problem! In der Regel bestehen Daten nicht nur aus einem Byte, sondern einem Datensatz. Und wenn der dann halb neue und halb alte Daten enthält, entsteht Chaos. Du hast z.B. ne Funktion, die nen Pixel gemalt haben will, der besteht aus X,Y,R,G,B. Greift sich nun die Malfunktion diesen Datensatz, während er noch nicht komplett geschrieben wurde, siehst Du nen Pixel am falschen Platz in der falschen Farbe. Die Lösung ist nun, Du schreibst als letztes ein Flag, was sagt, nun ist der Pixel gültig, mal ihn. So und nun das ganze umgekehrt, die Malfunktion hat sich noch nicht alles geholt und Du schreibst schon die nächsten Daten rein -> wieder Chaos. Also noch ein Flag nehmen, wo die Malfunktion sagt, ich habe fertig. Das Ganze geht aber beliebig weiter, genau wie in dem Lied: "Wenn der Topf aber nun ein Loch hat ...". Ich hab mal in der Elektronik nen Artikel gelesen, wo es darum ging. Angefangen hat er in etwa: Mainloop, Interrupts und globale Variablen sind Pfui-Bäh. Und dann wurden Lösungen vorgestellt, die ein Problem lösten, dafür aber 2 neue aufmachten. Am Ende war das ganze so komplex, daß außer erheblichen Speicher- und CPU-Mehrverbrauch keinerlei Vorteile zu sehen waren. Peter
Aber seis drum: Timer aufsetzen, der einen 10ms Sekunden Takt vorgibt. Weniger ist bei blinkenden Leds nicht notwendig, da sowieso kein Mensch auf 10ms genau Zeiten schätzen kann. Für jede Led gibt es eine Zählvariable, die die Zeit angibt, die die Led noch in ihrem bisherigen Zustand verbringen soll. Nach Ablauf der Zeit soll die Led umgeschaltet werden.
1 | #define PORT_LED1 PORTA
|
2 | #define PORT_LED2 PORTA
|
3 | #define LED1 ( 1 << PA2 )
|
4 | #define LED2 ( 1 << PA3 )
|
5 | |
6 | uint16_t TimeForLed1; // in wievielen 10ms Einheiten muss Led1 geschaltet werden |
7 | uint16_t TimeForLed2; // in wievielen 10ms Einheiten muss Led2 geschaltet werden |
8 | |
9 | volatile uint16_t Led1TimePreset; |
10 | volatile uint16_t Led2TimePreset; |
11 | |
12 | volatile uint8_t TimerTick; |
13 | |
14 | ISR( .... ) // Timer Interrupt alle 10ms |
15 | {
|
16 | TimerTick = 1; |
17 | }
|
18 | |
19 | void HandleLed1() |
20 | {
|
21 | if( TimeForLed1 == 0 ) { // Zeit abgelaufen? |
22 | TimeForLed1 = Led1TimePreset; |
23 | // Led 1 umschalten;
|
24 | PORT_LED1 ^= LED1; |
25 | }
|
26 | else
|
27 | TimeForLed1--; // 10 ms für LED1 runterzählen |
28 | }
|
29 | |
30 | void HandleLed2() |
31 | {
|
32 | if( TimeForLed2 == 0 ) { // Zeit abgelaufen? |
33 | TimeForLed2 = Led2TimePreset; |
34 | // Led 2 umschalten;
|
35 | PORT_LED2 ^= LED2; |
36 | }
|
37 | else
|
38 | TimeForLed2--; // 10 ms für LED2 runterzählen |
39 | }
|
40 | |
41 | int main() |
42 | {
|
43 | // Timer initialisieren und Interrupt freigeben
|
44 | |
45 | ....
|
46 | |
47 | Led1Preset = 1200; // Led 1 blinkt alle 12 Sekunden |
48 | Led2Preset = 250; // Led 2 blinkt alle 2.5 Sekunden |
49 | |
50 | sei(); |
51 | |
52 | while( 1 ) { |
53 | if( TimerTick == 1 ) { |
54 | TimerTick = 0; |
55 | |
56 | HandleLed1(); |
57 | HandleLed2(); |
58 | }
|
59 | }
|
60 | }
|
Welchen Interrupt du nimmst um die 10 ms zu erreichen: Würde ich mal einen Timer mit CTC Modus nehmen.
Na so ist auch nicht ganz. Multitasking macht dann Sinn wen die Statusmaschine im Main schwer wartbar wird. Mach mal in 5 Jahren eine Aenderung in einer verschachtelten Statusmaschine, die 200 Zustaende hat. Dann ist die Multitaskingloesung immer noch uebersichtlicher. Die Antwortzeit des Systems wird durch den Overhead des Multitaskuing natuerlich laenger, etwas Flash ist weg, und etwas RAM.
@ karl heinz: genau darauf habe ich gewartet....in der uni würde es heissen Thema verfehlt, neu machen... Du hast nich verstanden worauf ich hinaus will, das mit den LED's sollte nur ein simples beispiel eine context-switches sein...das man das ergebniss auch mit zählern erreicht ist mir völlig klar...aber darum geht es doch garnich
sechsminuszwei wrote: > Na so ist auch nicht ganz. Multitasking macht dann Sinn wen die > Statusmaschine im Main schwer wartbar wird. Mach mal in 5 Jahren eine > Aenderung in einer verschachtelten Statusmaschine, die 200 Zustaende > hat. Dann ist die Multitaskingloesung immer noch uebersichtlicher. Die > Antwortzeit des Systems wird durch den Overhead des Multitaskuing > natuerlich laenger, etwas Flash ist weg, und etwas RAM. Ist ja alles richtig. Nur setzte ich mich dann nicht hin und programmiere ein Multitasking System von Grund auf. Da hätte ich dann 2 Baustellen wo eine alleine auch schon völlig in der Komplexität ausreicht. Und wenn jetzt das Argument kommt: Ja, aber irgendwann muss man das doch auch lernen wie man ein derartiges System schreibt. Schon richtig. Aber dann hol ich mir aus dem Web ein fertiges System im Source Code und studiere das erst mal 1 oder 2 Wochen, bis ich zumindest im Überblick verstanden habe, was da so alles abgeht und was da noch alles notwendig ist. Task Wechsel ist sicherlich nicht unwichtig. Aber nur mit Taskwechsel alleine hat man noch kein MT-System.
Hirbel Ha wrote: > @ karl heinz: > genau darauf habe ich gewartet....in der uni würde es heissen Thema > verfehlt, neu machen... > Du hast nich verstanden worauf ich hinaus will, das mit den LED's sollte > nur ein simples beispiel eine context-switches sein...das man das > ergebniss auch mit zählern erreicht ist mir völlig klar...aber darum > geht es doch garnich Mooooooooment. Du wolltest wissen, wie ich dein Problem ohne einen Multitasking Kern lösen würde. Und genau so würde ich es lösen. Und die Tatsache, dass ich in 10 Minuten eine Lösung fertig habe und du noch immer kein Multitasking System hoch hast, spricht ja wohl Bände. Worum gehts hier eigentlich: Gehts darum, dass ein Problem gelöst werden muss Oder gehts darum, unbedingt ein Multitasking System hochzuziehen, mit dem man dann sein eigentliches Problem lösen kann. Weiter oben hats glaub ich schon mal wer gesagt: Du bist so versteift darauf, daß ein MT System Teil deiner Lösung ist, das du keinen Gedanken darauf verschwendest ob du ein MT System überhaupt brauchst bzw. ob du ohne nicht bessér drann wärst.
Nein, also die Aufgabe ist ein MT System. Wobei es als Multithreading bezeichnet wird, wie schon gesagt wurde is das bei µC eh das gleiche... In desem MT System wollte ich nur als Beispiel diese LED's zum laufen bringen, so das man sieht es funktioniert...anstelle die LEDFunktionen können dann später ganz andere aufgaben damit gelöst werden... aber danke das du das in 10 min für mich so aufgebaut hast :) jetzt nur noch als MT System...nein scherz, das ist ja meine aufgabe.
Hirbel Ha wrote: > Nein, also die Aufgabe ist ein MT System. Dann sieht die Sache anders aus. Wenn deine Aufgabenstellung dezidiert lautet ein MT System zu bauen, dann hast du keine andere Wahl. (Ausser du schaffst es deinem Auftraggeber diese Idee wieder auszureden) > aber danke das du das in 10 min für mich so aufgebaut hast :) > jetzt nur noch als MT System...nein scherz, das ist ja meine aufgabe. Ich kann dir nur wieder den Tip geben: Hol dir FreeRTOS http://de.wikipedia.org/wiki/FreeRTOS http://www.freertos.org/ und studiere, wie dort die kritischen Dinge gelöst wurden. Sowas schreibt man nicht mal eben in 10 min :-)
okay, ich werd mir das mal angucken, aber ich hab auch schon etwas vereinfachte systeme gesehen... naja hab ja ca. noch 8 wochen zeit dafür :)
Guten Abend, also als Assemblerprogrammierer wundere ich mich schon was hier für ein Aufstand um das echte Multitasking gemacht wird. OK, die Sicherung u.U. aller Register braucht eine gewisse Raumzeit, doch so man die hat und mit ein paar Gedanken vorher, nämlich um die möglichst exklusive Nutzung jeder Ressource durch einzelne Tasks und ein einfaches (Interrupt-)Flag für unterbrechungsloses Arbeiten in datentechnisch kritischen Situationen, einen globalen Speicherbereich und Hardware-Timer und sind alle Probleme zumindest bei so einem kleinen Mikrocontroller im Handstreich erledigt. Denn es ist ja nicht so, daß ein zu allem befähigter Universalbolide a la RTOS unbedingt erforderlich wäre- die spezielle Aufgabe erlaubt immer auch spezielle Lösungen. Echtes Multitasking hat ganz klare Vorteile, wenn es um die Übersichtlichkeit von Singlecorecode geht! Jens
wie wärs damit ?
1 | /*****************************************************
|
2 | This program was produced by the
|
3 | CodeWizardAVR V1.24.8 Standard
|
4 | Automatic Program Generator
|
5 | © Copyright 1998-2006 Pavel Haiduc, HP InfoTech s.r.l.
|
6 | http://www.hpinfotech.com
|
7 | |
8 | Project : MEGA128_MT_0
|
9 | Version : 00.
|
10 | Date : 14.12.2007
|
11 | Author : Winfried Jaeckel
|
12 | Company : Deviltronic
|
13 | Comments: alle variablen innerhalb der Task müssen "static" definiert !!!
|
14 | |
15 | |
16 | Chip type : ATmega32L
|
17 | Program type : Application
|
18 | Clock frequency : 11,059200 MHz
|
19 | Memory model : Small
|
20 | External SRAM size : 0
|
21 | Data Stack size : 512
|
22 | *****************************************************/
|
23 | |
24 | #include <mega32.h> |
25 | // Declare functions here
|
26 | //_________________________________________________
|
27 | |
28 | |
29 | void main(void); |
30 | interrupt [TIM0_OVF] void timer0_ovf_isr(void); |
31 | void task(int nr); |
32 | void task0(); |
33 | void task1(); |
34 | void task2(); |
35 | void task3(); |
36 | void task4(); |
37 | void task5(); |
38 | |
39 | //Funktionsdefinitionen
|
40 | //__________________________________________________
|
41 | void task5() |
42 | {
|
43 | static char a,b,c; |
44 | static int e,f,g; |
45 | }
|
46 | |
47 | void task4() |
48 | {
|
49 | static char a,b,c; |
50 | static int e,f,g; |
51 | }
|
52 | |
53 | void task3() |
54 | {
|
55 | static char a,b,c; |
56 | static int e,f,g; |
57 | }
|
58 | |
59 | void task2() |
60 | {
|
61 | static char a,b,c; |
62 | static int e,f,g; |
63 | }
|
64 | |
65 | void task1() |
66 | {
|
67 | static char a,b,c; |
68 | static int e,f,g; |
69 | for (e=0 to 250); |
70 | PORTB.1=!PINB.1 |
71 | }
|
72 | |
73 | |
74 | void task0() |
75 | {
|
76 | static char a,b,c; |
77 | static int e,f,g; |
78 | for (e=0 to 50); |
79 | PORTB.0=!PINB.0 |
80 | }
|
81 | |
82 | |
83 | void task(int nr) |
84 | {
|
85 | while nr==0; |
86 | {
|
87 | task0() |
88 | };
|
89 | while nr==1; |
90 | {
|
91 | task1() |
92 | };
|
93 | while nr==2; |
94 | {
|
95 | task2() |
96 | };
|
97 | while nr==3; |
98 | {
|
99 | task3() |
100 | };
|
101 | while nr==4; |
102 | {
|
103 | task4() |
104 | };
|
105 | while nr==5; |
106 | {
|
107 | task5() |
108 | };
|
109 | |
110 | }
|
111 | // Timer 0 overflow interrupt service routine
|
112 | interrupt [TIM0_OVF] void timer0_ovf_isr(void) |
113 | {
|
114 | // Place your code here
|
115 | tasknr++ |
116 | if (task>taskmax) tasknr=taskmin |
117 | }
|
118 | |
119 | // Declare your global variables here
|
120 | Int tasknr=0, taskmin=0, taskmax=1; |
121 | void main(void) |
122 | {
|
123 | // Declare your local variables here
|
124 | |
125 | // Input/Output Ports initialization
|
126 | // Port A initialization
|
127 | // Func7=In Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In
|
128 | // State7=T State6=T State5=T State4=T State3=T State2=T State1=T State0=T
|
129 | PORTA=0x00; |
130 | DDRA=0x00; |
131 | |
132 | // Port B initialization
|
133 | // Func7=In Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In
|
134 | // State7=T State6=T State5=T State4=T State3=T State2=T State1=T State0=T
|
135 | PORTB=0x00; |
136 | DDRB=0x00; |
137 | |
138 | // Port C initialization
|
139 | // Func7=In Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In
|
140 | // State7=T State6=T State5=T State4=T State3=T State2=T State1=T State0=T
|
141 | PORTC=0x00; |
142 | DDRC=0x00; |
143 | |
144 | // Port D initialization
|
145 | // Func7=In Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In
|
146 | // State7=T State6=T State5=T State4=T State3=T State2=T State1=T State0=T
|
147 | PORTD=0x00; |
148 | DDRD=0x00; |
149 | |
150 | // Timer/Counter 0 initialization
|
151 | // Clock source: System Clock
|
152 | // Clock value: 11059,200 kHz
|
153 | // Mode: Normal top=FFh
|
154 | // OC0 output: Disconnected
|
155 | TCCR0=0x01; |
156 | TCNT0=0x00; |
157 | OCR0=0x00; |
158 | |
159 | // Timer/Counter 1 initialization
|
160 | // Clock source: System Clock
|
161 | // Clock value: Timer 1 Stopped
|
162 | // Mode: Normal top=FFFFh
|
163 | // OC1A output: Discon.
|
164 | // OC1B output: Discon.
|
165 | // Noise Canceler: Off
|
166 | // Input Capture on Falling Edge
|
167 | // Timer 1 Overflow Interrupt: Off
|
168 | // Input Capture Interrupt: Off
|
169 | // Compare A Match Interrupt: Off
|
170 | // Compare B Match Interrupt: Off
|
171 | TCCR1A=0x00; |
172 | TCCR1B=0x00; |
173 | TCNT1H=0x00; |
174 | TCNT1L=0x00; |
175 | ICR1H=0x00; |
176 | ICR1L=0x00; |
177 | OCR1AH=0x00; |
178 | OCR1AL=0x00; |
179 | OCR1BH=0x00; |
180 | OCR1BL=0x00; |
181 | |
182 | // Timer/Counter 2 initialization
|
183 | // Clock source: System Clock
|
184 | // Clock value: Timer 2 Stopped
|
185 | // Mode: Normal top=FFh
|
186 | // OC2 output: Disconnected
|
187 | ASSR=0x00; |
188 | TCCR2=0x00; |
189 | TCNT2=0x00; |
190 | OCR2=0x00; |
191 | |
192 | // External Interrupt(s) initialization
|
193 | // INT0: Off
|
194 | // INT1: Off
|
195 | // INT2: Off
|
196 | MCUCR=0x00; |
197 | MCUCSR=0x00; |
198 | |
199 | // Timer(s)/Counter(s) Interrupt(s) initialization
|
200 | TIMSK=0x01; |
201 | |
202 | // Analog Comparator initialization
|
203 | // Analog Comparator: Off
|
204 | // Analog Comparator Input Capture by Timer/Counter 1: Off
|
205 | ACSR=0x80; |
206 | SFIOR=0x00; |
207 | |
208 | // Global enable interrupts
|
209 | #asm("sei")
|
210 | |
211 | while (1) |
212 | {
|
213 | // Place your code here
|
214 | task(tasknr) |
215 | };
|
216 | }
|
taskmin und taskmax entsprechend der definerten Anzahl der Task setzen. alle variablen innerhalb der Tasks static definieren!!!!
sucht mal mit google nach 'protothreads', das ist bestimmt das richtige für euch.
sorry 2 banale fehler drin 1. richtig ist in tasks die IF-Funktion hat diese syntax void task1() { static char a,b,c; static int e,f,g; for (e=0; e<250;e++){}; PORTB.1=!PINB.1; } void task0() { static char a,b,c; static int e,f,g; for (e=0 (e=0; e<50;e++){}; PORTB.0=!PINB.0; } 2. die timer IR muss wie folgt aussehen // Timer 0 overflow interrupt service routine interrupt [TIM0_OVF] void timer0_ovf_isr(void) { // Place your code here tasknr++ if (task>taskmax) tasknr=taskmin // die folgenden ASM anweisungen löschen die rücksprungadresse vom stack und bewart ihn vorm overflow #asm pop pop #endasm goto main() //verhindert Rückrung,springt immer nach main }
Also, sobald die Zustände etwas komplexer werden und viele kurze Delays im Millisekundenbereich bis zu einer Sekunde etc. vorkommen, ist ein einfacher Multitasking-Kernel wirklich die sehr viel einfacherer Lösung. Und kompliziert ist es auch nicht. Im Timer-Interrupt einfach ein Register auf den Stack pushen, dann das Status-Register in dieses Register einlesen und auf den Stack pushen, danach die restlichen Register pushen, und dann den Stackpointer auf den nächsten Stackframe setzen. Dann geht das ganze rückwärts mit pops, und am Schluss mit reti rausspringen und, oh Wunder, ein perfekter Kontextwechsel. Dazu noch eine Initialisierungsrouting die den Stack-Frame entsprechend vorbereitet mit der Einsprungsadresse eines Tasks und korrektem Status-Register auf dem Stack und dann einfach mit ret den Task starten. Dazu noch etwas Verwaltungskram, z.B. eine zirkuläre, lineare Liste um die Tasks nacheinander abzuarbeiten. Alternativ oder kombiniert noch ein yield() das einen Kontextwechsel forciert. Thema Overhead: Je nach Implementation benötigt man etwa 150 Taktzyklen für einen kompletten Kontextwechsel. Macht auf einem AVR pro MHz Takt etwa 7000 Kontextwechsel. Das sollte für die meisten Anwendungen reichen. Thema Synchronisierung: Mutexe, Semaphoren etc. kann man alles machen, ist aber in etwa 99,99% der Fälle schlichtweg nicht nötig. Die einfachste Synchronisation ist cli() und sei(). Wer's feiner haben will, setzt oder löscht das Interrupt-Mask-Bit des Tick-Timers. Thema RAM-Verbrauch: Der Registersatz mit Stackpointer und Status-Register sind auf dem AVR gerade mal 35 Bytes. Kleine Tasks sind mit rund 64 Bytes RAM problemlos möglich. Wenn ein Task mal 300 Bytes RAM benötigt, ist er schon richtig fett. Tasks die nie parallel laufen, können abwechselnd den selben RAM-Bereich nutzen. Ein Anfänger, der so etwas das erste mal programmiert, dürfte in etwa 4 Stunden locker fertig sein. Hat man so etwas schon mal gemacht, geht das deutlich schneller. Ist ja nur etwas Tipp-Arbeit um die 33 push und pops zu tippen.
> sucht mal mit google nach 'protothreads', das ist bestimmt das > richtige für euch. Protothreads (oder Spielvarianten davon) ist für kleine Sachen ganz praktisch. Sobald es aber kompliziert wird mit verschachtelten Bedingungen und Timings, wird man wahnsinnig, da man keine "Warte-Funktionen" verwenden kann. So etwas wie das hier, geht in protothreads nicht: void wait_for_something() { ticks_t start = systime(); while ( !some_condition() && systime()-start < milliseconds(50) ) yield(); } void task_a() { bla(); wait_for_something(); blub(); } void task_b() { foo(); wait_for_something(); blub(); } Bei protothreads wird man zum Wahnsinningen, weil man jeden Müll als statische Variable deklarieren muss und anfängt bescheurte Makros zu schreiben um die protothreads-Beschränkungen zu umgehen. Protothreads für kleine Mikrocontroller und/oder einfache (primitive) Tasks ganz nützlich, ansonsten wird es schnell zum Fallstrick.
>Also, sobald die Zustände etwas komplexer werden und viele kurze Delays >im Millisekundenbereich bis zu einer Sekunde etc. vorkommen, ist ein >einfacher Multitasking-Kernel wirklich die sehr viel einfacherer Lösung. Ich seh das Argument mit den kurzen Wartezeiten irgendwie nicht. WARUM sollen die sich einfacher realisieren lassen? Zum Rest: Ein Programm mit 5 Tasks und einem Timer-Tickintervall von 1 ms würde dann also nach dem Schema Task 1 rechnet genau 1 ms Task 2 rechnet genau 1 ms Task 3 rechnet genau 1 ms Task 4 rechnet genau 1 ms Task 5 rechnet genau 1 ms Task 1 rechnet genau 1 ms Task 2 rechnet genau 1 ms Task 3 rechnet genau 1 ms Task 4 rechnet genau 1 ms Task 5 rechnet genau 1 ms Task 1 rechnet genau 1 ms Task 2 rechnet genau 1 ms Task 3 rechnet genau 1 ms ........ ........ ablaufen? Alle Tasks dürfen gleich lange rechnen. Ist die Zeit für einen Task rum, entzieht der Scheduler ihm die Kontrolle und führt einen context switch zum nächsten Task aus. Hab ich das richtig verstanden?
Jetzt lasst das Preemtive mal weg. Kooperative ist viel einfacher. Auch wenn jeder Task auf einen Wait mit Timer laeuft. Beim Preemptiven MT muss man alle Register wechseln, beim kooperative nicht zwangslaeufig. Die grosse Vereinfachung liegt bei den Variablen. Variablen zwischen Tasks benoetigen beim Kooperativen MT keine Semaphore. Bleiben nur noch die Interrupt variablen. Sinnvollerweise weist man einer Interruptresource einen Task zu und muss so nur noch schauen, das der Task und der zugewiesene Interrupt miteinander klarkommen. Semaphoren sind ein zusaetzlicher Sonderaufwand der beim Preemptiven MT hinzukommt. Sie sind wesentlich mehr als nur Read-Modify-Write Strukturen. Hinter jeder Semaphore kommt noch eine Warteschlange, wo sich die Task die darauf warten eintragen. Das Schluesselwort dabei ist dynamische Variablen, Listen, beziehungsweise ein Memorymanager fuer dynamisches Memory, welcher auch eine Resource darstellt, da der nur einmal exitiert. Der Memortymanager muss daher direkt dem MT kernel unterstellt sein. Ich fuer mich tue einiges, um dynamisches Memory vermeiden zu koennen. So ganz nebenbei. Preemptives MT ist ein Zweihaender, der eigentlich auf einem 8bitter Overkill ist.
das MT ist keine Aufgabe, sondern ein Hilfsmittel. Darum muß erstmal in Erfahrung gebracht werden, wozu das Hilfsmittel gebraucht wird (Lehrer fragen ). Ich kauf mir auch keinen Rasenmäher, wenn ich nicht weiß, was ich damit machen könnte. Erst kommt die Aufgabe, Rasen muß gekürzt werden und dann kommen erst die Überlegungen, wie mache ich das. Daß das MT ein schlechtes Hilfsmittel ist, sieht man an Win. Nur fehler und Bugbereinigung, solange es Win gibt. Beim linearen System DOS gibt es keine Fehler. Die einzige Anwendung, die ich mir für das MT vorstellen kann, ist eine Regelmäßigkeit der Aufgabenabarbeitung. Ein Motor kann nicht solange stehen bleiben, bis andere Tasks ihre Arbeit erledigt haben. Dann und nur dann ist eine Unterbrechung erforderlich. Wenn eine Temperatur gemessen werden soll, kann man auch nicht warten, weil die Temp. sich in der Zwischenzeit verändert. Dein Beispiel 1. daten erfassen und 2. Erfaßte daten über Uart ist das schlechteste Beispiel, was Dir eingefallen ist, diese sind nämlich voneinander abhängig und können daher nur umständlich wie oben geschrieben verarbeitet werden. Solch eine Aufgabe muß 1 Task sein. Beim Kontextswitch müssen ggf. auch die Peripherie Register geswitched werden. Und Statusregister. Und selbst da muß man noch aufpassen, daß man nicht 2 Tasks einbaut, die beide den gleichen Uart verwenden. Dann gibt es auch Mist. Insofern sind die Tasks nicht ganz so unabhängig, wie Du Dir das vorstellst. Schwierig wird es, wenn man mehrere Aufgaben hat, die einer Regelmäßigkeit der Ausführung bedürfen. Da muß man sehen, ob diese zeitlich zueinander passen oder nicht. Wenn das nicht paßt, muß man mehrere CPUs verwenden. Um aber auf eine Aufgabe zeitlich noch reagieren zu können, sollten diese Aufgaben so schnell wie möglich erledigt werden. Und dafür muß dann auch eine geeignete Programmiersprache gewählt werden und das kann nur Assembler sein. Auch in der Hinsicht, daß andere Programmiersprachen Einschränkungen haben. Ich kenne Codevision nicht, aber wenn die Stackmanipulation mit Push und Pop da nicht möglich ist, kann man nur auf Assembler umsteigen. Bei Freertos habe ich weder eine ASM Datei noch eine .hex datei gefunden. Auch Downloads allgemein habe ich nicht gefunden. Wenn das hier öfter mal angesprochen wird, wäre es doch mal schön, einen direkten Link zum Download anzugeben. Mit Ausnahme der o.g. speziellen Aufgaben ist die bessere Programmierung wie meine Vorschreiber hier angegeben haben. Wenn MT so übersichtlich wäre, wo kommen dann die ganzen Fehler bei Win her? mfg
So leute ich habs geschafft.. Vier Tasks, alle haben ihren eigenen Hardware-und Softwarestack, alle sind gleichberechtigt, alle Task bestehen aus einer endlosschleife und toggeln eine LED. Alle Tasks sind in einer Ringliste initialisiert. Ein Timer macht alle 2 ms einen Kontext-Switch und springt zum task der als nächstes in der Ringliste steht... Ergebnis: 4 LEDs die unterschiedlich schnell blinken, wobei jede einzelne eine konstante geschwindigkeit hat (wird aber noch mit dem oszi überprüft). Läuft seit 20 min stabil, ich glaub das bleibt auch so :)
> Wolfram Quehl (quehl) wrote >Wenn MT so übersichtlich wäre, wo kommen dann die ganzen Fehler bei Win her? Naja, einen Realtimekernel mit einige kBytes mit Windows zu vergleichein ist unpassend. Zumal Windows kein Realtime System ist. Auch wenn es preemptive ist. Einen RTK verwendet man, wenn man verschiedene logiche Ablaeufe die irgendwie gekoppelt sind auf einem Geraet integrieren muss. Dabei steht eine definierte Antwortzeit im Vordergrund. Sie muss nicht minimal sein, sondern definiert. Da scheitert Windows. Der Unterschied zwischen RTK zur komplexen Zustandsmaschine ist die Uebersicht und die Wartbarkeit. Muss man einen der Ablaeufe anpassen, so bekommt man bei einer Zustandsmaschine mit 200+ Zustaenden an die Grenzen. Bis das ge-debugt ist und die Fehler gefunden worden sind ... ist man mit einem RTK schneller. Der wurde ja laenger und von mehreren Leuten geprueft. Richtig, wenn man fuer ein Projekt einen RTK selbst schreibt gewinnt man nichts, man kann auf nichts Geprueftem aufbauen. Ich halte es fuer vernuenftig von Hirbel Ha, sich das mal im Rahmen eines Projektes anzuschauen. Dann aber mit dem kooperativen Ansatz zuerst.
>4 LEDs die unterschiedlich schnell blinken, wobei jede einzelne eine >konstante geschwindigkeit hat (wird aber noch mit dem oszi überprüft). >Läuft seit 20 min stabil, ich glaub das bleibt auch so :) Klasse :-) Doch ich sagte es schon: Die Context Switches stellen kein Problem dar. Das kann man auf die Reihe kriegen. Der schwierige Teil ist die inter process communication. In Deinem LED-Programm findet keine statt. Damit das anders wird, musst Du nur einen Prozess haben, der irgendwelche Daten schreibt, die von einem anderen Prozess gelesen werden. So, nun will ich Dir aber keine weiteren Besserwissereien mehr zumuten... ;-) Gute Nacht.
also das sind echt schon ganz schöne besserwissereien die du mir hier zumutest :) Habe inzwischen ein paar funktionen hinzugefügt...z.b. kann ein task einen anderen task deaktivieren (dieser befindet sich dann noch zwar in der ringliste, wird aber beim switch übergangen)...das gleiche kann man für eine bestimmte anzahl an timerticks machen (also wie lange ein task nicht geswitcht wird)... sooo das wars erstmal und ob diese kontext-switch anweisungen so einfach sind....naja, ich glaube nicht, für dich vielleicht weil du ein avr-asm-genie bist ;)
Irgendwie fällt mir zu fehlerhaftem Multitasking der Therac-25 ein... http://de.wikipedia.org/wiki/Therac-25
@Error .. und mir fällt dabei der Hammer ein, der auch nicht daran schuld ist, wenn man sich selbst auf die Finger haut.
so mal ne allgemeine Frage: Also ich versuche grade, selber auch sowas zu implementieren (auf nem 68k allerdings). Kooperatives Multitasking ist kein Problem, das habe ich schon gemacht. Ging auch ganz gut. Ich will mich aber mal darin versuchen, ein präemptives zu machen. Das klappte bis jetzt so halbwegs: Also ich hab einen Timer aufgesetzt. Der läuft alle 10 ms über. Im Timerinterrupt muss ich dann den Kontext wechseln, das ist mir auch noch klar. Nun muss ich ja aber für jeden Task einen eigenen Stackbereich haben (ist kein Problem, auf meinem Board sind 1 MB SRAM). Ich habe es schon mal fertig gebracht, im Interrupt-Programm den Stack zu manipulieren, sodass der Prozessr nachher an einem anderen Task weiterrechnet. Mein Problem ist jetzt: Wie lege ich die verschiedenen Stack am gescheitesten an? Und wie starte ich einen zusätzlichen Task? Ich will hier nicht wieder so eine Diskussion starten "och Multitasking ist eh unnötig". Ich weiss, dass ich es nicht brauche. Aber ich will es mal versuchen, selber zu programmieren. Einfach dass ich es mal gemacht habe ;) Kennt jemand ne gute Internetseite zu dem Thema? Insbesondere was den 68k anbelangt? (also Multitasking auf dem 68k versteht sich). Grüsse
Hijack (gast), Ein praemtiver Taskwechsel wechselt den Stack. Stack meint in diesem Fall natuerlich den Stack fuer die Funktionsaufrufe. Hardwareinterrupts sind natuerlich von den Tasks unabhaengig und bleiben auf dem Hardware Stack.
also ich hab das so gelöst, das man sich zu vorher ein kopf machen muss, wieviel threads überhaupt maximal erstellt werden können, bin mir jetzt nicht ganz sicher (weil ist schon ein weilchen her) aber die anzahl wärend der laufzeit zu bestimmen, ist nicht möglich oder nur mit sehr viel aufwand...irgendwo gabs da ein problem also am anfang jeden thread einen speicherbereich zuordnen und fertig is :) noch was zur notwendigkeit g. es ist für die meisten anwendungen die hier besprochen werden nicht nötig, bzw. lohnt der aufwand nicht, es gibt aber anwendungen, die daraus profitieren und wo diese technik eigentlich unverzichtbar ist, da es keine wartezeiten mehr gibt wo die CPU nichts macht und wo kostbare zeit verschwendet wird und somit das ganze system viel viel langsamer ist...(so wars bei mir).
gast wrote: > Also ich versuche grade, selber auch sowas zu implementieren (auf nem > 68k allerdings). Dann ist es natürlich ne saugute Idee, nen 68k-Frage hinter nem AVR-Thread zu verstecken. > Wie lege ich die verschiedenen Stack am gescheitesten an? Und wie starte > ich einen zusätzlichen Task? Da wird Dir wohl nichts weiter übrig bleiben, als erstmal tief in den Eingeweiden Deines ungenannten C-Compilers zu wühlen, welche Stacks er anlegt, wie er Parameter übergibt, wie er Speicher verwaltet usw. > Kennt jemand ne gute Internetseite zu dem Thema? Insbesondere was den > 68k anbelangt? (also Multitasking auf dem 68k versteht sich). Die einen AVR-Thread lesen, höchstwarscheinlich nicht. Peter
@Peter Dannegger Guten Morgen! Hast Du schon einmal einen 68K in der Hand gehabt oder stänkern wir heute wieder? @gast Bei soviel RAM kannst Du doch großzügig verteilen, zumal beim 68K dafür ein separater Userstack zur Verfügung steht und die Anzahl der möglichen Tasks auch endlich ist. Aufpassen sollte man allerdings bei lokalen Feldern, die auf dem Stack gelegt werden.
Danke @peter dannegger, genau das meinte ich - solche Diskussionen wollte ich grade eben nicht hervorrufen. Wenn du den 68k nicht kennst (und da du ja sowieso nichts davon hältst, Multitaskig in einem Bastelprojekt einzusetzen) solltest du vielleicht nicht unbedingt auf die Frage antworten. Dein Post ist reiner Offtopic und gehört demnach ins entsprechende Forum (siehe http://www.mikrocontroller.net/forum/offtopic). @NochEinGast: Danke erstmal. Du hast also so ein System implementiert? Wie viele Threads laufen denn bei dir max. ? (so nur aus Interesse). Wie sorge ich denn dafür, dass jeder Task seine Daten auf dem eigenen Stack ablegt, aber wenn ein Interrupt auftritt (Timer-Tick) wird die Rücksprungadresse auf dem Systemstack gespeichert? Das ist mir noch etwas unklar. Grüsse
gast wrote: > Danke @peter dannegger, > genau das meinte ich - solche Diskussionen wollte ich grade eben _nicht_ > hervorrufen. Wenn du den 68k nicht kennst Zumindest weiß ich, daß er sich vom AVR unterscheidet, daher der ernstgemeinte Hinweis. Es ist mir doch völlig wurst, ob Du nen AVR-Thread hijackst, Du wirst dann eben weniger Antworten zum 68k kriegen, als mit nem 68k-Thread. Und das man für ein MT erstmal wissen muß, welchen Compiler Du benutzt, ist keinesfalls offtopic. Ein MT aufm AVR für den GCC geschrieben läuft nicht mit dem IAR oder Codevision und umgekehrt. Peter
>Ein MT aufm AVR für den GCC geschrieben läuft >nicht mit dem IAR oder Codevision und umgekehrt. Wer hätte das gedacht? @gast Was mit Usermode und Supervisormode los ist, solltest Du schon wissen. Demzufolge sollten Deine Tasks im Usermode laufen. Die Timerticks greifen sich per Interrupt dann den Supervisorstack; das ist ja das Schöne am 68K.
Hallo, also irgendwie gehen hier einige Dinge durcheinander. Sowohl preemptives als auch kooperatives Multitasking benutzen Endlosschleifen in den Tasks. Allerdings ist richtig, daß beim kooperativen Multitaksing die Prozesse freiwillig die Kontrolle abgeben. void task1() { while(1) { PORTA |= 1<<LED1; wait(100); PORTA &= ~(1<<LED1); wait(900); } } void task2() { while(1) { PORTA |= 1<<LED2; wait(333); PORTA &= ~(1<<LED2); wait(333); } } int main() { create(task1, ....); create(task2, ....); // mach irgendwas sinnvolles oder while (1); } wait übergibt die Kontrolle an andere Task oder loopt ggf. vor sich hin Preemptiv wird die Sache, wenn ein Interrupt eine Task aufwecken kann, was dann voraussetzt, dass alle 32 regs und sreg im Stack gesichert sein müssen, beim kooperativen könnte der laufende Prozess kontrolliert nur die kritischen register retten. das Beispiel int main() { while(1) { task1(); task(2); } } sind 2 Statusmaschinen!!! Gruss, Michael
> Was mit Usermode und Supervisormode los ist, solltest Du schon wissen. > Demzufolge sollten Deine Tasks im Usermode laufen. Die Timerticks > greifen sich per Interrupt dann den Supervisorstack; das ist ja das > Schöne am 68K. Danke, das ist ein guter Tipp. Auf die Idee bin ich gar noch nicht gekommen! Natürlich kenne ich die beiden Modi, aber bisher (und ohne Betriebssystem) habe ich natürlich immer im Supervisor Mode gearbeitet. Den Usermode habe ich dabei komplett vergessen.... Danke für den Hinweis. @peter > ob Du nen AVR-Thread hijackst Ja sorry. Übrigens wollte ich dich auch nicht angreifen mit meine vorhergehenden Post. Es ist einfach so, dass ich das Gefühl hatte, die Leute in dem Thread hier haben ne Ahnung von der Materie. Wieso sollte ich also nen neuen Thread aufmachen, wenn hier schon einer ist, wo es um dasselbe geht? Grundsätzlich erstmal ist mir der 68k egal. Wenn ich nämlich weiss wie das Multitasking auf nem AVR oder 8051 oder irgendwas anderem genau funktioniert, dann kann ich das auch im 68k implementieren. Grüsse
Falls es jemanden interessiert: habe jetzt den Context-Wechsel fertiggebracht! *jubel, freu*. Es ist zwar noch eine quick-and-dirty Lösung, denn im Moment sichere ich den Context der Tasks an fixe Adressen. Später soll dann natürlich dynamisch entschieden werden, an welcher Adresse der Context gespeichert wird, da muss ich mir noch was einfallen lassen... Auf jeden Fall klappt es jetzt (habe mal den Code in nem Simulator laufen lassen, wo ich Schritt für Schritt anchvollziehen konnte, was die CPU macht). Context Switch läuft jetzt einwandfrei, dank dem Tipp vom anderen Gast hier.... (User und Supervisor Mode).
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.