Hallo, ich benutze Microcontroller, um damit Maschinensteuerungen zu realisieren. Um das komplett in C zu machen, fehlt mir eine Implementierung von Monoflops, die in Speicherprogrammierbaren Steuerungen (SPS) unter dem Namen "Timer" kennt. Daher behelfe ich mir bisher mit der grafischen Programmieroberfläche Parsic [1], wo es so etwas schon gibt. Kennt jemand ein C-Programm(modul), indem etwa 100 solche Monoflops/Timer bereitgestellt werden?. Diese müssen auch zeitlich überlappend laufen können. Daher funktioniert die "delay"-Lösung nicht, die einfach nur eine No-Operation Warteschleife produziert. Ich weiß, dass man für die Lösung meines Problems Ticks zählen soll. Aber ich habe noch keine funktionierende Implementation gesehen. Die Timer, von denen ich hier spreche, sind nicht zu verwechseln mit dem Hardware-"Timer" in Microcontrollern. [1] http://www.parsicitalia.it/development-tools-compilers-visual-parsic-v4.html
Eine normale SPS hat auch keine 100 Hardwaretimer. Sowas wird in Software gelöst. Bei einer SPS hat man ja meist eh eher langsame Timer. Da benutzt du einen Hardwaretimer der z.B. einen 10ms oder 100ms Takt erzeugt und damit läßt du deine Software Timer zählen.
Thomas der Bastler schrieb: > Gibt es für AVRs auch solche grafischen Programieroberfläche , wie das > erwähnte Parsic Programm ? Ja: http://microsps.com/ Aber wieso man sich das antun will? In C ist es doch viel komfortabler als mit diesen Klötzchen zu speilen.
Einfache Implementierung mit Polling:
1 | // MODULE: SwTimer.c
|
2 | static uint32 currentTime = 0u; |
3 | |
4 | void SwTimerHandler(void) |
5 | {
|
6 | // update every X ms
|
7 | currentTime += X; |
8 | }
|
9 | |
10 | uint32 SwTimerNow() |
11 | {
|
12 | return currentTime; |
13 | }
|
14 | |
15 | bool SwTimerPassed(uint32 timeStamp, uint32 delay) |
16 | {
|
17 | return (currentTime - timeStamp) > delay; |
18 | }
|
Benutzung:
1 | uint32 time; |
2 | |
3 | // get "now"
|
4 | time = SwTimerNow(); |
5 | |
6 | // check if 1000ms have passed since setting time
|
7 | if(SwTimerPassed(time, 1000) |
8 | {
|
9 | // Yes! At least 1000ms have passed
|
10 | }
|
Jetzt kannst du so viele "Timer" aufsetzen wie an RAM zur Verfügung steht :-)
Picer schrieb: > Eine normale SPS hat auch keine 100 Hardwaretimer. > Sowas wird in Software gelöst. Genau diese Software suche ich. > Bei einer SPS hat man ja meist eh eher langsame Timer. > Da benutzt du einen Hardwaretimer der z.B. einen 10ms oder 100ms Takt > erzeugt und damit läßt du deine Software Timer zählen. Weiß ich, hatte ich oben schon beschrieben. Diesen 10ms oder 100ms Takt nennt man Ticks. Obwohl dieser Lösungsansatz vielfach beschrieben wird, habe ich noch keine funktionierende Implementation gesehen. Entweder liegt der Teufel im Detail oder ich finde eine implementierte Lösung einfach nicht. Kennt jemand eine solche?
Eric B. schrieb: > Benutzung:
1 | >uint32 time; |
2 | >
|
3 | > // get "now" |
4 | > time = SwTimerNow(); |
So weit so gut, aber das hier muss man natürlich regelmässig wiederholen (Polling!)
1 | > // check if 1000ms have passed since setting time |
2 | > if(SwTimerPassed(time, 1000)) |
3 | > { |
4 | > // Yes! At least 1000ms have passed |
5 | > } |
:
Bearbeitet durch User
Eric B. schrieb: > Einfache Implementierung mit Polling:// MODULE: SwTimer.c Vielen Dank! Ich denke, ich verstehe. Man muss zudem noch dafür sorgen, dass currentTime = 0u immer dann ausgeführt wird, wenn der Eingang des Monoflops gesetzt wird. Nun muss aber für jedes neue Monoflop immer eine Menge Code ins Programm eingefügt werden. Bei einer SPS geht das komfortabler, weil deutlich kürzer. Gibt es auch unter C eine Möglichkeit, als Aufruf einfach nur zu schreiben (oder ähnlich): //verzögert monoflopinput{1) um 1000ms und schreibt das Ergebnis nach monoflopoutput(1) monoflopoutput(1,1000) Sobald ich weitere Monoflops brauche, schreibe ich als Aufruf nur: monoflopoutput(2,400) monoflopoutput(3,80) monoflopoutput(4,300) .... monoflopinput(x) und monoflopoutput(x) seien dabei bool.
Baltar schrieb: > Man muss zudem noch dafür sorgen, > dass > currentTime = 0u > immer dann ausgeführt wird, wenn der Eingang des Monoflops gesetzt wird. Nein, dass passt so. > Nun muss aber für jedes neue Monoflop immer eine Menge Code ins Programm > eingefügt werden. "eine Menge"? Drei Zeilen + dass was du machen willst wenn die Zeit abgelaufen ist.
:
Bearbeitet durch User
Eric B. schrieb: > Einfache Implementierung mit Polling:// MODULE: SwTimer.c > static uint32 currentTime = 0u; > > void SwTimerHandler(void) > { > // update every X ms > currentTime += X; > } > > uint32 SwTimerNow() > { > return currentTime; > } > > bool SwTimerPassed(uint32 timeStamp, uint32 delay) > { > return (currentTime - timeStamp) > delay; > } > > Benutzung:uint32 time; > > // get "now" > time = SwTimerNow(); > > // check if 1000ms have passed since setting time > if(SwTimerPassed(time, 1000) > { > // Yes! At least 1000ms have passed > } > Jetzt kannst du so viele "Timer" aufsetzen wie an RAM zur Verfügung > steht :-) O.K., offensichtlich verstehe ich das Programm doch nicht. Möglicherweise fehlt mir die Kenntnis eines Konzeptes der Sprache C, das zum Verständnis notwendig wäre. Könnte mir jemand den genauen Ablauf mit zwei Sotwaretimern/Monoflops erklären, die sich zeitlich überlappen? Sagen wir, der erste Timer verzögert um 1000 msec und der zweite um 1200ms. Der Zufall will es, dass der zweite Timer 400ms nach dem ersten gestartet wird. Dann hätten wir eine zeitliche Überlappung. Irgendwo muss es doch dann zwei bool Variable geben, welche die Eingänge und zwei andere bool Variable, welche die Ausgänge darstellen. Im Code erkenne ich als Ausgang "SwTimerPassed". Stimmt das? Aber wo ist der Eingang?
Baltar schrieb: > Im Code erkenne ich als Ausgang "SwTimerPassed". Stimmt das? Mit SwTimerPassed(time, 1000) kannst du abfragen, ob seit dem Zeitpunkt time 1000 ms vergangen sind, gestartet wird das indem du time auf den aktuelle Zeit setzt. Wenn es dir besser gefällt kannst du das auch noch in eine Funktion packen.
1 | // Untested
|
2 | |
3 | |
4 | // Once
|
5 | typedef struct |
6 | {
|
7 | uint32_t start_time_; |
8 | uint32_t delay_; |
9 | } Monoflop; |
10 | |
11 | Monoflop monoflops[NUMBER_OF_MONOFLOPS]; |
12 | |
13 | |
14 | void swTimerHandler(void) |
15 | {
|
16 | // update every X ms
|
17 | current_time += X; |
18 | }
|
19 | |
20 | uint32 swTimerNow() |
21 | {
|
22 | return current_time; |
23 | }
|
24 | |
25 | void startMonoflop(Monoflop *monof, unit32_t delay) |
26 | {
|
27 | monof->delay_ = delay; |
28 | monof->start_time_ = swTimerNow(); |
29 | }
|
30 | |
31 | uint8_t monoflopTimePassed(Monoflop *monof) |
32 | {
|
33 | if(monof->delay_ == 0) |
34 | return 1; |
35 | |
36 | if((swTimerNow() - monof->start_time_) > monof->delay_) |
37 | {
|
38 | monof->start_time_ = 0; |
39 | return 1; |
40 | }
|
41 | return 0; |
42 | }
|
43 | |
44 | |
45 | |
46 | |
47 | // Usage
|
48 | {
|
49 | |
50 | // Start Monoflop 0 for 100ms
|
51 | startMonoflop(&monoflops[0], 100); |
52 | |
53 | |
54 | //.....
|
55 | |
56 | // If time is passed
|
57 | if(monoflopTimePassed(&monoflops[0]) |
58 | {
|
59 | |
60 | }
|
61 | |
62 | }
|
:
Bearbeitet durch User
O.T. Thomas der Bastler frog: > Gibt es für AVRs auch solche grafischen Programieroberfläche , wie das > erwähnte Parsic Programm ? Great Cow Basic gibt es mit wahlweise grafischer Eingabe für AVR und PIC http://gcbasic.sourceforge.net/ MfG Paul
Hallo Max, ich würde vorschlagen, es noch etwas einfacher zu lösen, z.B. mit einem einfachen Zahlen-Array (statt Structs):
1 | u32 monoflops[NUMBER_OF_MONOFLOPS]; |
und zwei Funktionen:
1 | void set_timer(u8 select, u32 duration) |
2 | { |
3 | monoflops[select] = systemtime + duration; |
4 | } |
5 | |
6 | bool check_timer(u8 select) |
7 | { |
8 | if ((s32)(monoflops[select] - systemtime) <= 0) |
9 | return TRUE; |
10 | else |
11 | return FALSE; |
12 | } |
:
Bearbeitet durch User
Bin zwar kein Basic-Fan, aber das Ding kann ja zumindest Ausdrücke, die komplexer sind als nur ein ASM-Statement. Noch weniger Grund die hier gehätschelte Basic-Variante zu --kaufen-- .
Thomas Elger schrieb: :u32 > monoflops[NUMBER_OF_MONOFLOPS]; > > und zwei Funktionen:void set_timer(u8 select, u32 duration) > { > monoflops[select] = systemtime + duration; > } > > bool check_timer(u8 select) > { > if ((s32)(monoflops[select] - systemtime) <= 0) > return TRUE; > else > return FALSE; > } Sieht ja schonmal deutlich übersichtlicher aus. Dennoch verstehe ich es leider nicht. Wo wird denn der Variable "systemtime" ein Wert zugeordnet?
Baltar schrieb: > Sieht ja schonmal deutlich übersichtlicher aus. Dennoch verstehe > ich es leider nicht. Wo wird denn der Variable "systemtime" ein > Wert zugeordnet? In der natürlich notwendigen Interrupt-Routine des (Hardware-)Timers. "systemtime" ist, wie der Name suggeriert, die Systemzeit, die also z.B. jede Millisekunde um eins erhöht wird.
Baltar schrieb: > Wo wird denn der Variable "systemtime" ein Wert > zugeordnet? Normalerweise in der Timer ISR.
Possetitjel schrieb: >> Sieht ja schonmal deutlich übersichtlicher aus. Dennoch verstehe >> ich es leider nicht. Wo wird denn der Variable "systemtime" ein >> Wert zugeordnet? > > In der natürlich notwendigen Interrupt-Routine des (Hardware-)Timers. > "systemtime" ist, wie der Name suggeriert, die Systemzeit, die also > z.B. jede Millisekunde um eins erhöht wird. O.K. verstanden. Mir war nicht klar, dass Systemzeit ein etablierter Begriff ist. Er ist mir noch nie über den Weg gelaufen.
Korrekt - "systemtime" muss natürlich irgendwo extern auf dem Laufenden gehalten werden (am Besten natürlich in einer ISR, ausser man hat den Luxus eines entsprechenden Hardware-Systemtimers). Vielleicht hätte ich noch anmerken sollen, daß der Code natürlich kein komplettes, 1:1 verwendbares C-Programm sein soll, sondern nur zur Verdeutlichung des Funktionsprinzips dienen soll. "bool" z.B. ist ja auch kein Standard "C"-Datentyp, macht aber die Natur des Rückgabewertes der Funktion (hoffentlich) einigermassen deutlich...
:
Bearbeitet durch User
Thomas der Bastler schrieb: > Gibt es für AVRs auch solche grafischen Programieroberfläche , wie > das > erwähnte Parsic Programm ? Das Einzigartige an Parsic ist, dass die im Funktionsschaltplan vergebenen Namen für Eingänge und Ausgänge eines Funktionsblockes (z.B. Zähler, Gatter, Monoflop etc) auch im "compilierten" Assemblerprogramm diese Namen als Name der Speicherzelle im RAM behalten. Außerdem ist derjenige Assemblercode, der einen Funktionsblock abarbeitet, mit dem dafür im Funktionsschaltplan vergebenen Namen gekennzeichnet. Das versetzt einen in die Lage, mit Hilfe einer Hardware (Microchip ICD3) und PC-Software (Microchip MPLABX) während des Abarbeiten des Programmes im Controller dieses live (auch im Einzelschritt) zu verfolgen, in jede Variable hineinzusehen, ein Software-Oszilloskop anzuschließen und vieles Andere mehr und so live zu debuggen. Ich habe noch keine andere grafische Programmieroberfläche gesehen, die das leistet. Von daher wäre Deine Frage aus meiner Sicht mit "Nein" zu beantworten.
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.