Hi Leute, ich habe eine kleine Frage zu dem "Multitasking des kleinen Mannes": http://www.mikrocontroller.net/attachment/111803/MdkM.c Der Sinn der State Machines erschließt sich mir, und ich bin gerade dabei, sie sinnvoll einzusetzen, bin aber auf ein Problem gestoßen, als ich das Programm erweitern wollte: Ich benötige einige weitere Zustände, um auf Ereignisse von außen warten zu können. Ist z.B. eine bestimmte Flag gesetzt (z.B. DMA läuft gerade (nicht)) UND ist der timer dran, kann die eigentliche Arbeit begonnen werden. Dafür habe ich einen IDLE task, der nur auf den timer wartet und einen WAIT task eingebaut, der die flag pollt. Jetzt habe ich aber z.B. 5 tasks, die in unregelmäßigen Zeitabständen laufen. Die werden in der main einfach im round robin Verfahren durchgeschaltet. Angenommen der timertick eines tasks kam. Wir befinden uns im Zustand WAIT. Dann wird die DMA flag gesetzt (oder der mutex ist nicht mehr gelockt oder so), und es wird weiter auf den Zustand WORK geschaltet. Aaaaber: dieser Zustand tritt ja erst beim nächsten Aufruf wieder ein. Es kann aber sein, dass in der Zeit ein anderer Task läuft, der ein paar ms benötigt. Dann könnte aber der DMA schon wieder an sein und der Zustand stimmt nicht mehr. Ich könnte das Dilemma lösen, indem ich warte, bis der DMA nicht mehr läuft, aber das würde ja alles wieder kaputt machen. Hintergrund der ganzen Geschichte: Ich zähle in der DMA ISR einen Zähler hoch, der mir die Anzahl der Durchläufe angibt. In einem Task frage ich die Anzahl ab. Ist die Anzahl >= einer Schwelle, hole ich die Daten ab. Dann setze ich den counter zurück. Das ist zwar atomar, aber die Inkrementierung in der ISR nicht. Es kann also sein, dass ich den Counter zurücksetze, das in der ISR aber wegen read-modify-write gar nicht ankommt. Daher möchte ich sichergehen, dass der DMA gerade nicht läuft. Oder ist das total kacke und ich sollte auf eine Inkrementierung (zumindest als "public" Variable) verzichten, nur lokal in der ISR mitzählen und bei z.B. 10 vorhandenen samples eine binäre Flag setzen, deren Zugriff ja wirklich atomar ist? Vielen Dank für eure Antworten!
UM beim verlinkten Beispiel zu bleiben. Du musst hier (original]
1 | uint8_t Blink_2S( uint8_t state ) |
2 | {
|
3 | switch( state ) { |
4 | |
5 | case INIT: |
6 | StartTimer( 0, 2000 ); // Timer starten, 2 Sekunden |
7 | return WAIT_2S; // beim nächsten Aufruf soll die Statemachine im Zustand WAIT_2S sein |
8 | break; |
9 | |
10 | case WAIT_2S: |
11 | if( !IsTimerStopped( 0 ) ) // Timer abgelaufen? |
12 | return WAIT_2S; // nein: Task bleibt im Zustans WAIT_2S |
13 | |
14 | return WORK_2S; // ja: nächster Zustand ist: 2 Sek sind vorbei, jetzt wird gearbeitet! |
15 | break; |
16 | |
17 | case WORK_2S: |
18 | PORTA = PORTA ^ 0x01; // na ja, soviel gibts nicht zu tun |
19 | |
20 | StartTimer( 0, 2000 ); |
21 | return WAIT_2S; |
22 | break; |
23 | }
|
24 | |
25 | return state; |
26 | }
|
keineswegs einen Statuswechsel zwischen WAIT_2S und WORK_2S machen. Das war nur im Beispiel so und ist keineswegs in Stein gemeisselt. Genausogut könnte man da auch mit nur 2 Zuständen arbeiten
1 | case WAIT_2S: |
2 | if( !IsTimerStopped( 0 ) ) // Timer abgelaufen? |
3 | return WAIT_2S; // nein: Task bleibt im Zustans WAIT_2S |
4 | |
5 | PORTA = PORTA ^ 0x01; // na ja, soviel gibts nicht zu tun |
6 | |
7 | StartTimer( 0, 2000 ); |
8 | return WAIT_2S; |
9 | break; |
In deinem Beispiel: sobald deine Voraussetzungen im WAIT Zustand so sind, dass der DMA gestartet werden kann, dann machst du das auch. Somit ist dieses Problem > und es wird weiter auf den Zustand WORK geschaltet. Aaaaber: > dieser Zustand tritt ja erst beim nächsten Aufruf wieder ein. > Es kann aber sein, dass in der Zeit ein anderer Task läuft, > der ein paar ms benötigt. keines mehr. Denn dann kann zwischen feststellen der Arbeitsfähigkeit und dem tatsächlichen Reservieren des DMA kein anderer Task per Definition dazwischen funken. Den Rest deiner Ausführungen hab ich auf die schnelle nicht verstanden.
:
Bearbeitet durch User
Vieleicht noch ne DMA-State-machine bzw. DMA-Sequencer, der weiß was geht und was nicht. Dieser reiht dann die Operationen in eine Warteschlage ein und kann auch einen error wie "busy" zurückgeben.
bzw. die DMA-State-machine ist dann halt im State "busy" und nicht im State "ready".
Hi Jan, also vom uebersichtlicher machen deines Quellcodes - wie du da in deinem Thread von vor 'ner Woche oder so geschrieben hattest (STM Cortex-M3?) - davon bist Du ganz weit weg mittlerweile! In einer Schleife Funktionen periodisch aufzurufen und diese Funktionen mit nem expliziten Zustand versehen zu m"ussen und den Code diesem Zustand anzupassen, das hat bei mir nicht mehr viel mit dem Multitasking zu tun. Das ist Bitgefummel. Da kannst Du auch Sudoku spielen, daf"ur ist genausoviel Gr"utze in der Birne notwendig und es wird halt gefummelt bis das R"atsel gel"ost ist. Eleganz? Nicht vorgesehen. Nach der (Forums-) Definition ist fast jedes Programm 'multitasking' denn die meisten Programme (auf einem uC) fragen in einer Hauptschleife irgendwelche Hardware-Flags ab und tun irgendwas anderes, wenn die Flags anzeigen, dass nichts zu tun ist. Das geht ganz leicht, seit der Erfindung der Unterfunktion! Mensch und ich Pappnase hab in der 90er Jahren nicht kapiert, dass MSDOS auch schon Multitasking war! "Multitasking des kleinen Mannes" sehe ich auf 'nem ganz kleinen uC noch ein (8-Bitter). Aber auf einem 32-Bitter wirst Du doch (nur zum Beispiel) nicht im Ernst f"ur eine Reihe von Software-Timern in einer ISR f"ur jeden Timer den Stand herunterz"ahlen? Nein, du wirst eine System-Uhr t laufen lassen (HW oder SW) und f"ur jeden Software-Timer nur die Zeit te, wann abgelaufen festhalten. Wenn Du wissen willst ob der Timer abgelaufen ist, pr"ufst Du (int)(te-t)<0. Timer aufziehen: te = t + Dauer. Signed oder unsigned ist egal, bloss nicht beim Vergleich. Und (int)(te-t)<0 ist nicht gleich te<t, "ubrigens. Und das funktioniert auch wenn der Timer "uberl"auft, falls Du mit 32bit rechnest UND der Timer genau 32bit hat und bei 2^32 "uberlauft. Keine ISR, keine Schleife in der ISR, keine Rechenoperationen wenn nichts wichtiges passiert. Geht aber bloss, wenn man mal von der Denke von 8bit wegkommt, so dass jeder Timer schon nach 256us "uberl"auft und die zu messenden Zeiten viel gr"osser sind... "Multitasking des kleinen Mannes" - Falsche Lekt"ure! Nat"urlich kannst Du, wenn Du Dich f"ur's Welle-Nabe-Verklebungen interessierst, dich in die Herstellung von Pfeilen mit Birkenpech und Feuerstein vertiefen und dein Wissen dann auf das Welle-Nabe-Problem anwenden. Vielleicht 'hebt' ein Kugellager ja auch mit was 'einfacherem' als Loctite 603, wer weiss? Ich hab's mir schon gedacht, als gar niemand sich f"ur meinen Kontext-Switcher interessierte... Der Code des "Multitasking des kleinen Mannes" ist gar nicht so klein f"ur das was er leistet. MEINER ist KLEINER! ;-)
Hm bin etwas verwirrt. Was wäre denn vernünftige Literatur für einen 32-Bitter?
>Ich hab's mir schon gedacht, als gar niemand sich f"ur meinen >Kontext-Switcher interessierte... Wo findet sich denn Dein Kontext-Switcher? Link ?
Jan K. schrieb: > Was wäre denn vernünftige Literatur für einen 32-Bitter? Jedenfalls keine "uber 8-Bit Prozessoren! Wie w"ar's denn damit: http://www.lpcware.com/content/forum/minimalist-threading-lpc800 Okay das war selbsts"uchtig, aber es ist halt auch der Grund, warum ich heute nochmals nach deinem Thread / Deinen Aktivit"aten geschaut habe. Nein, Hass beiseite, ich hab nichts gegen 8-Bitter, ich hab was gegen die 8-Bit-Denke an Stellen, wo sie nicht angebracht ist. Du verlierst Dich - meines Erachtens - zu schnell in Details. Du schreibst zuviel und l"asst Dir zuviel ratschlagen. Du brauchst Wissen "uber Synchronisationsmechanismen auf DEINEM uC: LDREX, STREX, atomare Updates. Wenn Du wieder bei den 8-Bittern schaust, wirst Du Bit-Test-and-Set oder swap finden. Und die bringen Dir auf deinem STM32 genau so viel: Null. Du brauchst Wissen "uber Scheduling, Stacks, was 'Warten' bedeutet, usw. Mein Link oben f"uhrt zu einem Context-Switcher und der ist f"ur 'nen Cortex-M0. Das kannst Du 1:1 auf dem M3 verwenden, nur zur Info. Du brauchst Wissen, wie man generell guten Code schreibt. Es gibt einen Artikel "uber dietlibc von Felix von Leitner - LESEN! Schau Dir die normale C-lib an und du weisst wie man's nicht macht: printf/scanf, Namen ohne Systematik, Pr"aprozessor-Exzesse, ... Finde Leute die guten Code schreiben und schau Dir diesen an! Die legend"aren Linux-Kernel-Hacker geh"oren nicht dazu, auch kein noch so oft ausgezeichneter Adam Furunkels mit seinem uIP/lwIP. Am besten Leute, die Programme schreiben, die was richtig heftiges Steuern, das gleich explodiert wenn das Programm falsch ist. ABS-Steuerungen, Frequenzumrichter, Energieanlagen, Medizintechnik, Milit"ar. Die sind nur leider schwer zu finden und das was die schreiben i.d.R. nicht offentlich zug"anglich. Immerhin, die Java API find ich ein gutes Vorbild obwohl das nat"urlich ein ganz anderes Gebiet ist. Lerne Bibliotheken selbst zu erstellen und zu verwenden. Bei mir bestehen beispielsweise fast alle Programme aus einer main.c + Bibliotheken. Und die main.c hat nicht riesengross zu sein, das ist klar, oder? Lerne, einer Funktion, die du gerade geschrieben hast und in der main ihre Arbeit tut so zu gestalten, dass sie in die Bibliothek passt und in der neuen Form auch f"ur zuk"unftige Projekte taugt. Ahnliches gilt f"ur Datenstrukturen. Richtig schwierig wird's, wenn Du auf mehreren Plattformen/Cores arbeitest und die Duplizierung von Quellcode vermeiden m"ochtest. Heftig! Aber es geht, mit Hirnschmalz und z.B. Subversion. Und dann kommt irgendwann der Schritt, wo Du analysieren musst, was Dir an deinem Programm nicht gef"allt, so wie's jetzt ist und wie Du das gerne anders h"attest. Und da musst Du Ideen haben und absch"atzen k"onnen was die kosten und ob sie umsetzbar sind. Dazu braucht man Erfahrung und die sammelt man durch "Ubung, nicht durch Forumsdiskussionen. Obiger Task-Switcher ist entstanden durch Basteln eines Modellprogramms aus main() und paar Variablen und Funktionsprototypen (auch ISRs) und herumprobieren, wie mir das Zusammenspiel von Variablen (Thread-Context), Initialisierung und Aufrufen gefallen w"urde. Und dann hab ich's erst versucht umzusetzen - zun"achst auf Papier. Das Modellprogramm war nat"urlich nicht compilierbar, sondern nur die Skizze der sp"ateren Anwendung. So was in der Art wirst Du wohl vor dir haben. Hast Dir vermutlich sogar ein zu grosses Thema rausgesucht. Ich wollte fr"uher immer (Turbo-Pascal-Zeiten) eine GUI-Bibliothek machen und bin daran monumental gescheitert. Die ist bis zum heutigen Tage nicht fertig... So, genug gelabert f"ur die 'Community' - nun will ich endlich meinen LPC812 per pin-Interrupt aus dem power-down (2uA, Outputs aktiv!) holen k"onnen.
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.