Tmicrotimer - timer à la microseconde !

Soyez le premier à donner votre avis sur cette source.

Vue 5 470 fois - Téléchargée 736 fois

Description

Bonsoir !
Voici maintenant mon dernier né, le TMicroTimer - c'est-à-dire un Timer qui peut gérer une précision de l'ordre de la microseconde (1/1000000 de seconde !). Bien sûr, ce code est ici seulement pour un but pédagogique - nous n'arriverons jamais à une telle précision avec les machines d'aujourdhui (le hardware ne suit pas le software, malheureusement ...), et de toute façon d'autres s'en chargeront volontiers.
Donc voilà, en gros c'est une démonstration de composant associé à un thread : leçon de thread, de propriétés, de composant, et un rapide briefing basique des pointeurs (2 ou 3 lignes de code traitant de çela !).

Bref, j'espère que vous apprécierez.

Je tiens quand même à préciser qu'il ne faut pas changer la priorité du thread ! Si vous la mettez au plus haut, votre système plantera (enfin le mien a répondu comme ça ...).
Ce timer peut être utilisé pour des intervalles genre 1 seconde moins 100 microsecondes, mais sera presque inutile pour des intervalles de 1 à 100 microsecondes - il faut savoir qu'on est pas très loin du temps d'un cycle machine avec 1 microseconde - le temps d'executer tout le code du thread, de voir les conditions, de passer à l'itération suivante de la boucle principale, on a déjà sauté au moins 5 microsecondes :'(
Mais bon, il faut faire avec !
Et voilà !
J'ai inclus un petit exemple sous la forme d'un tutorial, que les débutants (et les autres aussi !) pourront lire pour savoir comment utiliser mon composant, et ce qu'il offre.
Si vous avez des problèmes "d'écran noir", allez dans le fichier projet du tutorial et supprimez la ligne {$R WindowsXP.RES}.

Voilà, ça sera tout !
Attention à la priorité je ne rigole pas avec ça, un système qui se plante ça fait mal ... le mien a réussi à se débloquer après une dizaine de minutes (quand le CPU a trouvé le temps de mettre un peu de cycles sur Delphi, réinitialiser le programme ^^).

Bonne programmation à tous au fait !

Source / Exemple :


// In the Zip !

Conclusion :


Bon, tous remarques, commentaires, critiques, conseils, etc ... ici ;)

Cordialement, Bacterius !

PS : Codé sous Delphi 6 Personal Edition !

Codes Sources

A voir également

Ajouter un commentaire

Commentaires

Messages postés
6
Date d'inscription
mardi 29 décembre 2009
Statut
Membre
Dernière intervention
31 décembre 2009

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.
Messages postés
190
Date d'inscription
mercredi 3 septembre 2008
Statut
Membre
Dernière intervention
17 janvier 2009

Bon Composant !!
BY OFFLAKE
Messages postés
3793
Date d'inscription
samedi 22 décembre 2007
Statut
Membre
Dernière intervention
3 juin 2016
8
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 !
Messages postés
400
Date d'inscription
samedi 6 août 2005
Statut
Membre
Dernière intervention
11 février 2016
1
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)
Messages postés
992
Date d'inscription
jeudi 2 novembre 2000
Statut
Membre
Dernière intervention
24 avril 2013
2
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 ...
Afficher les 11 commentaires

Vous n'êtes pas encore membre ?

inscrivez-vous, c'est gratuit et ça prend moins d'une minute !

Les membres obtiennent plus de réponses que les utilisateurs anonymes.

Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.

Le fait d'être membre vous permet d'avoir des options supplémentaires.