Hallo,
auf Anragen von Jim Meba hier nochmal ein "extra post"
ich habe eine werteliste welche abgearbeitet werden muss... die ganze
liste hat einen fest vorgegebenen timer...
also z.B. port1: [ 0, 1, 1, 0, 1 ] mit 300ms dann muss ich alle 300ms
parameter für parameter abarbeiten...
allerdings soll nebenbei das "normale" program weiterarbeiten. Ebenso
kann es sein, dass nebenbei mehrere parameter laufen also port2: [ 0, 1
] 10000ms, hier ist die parametergruppe post1 evtl noch nicht fertig
aber es startet parametergruppe 2
da ich verschiedene "unterprogramme" habe die parallel laufen sollen
habe ich mir das in einen vorherigen foreneintrag abgeguckt...
ich habe also verschiedene counter welche zählen und sobald einer auf
seinen wert ist, wird der befehl ausgefürt und der wert zurückgesetzt:
1
intcnt[4];
2
intprm[4]={20,5,316};
3
4
while(1){
5
for(inti=0;i<4;i++){
6
cnt[i]++;
7
}
8
9
if(cnt[0]<prm[0]){
10
cnt[0]=0;
11
//aufgabe 1 ausführen
12
}
13
14
//selbiges gilt auch für aufgabe 2...
15
16
for(t=0;t<(sizeof(cnt)/4);t++){
17
cnt[t]++;
18
}
19
20
inttSleep=speed*5*1000;// ca 5ms
21
usleep(tSleep);
22
}
23
24
return0;
ich weiß, dass ich hier keine genauen zeiten hinbekomme... das
interessiert mich aber auch garnicht... ich habe für die Hardware ein
paar Zeiten einzuhalten (lesen von adc schreiben von gpio) und diese
werde ich "großzügig" einhalten... ob da jetzt 50ms mehr oder weniger
sind ist mir egal
ich weiß jetzt noch nicht exakt wie (da bin ich noch bei der findung)
aber irgendwie werd ich jetzt von außen befehle Reinbringen (vermutlich
habe ich eine SQLite Datenbank oder ein File) und da wird dann eine
schleife oder [das
bekomm ich noch nicht so hin], die einzelnen Zustände eingebracht.
=> es ist allerdings so, dass von außen der befehl zum Startzeitpunkt
(der abarbeitung), komplett vorliegt, aber er dann aber nicht komplett
gleich sondern mit einer zeitlichen abfolge (delay / timeout)
abgearbeitet werden soll.
hat jemand einen tipp für mich?
kurzum... mir liegt ein array vor welches abgearbeitet werden soll...
ich habe sogar bereits eine "forschleife" welche alle schaltzustände
durchläuft... allerdings durchläuft die forschleife innerhalb von ms
(oder noch schneller) und ich brauche aber die vordefinierte zeit...
einen SLEEP kann ich nicht einbauen, weil sonst das programm steht :(
1
for(j=0;j<(sizeof(cmd)/4);j++){
2
printf(cmd[j]);
3
// HIER BRAUCH ICH DIE PAUSE, ABER SO, DASS MEIN HAUPTSCRIPT WEITERLÄUFT
C kennt keine Nebenläufigkeit. Entweder es gelingt Dir, Deine
verschiedenen Aufgaben in kleine Schnipselchen zu zerlegen, die Du
zeitlich gesteuert wechselweise aufrufst, oder Du musst Dir ansehen,
unter welchem Betriebssystem Dein C-Programm läuft, und ob das
Betriebssystem Dir Funktionen für Nebenläufigkeit zur Verfügung
stellt.
Worauf soll denn das Programm laufen? Auf einem PC mit Windows, Linux
o.ä., oder auf einem Microcontroller ohne Betriebssystem?
Möglichkeiten...
...für jede Aufgabe einen separaten Thread starten
...für jede/alle Aufgabe(n) läuft ein asynchroner Timer der mit dem
ersten auszuführenden Zeitpunkt geladen wird und nach dem Eintreten mit
dem nächsten Zeitpunkt usw. Als Timer bieten sich Peripherie + Interrupt
an oder UNIX Timer und Signale.
...wenn dein Hauptprogramm Ereignisgesteuert ist und in kleine
(zeitliche) Tasks zerlegt werden kann baust du dir einen Mini Scheduler
mit Timer und Task Queue wo Timer Events bspw. bevorzugt werden.
... wenn das alles nicht geht dann kannst du vielleicht in deinem
Hauptprogramm "Synchronisationspunkte" auswählen an denen du prüfst ob
eine Aufgabe vorliegt (polling).
Oder die sogenannte SPS-loop.
Falls ohne rtos, dann mit den wichtigsten Tasks im Interrupt. Wichtig
scheinen bei dir robuste brotlose Timer. Da scheint es hier im Forum
abenteuerliche Auffassungen zu geben.
ou sorry,
ich habe vergessen dazu zu schreiben, dass ich hier auf einen rPi3
arbeite mit neusten Linux.
meine aktuelle Idee ist es sozusagen immer ein "neues" Programm
aufzurufen, also ein 2. c schreiben, dass das ganze dann abarbeitet...
hat obendrein den großen vorteil, wenn das "Hauptprogramm" abstürzt
werden die schritte sauber "Fertig" abgearbeitet.
Leider weiß ich noch nicht genau wie ich das mache. <- bin da auf den
Beta Beginner Level :) ...
aber Interessant wäre ob es ggf. andere "Anfänger-Möglichkeiten" gibt...
gleichzeitig weiß ich auch nicht wie genau ich verhinder, dass
gleichzeitig gelesen und geschrieben wird
=> also wenn ich jetzt meinen 2. Thread starte, der z.B. alle Sekunden
nen Port an und ausschaltet darf "genau" in dieser Zeit (beim schalten)
<- (zeitraum von n Millisekunden) der selbe Port nicht gelesen werden...
=> meine Idee war es also ein fifo file anzulegen in dem ein "HALT"
steht und sozusagen der "Reader" für n Millisekunden ausgebremst wird...
Was haltet ihr von meinen "Gedankengängen"? Ist das zu Kompliziert oder
zu Einfach gedacht?
Program 1:
dauerschleife {
lese informationen von Programm 2, falls halt da warte für n ms
Informationen lesen
befehle an Programm 2 Senden
}
Programm 2
Befehlsinput
Arbeite einzelne schritte mit nötigen Delay ab
vor jedem "Schaltvorgang" sende Informationen an Program 1 mit PAUSE
Hans schrieb:> meine aktuelle Idee ist es sozusagen immer ein "neues" Programm> aufzurufen, also ein 2. c schreiben, dass das ganze dann abarbeitet...
Schau Dir einmal fork() an und lese Dich in named Pipes ein.
Dann sollte der Groschen fallen.
So einfach wie du Dir das denkst funktioniert das nicht bzw. nicht
stabil.
Vergiss ganz schnell den sleep(), der bringt Dich hier nicht weiter.
Einen Prozess schlafen zu lassen ist oft der falsche Weg.
Ich möchte Dir raten zunächst einmal einige vorbereitende Schritte zu
machen damit Du gedanklich etwas klarer siehst, welche Aspekte dieses
Problem hat. Damit zerlegst Du es in kleinere Einheiten und kannst Dich
Schritt für Schritt vorarbeiten. Es ist wesentlich umfangreicher, als
Du, nachdem wie und was Du schreibst, vermutest.
Bitte fasse das nicht so auf, dass ich Dich für dumm halte oder so. Es
ist gute alte Methodik, Probleme von unten her anzugehen, anstatt alle
auf einmal. Das haben auch die Überflieger so gemacht. Jedenfalls viele.
Es wäre aus meiner Sicht sogar ratsam dabei zunächst auf einen einfachen
Mikrocontroller wie den AVR zurückzugehen, denn die Verwendung eines
ausgewachsenen Betriebssystems würde erfordern, sich allen Problemen
gleichzeitig zu stellen. Das ist aber bis zu einem gewissen Grad
Ansichtssache. Dennoch scheint mir dieser Rat nützlich zu sein.
Man müsste vielleicht erstmal ein paar Punkte klären, damit man einen
genauer auf Dich zugeschnittenen Rat geben kann.
1. Ich vermute mal, dass Du mit einem rPi zunächst mal ein paar LEDs
hast leuchten lassen. Ist das so?
2. Was ich nicht ohne weiteres vermutet hätte, ist, dass Du so ohne
weiteres an die Timer herankommst. Eher noch an irgendwelche abstrakten
Funktionen, die etwa Callbacks aufrufen, wenn gewisse Zeiten abgelaufen
sind. Ist das so?
3. Problemstellungen wie "Dining Philosophers" sagen Dir wahrscheinlich
nichts, oder?
4. State-Machines sagen Dir vermutlich nichts, oder?
5. Read-Modify-Write sagt Dir vermutlich nichts, oder?
6. Locks sagen Dir vermutlich nichts, oder?
7. Was Treiber von Anwendungen (im Kontext von Linux) unterscheidet,
weißt Du?
8. Was grundsätzlich Echtzeitanwendungen von üblichen Anwendungen
unterscheidet, weißt Du?
9. Hast Du mal was von Minix oder einem Herrn Tanenbaum gehört?
Was hast Du bisher gemacht? Was für Projekte realisiert? Welchen Code
hast Du selbst geschrieben? Auf welchen Plattformen? Wie lange? Was hast
Du gelesen? Was studiert? Was arbeitest Du?
Meine Vorgehensweise ist eher methodisch als mittendrein zu springen.
Muss man nicht so machen. Aber das ist mein ehrlich gemeinter Rat.
@ Hans
Naja. Vielleicht war meine vorherige Antwort unnötig elaboriert.
Wenn ich mal von Deiner Eröffnungspost ausgehe, versuche ich Deinen
Algorithmus mal neu zu formulieren.
A) Die periodische Verarbeitung von Listenwerten
1. Du hast Listen von Werten.
2. Die Anzahl der Listen steht fest, wenn Du das Programm schreibst und
kompilierst.
3. Die Inhalte der Listen stehen fest, wenn Du das Programms schreibst
und kompilierst.
4. Jeder Liste ist jeweils eine Zeit zugeordnet.
5. Diese Zeit gibt an, in welchem zeitlichen Abstand jeweils ein Wert
aus dieser Liste verarbeitet werden soll.
Das geht, in dem Du einen Timer nimmst, der periodisch einen Interrupt
auslöst. Der Zeitabstand ist so gewählt, dass die Timer-Periode ein
Vielfaches aller denkbaren Zeiten der jeweiligen Liste ist. (Falls das
nicht so geht, müsstest Du das mal erklären, warum). In dem Interrupt
zählst Du, je Liste eine Zeitvariable herunter. Das muss so sein, weil
die Zeiten unterschiedlich sein können. (Es gäbe da noch andere
Möglichkeiten, aber das ist die einfachste). Ist für eine Liste die Zeit
abgelaufen, dann setzt Du deren Ablaufzeit wieder auf den Anfangswert,
nimmst das nächste Element aus der Liste und verarbeitest es und zählst
einen Index auf das aktuelle Element hoch. Das machst Du für alle
Listen.
Es gibt für die Funktion, die ein Listenelement verarbeitet zwei
Möglichkeiten, von denen ich eine nenne. Du kannst je Funktion ein Flag
vorsehen, dass Du im Interrupt setzt und in einer endlosen Schleife im
Hauptprogramm immer wieder abfragst und die Funktion aufrufst, falls das
Flag gesetzt ist. Danach setzt Du das Flag wieder zurück. Die Funktion
darf niemals länger dauern als die Periode der jeweiligen Liste.
(Tatsächlich muss sie sogar noch schneller sein, denn die Funktionen der
anderen Listen und Dein Hauptprogramm brauchen ja auch Zeit)-
B) Das "normale" Programm
Da kann ich Deinem Text nicht viele Einzelheiten entnehmen.
1. Das "normale" Programm bekommt Befehle.
2. Diese Befehle sind mehrgliedrig in Bezug auf das was sie auslösen.
Oder anders ausgedrückt: Sie lösen mehrere Funktionen aus.
3. Diese Funktionen sollen parallel zu den Listen-Timer-Funktion
abgearbeitet werden.
4. Die Befehle bzw. Befehlsteile werden fallweise mit einer gewissen
Verzögerung abgearbeitet.
Dazu hat Dir Rufus schon einmal einen groben Anhaltspunkt gegeben.
Nämlich die nötigen Funktion zu untergliedern.
Daraus, dass die Befehle ohnehin schon mehrere Funktionen "meinen"
ergibt sich eine der Gliederungen.
Je nachdem wie lange die Funktionen jeweils dauern, kann es nötig sein,
sie weiter in kleinere Funktionen zu zerlegen.
Deren Reihenfolge kannst Du über State-Machines (im einfachsten Fall ein
Zähler) steuern. Wichtig ist, das selbst im ungünstigsten Fall jede
dieser Teilfunktionen höchstens so lange dauert, das alle
Listen-Timer-Funktionen noch ausgeführt werden können.
Dazu sind ein paar Überlegungen nötig, für die Du folgendes vorher
feststellen muss. Wie lange können die Funktionen jeweils maximal
dauern? In welchen Zeitabständen werden sie minimal aufgerufen. Was ist
die Summe der Ausführungszeiten aller dieser Listen-Timer-Funktionen
maximal?
Was die verzögerte Ausführung betrifft, lässt sich die in das Schema der
Listen-Timer-Funktionen einordnen. Es ist ja egal, ob die Funktion auf
eine Liste zugreift oder nicht. Wesentlich ist nur, dass sie nach einer
gewissen Zeit anfängt zu arbeiten. Die möglichen minimalen Zeitabstände
bzw. die Auflösung der Zeitangaben musst Du dann bei der Periode des
Timers berücksichtigen.
C. Resourcen - verhindern des gleichzeitigen Setzens und Lesens
1. OK. Das ist soweit einfach. Man darf nicht gleichzeitig lesen und
schreiben.
Das Problem hast Du, wenn Du der obigen Beschreibung folgst sozusagen
nebenbei erledigt, in dem Du diese Funktionen, die auf die Resourcen
(die Ports) zugreifen, nicht im Interrupt sondern aufgrund des Flags im
Hauptprogramm abarbeitest.
Das gilt aber nur, falls diese Funktionen nicht unterteilt wurden, weil
sie sonst zeitlich zu lang geworden wären.
Dann musst Du ein Flag verwenden, ein Lock. Das wird zunächst geprüft ob
es nicht schon gesetzt ist. Falls nicht, wird es gesetzt und die
Resource kann verwendet werden. Fall es doch gesetzt ist, macht die
Funktion erstmal garnichts.
Ein kniffliges Detail dabei ist, ob die Timer-Listen-Funktionen (falls
sie denn auf diese gemeinsamen Resourcen überhaupt zugreifen) eigentlich
zwingend so und so viel mal während eines Zeitraum, wenn auch mit
möglichen Verzögerungen aufgerufen werden sollen oder ob es egal ist,
wenn eine Funktion mal nicht aufgerufen wird. Allerdings ergibt sich das
erst aus den Überlegungen über die Zeitdauer der Funktionen (und die
wird sich real erst aus verschiedenen Zeitnahmen der Einzelfunktionen)
ergeben.
Auf die beschriebene Weise aber umgehst Du das Problem, das faktisch
nebenläufige Prozesse auftreten, die auf das Lock-Flag zugreifen. Da das
auf die beschriebene Weise nur im Hauptprogramm geschieht, kann nichts
passieren. Falls das so für Dich nicht geht, müsste man das mal einzeln
besprechen.
OK. Langer Sermon.
Du siehst jedenfalls, dass das Alles nicht wirklich trivial ist und das
einem das nicht so in den Schoß fällt. Das musste ich auch erst alles
lesen und ausprobieren.
Mein Auge fällt da gerade noch einmal auf folgende Sätze von Dir.
> Leider weiß ich noch nicht genau wie ich das mache. <- bin> da auf den Beta Beginner Level :) ...> aber Interessant wäre ob es ggf. andere "Anfänger-Möglichkeiten" gibt...
Ich bezweifle, dass es so eine Anfängermöglichkeit gibt. Das von mir
beschriebene ist allerdings durch bestimmte Spezifikationen (die ich
oben auch genannt habe) in der Weise beschränkt, dass es recht einfach
zu verstehen und zu realisieren sein müsste.
Es gibt vielleicht eineb Satz von Funktionen und Datenstrukturen (ein
Framework - eine Library), welche die von mir beschriebene
Vorgehensweise in der Schreibweise vereinfacht. Aber sowas ist mir nicht
bekannt.
Es scheint mir auch fraglich ob sowas veröffentlicht würde, denn im
Detail gibt es dann meist soviele Unterschiede in den Anforderungen,
dass es entweder garnicht passt oder dass der Overhead bei der
Ausführung oder die Einarbeitung nicht lohnen würde.
Aber ganz und gar ausschliessen würde ich das auch nicht.
Das Ganze ist aber de-fakto nur das Ergebnis vieler trivialer
Einzelüberlegungen.
Wenn Du Dir Zeit nimmst, das mal nachzuvollziehen bzw. vielleicht meinem
obigen Rat folgst und die einzelnen Aspekte selbst erforschst, wirst Du
hinterher denken: Das ist Alles? Ist ja simpel!
Ist es auch, aber die Wirkung ist enorm, weil man sich das als Anfänger
sehr kompliziert denkt und garnicht weiß wo anfangen! Denk nur: Viele
einzelne Funktionen werden periodisch, mit unterschiedlichen Perioden
und zusammen mit befohlenen Funktionen ausgeführt und alles klappt wie
am Schnürchen. Also ich finde das bemerkenswert.
Naja. Hoffe Dir ist ein wenig geholfen.
@ Hans
Da ist mir doch leider ein Fehler unterlaufen. Sorry.
Es hiess oben:
"Das geht, in dem Du einen Timer nimmst, der periodisch einen Interrupt
auslöst. Der Zeitabstand ist so gewählt, dass die Timer-Periode ein
Vielfaches aller denkbaren Zeiten der jeweiligen Liste ist."
Das ist falsch.
Die Timerperiode muss so gewählt werden, dass alle möglichen Perioden
der Funktionen als ganzzahliges Vielfaches dieser Periode ausgedrückt
werden können.
Z.B.
Funktion 1 soll in 30ms Periode abgearbeitet werden.
Funktion 2 in 25ms Periode.
Ein gemeinsamer Teiler ist 5ms. Also Funktion 1 jeweils nach 6 und
Funktion 2 jeweils nach 5 Perioden von 5ms.
OK. Jetzt mache ich aber mit meinem eigenen Kram weiter.
Hans schrieb:> gleichzeitig weiß ich auch nicht wie genau ich verhinder, dass> gleichzeitig gelesen und geschrieben wird> => also wenn ich jetzt meinen 2. Thread starte, der z.B. alle Sekunden> nen Port an und ausschaltet darf "genau" in dieser Zeit (beim schalten)> <- (zeitraum von n Millisekunden) der selbe Port nicht gelesen werden...> => meine Idee war es also ein fifo file anzulegen in dem ein "HALT"> steht und sozusagen der "Reader" für n Millisekunden ausgebremst wird...>> Was haltet ihr von meinen "Gedankengängen"? Ist das zu Kompliziert oder> zu Einfach gedacht?
Die Lösungen, die Du suchst, liegen üblicherweise auf der Ebene des
Betriebssystems. Stichworte:
-Threads
-Interprozesskommunikation
-Semaphoren bzw. Mutual Exclusion