Forum: Mikrocontroller und Digitale Elektronik Befehlzähler Arduino


von andreaspaul (Gast)


Lesenswert?

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

von Wolfgang (Gast)


Lesenswert?

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

von Karl M. (Gast)


Lesenswert?

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.

von Wolfgang (Gast)


Lesenswert?

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.

von Joachim B. (jar)


Lesenswert?


von Manfred (Gast)


Lesenswert?

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.

von Joachim B. (jar)


Lesenswert?


von Wolfgang (Gast)


Lesenswert?

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.

von 50c (Gast)


Lesenswert?

Manfred schrieb:
> man ruft aus der "loop()" heraus alle Aufgaben

...was ist "der loop()"?

von andreaspaul (Gast)


Lesenswert?

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

von holger (Gast)


Lesenswert?

>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.

von Roth (Gast)


Lesenswert?

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 ;)

von A. S. (Gast)


Lesenswert?

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.

von Egon D. (Gast)


Lesenswert?

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.

von Wolfgang (Gast)


Lesenswert?

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.

von Marc (Gast)


Lesenswert?


von Markus F. (mfro)


Lesenswert?

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.

von Clemens L. (c_l)


Lesenswert?

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
}

von jemand (Gast)


Lesenswert?


von Axel S. (a-za-z0-9)


Lesenswert?

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.

von andreaspaul (Gast)


Lesenswert?

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

von A. S. (Gast)


Lesenswert?

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...

von andreaspaul (Gast)


Lesenswert?

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

von Axel S. (a-za-z0-9)


Lesenswert?

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

von Markus F. (mfro)


Lesenswert?

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.

von Axel S. (a-za-z0-9)


Lesenswert?

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.

von Markus F. (mfro)


Lesenswert?

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
Noch kein Account? Hier anmelden.