Forum: Mikrocontroller und Digitale Elektronik Monoflop bzw SPS-Timer als C Programm für Microcontroller


von Baltar (Gast)


Lesenswert?

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

von Picer (Gast)


Lesenswert?

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.

von Thomas D. (thomasderbastler)


Lesenswert?

Gibt es für AVRs auch solche grafischen Programieroberfläche , wie das 
erwähnte Parsic Programm ?

von (Gast) (Gast)


Lesenswert?

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.

von Eric B. (beric)


Lesenswert?

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

von Baltar (Gast)


Lesenswert?

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?

von Eric B. (beric)


Lesenswert?

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
von Baltar (Gast)


Lesenswert?

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.

von Max H. (hartl192)


Lesenswert?

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
von Peter D. (peda)


Lesenswert?


von Baltar (Gast)


Lesenswert?

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?

von Max H. (hartl192)


Lesenswert?

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
von Paul B. (paul_baumann)


Lesenswert?

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

von Thomas E. (picalic)


Lesenswert?

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
von Bastler (Gast)


Lesenswert?

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

von Baltar (Gast)


Lesenswert?

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?

von Possetitjel (Gast)


Lesenswert?

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.

von Max H. (hartl192)


Lesenswert?

Baltar schrieb:
> Wo wird denn der Variable "systemtime" ein Wert
> zugeordnet?
Normalerweise in der Timer ISR.

von Baltar (Gast)


Lesenswert?

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.

von Thomas E. (picalic)


Lesenswert?

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
von Baltar (Gast)


Lesenswert?

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