IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Langage Delphi Discussion :

Acquisition d'un signal en fréquence


Sujet :

Langage Delphi

  1. #1
    Nouveau Candidat au Club
    Profil pro
    Inscrit en
    Mai 2008
    Messages
    15
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2008
    Messages : 15
    Points : 1
    Points
    1
    Par défaut Acquisition d'un signal en fréquence
    Bonjour, étant débutant en programmation et ayant un programme sur Delphi 6.0 Perso a realiser pour mon BTS electrotechnique j'aurais une petite question a vous poser... Voila le probleme:

    Je souhaite à l'aide d'une entrée "tout ou rien" de ma carte d'acquisition K8000 de chez Velleman récuperer le signal de sorti d'un anémometre (il fonctionne comme un hacheur et j'obtien donc en sorti un signal crénaux...). Ma carte me permet donc de savoir, a un temps donnée, si l'etat de cette entrée est a "1" ou a "0", ms comment faire pour ne prendre en compte que les changement d'etat et ainsi au lieu d'avoir l'information "1" ou "0" obtenir le nombre de changement par seconde de cet état...
    Et etant donné que pour 4 periode (soit 8 changement d'etat) ma vitesse correspond a 1m/s comment faire pour recuperer cette information et permettre d'afficher (a l'aide dun Label) la vitesse du vent mesuré grace à l'anémometre?

    Merci d'avance

  2. #2
    Expert éminent sénior
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    13 459
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Seine Saint Denis (Île de France)

    Informations professionnelles :
    Activité : Développeur C++\Delphi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2006
    Messages : 13 459
    Points : 24 873
    Points
    24 873
    Par défaut
    Alors, pour une mesure avec précision, Windows, ce n'est pas le top, ...

    Il faut partir sur un Objet TThread (Processus Parallèle) (je te conseille de lire les tutoriels, et les sujets récents sur le forum qui y font référence, la recherche fonctionne assez bien normalement

    La partie changement de signal, soit tu dois l'a gérer manuellement, soit tu dois étudier ta carte qui normalement possède une interruption système où tu dois pouvoir brancher ton propre traitement assembleur, bon c'est la théorie, n'ayant fait qu'un DUT Info Gestion en Apprentissage, je n'ai pas cette connaissance, et je n'ai pas encore eu la chance d'aller si loin dans la technique ...

    alors, ensuite ton histoire de période, j'ai pas tout pigé ...
    Quel est la fréquence maximale mesurable ? car en toute logique, ton programme doit détecter chaque changement, ... soit ton thread tourne à 100%, c'est un peu lourd, soit tu mets un sleep et là les problèmes de fréquence arrive ...
    Aide via F1 - FAQ - Guide du développeur Delphi devant un problème - Pensez-y !
    Attention Troll Méchant !
    "Quand un homme a faim, mieux vaut lui apprendre à pêcher que de lui donner un poisson" Confucius
    Mieux vaut se taire et paraître idiot, Que l'ouvrir et de le confirmer !
    L'ignorance n'excuse pas la médiocrité !

    L'expérience, c'est le nom que chacun donne à ses erreurs. (Oscar Wilde)
    Il faut avoir le courage de se tromper et d'apprendre de ses erreurs

  3. #3
    Nouveau Candidat au Club
    Profil pro
    Inscrit en
    Mai 2008
    Messages
    15
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2008
    Messages : 15
    Points : 1
    Points
    1
    Par défaut
    la frequence max est d'envirron 300Hz soit une mesure de 75m/s et question precision, on va dire, tant que ça me donne une valeur a 2 ou 3 m/s pres c'est acceptable (faut juste que ça marche pr le jour de mon exam lol)

  4. #4
    Expert éminent sénior
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    13 459
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Seine Saint Denis (Île de France)

    Informations professionnelles :
    Activité : Développeur C++\Delphi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2006
    Messages : 13 459
    Points : 24 873
    Points
    24 873
    Par défaut
    75 ms, oui pas de soucis donc ... ça passera ... tient ce code pourrait te donner des idées, tu verras que 1 ms passer à sleep monte généralement entre 1.995 et 2.005 ms ...

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    const
      CM_SHAI_ELAPSED_TIME = WM_USER + 123;
     
    type
      TTestForm = class(TForm)
        LabelElapsedTime: TLabel;
        MemoElapsedTimes: TMemo;
        ButtonTestSleep: TButton;
        procedure ButtonTestSleepClick(Sender: TObject);
      private
        { Déclarations privées }
        procedure ElapsedTimeMessage(var Msg: TMessage); message CM_SHAI_ELAPSED_TIME;
      public
        { Déclarations publiques }
      end;
     
      TSleepThread = class(TThread)
      private
        FHandleReceiver: Cardinal;
      public
        constructor Create(CreateSuspended: Boolean; AHandleReceiver: Cardinal);
        procedure Execute; override;
      end;
     
    { TSleepThread }
     
    constructor TSleepThread.Create(CreateSuspended: Boolean; AHandleReceiver: Cardinal);
    begin
      // Le Create est executé encore dans le Thread Appelant seul Execute est dans le Thread Fils
      inherited Create(True);
     
      FHandleReceiver := AHandleReceiver;
      FreeOnTerminate := True;
     
      if not CreateSuspended then
        Resume();
    end;
     
    procedure TSleepThread.Execute;
    var
      TickPerSec, BeginTime, EndTime: Int64;
      TickPerMicroSec: Int64;
      ElapsedTime: TULargeInteger; // Micro
    begin
      QueryPerformanceFrequency(TickPerSec);
      TickPerMicroSec := TickPerSec div 1000000;
      while not Terminated do
      begin
        QueryPerformanceCounter(BeginTime);
        Sleep(1);
        QueryPerformanceCounter(EndTime);
        ElapsedTime.QuadPart := Round((EndTime - BeginTime) / TickPerMicroSec);
        PostMessage(FHandleReceiver, CM_SHAI_ELAPSED_TIME, Integer(ElapsedTime.LowPart), Integer(ElapsedTime.HighPart));
      end;
    end;
     
    { TTestForm }
     
    procedure TTestForm.ButtonTestSleepClick(Sender: TObject);
    begin
      TSleepThread.Create(False, Self.Handle);
    end;
     
    procedure TTestForm.ElapsedTimeMessage(var Msg: TMessage);
     
      function FormatMicroSec(MicroSec: Int64): string;
      var
         Hour, Min, Sec, MilliSec: Int64;
      begin
         Hour := MicroSec div 3600000000;
         MicroSec := MicroSec mod 3600000000;
         Min := MicroSec div 60000000;
         MicroSec := MicroSec mod 60000000;
         Sec := MicroSec div 1000000;
         MicroSec := MicroSec mod 1000000;
         MilliSec := MicroSec div 1000;
         MicroSec := MicroSec mod 1000;
         Result := Format('%.2d:%.2d:%.2d:%.3d:%.3d', [Hour, Min, Sec, MilliSec, MicroSec]);
      end;
     
    var
      MicroSec: TULargeInteger;
    begin
      MicroSec.LowPart := Cardinal(Msg.WParam);
      MicroSec.HighPart := Cardinal(Msg.LParam);
      LabelElapsedTime.Caption := FormatMicroSec(MicroSec.QuadPart);
      MemoElapsedTimes.Lines.Add(LabelElapsedTime.Caption);
    end;
    Donc, en gros à chaque milli, tu lit ta carte, et quelques part, tu gère un chrono pour connaitre le temps et tu compte les bits ...
    Aide via F1 - FAQ - Guide du développeur Delphi devant un problème - Pensez-y !
    Attention Troll Méchant !
    "Quand un homme a faim, mieux vaut lui apprendre à pêcher que de lui donner un poisson" Confucius
    Mieux vaut se taire et paraître idiot, Que l'ouvrir et de le confirmer !
    L'ignorance n'excuse pas la médiocrité !

    L'expérience, c'est le nom que chacun donne à ses erreurs. (Oscar Wilde)
    Il faut avoir le courage de se tromper et d'apprendre de ses erreurs

  5. #5
    Expert confirmé

    Profil pro
    Leader Technique
    Inscrit en
    Juin 2005
    Messages
    1 756
    Détails du profil
    Informations personnelles :
    Âge : 46
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Leader Technique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juin 2005
    Messages : 1 756
    Points : 4 170
    Points
    4 170
    Par défaut
    Je souhaite à l'aide d'une entrée "tout ou rien" de ma carte d'acquisition K8000 de chez Velleman récuperer le signal de sorti d'un anémometre
    D'après ce que je vois de la doc de ta carte, c'est une carte qui se branche sur le port parallèle et qui détourne certains signaux de l'imprimante pour en faire un bus série. Il y a en plus un protocole de dialogue entre le PC et la carte pour lire l'état de ses entrées.
    Autant dire que tu peux oublier les interruptions. Dommage c'est pourtant la solution pour ce type de problème.

    Il ne reste donc plus que le pooling de la carte.

    75 ms, oui pas de soucis donc ... ça passera ...
    Je n'en suis pas si sûr, pas si la mesure est faite avec le thread. Je m'explique :
    Nok3dZion, on t'a sûrement expliqué en cours que lorsqu'on échantillonne un signal, il faut que la fréquence d'échantillonnage soit au moins égale au double de la fréquence du signal échantillonné (théorème de Nyquist-Shannon).
    C'est facile à comprendre dans ton cas avec un signal carré. A 300 Hz ça veut dire que la période dure 3,333 ms. Donc que pendant 1,66 ms tu as un "1" et pendant 1,66 ms tu as un "0".
    Ca veut dire que tu dispose d'un laps de temps de 1,66 ms maximum pour faire la lecture de la carte.

    Si tu fais un sleep de 1ms, tu demandes à Windows de t'endormir pendant 1ms. Ca veut dire que tu lui dit : "Je n'ai rien à faire pendant 1ms, tu peux donner la main à une autre tâche". Une fois la ms écoulée, le thread devient activable, mais ça ne veut pas dire qu'il sera activé. Si un autre thread utilise le CPU, tu ne reprendra la main qu'une fois que l'autre thread voudra bien la rendre, ou que sa fenêtre d'exécution sera écoulée.

    D'ailleurs, si j'ai bien compris, ShaiLeTroll nous explique que lorsque tu demandes un sleep de 1ms, en réalité on constate qu'on ne récupère la main que 2ms plus tard... Ce qui est beaucoup trop tard. Tu vas rater un signal sur deux et donc faire des mesures complètement fausses.
    Pire encore, la fréquence de ton échantillonnage sera aléatoire, ce qui fait que tu risques de voir ta mesure varier grandement au cours du temps alors qu'il est peu probable que la vitesse du vent varie d'autant aussi vite. Dit autrement, le problème risque d'être particulièrement visible.

    Tu dois cependant pouvoir faire une mesure à peu près fiable en procédant différemment :
    Au lieu de compter les tics délivrés par l'anémometre en permanence, tu peux certainement te contenter de faire la mesure de la vitesse à intervale de temps réguliers, par exemple toutes les 10s (ou encore plus simple, lorsqu'on clique sur un bouton). Bref, tu n'as pas besoin d'effectuer la mesure en permanence.
    Donc tu peux te contenter de compter les tics uniquement pendant le temps d'une mesure.

    - Lorsque tu démarres une mesure, tu commences par demander à Windows d'élever la priorité de ton process pour limiter le risque d'être interrompu par une autre tâche, ce qui fausserait complètement la mesure. L'idéal serait de passer en priorité temps réel, qui permet d'être plus prioritaire que l'OS lui même, mais il vaut mieux éviter d'aller jusque là car ça peut perturber grandement Windows.
    - Puis tu te donnes un certain temps pour effectuer la mesure. Pendant ce temps, tu travailles en attente active, c'est à dire que tu boucles et que tu lis en permanence l'état de l'entrée sur la carte.
    - Tu détectes un changement d'état lorsque le résultat de ta lecture est différent de la lecture précédente. Ensuite à toi de voir si tu veux compter les fronts montants, les fronts descendants, voir les deux.
    - A la fin de ta mesure (durée de mesure écoulée, nombre maximal d'itérations de lecture atteind...) tu regardes le nombre de changement d'état que tu as compté ainsi que la durée effective de la mesure.
    - Enfin tu reconfigures la priorités du process pour retourner à une priorité normale.

    De cette façon, tu devrais avoir une assez bonne précision de mesure, à condition que la durée de cette dernière ne soit pas trop élevée. Car sinon, a moins d'être en priorité temps réel (et encore, je n'en suis pas sûr), Windows finira tôt ou tard par reprendre la main sur ta tâche et ta mesure sera faussée...


    En général, lorsqu'on doit faire une acquisition d'un signal, on travaille par interruption lorsque la fréquence de ce dernier est faible (par exemple pour gérer le clavier). On passe en entrée/sortie programmée (PIO) lorsque la fréquence devient plus importante (par exemple, pour écrire sur une imprimante matricielle).
    Lorsque la fréquence du signal devient bien plus importante, on passe en logique câblée. C'est à dire qu'on fait la même chose que ce qu'on fait par code, mais qu'on câble le programme en dur dans une carte d'acquisition avec un DMA pour récupérer les données (cartes son...).

    Mais a ta place, je commencerais quand même par vérifier que la carte est capable de lire des E/S digitales avec une fréquence de 300 Hz. Parce que si elle est branchée sur le port parallèle et qu'elle simule une liaison série avec (enfin un bus I2C si j'ai bien compris), ce n'est pas garanti.
    Commence par vérifier que tu peux effectivement faire 1000 lectures en moins d'une seconde...

  6. #6
    Membre confirmé

    Inscrit en
    Novembre 2002
    Messages
    744
    Détails du profil
    Informations forums :
    Inscription : Novembre 2002
    Messages : 744
    Points : 500
    Points
    500
    Par défaut
    salut ,

    Es tu obligé d'utiliser la carte K8000 ?

    Sinon une solution consiste a utiliser l'entrée d'une carte son et traiter le signal reçu, il existe des exemples sources sur le net pour simuler un scope.
    Bye et bon code...

    Ce n'est pas tant l'aide de nos amis qui nous aide , mais notre confiance dans cette aide .

  7. #7
    Membre éprouvé
    Avatar de Montor
    Homme Profil pro
    Autre
    Inscrit en
    Avril 2008
    Messages
    879
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Autre

    Informations professionnelles :
    Activité : Autre

    Informations forums :
    Inscription : Avril 2008
    Messages : 879
    Points : 963
    Points
    963
    Par défaut
    Pour moi un simple compteur binaire comme 4040 placer entre votre pc et le gadget pilote par un quartz et remis a zéro à chaque tourne

  8. #8
    Membre confirmé

    Inscrit en
    Novembre 2002
    Messages
    744
    Détails du profil
    Informations forums :
    Inscription : Novembre 2002
    Messages : 744
    Points : 500
    Points
    500
    Par défaut
    Salut ,

    si j'ai bien compris, le but est de mesurer le nombre de changement d'état dans un temps donné ( une seconde ), donc mesurer une fréquence qui est proportionnelle à la vitesse du vent. on suppose que l'anémomètre retourne une fréquence linéaire en fonction de la vitesse du vent avec un rapport cyclique de 50%.

    Pour 4 changements d'état par seconde tu as un vent de 1m/s donc quand tu as une période de 250ms et plus le vents souffle fort, plus cette fréquence augmente.

    Donc le tout est de savoir jusqu'à quelle vitesse tu dois être capable de faire les mesures?
    Avec une période de 10 ms, on mesure un vent qui aura déjà emporté l'anémomètre .

    une fréquence d'échantillonnage de 2.5ms est donc théoriquement suffisante. une résolution de 1ms est réaliste avec la technique indiqué par ShaiLeTroll.

    il suffirait donc de mesurer le temps écoule entre deux changement d'état (une demi période) et de calculer la vitesse. le temps de lecture sera fonction de la vitesse du vent, deux seconde max si il y a pas de vent.


    1°lancer l'acquisition
    2°détecter un 1er changement d'état qui lance un timer.
    3°détecter un 2eme changement d'état qui arrête un timer.

    ou plus simple de compter le nombre d'échantillonnages entre deux changement d'état.

    salut
    Bye et bon code...

    Ce n'est pas tant l'aide de nos amis qui nous aide , mais notre confiance dans cette aide .

  9. #9
    Nouveau Candidat au Club
    Profil pro
    Inscrit en
    Mai 2008
    Messages
    15
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2008
    Messages : 15
    Points : 1
    Points
    1
    Par défaut
    Oula doucement lol, j'ai un peu du mal a suivre certaines de vos explications! Deja merci pour ces quelques reponse...

    Mais je tiens a preciser que je suis en bts electrotechnique et qu'aucun cour de programmation ne m'as été donné (a par pour la programmation d'automates type tsx... ce qui n'a evidemment rien a voir avec Delphi) et que cet exercice etant donné dans le cadre de mon examen et etant avec 15 autes personnes en cour, aucun prof n'a reelement le temps (et surtout un maitrise suffisante du logiciel ) pour pouvoir se pencher suffisament sur mon probleme pour m'aider.
    Et dans un second temps, je suis obligé d'utiliser la carte k8000 qui a pour carateristique, pour ses entrées digitale (TOR):
    Entrée opto-coupler, min 5V/5mA, max 20V/40mA
    Temps minimum de conversion pour lire 16 entrées: 800µs

    et la fonction / procedure fournie et definie avec la carte par un .dll permettant d'utiliser ces entrées est:

    ReadIOchannel (Channelno) : L'etat du canal d'entré est lu

    et cette fonction, lorsquelle est appelée par une procedure n'est active QUE lorsque cette procedure démarre et non en continu a partir du moment ou on l'active dc voila...

    Aussi une tite rectification, il y a 8 changement d'état, soit 4Hz pour 1m/s ( et non 4 changement d'état)


    Je pense que l'idée de "chronometrer" le temps pendant lequel mon entrée est a 1, le temps ou l'entrée est a 0 et apres faire le calcul:

    1/(temps a 1 + temps a 0) = Frequence

    ou directement:

    1/(4*(temps a 1 + temps a 0)) = Vitesse en m/s
    (c'est juste un idée, etant débutant, jpeu pas vraimen savoir si c'est plus facile a realiser ou non


    je ne demande pas un extreme precision, il m'est inutile de savoir si je suis a 20.3m/s ou 19.8m/s , je souhaite juste afficher (pour cet exemple) 20m/s.

    Et cette mesure ne pourras pas etre réalisé juste lorsque je le demande en appuyant sur un bouton car ce système serat étudié par les futurs eleves de BTS et il est donc demandé de pouvoir observer le changement de la vitesse pendant une acceleration par exemple et plus tard il serat peut etre envisagé de reporter ces mesure dans un tableau ou un graphique en fonction du temps...

    J'espere avoir reussi a etre assez clair et complet dans mes explication et si vous avez besoin d'autre precision, je suis la pour ça...

    Merci.

  10. #10
    Membre confirmé

    Inscrit en
    Novembre 2002
    Messages
    744
    Détails du profil
    Informations forums :
    Inscription : Novembre 2002
    Messages : 744
    Points : 500
    Points
    500
    Par défaut
    salut

    un petit bout de code qui devrai te mettre peut être sur la voie ( j'ai pas moyen d'essayer .. ) donc c'est juste pour te mettre sur la voie..

    bon courage

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
     
    unit Unit32;
     
    interface
     
    uses
      Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
      Dialogs, StdCtrls, ExtCtrls;
     
    type
      Tform32 = class(TForm)
        acquisition: TButton;
        Button1: TButton;
        Edit1: TEdit;
        Edit2: TEdit;
        Label1: TLabel;
        Label2: TLabel;
        Edit3: TEdit;
        Label3: TLabel;
        Edit4: TEdit;
        Label4: TLabel;
     
        procedure Clock(duree:integer);
        procedure acquisitionClick(Sender: TObject);
        function  AcquisitionData:real;
        procedure Button1Click(Sender: TObject);
        procedure FormClose(Sender: TObject; var Action: TCloseAction);
     
      private
        { Déclarations privées }
      public
        { Déclarations publiques }
      end;
     
    var
      form32: Tform32;
     
    implementation
     
    {$R *.dfm}
    var fini:boolean;
    //==============================================================================
    procedure Tform32.Button1Click(Sender: TObject);
    begin
       fini:=true; // quitte la lecture
    end;
    //==============================================================================
    procedure Tform32.FormClose(Sender: TObject; var Action: TCloseAction);
    begin
       fini:=true; // quitte la lecture
    end;
    //==============================================================================
    // ici je simule la lecture de ton canal
    // a remplacer par ta procedure et faire un masque au besoin si tu lis un octet plutot qu'un bit
    function ReadIOchannel(Channelno:integer):byte;
    begin
        // a remplacer , je simule la lecture d'un bit
        randomize;
        result:=random(256);
        // affiche la valeur de l'octet
        form32.edit4.text:=inttostr(result);
        // lecture du bit 0
        result:=result and ($01);
    end;
    //==============================================================================
    procedure Tform32.Clock(duree:integer);
    Var
       F : Int64 ;
       Start,Stop : Int64 ;
       R:double;
    begin
         start:=0;
         // controle si le pc possede ce type de compteur, sinon utilise le GetTickCount
         // initialise valeur de depart
         if QueryPerformanceFrequency(F) then  QueryPerformanceCounter(Start)
         else Start:=GetTickCount;
         // rentre dans une boucle d'attente
         repeat
              if QueryPerformanceFrequency(F) then  QueryPerformanceCounter(Stop)
              else
              begin
                F:=1000;
                Stop:=GetTickCount;
              end;
              R:=(Stop-Start)*1000/F; //resultat en ms
              application.ProcessMessages;
         until R>duree; // quitte au bout de 1 ms
    end;
    //==============================================================================
    function Tform32.AcquisitionData:real;
    var FinAcq        :boolean;
        CompteurActif :boolean;
        etat          :byte;
        Oldetat       :byte;
        compteur      :integer;
        NbAqc         :integer;
    begin
       Oldetat:=0;
       CompteurActif:=false;
       compteur:=0;
       NbAqc:=0;
       FinAcq:=false;
       edit3.text:='';
       repeat
          // delai de 1 ms, cette "clock" est l'echantillonage du signal
          Clock(1);
          // lecteure du canal de la carte
          etat:=ReadIOchannel(0);
     
          // controle si l'etat lus est different de l'etat precedant,
          // si l'etat change j'active ou desactive le compteur
          if Oldetat<>Etat then
          begin
              CompteurActif:=not(CompteurActif);
              // ici je desactive le compteur donc l'acquisition est finie.
              if CompteurActif=false then FinAcq:=true;
          end;
          // iniatilise l'etat precedant
          Oldetat:=etat;
          // compte le nombre d'acquisition entre deux changements d'etat ( donc le nombre de ms)
          if CompteurActif then
          begin
              inc(compteur);
              // affiche le nombre de bit a 1 concecutifs
              edit3.text:=edit3.text+inttostr(etat);
          end;
     
          // variable de controle, je quitte si l'acquisition dure plus de 1 seconde sans changement d'etat
          inc(NbAqc);
          if (CompteurActif=false) and (NbAqc=1000) then FinAcq:=true;
          application.ProcessMessages;
       until FinAcq;
     
       // la valeur retournée par cette fonction est le nombre de ms ecoulé entre
       // deux changements d'etat donc un demi periode ( si le signal a un rapport
       // cyclique de 50 !!) donc je le multipli par deux .
     
       compteur:=compteur*2;
       edit2.text:=inttostr(compteur);   
       // resultat en m/s
       result:=250/(compteur);
    end;
    //==============================================================================
    procedure Tform32.acquisitionClick(Sender: TObject);
    var V:real;
    begin
       fini:=false;
       repeat
          // delai t'attente de 500 ms entre deux lecture de frequence
          Clock(500);
          V:=AcquisitionData;
          edit1.text:=FormatFloat('###.##',v);
          application.ProcessMessages;
       until fini ;
    end;
    //==============================================================================
    end.
    Bye et bon code...

    Ce n'est pas tant l'aide de nos amis qui nous aide , mais notre confiance dans cette aide .

  11. #11
    Expert confirmé

    Profil pro
    Leader Technique
    Inscrit en
    Juin 2005
    Messages
    1 756
    Détails du profil
    Informations personnelles :
    Âge : 46
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Leader Technique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juin 2005
    Messages : 1 756
    Points : 4 170
    Points
    4 170
    Par défaut
    Oula doucement lol, j'ai un peu du mal a suivre certaines de vos explications! Deja merci pour ces quelques reponse...
    Bon, je vais essayer d'être plus claire. Windows est un OS multi-tâche, mais pas un OS temps réel. Comme le PC ne possède pas autant de processeur que de tâche à faire tourner, le ou les processeurs sont partagés entre les tâches. Par exemple, une tâche s'exécute pendant 15 ms, elle occupe alors un processeur à 100% pendant ce temps et les autres programmes ne peuvent pas s'exécuter pendant ce temps. Au bout des 15 ms, le premier programme n'a pas fini de s'exécuter. Cependant Windows n'attend pas qu'il ait terminé et décide de lui même de suspendre le programme pour qu'un autre puisse utiliser le processeur pendant un certain temps.
    Si les fenêtres d'exécution sont suffisament courtes, ça donne l'impression que tous les programmes s'exécutent en même temps.

    Il existe ensuite des fonctions spéciales dans l'OS, de synchronisation inter-processus qui permettent de modifier un peu ce comportement.
    En particulier, il arrive fréquemment qu'un programme ne puisse pas continuer son exécution parce qu'il attend quelque chose (une touche au clavier, la fin d'une lecture sur un disque...) ou tout simplement parce que le programme veut intentionnellement faire une pause.
    C'est à ça que sert la fonction Sleep. Elle permet de dire à l'OS "je veux dormir pendant x ms". Si le programme appelle cette fonction, ça permet à Windows de savoir que l'on n'a pas besoin du CPU pendant x ms et donc que le processeur est disponible pour les autres programmes. Donc au début d'un Sleep, Windows va donner le CPU à un autre programme qui en a besoin, jusqu'a ce que le Sleep soit terminé et que le processeur soit disponible.

    Le principal problème comme tu peux le voir, c'est que Windows va t'interdire de monopoliser le processeur de façon continu pendant une durée importante. Hors pendant le temps où tu n'auras pas la main pour lire l'état de la carte tu risques de rater des impulsions. Et tu n'as aucun moyen de le savoir !

    je ne demande pas un extreme precision, il m'est inutile de savoir si je suis a 20.3m/s ou 19.8m/s , je souhaite juste afficher (pour cet exemple) 20m/s.
    Le problème n'est pas là. C'est que si tu rates un changement d'état parce que windows a donner la main à un autre processus, du fait du théorème de Nyquist-Shannon, tu vas rencontrer un phénomène de repliement de spectre qui fait que tu vas croire que tu mesures une fréquence complètement différente. Imagine que tu perdes la main pendant une période et que tu rates un 0, tu auras l'impression que le 1 a duré deux fois plus longtemps. Donc tu mesureras une fréquence qui est la moitié de la fréquence réelle !

    Et malheureusement, Windows n'étant pas un OS temps réel, tu n'as aucun moyen d'être parfaitement sûr de la fréquence mesurée.

    L'idéal serait que tu n'ais pas à compter les 0 et les 1 toi-même. Donc comme le suggère delphidelphi, ce serait de mettre un compteur entre l'anénomètre et la carte (Sauf que j'essayerais plutôt d'utiliser l'anénomètre comme horlogue pour le compteur en lui faisant compter une entrée à 1).
    Ensuite, il te suffirait de lire la valeur du compteur à interval de temps régulier et tu obtiendrais facilement la fréquence en fonction de sa progression et du temps écoulé entre chaque lecture (mais attention aux overflow du compteur).

    Sinon tu peux essayer de compter les 1 ou les 0 pendant un certain temps, comme le propose petitcoucou31. Mais n'utilise pas GetTickCount pour mesurer le temps, cette fonction à une incertitude de 15 ms à chaque lecture (et ne compte que de 15 ms en 15 ms d'ailleurs).
    Cependant, je fairais plutôt la mesure de la façon suivante :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
     
    function MesureFrequence: double;
    const DUREE_MESURE = 250; // Durée de la mesure en ms.
    var
      debut, fin, freq:  int64;
      e : byte;
      old : byte;
      compteur : cardinal;
      duree : cardinal;
    begin
      QueryPerformanceFrequency(freq); // Lit la fréquence du compteur de performances.
     
      Sleep(1); // Permet de poursuivre l'exécution de la mesure au début d'une
                // fenêtre d'exécution CPU.
     
      // Pour commencer, on se synchronise sur un front montant
      // pour avoir une référence pour le début de la mesure.
      while ((ReadIOchannel(0) and $1)<>0) do
      begin
        // On attend que l'entrée soit à 0.
      end;
      while ((ReadIOchannel(0) and $1)<>1) do
      begin
        // On attend que l'entrée passe à 1.
      end;
     
      // Début de la mesure.
      compteur := 0;
     
      // On lit la date de début de la mesure.
      QueryPerformanceCounter(debut);
      old := ReadIOchannel(0);
      repeat
        e := ReadIoChannel(0);
        if (e<>old) // On vient de détecter un changement d'état.
        then inc(compteur);
     
        // On lit la date en cours.
        QueryPerformanceCounter(fin);
      until (fin-debut)*1000 div freq > DUREE_MESURE;
     
      // La durée de mesure DUREE_MESURE est écoulée.
      // Pour une meilleure précision on attend le front suivant.
      while ReadIOchannel(0) = old do
      begin
        // On attend le prochain changement d'état.
      end;
      // On lit la date en cours.
      QueryPerformanceCounter(fin);
     
      // Calcul de la durée de la mesure en ms.
      duree := (fin-debut * 1000) div freq;
     
      // Calcul de la fréquence en Hz duree est mesurée en ms.
      // Compteur contient le nombre de demi-périodes comptée pendant cette durée.
      if compteur = 0
      then result := 0
      else result := (duree*2 / compteur) / 1000;
    end;
    Cependant même de cette façon, la mesure deviendra hasardeuse lorsque la fréquence augmentera. Mais difficile de prévoir à quel moment. Comme l'a fait remarquer petitcoucou31, la fréquence de 300 Hz correspond à une vitesse très importante qui ne sera probablement jamais atteinte.

  12. #12
    Nouveau Candidat au Club
    Profil pro
    Inscrit en
    Mai 2008
    Messages
    15
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2008
    Messages : 15
    Points : 1
    Points
    1
    Par défaut
    j'vais essayer tout ça

    et encore une tite question pck jsuis pas sur... pour afficher tout ça dans un label (je prend l'exemple du prog de Franck SORIANO), je dois mettre quoi comme procedure?

    Je suis pas sur, et comme j'ai pas acces a mon prog la tout de suite, de tete j'sais pas vraiment quoi metre comme procedure pour realiser cela, donc si vous pourriez m'aider pour cela aussi....

    Merci

  13. #13
    Nouveau Candidat au Club
    Profil pro
    Inscrit en
    Mai 2008
    Messages
    15
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2008
    Messages : 15
    Points : 1
    Points
    1
    Par défaut
    J'ai donc programmé ce que Franck SORIANO m'a donné et donc Delphi m'indique un petit problème...

    while ((ReadIOchannel(2) and $1)<>0) do [j'utilise l'IO 2 ]

    => Opérateur non applicable à ce type d'opérande

    et il me place le | juste avant: <>1) do


    Donc comme je ne sais pas trop comment regler ce pb...

    Merci

  14. #14
    Membre confirmé

    Inscrit en
    Novembre 2002
    Messages
    744
    Détails du profil
    Informations forums :
    Inscription : Novembre 2002
    Messages : 744
    Points : 500
    Points
    500
    Par défaut
    Salut ,

    je suis d'accord sur le fait qu'une adaptation hard serait la meilleure des solutions , mais ce projet reste plutôt pédagogique qu'autre chose.

    Par contre si j'ai bien compris ta routine "Franc Soriano", ton but est de lire le nombre de périodes complètes dans un temps fini (250 ms) pour en déduire la fréquence. Elle ne traite pas les fréquence inférieure a 4hz. Si je ne me trompe donnera qu'une valeur "moyenne" de la variation du signal durant la mesure sur 250 ms, mais surement insignifiante vu la vitesse de variation du vent.

    Dans mon cas j'essaye de mesurer seulement la durée d'un changement d'état, donc seulement une demi période à un instant T. Cela me donne une valeur réelle ( pour autant quelle puisse l'être) à l'instant T , mais ne tiendra pas compte de variation de vitesse entre deux mesures. L'utilisateur pourrait pouvoir régler la durée en deux lecture pour accroitre la précision.

    //------

    La fonction de Franc te retourne un résultat de mesure (adapte le résultat à ta convenance).Et cette fonction doit donc se trouver dans une boucle et ton affichage juste après, un truc dans ce style

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
      repeat
          V:=MesureFrequence;
          // adaptation resultat
          edit1.text:=FormatFloat('###.##',v);
          application.ProcessMessages;
       until fini ;
    Bye et bon code...

    Ce n'est pas tant l'aide de nos amis qui nous aide , mais notre confiance dans cette aide .

  15. #15
    Expert confirmé

    Profil pro
    Leader Technique
    Inscrit en
    Juin 2005
    Messages
    1 756
    Détails du profil
    Informations personnelles :
    Âge : 46
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Leader Technique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juin 2005
    Messages : 1 756
    Points : 4 170
    Points
    4 170
    Par défaut
    Citation Envoyé par Nok3dZion Voir le message
    while ((ReadIOchannel(2) and $1)<>0) do [j'utilise l'IO 2 ]

    => Opérateur non applicable à ce type d'opérande

    et il me place le | juste avant: <>1) do
    Quel est le prototype exacte de ReadIOchannel ? Je suis repartis de l'exemple de petitcoucou31 où la fonction renvoyait un byte.
    Si tu as cette erreur, c'est sûrement que ReadIOChannel renvoie déjà un boolean.

    Dans ce cas, c'est encore plus simple :
    remplace
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    (ReadIOChannel(2) and $1)<>0
    par :

    Et :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    (ReadIoCHannel(2) and $1)<>1
    Par :
    Il faut également modifier la déclaration des variables :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    var
      e : byte;
      old : byte;
    Doit être remplacé par :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    var
      e : boolean;
      old : boolean;
    Par contre si j'ai bien compris ta routine "Franc Soriano", ton but est de lire le nombre de périodes complètes dans un temps fini (250 ms) pour en déduire la fréquence. Elle ne traite pas les fréquence inférieure a 4hz.
    Pas tout a fait. On compte le nombre de demi-periode pendant une durée normale de 250ms. La durée de 250ms est à ajuster en fonction des besoins à l'usage. L'idéal serait de la réduire à <10ms mais la fréquence sera trop faible pour ça.
    A la fin de la boucle, on attend qu'en même le changement d'état suivant, ce qui fait que de toute façon on peut mesurer une fréquence très basse.

  16. #16
    Membre régulier Avatar de user 56gt8
    Profil pro
    Inscrit en
    Avril 2006
    Messages
    86
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2006
    Messages : 86
    Points : 92
    Points
    92
    Par défaut
    Pour un capture précise, il serait également possible d'utiliser une horloge multimédia comme celle de direct sound ou de windows multimedia qui permettent d'effectuer des actions précises quelque chose comme chaque 1/44100 de seconde (comme la capture dans ce cas).
    De plus comme l'horloge serait fournie par un driver bas niveau , la précision s'en retrouverait accrue, et les calculs toujours justes.
    Petites idée au passage.

  17. #17
    Nouveau Candidat au Club
    Profil pro
    Inscrit en
    Mai 2008
    Messages
    15
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2008
    Messages : 15
    Points : 1
    Points
    1
    Par défaut
    Code :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
      repeat
          V:=MesureFrequence;
          // adaptation resultat
          edit1.text:=FormatFloat('###.##',v);
          application.ProcessMessages;
       until fini ;
    J'ai donc essayé de rajouter le code de 'ptitcoucou31' ms lorsque j'effectue le débogage, delphi m'indique differents problèmes:

    Identificateur non déclaré pour: v, fini, et edit1 (bien que j'ai mis en place ce composant)
    et il me signale aussi qu'un opérateur ou un point virgule est manquant...

    donc que dois je faire pour resoudre ces petits problèmes?




    Et dans mon programme j'ai aussi deja mis en place une trackbar afin de pouvoir regler une des sorties 0-10V de ma carte k8000. Cette trackbar est associé a un label afin de pouvoir visualiser la tension de cette sortie, j'ai donc mis le code:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    procedure TForm1.TrackBar1Change(Sender: TObject);
    begin
      OutputDACchannel (1,TrackBar1.Position);
      Label6.Caption:=inttostr(Trackbar1.Position);
    end;
    Ma sortie ayant une resolution de 64 palliers, j'ai donc réglé ma trackbar avec pour min:0 et pour max:63. Mais le problème que ça pose, c'est que Mon lable m'indique une donc une valeur variant de 0 à 63 et je desire avoir une valeur variant de 0 à 10 V (il faut donc que je réalise une division par 6.3 (je sais jsuis trop fort lol... dsl...)) mais je ne sais pas comment la réaliser... en tous cas, toutes mes tentatives on échouées...
    Et si se serait possible, j'aimerais que mon label affiche une tension au 10eme pres (xx.x) pour ne pas avoir je ne sais combien de chiffres apres la virgule...

    et encore merci

  18. #18
    Membre confirmé

    Inscrit en
    Novembre 2002
    Messages
    744
    Détails du profil
    Informations forums :
    Inscription : Novembre 2002
    Messages : 744
    Points : 500
    Points
    500
    Par défaut
    salut ,
    le petit bout de code que je t'ai envoyé et bien sur à adapter ton programme qui nous est inconnu. tu dois donc déclarer les variables a ton propre besoin

    Dans mon exemple :

    la variable "V" est de même type que la fonction de Franc Soriano et qui stocke bien le résultat de la fonction.
    Ensuite tu adaptes ou (convertis) ton résultat à ta convenance et tu affiches le résultat formaté dans un Tedit, que tu dois aussi avoir déclare dans ta fiche ...

    La méthode FormatFloat('###.##',v) permet de formater ta valeur numérique en texte ( dans ce cas deux decimale max ), mais je te laisse quand même le plaisir de faire "F1" pour savoir comment çà marche ..

    Quand a faire une division qu'est ce qui marche ??
    Bye et bon code...

    Ce n'est pas tant l'aide de nos amis qui nous aide , mais notre confiance dans cette aide .

  19. #19
    Membre habitué

    Inscrit en
    Août 2005
    Messages
    253
    Détails du profil
    Informations forums :
    Inscription : Août 2005
    Messages : 253
    Points : 197
    Points
    197

  20. #20
    Nouveau Candidat au Club
    Profil pro
    Inscrit en
    Mai 2008
    Messages
    15
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2008
    Messages : 15
    Points : 1
    Points
    1
    Par défaut
    Merci helmis mais j'ai deja utilisé ce prog pour faire tout le reste de mon programme, je bloque juste sur la partie frequence...

    petitcoucou31, c'est justement pck j'ai du mal a comprendre tout cela que je vien sur ce forum , je programme en Delphi depuis 15 jours seulement et je ne comprend pas tres bien toutes les variables... donc j'ai un peu du mal a les adapter...

    et pour la division par 6.3 , je ne sais pas ou ajouter , ni comment (parce que le programme me dit que les variable string et integer ne sont pas compatible ou alors que les variables integer et extended ne sont pas compatible c'est pour ça que j'aimerais savoir comment ajouter cette division dans ce petit bout de programme, quel genre de variable ajouter si cela est necessaire etc.

    Dsl de demander telement d'explications mais j'ai un peu du mal ...

Discussions similaires

  1. Réponses: 5
    Dernier message: 30/10/2012, 15h38
  2. Réponses: 1
    Dernier message: 02/08/2010, 13h53
  3. Réponses: 6
    Dernier message: 21/07/2008, 18h42
  4. Réponses: 4
    Dernier message: 30/05/2007, 14h42

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo