Hallo Leute, ich steige direkt ins problem ein: Ich war gestern auf der Uni bei einer Vorlesung über "Betriebssysteme" (Sehr interessant). Dort lernte ich die genaue Funktionsweise eines Multitaskenden Systems kennen. Im Prinzip funktioniert es Ja so. Es gibt eine Programmtabelle wo alle Daten über die Prozesse gespeichert sind und jeder Prozess erhält eine Id Irgendwann gibt es einen interrupt und der dispatcher setzt ein Alle Werte werden gesichert, das neue Programm geladen und der Befehlszähler auf Startadresse + eigenen Befehlzähler gesetzt Soweit so gut, ich würde nun gerne eine art Minimultitaskingsyste für den Arduino schreiben, aber könnte mir bitte jemand erklären wie ich den Befehlszähler des Atmega8 in der Arduino IDE manipulieren kann? Ich danke schon jetzt für die Antworten, Lg
andreaspaul schrieb: > Soweit so gut, ich würde nun gerne eine art Minimultitaskingsyste für > den Arduino schreiben, aber könnte mir bitte jemand erklären wie ich den > Befehlszähler des Atmega8 in der Arduino IDE manipulieren kann? Neuen Wert auf den Stack schieben und RET wäre eine Möglichkeit
Hallo, Was du mit "Befehlszähler" bei einem AVR meinen könntest, ist der Stackbereich des AVR µP. Einen Atmega8 würde ich nicht nehmen. Eher einen großen modernen AVR µP Atmega1284p (16K Bytes Internal SRAM) u.a. Hier ist RAM dein Freund! Man kann effizient für jeden Prozess einen eigenen Stackbereich deklarieren und diesen, neben einigen weiteren Prozess Variable speichern. Dann wird es auf einmal einfach - vereinfacht gesprochen - der Dispatcher schaltet auf den neuen Thread, indem er den alten (bereinigten) Stack Pointer sichert, und auf den nächsten "umschaltet". Dann benötigt man auch noch Verfahren, um zwischen den Prozessen Daten austauschen zu können. Einen direkte Zugriff auf das SRAM (Variable) für mehrere Prozesse darf es nicht geben.
Karl M. schrieb: > Was du mit "Befehlszähler" bei einem AVR meinen könntest, ist der > Stackbereich des AVR µP. Gewöhnlich wird mit "Befehlszähler" der Program Counter bezeichnet. Mit dem Stack hat der, außer dass sein Wert dort abgelegt oder von der geholt werden kann, nichts weiter zu tun. Der Stack ist ein Speicherbereich, der als FILO betrieben wird.
andreaspaul schrieb: > ich würde nun gerne eine art Minimultitaskingsyste für > den Arduino schreiben Karl M. schrieb: > Eher einen großen modernen AVR µP Atmega1284p (16K Bytes Internal SRAM) > u.a. Hier ist RAM dein Freund! ja und das geht ja auch Arduino mighty mini 1284p o.ä https://github.com/JChristensen/mini1284 https://www.instructables.com/id/Arduino-18x-Clone-With-Atmega-853532644p1284p-the-/ http://www.hobbytronics.co.uk/arduino-uno-pro-1284p http://www.mkroll.mobi/?p=1722 https://uge-one.com/arduino-board-based-on-atmega1284p.html https://feilipu.me/2013/03/08/goldilocks-1284p-arduino-uno-clone/ https://rlx.sk/sk/arduino-boards/3515-arduino-atmega1284p-arduino-compatibile-board-with-atmega1284p-support-ide-106-.html
andreaspaul schrieb: > ich würde nun gerne eine art Minimultitaskingsyste für den Arduino schreiben, Das sehe ich als groben Unfug an. Ein Mikrocontroller hat feste Aufgaben, man ruft aus der "loop()" heraus alle Aufgaben der Reihe nach auf und lässt diese ausführen.
Manfred schrieb: > Ein Mikrocontroller hat feste Aufgaben, man ruft aus der "loop()" > heraus alle Aufgaben der Reihe nach auf und lässt diese ausführen. Und was spricht dagegen, den Task Scheduler als einzige Aufgabe zu betrachten, der dann nach gewünschten Kriterien (Zeitscheibe, Priorität oder was auch immer) die Prozesse laufen läst. "alle Aufgaben der Reihe nach" ist nur eine Möglichkeit davon.
Ok danke für die Antworten liebe Leute, alle sehr Hilfreich ;) Ich schau mir mal die Links an und das mit der Stack ist interessant. Mit welchem Arduino IDE behelfen kann man auf diese zugreifen bzw wie kann man in der Arduino IDE prinzipiell den RAM einteilen, verwalten etc. (Und noch eine Sache an dich Manfred, wenn du das hier als "Unfug" ansiehst und nichts Produktives dazu sagst -> Halt dich raus! Ich habe nicht nach deiner Meinung sondern nach einer produktiven Lösung gefragt. Kleingeister die alles als Blödsinn und Unfug abstempeln und über nichts nachdenken wollen hab ich im Alltag genug. Mir geht es hier um das Lernen, dass man mit sowas kein Softwarebaron wird weiß ich auch. Mein Tipp an dich: Denk über dinge nach, lass dich begeistern und Stempel nicht alles als "Unfug ab" "Think different! (Zitat Apple Inc.)" Ich hoffe ich hab jetzt euch anderen nicht mit dieser kurzen "rede" vergrault und hofft dass ich meine Meinung über Manfreds Post versteht.) An die anderen einen großen Dank für eure Antworten Ihr seid alle super, Danke und alles Liebe, Paul
>Einen Atmega8 würde ich nicht nehmen. Ich auch nicht. >Eher einen großen modernen AVR µP Atmega1284p (16K Bytes Internal SRAM) Auch den würde ich nicht nehmen. Eher einen ARM Cortex M3 oder M4 und dazu FreeRtos. Dann muss man nichts mehr selber machen. Gibt es alles schon und funktioniert super.
andreaspaul schrieb: > aber könnte mir bitte jemand erklären wie ich den > Befehlszähler des Atmega8 in der Arduino IDE manipulieren kann? Der "Befehlszähler" ist doch der Program Counter. Allerdings ist das eher ein Programmzeiger. Kenne den Befehlssatz dieser MPU nicht, aber Manipulieren kann man Programmzeiger einfach, z.B. call oder jp. Auch ret "manipuliert" ihn. Aber das war eine Scherzfrage, oder ;)
andreaspaul schrieb: > (Und noch eine Sache an dich Manfred, wenn du das hier als "Unfug" > ansiehst und nichts Produktives dazu sagst -> Halt dich raus! Ich finde deine Reaktion zu hart. Du willst ein RTOS selber bauen, ohne eines zu kennen (um dessen Strategien erforschen zu können), ok. Sportlich, und Du lernst sehr viel. So solltest dabei aber nicht vergessen, dass es auch den SPS-Loop-Ansatz gibt, dass dieser in vielen Fällen deutlich robuster, einfacher und deterministischer ist, und dass er für Anfänger noch steilere Lernkurven hat.
Achim S. schrieb: > So solltest dabei aber nicht vergessen, dass es auch > den SPS-Loop-Ansatz gibt, [...] Sicher. Die Ironie dabei ist, dass industrielle SPS durchaus eine Art Betriebssystem haben, denn das Speicherabbild der Eingänge bzw. Ausgänge stellt sich nicht von allein her, und auch die Hintergrund- kommunikation zu irgendwelchen Terminals bzw. zum Programmiergerät muss jemand machen. Der Gegensatz ist also bei weitem nicht so scharf, wie er hier konstruiert wird.
holger schrieb: > Auch den würde ich nicht nehmen. Eher einen ARM Cortex M3 oder M4 > und dazu FreeRtos. Dann muss man nichts mehr selber machen. Gibt es > alles schon und funktioniert super. Nur das Lernen funktioniert damit nicht. Wenn alles vorgekaut da liegt und funktioniert, besteht meist kein Anlass, sich mit den Detais zu beschäftigen und etwas ggf. auch noch mal selber zu erfinden. andreaspaul schrieb: > Mit welchem Arduino IDE behelfen kann man auf diese zugreifen bzw wie > kann man in der Arduino IDE prinzipiell den RAM einteilen, verwalten > etc. Da wirst du dich wohl auf AVR-Assembler Ebene herunter bewegen müssen und dann mit in den C-Code einbinden.
Hier ein paar R-Tosse für die Arduino Welt: https://exploreembedded.com/wiki/Setting_Up_FreeRTOS_on_Arduino https://www.youtube.com/watch?v=JXy86GrjVso https://github.com/gatATAC/gatArduinOSEK
Um den "Befehlszähler" zu manipulieren, gibt es in der C-Library einen Standardmechanismus (der zwar nicht unbedingt schön ist, aber portabel und das Assemblergefummel spart). Der heißt longjmp()/setjmp(). Und der berücksichtigt auch gleich die Tatsache, daß es mit einer Program Counter Manipulation ja nicht getan ist, wenn man den CPU Kontext wechseln will. Da gehören ja noch (mindestens) alle Registerinhalte dazu.
50c schrieb: > ...was ist "der loop()"? Eine der beiden Funktionen, die du im Arduino-Framework selbst schreiben musst:
1 | main() |
2 | {
|
3 | setup(); |
4 | for (;;) |
5 | loop(); |
6 | }
|
holger schrieb: >>Einen Atmega8 würde ich nicht nehmen. > > Ich auch nicht. > >>Eher einen großen modernen AVR µP Atmega1284p (16K Bytes Internal SRAM) > > Auch den würde ich nicht nehmen. Eher einen ARM Cortex M3 oder M4 > und dazu FreeRtos. Dann muss man nichts mehr selber machen. Gibt es > alles schon und funktioniert super. Wenn die Intention ist, ein RTOS selber zu schreiben, dann ist das ein schlechter Rat. Wenn Linus Torvalds auf einen solchen Rat gehört hätte (und tatsächlich hat er ihn von seinem Prof bekommen: "nimm Minix") dann gäbe es heute kein Linux. Daß der TE allerdings weit davon entfernt ist, das zu können, steht auf einem anderen Blatt. Ebenso, daß das Arduino Framework (nicht: die Arduino Hardware-Plattform) dafür nun wirklich ungeeignet ist.
Danke liebe Leute, ich werde mich mal mit longjmp() und den Rios vertraut machen. Danke für die guten antworten ;) Ihr seid alle toll, Alles Liebe Paul
Egon D. schrieb: > Der Gegensatz ist also bei weitem nicht so scharf, > wie er hier konstruiert wird. Nein, scharf sowieso nicht. Zumal nicht jede Implementierung alle Features hat: a) Interrupts (mit verschiedenen Prioritäten), ausgelöst durch z.B. Timer sind prinzipiell auch ein RTOS (ohne Sleep). b) Für ein Autosar-RTOS sind Basic-Tasks empfohlen. Das sind quasi nur SPS-Funktionen, auch ohne Sleep. Zwar mit verschiedenen Prioritäten, aber jede "Task" soll nach jedem Aufruf zurückkehren. c) Ja, auch bei SPS-Loop-Systemen sollte man z.B. für die GUI eine eigene Tasks haben. d) Die Windows-Nachrichtenloop ist wieder eine andere Metapher. Asynchrone Ereignisse mit quasi synchroner Verarbeitung. Und dann noch beim "normalen" RTOS Prios und Round-Robin, preemptiv und kooperierend, ... die Reine Lehre gibt's (zum Glück) nirgends...
Ok danke. Leider komme ich mit longjmp() nicht wirklich weiter ;( Wäre jemand so lieb mir ein Stück kommentierten Code zu geben mit dem man über die Stack den Wert des Programmcounters verändern kann? Danke und Alles Liebe, Paul
andreaspaul schrieb: > Leider komme ich mit longjmp() nicht wirklich weiter ;( Wäre jemand so > lieb mir ein Stück kommentierten Code zu geben mit dem man über die > Stack den Wert des Programmcounters verändern kann? Du bist massiv verwirrt. In C - wo du dich anscheinend aufhältst - gibt es kein Konzept "Program Counter". Das ist ein Konzept vom Maschinensprache-Niveau. Deswegen kannst du den Programmzähler von C aus weder sehen noch direkt verändern. Indirekt natürlich schon. Immer wenn dein C-Programm zur nächsten Zeile fortschreitet, ändert sich der Programmzähler. Wenn du eine C-Funktion aufrufst, ändert er sich auch. usw. usf. Den Programmzähler kannst du nur sehen, wenn du dich auf die Maschinen- Ebene begibst. Z.B. indem du den Ablauf deines Programms mit dem Debugger verfolgst. Dann gibt es ein CPU-Register namens "PC" - das steht für Program Counter. Es gibt auch einen Maschinensprach- (vulgo: Assembler-) Befehl, um den PC zu ändern. Der heißt typisch JMP (für Jump) und nimmt als Argument den neuen Wert für den PC. Z.B. setzt JMP 1234 den PC auf den Wert 1234. Nicht daß dir das jetzt direkt weiterhelfen würde. Wenn du diese Interna wirklich verstehen möchtest, dann würde ich dir empfehlen, ein Assembler-Tutorial zum AVR durchzuarbeiten. Z.B. das von dieser Seite: AVR-Tutorial Oder eins der gefühlt 1000 weiteren im Web: https://www.google.com/search?&q=avr%20assembler%20tutorial
Axel S. schrieb: > Du bist massiv verwirrt. In C - wo du dich anscheinend aufhältst - gibt > es kein Konzept "Program Counter". Das ist ein Konzept vom > Maschinensprache-Niveau. Deswegen kannst du den Programmzähler von C aus > weder sehen noch direkt verändern. Indirekt natürlich schon. Immer wenn > dein C-Programm zur nächsten Zeile fortschreitet, ändert sich der > Programmzähler. Wenn du eine C-Funktion aufrufst, ändert er sich auch. > usw. usf. Das stimmt so nicht. Jede C-Library, die setjmp()/longjmp() (bzw. sigsetjmp()/siglongjmp()) korrekt implementiert (und das sollte eigentlich jede einigermaßen ernstzunehmende tun), erlaubt (mehr oder weniger direktes) Verändern des Programmzählers. Jedes Mal, wenn Du STRG-C drückst, wechselt die Task. Ohne eine einzige Zeile Assembler, selbst ohne zu wissen, welche CPU überhaupt werkelt:
1 | #include <stdio.h> |
2 | #include <setjmp.h> |
3 | #include <stdbool.h> |
4 | #include <signal.h> |
5 | |
6 | jmp_buf contexts[3]; |
7 | static volatile sig_atomic_t task = 1; |
8 | void handler(int); |
9 | |
10 | void task0(void) |
11 | {
|
12 | if (sigsetjmp(contexts[0], 1)) |
13 | {
|
14 | while (true) |
15 | {
|
16 | printf("Hello from task0 (task=%d)\r", task); |
17 | }
|
18 | }
|
19 | }
|
20 | |
21 | void task1(void) |
22 | {
|
23 | if (sigsetjmp(contexts[1], 1)) |
24 | {
|
25 | while (true) |
26 | {
|
27 | printf("Hello from task1 (task=%d)\r", task); |
28 | }
|
29 | }
|
30 | }
|
31 | |
32 | void task2(void) |
33 | {
|
34 | if (sigsetjmp(contexts[2], 1)) |
35 | {
|
36 | while (true) |
37 | {
|
38 | printf("Hello from task2 (task=%d)\r", task); |
39 | }
|
40 | }
|
41 | }
|
42 | |
43 | void handler(int sig) |
44 | {
|
45 | printf("\nsignal handler task=%d\n", task); |
46 | task = (task + 1) % 3; |
47 | printf("\nswitch to task %d\n", task); |
48 | longjmp(contexts[task], 1); |
49 | }
|
50 | |
51 | |
52 | int main(int argc, char *argv[]) |
53 | {
|
54 | signal(SIGINT, handler); |
55 | /*
|
56 | * init contexts
|
57 | */
|
58 | task0(); |
59 | task1(); |
60 | task2(); |
61 | |
62 | while (true) |
63 | {
|
64 | }
|
65 | }
|
Obwohl das Beispielprogramm so nicht (ganz) clean ist (es macht stdio-Ausgaben im Signal-Handler, aber anders sähe man nichts), sollte es 100%ig portabel sein. Wenn Du aus "SIGINT" "SIGALRM" machst, hast Du einen (rudimentären) Multithreading-Kernel. Ohne eine einzige Zeile Assembler.
Markus F. schrieb: > Axel S. schrieb: >> Du bist massiv verwirrt. In C - wo du dich anscheinend aufhältst - gibt >> es kein Konzept "Program Counter". Das ist ein Konzept vom >> Maschinensprache-Niveau. Deswegen kannst du den Programmzähler von C aus >> weder sehen noch direkt verändern. Indirekt natürlich schon. Immer wenn >> dein C-Programm zur nächsten Zeile fortschreitet, ändert sich der >> Programmzähler. Wenn du eine C-Funktion aufrufst, ändert er sich auch. >> usw. usf. > > Das stimmt so nicht. Jede C-Library, die setjmp()/longjmp() (bzw. > sigsetjmp()/siglongjmp()) korrekt implementiert (und das sollte > eigentlich jede einigermaßen ernstzunehmende tun), erlaubt (mehr oder > weniger direktes) Verändern des Programmzählers. (Beispiel geschnippelt) Schön. Nur ist das kein bißchen direkter als ein Funktionsaufruf oder meinetwegen auch goto. Du hast nach wie vor nicht so etwas wie einen "Befehlszähler", dessen Wert du sehen oder direkt ändern könntest. Du hast nur mehr oder weniger abstrakte Labels, zu denen du hinspringen kannst. Du hast weder eine Möglichkeit, einen "Wert" dieser Labels zu sehen noch hast du Einfluß darauf, wo sie im Speicher abgelegt werden. Ganz im Gegensatz zu Assembler, wo du per org Direktive festlegen kannst, wo im Speicher dein Code landet oder wo du jmp die Adresse auch einfach als Zahl geben kannst. Für jemanden, der das ganze aus Assemblersicht betrachtet, ist setjmp() sogar viel komplexer, weil es ganze Stackframes sichert und (bei longjmp) wiederherstellt.
Axel S. schrieb: > Du hast nur mehr oder weniger abstrakte Labels, zu denen du hinspringen > kannst. Du hast weder eine Möglichkeit, einen "Wert" dieser Labels zu > sehen noch hast du Einfluß darauf, wo sie im Speicher abgelegt werden. > Ganz im Gegensatz zu Assembler, wo du per org Direktive festlegen > kannst, wo im Speicher dein Code landet oder wo du jmp die Adresse auch > einfach als Zahl geben kannst. Du verkennst setjmp()/longjmp(). Das ist nicht nur ein "einfaches goto", das ist ein kompletter Kontext-Switch. Und was interessieren mich "Werte meiner Labels" oder wo die im Speicher liegen? Vielleicht nochmal zurück zur eigentlichen Ausgangsfrage: der TO wollte ein Multitasking-System schreiben und erhielt die Auskunft, daß das nur mit C und ohne Assembler nicht (und mit seinem Wissenstand schon gar nicht) ginge. Darum ging's mir. Und daß ich dabei deine Antwort gequotet habe, ist eigentlich nur Zufall (ich hätte auch fast jede andere nehmen können). Es geht (wenn auch sehr rudimentär). Und war in weniger als zehn Minuten runtergeschrieben und lauffähig. Mich würde interessieren, wie lang jemand braucht, ähnliche Funktionalität in Assembler zum Fliegen zu bringen.
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.