Timer

Für Fragen zur Programmiersprache auf welcher Lazarus aufbaut
braspi
Beiträge: 57
Registriert: So 20. Mär 2016, 16:39

Timer

Beitrag von braspi »

Frohe Ostern,

habe ein neues Problem !
Mein Timer mit einem Interval := 60000; sollte eigentlich bei jeder neuen Minute eine Reihe von Anweisungen ausführen.
Die Dauer der Anweisungen (DHT-Temperatur ermitteln, DS18B20-Temperatur ermitteln und ein paar If-Anweisungen) dauert ca. 3 bis 5 Sekunden.
Das Problem ist : Der Timer springt machmal erst in den letzten Sekunde der aktuelle Minute an. Dadurch werden meine Anweisungen nicht vollständig abgearbeitet.
Dadurch, dass eine Zeit verglichen wird, wird diese alle 4 bis 3 Minuten übersprungen. (if (s_Zeit_soll = s_Zeit_Ist) then ..)
Machmal springt der Timer bei xx.04 Minuten an und dann xx.57.
Beispiel :
OnTimer(Sender ;Tobject);
begin
Memo.Lines.Add('1 ' + TimeToStr(now));
....
.....
Memo.Lines.Add('2 ' + TimeToStr(now));
end;

-> String '2 ' xx.xx.xx wird nicht ausgegeben.

Wie kann ich das verhindern ?

Grüße (Ich hoffe es ist verständlich)
Peter

Benutzeravatar
kupferstecher
Beiträge: 422
Registriert: Do 17. Nov 2016, 11:52

Re: Timer

Beitrag von kupferstecher »

Du kannst den Zeitpunkt fuer die Berechnung am Anfang zwischenspeichern. Wenn du den Timer mit niedrigerem Intervall einstellst (z.b. alle 20sek) und auf die Minutenaenderung pruefst, kannst du dir sicher sein, dass wirklich jede Minute erfasst ist.


OnTimer(Sender ;Tobject);
var
CallTime: TDateTime;
begin
CallTime:= Now;

Memo.Lines.Add('1 ' + TimeToStr(CallTime));
....
.....
Memo.Lines.Add('2 ' + TimeToStr(CallTime));
end;

wp_xyz
Beiträge: 4889
Registriert: Fr 8. Apr 2011, 09:01

Re: Timer

Beitrag von wp_xyz »

braspi hat geschrieben:Ich hoffe es ist verständlich

Nö...

braspi hat geschrieben:Mein Timer mit einem Interval := 60000; sollte eigentlich bei jeder neuen Minute eine Reihe von Anweisungen ausführen.

"Bei jeder neuen Minute" - heißt das, dass dein Prozess mit den Minuten der Uhrzeit synchronisiert sein muss? In diesem Fall würde ich dem Timer ein erheblich kürzeres Intervall geben, je nachdem wie genau du den Minutenwechsel erwischen willst, wahrscheinlich maximal 1 Sekunde. Jedes Mal, wenn der Timer feuert, berechnest du die aktuelle Minute und vergleichst sie mit der vorigen Minute, die du beim Programmstart gespeichert hast. Wenn sich die aktuelle Minute von der vorigen Minute unterscheidet, ist der Minutenwechsel erfolgt - du speicherst die neue Minute wieder als vorige Minute ab und kannst deinen Prozess ausführen, für den du 1 Minute Zeit hast.

Hier ein paar Code-Zeilen, wie ich mir das vorstelle:

Code: Alles auswählen

var
  AktuelleMinute, VorigeMinute: Word;
 
procedure TForm1.FormCreate(Sender: TObject);
var
  h,s,s100: Word;
begin
  DecodeTime(Time(), h, VorigeMinute, s, s100);
end
 
procedure TForm1.Timer1Timer(Sender: TObject)// Intervall: 1000 ms oder kürzer
var
  t: TTime;
  h,s,s100: Word;
begin
  t := Time();
  DecodeTime(t, h, AktuelleMinute, s, s100);
  if AktuelleMinute <> VorigeMinute then begin
    VorigeMinute := AktuelleMinute;
    Memo1.Lines.Add(TimeToStr(t));   // <---- Hier würde dein Prozess aufgerufen, ich schreibe nur einen Eintrag in ein Memo
  end;
  Caption := TimeToStr(t);
end;

braspi
Beiträge: 57
Registriert: So 20. Mär 2016, 16:39

Re: Timer

Beitrag von braspi »

Danke,
ich habe es jetzt so gemacht.
Alle 20 Sekunden schaue ich nach (Intervall := 20000). Der Zeitversatz ist jetzt ca 4 Sekunden, sicher kann ich damit leben.
Bei Intervall := 60000; war der Zeitversatz zwischen 2 Sekunden und 57 Sekunden, heißt für mich im Umkehrschluss, Alles Glückssache.
    procedure Tgt.On_Timer_Min(Sender: TObject);
    var
    hh_n, mm_n, ss_n, ms_n : Word;
    hh_o, mm_o, ss_o, ms_o : Word;
    begin
    gv.t_MinNew := now;
    DecodeTime(gv.t_MinNew, hh_n, mm_n, ss_n, ms_n);
    DecodeTime(gv.t_MinOld, hh_o, mm_o, ss_o, ms_o);

    if (mm_n <> mm_o) then
    begin
    gv.t_MinOld := gv.t_MinNew; // gv sind bei mir globale Variablen
    myRun(gv.t_MinNew); // Durchlauf ca. 4 Sekunden, DHT22 und DS18B20 brauchen ihre Zeit
    end;
    end;

Schöne Grüße

sstvmaster
Beiträge: 576
Registriert: Sa 22. Okt 2016, 23:12
OS, Lazarus, FPC: W10, L 2.2.6
CPU-Target: 32+64bit
Wohnort: Dresden

Re: Timer

Beitrag von sstvmaster »

@braspi, für welches OS brauchst du den Timer?

Vielleicht wäre so was auch Interessant für dich https://forum.lazarus.freepascal.org/index.php/topic,45077.msg318567.html#msg318567 ?

mfg Maik
LG Maik

Windows 10,
- Lazarus 2.2.6 (stable) + fpc 3.2.2 (stable)
- Lazarus 2.2.7 (fixes) + fpc 3.3.1 (main/trunk)

mschnell
Beiträge: 3444
Registriert: Mo 11. Sep 2006, 10:24
OS, Lazarus, FPC: svn (Window32, Linux x64, Linux ARM (QNAP) (cross+nativ)
CPU-Target: X32 / X64 / ARMv5
Wohnort: Krefeld

Re: Timer

Beitrag von mschnell »

braspi hat geschrieben:Mein Timer mit einem Interval := 60000; sollte eigentlich bei jeder neuen Minute eine Reihe von Anweisungen ausführen.

Nö.
Nicht " bei jeder neuen Minute", sondern "möglichst bald nachdem dieser Zeitpunkt überschritten wurde". (So funktioniert TTimer.)
Und "möglichst bald" kann je nach Belastung des gesamten Rechners "beliebig" lange dauern. (Theoretisch auch mehr als eine Minute)

braspi hat geschrieben: if (s_Zeit_soll = s_Zeit_Ist) then ..

Auch nö.
Ich vermute dass die "Zeit" den Pascal datetime-Typ hat, und der ist ein Real-Wert. Real - Werte darf man (eigentlich) nie auf Gleichheit testen.

Der neue Code ist da auch nicht besser !

-Michael

creed steiger
Beiträge: 957
Registriert: Mo 11. Sep 2006, 22:56

Re: Timer

Beitrag von creed steiger »

Ohne jetzt deine Berechnungen/Anweisungen zu kennen die du ausführst, es könnte helfen die in einen seperaten
Thread auszulagern der nur von dem Timer angestossen wird.


In der Theorie sollte es ja nicht vorkommen das die Threads sich überholen ;)

Timm Thaler
Beiträge: 1224
Registriert: So 20. Mär 2016, 22:14
OS, Lazarus, FPC: Win7-64bit Laz1.9.0 FPC3.1.1 für Win, RPi, AVR embedded
CPU-Target: Raspberry Pi 3

Re: Timer

Beitrag von Timm Thaler »

creed steiger hat geschrieben:In der Theorie sollte es ja nicht vorkommen das die Threads sich überholen


In der Praxis braucht dafür nur der Sensor nicht antworten.

braspi hat geschrieben:if (mm_n <> mm_o) then


Das kann jetzt noch schiefgehen, wenn der Computer eine neue Zeit bekommt. Was aber selten vorkommen dürfte.

braspi hat geschrieben:myRun(gv.t_MinNew); // Durchlauf ca. 4 Sekunden, DHT22 und DS18B20 brauchen ihre Zeit


Das klingt nach schlechter Programmierung. Ich hoffe, Du blockierst das Programm nicht so lange.

Normalerweise macht man das so, dass man die Messung anstößt, sich ein Timeout setzt, bis wann die Messung fertig sein soll und dann die Messwerte abfragt. Oder einen Scheduler macht, der die Sensoren zyklisch durchgeht.

Ich mache es auf dem AVR z.B. so, Pseudocode:

Schleife
Zähler inc, Überlauf bei 20
wenn Zähler = 1
Sensor 1 starten, dauert 750msec
wenn Zähler = 2
Sensor 1 auslesen
wenn Zähler = 3
Sensor 2 starten
wenn Zähler = 4
Sensor 2 auslesen
Werte Sensor 2 berechnen
wenn Zähler = 5
Ausgänge nach Messwerten berechnen
Ausgänge an I2C setzen
wenn Zähler = 20
Messwerte senden
Zähler = 0
Display aktualisieren
Warte 1sec
Ende Schleife

Hat den Vorteil, egal wie lange ein Sensor braucht (da muss man eventuell mehrere Zählerschritte überspringen), das Programm läuft immer mit 1sec Taktung, das Display wird immer aktualisiert, der Nutzer hat nie den Eindruck, das Programm wäre stehengeblieben.

Die Zyklenzeit und Zyklenzahl wird natürlich den Erfordernissen angepasst, von einigen Millisekunden für Datenlogger bis Sekunden für Temperatursensoren, die nur ab und zu mal einen Wert messen.

creed steiger
Beiträge: 957
Registriert: Mo 11. Sep 2006, 22:56

Re: Timer

Beitrag von creed steiger »

Timm Thaler hat geschrieben:
In der Praxis braucht dafür nur der Sensor nicht antworten.



bei den Dallas one wire gibts das Problem bei mir nicht
(w1-gpio w1-therm aufm raspi)
fehlerhafte Werte werden halt Verworfen
bei /sys/bus/w1/devices/ auslesen gibts keinen Timeout
(und der 750ms Abfrageintervall sollte auch reichen)

mschnell
Beiträge: 3444
Registriert: Mo 11. Sep 2006, 10:24
OS, Lazarus, FPC: svn (Window32, Linux x64, Linux ARM (QNAP) (cross+nativ)
CPU-Target: X32 / X64 / ARMv5
Wohnort: Krefeld

Re: Timer

Beitrag von mschnell »

creed steiger hat geschrieben:Ohne jetzt deine Berechnungen/Anweisungen zu kennen die du ausführst, es könnte helfen die in einen seperaten
Thread auszulagern der nur von dem Timer angestossen wird.

??? Worker Threads kann man nicht mit einem TTmer anstoßen !!!
Da muss man andere Mechanismen verwenden.
-Michael
Zuletzt geändert von mschnell am Di 30. Apr 2019, 11:05, insgesamt 1-mal geändert.

mschnell
Beiträge: 3444
Registriert: Mo 11. Sep 2006, 10:24
OS, Lazarus, FPC: svn (Window32, Linux x64, Linux ARM (QNAP) (cross+nativ)
CPU-Target: X32 / X64 / ARMv5
Wohnort: Krefeld

Re: Timer

Beitrag von mschnell »

Timm Thaler hat geschrieben:Was aber selten vorkommen dürfte.

Ich halte es für eine extrem schlechte Idee, davon auszugehen, dass "NOW" in irgendeiner Weise mit TTimer synchron läutf.
-Michael

mschnell
Beiträge: 3444
Registriert: Mo 11. Sep 2006, 10:24
OS, Lazarus, FPC: svn (Window32, Linux x64, Linux ARM (QNAP) (cross+nativ)
CPU-Target: X32 / X64 / ARMv5
Wohnort: Krefeld

Re: Timer

Beitrag von mschnell »

braspi hat geschrieben:Die Dauer der Anweisungen (DHT-Temperatur ermitteln, DS18B20-Temperatur ermitteln und ein paar If-Anweisungen) dauert ca. 3 bis 5 Sekunden.

Das hört sich katastrophal an: Das darf nur ein paar Mikrosekunden dauern. Nichts, was in einem "TTimer" Event passiert, darf auf irgendetwas warten !!!
-Michael

Timm Thaler
Beiträge: 1224
Registriert: So 20. Mär 2016, 22:14
OS, Lazarus, FPC: Win7-64bit Laz1.9.0 FPC3.1.1 für Win, RPi, AVR embedded
CPU-Target: Raspberry Pi 3

Re: Timer

Beitrag von Timm Thaler »

mschnell hat geschrieben:Ich halte es für eine extrem schlechte Idee, davon auszugehen, dass "NOW" in irgendeiner Weise mit TTimer synchron läutf.


Nein, läuft es definitiv nicht.

Wenn ich bei einem Programm eine Sekundenanzeige haben will, setzte ich den Timer auf 100msec, max. 125msec. Dann laufen die Sekunden flüssig durch, mit einem Jitter von 100msec, den man nicht sieht.

Nimmt man für eine Sekundenanzeige einen Timer von 1sec, wird regelmäßig ein Wert vergessen, und dann folgen zwei Werte kurz hintereinander. Eben weil sie nicht synchron laufen. Physikalisch entspricht das einer Schwebung.

Nach Nyquist wäre für eine Sekunde ein Timer von <500msec ausreichend. Dann werden zwar alle Werte angezeigt, aber man sieht, dass die Werte nicht gleichmäßig aufeinander folgen.

Ich muss mir also überlegen, wie "genau" ich die Minute treffen will, und so muss ich meinen Timer setzen. Reicht mir ein Wert pro Minute, reichen 29sec Timer, möchte ich den Wert jede Minute mit maximal 5sec Fehler, muss ich den Timer auf 2sec setzen.

Timm Thaler
Beiträge: 1224
Registriert: So 20. Mär 2016, 22:14
OS, Lazarus, FPC: Win7-64bit Laz1.9.0 FPC3.1.1 für Win, RPi, AVR embedded
CPU-Target: Raspberry Pi 3

Re: Timer

Beitrag von Timm Thaler »

creed steiger hat geschrieben:bei den Dallas one wire gibts das Problem bei mir nicht


Weil bei 1-wire der Master den Takt vorgibt. Bei I2C kann der Slave einen Clock stretch machen, und dann muss theoretisch der Master warten, bis der Slave den Bus wieder freigibt. Damit kann ein Slave einen Bus blockieren. (Allerding ist mir noch kein I2C Chip untergekommen, der das macht, aber das Protokoll sieht es vor.)

Ich weiss nun nicht, was z.B. der Raspi an dieser Stelle macht. Arduino-Libs sind ja teilweise so bescheuert programmiert, dass sie dann wirklich den µC erstmal lahmlegen. Und ich hatte auch schon nRF24 Code für den Raspi, der beim Warten auf den Interrupt am SPI den Prozessor in die 100% Kernellast getrieben hat, was natürlich eine ganz blöde Idee ist.

mschnell
Beiträge: 3444
Registriert: Mo 11. Sep 2006, 10:24
OS, Lazarus, FPC: svn (Window32, Linux x64, Linux ARM (QNAP) (cross+nativ)
CPU-Target: X32 / X64 / ARMv5
Wohnort: Krefeld

Re: Timer

Beitrag von mschnell »

Timm Thaler hat geschrieben:Weil bei 1-wire der Master den Takt vorgibt.
"Vorgibt" schon, dann muss das Timing aber auf wenige Prozent konstant bleiben. Davon kann man bei einem User-Programm, das in einem Betriebssystem läuft i.A. nicht ausgehen.
Timm Thaler hat geschrieben:Allerding ist mir noch kein I2C Chip untergekommen, der das macht,

Hardware-Chips sind ja im Allgemeinen "beliebig" schnell und brauchen das nicht. Wenn man aber einen kleinen Prozessor als I²C Slave programmiert kann es nötig sein.
Timm Thaler hat geschrieben:Ich weiss nun nicht, was z.B. der Raspi an dieser Stelle macht.
Ich vermute der RASPi Chip hat I²C Hardware. Deshalb sollte ein Linux-Treiber das richtig machen. Das I²C-Protokoll - oder sonst irgendeinen getimeten direkten Hardware-Zugriff in einem User-Programm (das in einem Betriebssystem läuft) zu basteln, halte ich ohnehin für ziemlich daneben.
-Michael

Antworten