TMICROTIMER - TIMER À LA MICROSECONDE !

Nicolas___ Messages postés 992 Date d'inscription jeudi 2 novembre 2000 Statut Membre Dernière intervention 24 avril 2013 - 12 janv. 2009 à 22:12
production1ae Messages postés 6 Date d'inscription mardi 29 décembre 2009 Statut Membre Dernière intervention 31 décembre 2009 - 31 déc. 2009 à 17:28
Cette discussion concerne un article du site. Pour la consulter dans son contexte d'origine, cliquez sur le lien ci-dessous.

https://codes-sources.commentcamarche.net/source/48955-tmicrotimer-timer-a-la-microseconde

production1ae Messages postés 6 Date d'inscription mardi 29 décembre 2009 Statut Membre Dernière intervention 31 décembre 2009
31 déc. 2009 à 17:28
Bonjour Bacterius,

Merci pour ce Timer !!!

Je trouvais qu'il utilisait beaucoup le processeur et qu'il se déphasait quand l'event était long. Donc j'ai fait quelques modification et tester le tout.

Mod 1) Utilise la commande "Sleep" pour des intervals de plus de 3 milisecond.
Mod 2) Globalise qulques variable pour enlever du temps processus et aussi aider à la synchrosination.
Mod 3) Compaser si le wait dépasse du au sleep ou autre chose.
Mod 4) Remetre à 0 si le Int64 est dépasser (Peut-être très rarement mais qui sait ?)
Mod 5) Je préfère que la composante soit enregister dans system (goût personnelle !!! plus vite à trouver.)

NB J'ai utiliser un temp de 2 miliseconde entre les sleep car en bas de cela ce n'était pas stable. (Sur XP-Pro, pas vérifier sur vista ou Win 7 ni sur plusieurs ordi.)

Voici le code modifier de "MicroTimer.pas"
-----------------

{------------------------------- MICRO TIMER ----------------------------------
Ce composant s'utilise exactement comme un Timer normal, sauf que l'intervalle
est en microsecondes : donc, 1 => 1 microseconde, 1000 => 1 milliseconde, et
1000000 => 1 seconde.
Code lourdement commenté.
Auteur : Bacterius (thomas.beneteau@yahoo.fr, thomas777@live.fr).
Modifier par François Dumoulin (Custom-Soft.ca)

Pour tout Delphi inférieur à Delphi 4 : changez Int64 en Cardinal - pas trop de
conséquences.

--- AVERTISSEMENT ---

Ne mettez pas des tonnes de timers dans une application : 4 MicroTimers,
c'est un maximum !
-------------------------------------------------------------------------------}

unit MicroTimer; // MicroTimer !

interface

uses
Windows, Messages, SysUtils, Classes, Forms; // Unités requises

type
TTimerThrd = class; // Classe non définie pour l'instant de TTimerThrd (pour des raisons de compilation)

TNotifyEvent = procedure(Sender: TObject) of object; // Définition de TNotifyEvent

TMicroTimer = class(TComponent) // Notre composant Timer !!
private
{ Déclarations privées }
FEnabled: Boolean; // Champ objet Enabled
FInterval: Int64; // Champ objet Interval
FThrd: TTimerThrd; // Variable du thread timer
FOnTimer: TNotifyEvent; // Gestionnaire d'évènement OnTimer
FPriority: TThreadPriority; // Priorité du thread

procedure SetOnTimer(Value: TNotifyEvent); // Setter OnTimer
procedure SetEnabled(Value: Boolean); // Setter Enabled
procedure SetInterval(Value: Int64); // Setter Interval
procedure SetPriority(Value: TThreadPriority); // Setter Priority
protected
{ Déclarations protégées }
public
{ Déclarations publiques }
constructor Create(AOwner: TComponent); override; // Constructeur
destructor Destroy; override; // Destructeur
published
{ Déclarations publiées }
property OnTimer: TNotifyEvent read FOnTimer write SetOnTimer; // Evènement OnTimer
property Enabled: Boolean read FEnabled write SetEnabled; // Propriété Enabled
property Interval: Int64 read FInterval write SetInterval; // Propriété Interval
property Priority: TThreadPriority read FPriority write SetPriority; // Propriété Priority
procedure Timer(Sender: TObject); dynamic; // Procédure dynamique qui appelle le gestionnaire de Timer
end;

TTimerThrd = class(TThread)
private
FEnabled: Boolean; // Champ objet Enabled (strictement identique à celui de TMicroTimer)
FInterval: Cardinal; // Champ objet Interval (strictement identique à celui de TMicroTimer)
FTimer: TNotifyEvent; // Pointeur de méthode vers la procédure Timer de TMicroTimer
Frq_Base : Int64;
T_Mem : Int64;
T_Now : Int64;
Dif : Int64;
FUseSleep : Boolean;
FSleepCount : Integer;

procedure CentralControl; // Procédure principale
procedure Wait(MS: int64);
public
constructor Create(CreateSuspended: Boolean); // Constructeur du thread
protected
procedure Execute; override; // Boucle principale du thread
end;

procedure Register; // Déclaration du recensement du composant

implementation

// Je vais modifier cette fonction pour la rendre plus lisible à moi
// Je cite donc son auteur : Rylryl !!

procedure Register; // Recensement du composant
begin
RegisterComponents('System', [TMicroTimer]); // On enregistre le composant !
end;

constructor TMicroTimer.Create(AOwner: TComponent); // Création du composant
begin
inherited Create(AOwner); // On crée le TComponent qui se cache derrière mon timer :)
FInterval := 1000000; // 1 seconde d'intervalle (héhé oui c'est en microsecondes !!)
FEnabled := True; // On l'active par défaut
FThrd := TTimerThrd.Create(True); // On crée le thread
FThrd.FEnabled := True; // On fait pareil qu'avec le timer dans le thread
FThrd.FInterval := 1000000; // Pareil pour le thread - ultra-important
FThrd.FUseSleep := True; // Pour diminuer la load du processeur;
FThrd.FSleepCount := 100; // Pour diminuer la load du processeur;
FOnTimer := nil; // Même chose ...
FPriority := tpLowest; // Priorité par défaut
FThrd.FTimer := Timer; // Attention : ça devient interessant :
// on établit un lien entre la procédure du thread et celle du timer !
end;

destructor TMicroTimer.Destroy; // Destruction du composant
begin
FThrd.FEnabled := False; // On arrête le timer
FThrd.Suspend; // On arrête le thread
FThrd.Terminate; // On éteint le thread
inherited Destroy; // On détruit notre composant !
end;

procedure TMicroTimer.Timer(Sender: TObject); // Procédure qui appelle le gestionnaire d'évènement OnTimer
begin
if Assigned(FOnTimer) then FOnTimer(self);
// Si le gestionnaire est assigné alors on l'execute, sinon on ne fait rien !
end;

procedure TMicroTimer.SetOnTimer(Value: TNotifyEvent); // On définit le gestionnaire d'évènement
begin
FOnTimer := Value; // On remplace le gestionnaire d'évènement par Value
end;

procedure TMicroTimer.SetEnabled(Value: Boolean); // On définit l'état du timer
begin
if Value <> FEnabled then // Si l'état voulu est différent de celui d'avant
begin
FEnabled := Value; // On change
FThrd.FEnabled := Value; // On change aussi dans le thread
end;
end;

procedure TMicroTimer.SetInterval(Value: Int64); // On change l'intervalle du timer
Var SleepCount : Integer;
begin
// On fait Abs(Value) car Int64 est signé, et un intervalle est toujours positif !
if Abs(Value) <> FInterval then // Si l'ancien et le nouveau sont différents
begin
FInterval := Abs(Value); // On change
FThrd.FInterval := Abs(Value); // On change aussi dans le thread
if (Abs(Value) > 2999) then
Begin
SleepCount := Trunc(Abs(Value) / 10000);
if (SleepCount = 0) then
Begin
SleepCount := 1;
End;
FThrd.FSleepCount := SleepCount;
FThrd.FUseSleep := True;
End else
Begin
FThrd.FUseSleep := False;
End;
end;
end;

procedure TMicroTimer.SetPriority(Value: TThreadPriority); // Setter Priority
begin
if Value <> FPriority then
begin
FThrd.Priority := Value; // Si différent, on change la priorité
FPriority := Value; // Idem
end;
end;

{-------------------------------------------------------------------------------
------------------ FONCTIONS RELATIVES AU THREAD PERIODIQUE --------------------
-------------------------------------------------------------------------------}

constructor TTimerThrd.Create(CreateSuspended: Boolean); // Création du thread
begin
inherited Create(CreateSuspended); // On crée le thread
FreeOnTerminate := False; // On le libérera nous-même c'est plus sûr
Resume; // On lance notre thread
Priority := tpLowest; // Pour éviter que le système ne plante
// En effet, le système est tellement sollicité qu'il est capable de perdre le contrôle
// donc, la plus basse priorité (et ce n'est pas encore assez !)
end;

procedure TTimerThrd.CentralControl; // Procédure principale
begin
FTimer(self); // On execute la procédure qui est censée lancer le gestionnaire d'évènement
// En réalité on lance une procédure qui pointe vers celle citée plus haut ^^
end;

procedure TTimerThrd.Execute; // Boucle principale
begin
if QueryPerformanceFrequency(Frq_Base) then
begin
// On récupère le repère temps origine
QueryPerformanceCounter(T_Mem);
repeat // On répète l'execution du thread ...
// if (not FEnabled) or (csDesigning in Application.ComponentState) then Continue;
// On récupère l'indice fréquence du système
if FEnabled and not ((csDesigning in Application.ComponentState)) then
Begin
if QueryPerformanceFrequency(Frq_Base) then
begin
Wait(FInterval); // On attend FInterval µsecondes !
Synchronize(CentralControl);
end;
End else
Begin
Sleep(FSleepCount);
End;
until Terminated; // ... jusqu'à ce que le thread soit terminé
end;
end;

procedure TTimerThrd.Wait(MS: int64); // On fait une pause en microsecondes !!
//var
// Frq_Base, T_Mem,
// T_Now, Dif: Int64;
begin
if FUseSleep then
Begin
repeat
sleep(FSleepCount);
// On récupère le temps actuel
QueryPerformanceCounter(T_Now);
if (T_Now < T_Mem) then
Begin
T_Mem := T_Now; // Pour prévenir la mise à 0 du Int64
End;

// On compare le temps actuel au temps d'origine
Dif := (T_Now - T_Mem) * 1000000 div Frq_Base;
until Dif >= MS - 2000;
End;

repeat
// On récupère le temps actuel
QueryPerformanceCounter(T_Now);
if (T_Now < T_Mem) then
Begin
T_Mem := T_Now; // Pour prévenir la mise à 0 du Int64
End;
// On compare le temps actuel au temps d'origine
Dif := (T_Now - T_Mem) * 1000000 div Frq_Base;

until Dif >= MS; // Jusqu'à ce qu'on ai atteint notre délai voulu

if not (Dif = MS) then
Begin
T_Mem := T_Now + MS - Dif; // Pour ajuster si le delais n'est pas exact
End else
Begin
T_Mem := T_Now;
End;
end;

end.
offlake Messages postés 190 Date d'inscription mercredi 3 septembre 2008 Statut Membre Dernière intervention 17 janvier 2009
14 janv. 2009 à 13:17
Bon Composant !!
BY OFFLAKE
Bacterius Messages postés 3792 Date d'inscription samedi 22 décembre 2007 Statut Membre Dernière intervention 3 juin 2016 10
13 janv. 2009 à 19:45
Oui pour la première mise à jour j'avais vu, la propriété ne changeait pas dans l'inspecteur mais bien dans le code. La deuxième mise à jour répare ce bug ... mais peut-être que le zip n'est pas passé ?
Je revérifie dans le forum, dans le code, puis je reposte.

@Nicolas : ben dis donc ! moi c'est un Intel 1.50GHz, 512 Mo RAM ... Ca ne m'etonne pas lol.

Cordialement, Bacterius !
Francky23012301 Messages postés 400 Date d'inscription samedi 6 août 2005 Statut Membre Dernière intervention 11 février 2016 1
12 janv. 2009 à 22:48
Il y a un probleme en design time avec ton source Bacterius : on peut pas modifier la priorité du thread et je sais d'ou vient (Voir l'une des remarques que je t'ai fais dans ton thread)
Nicolas___ Messages postés 992 Date d'inscription jeudi 2 novembre 2000 Statut Membre Dernière intervention 24 avril 2013 1
12 janv. 2009 à 22:44
Ben chez moi, ca passe pas !
non je n'ai pas le pc de la nasa ! (malheuresement)
et en plus il est plus ou moins ancien : Amd 64 5000+ et 3go de ram

Par contre si le texte passe pas, c'est peut etre parce que j'ai patché ma dll de thème windows, j'ai déjà eu des problèmes à cause de ca ...
Bacterius Messages postés 3792 Date d'inscription samedi 22 décembre 2007 Statut Membre Dernière intervention 3 juin 2016 10
12 janv. 2009 à 22:38
Voilà, j'ai juste arrangé un petit bug.

Cordialement, Bacterius !
Bacterius Messages postés 3792 Date d'inscription samedi 22 décembre 2007 Statut Membre Dernière intervention 3 juin 2016 10
12 janv. 2009 à 22:35
Par contre j'ai pas mis à jour le tutorial, pour la propriété Priority :'(
Ca sera probablement pour mercredi (demain soir => beaucoup de travail :x)

Cordialement, Bacterius !
Bacterius Messages postés 3792 Date d'inscription samedi 22 décembre 2007 Statut Membre Dernière intervention 3 juin 2016 10
12 janv. 2009 à 22:33
Voilà ! Enfin quelqu'un d'accord avec moi :)
Maintenant on peut régler la priorité, mais je l'ai mis à tpLowest par défaut :)
@Nicolas : comment ça, le texte qui passe par-dessus l'ancien ? C'est un NoteBook, tout simplement ? C'est une liste de panels ?
Chez moi ça ne beug pas en tout cas :/

Cordialement, Bacterius !
Caribensila Messages postés 2527 Date d'inscription jeudi 15 janvier 2004 Statut Membre Dernière intervention 16 octobre 2019 18
12 janv. 2009 à 22:26
Pas encore regardé...

Mais tpTimeCritical est à éviter je pense. En tout cas, ça m'a toujours posé bcp de problèmes pour des traitements un peu longs.

Et sur Vista, c'était carrément le crash ! ! !
Bacterius Messages postés 3792 Date d'inscription samedi 22 décembre 2007 Statut Membre Dernière intervention 3 juin 2016 10
12 janv. 2009 à 22:15
Ah ben dis donc lol !
T'as le PC de la NASA ou quoi ?
Mais je vais le faire si tu veux ...

Cordialement, Bacterius !
Nicolas___ Messages postés 992 Date d'inscription jeudi 2 novembre 2000 Statut Membre Dernière intervention 24 avril 2013 1
12 janv. 2009 à 22:12
J'veux bien lire ton tuto mais avec le texte qui passe par dessus l'ancien , c'est assez compliqué !

Par contre rachète toi un pc , même en le mettant en tpTimeCritical avec un interval de 10 , il y a pas de problème !

D'ailleurs, je trouve qu'on devrait pouvoir choisir cette option dans ton composant : le choix de la priority

Nico
Rejoignez-nous