uFreq.pas

UNIT Ufreq;
{-----------------------------------------------------------------------------
  NOM DE L'UNITE : UFREQ.PAS
  BUT            : Fréquencemètre.
  AUTEURS        : Michael Amarantidis / Stéphane Claus
  DATE           : Avril 1997
 
  MODIFIE LE     : 27.05.1997  -  EDT:01
  PAR            : Michael
  RAISON         : remplacement des valeurs par des constantes lors de la
                   configuration du PIT
 
  REMARQUES      : - Le fréquencemètre n'a pu être intégré à l'objet car
                     l'utilisation d'interruptions y est interdite !
                   - Pour augmenter la vitesse des traitement, tout le code
                     utilisé pour accéder au 8254 a été recopié dans cette
                     procédure
                   - Cette unité ne peut pas fonctioner sous Delphi 2 à cause
                     des CLI et STI qui sont interceptés par NT... :(
                   - 1 Tick = 18.2 ms
 
                    - Signification des mots de commande du PIT8254:
 
 0  1  1  1  0  1  0  0  74 - Cnt 1 - R/W byte de poids faible en premier puis
 :  :  :  :  :  :  :  :       le byte de poids fort ensuite - mode 2 (Rate
 :  :  :  :  :  :  :  :       Generator) - Code binaire
 :  :  :  :  :  :  :  :
 0  1  0  0  0  1  0  0  40 - Cnt 1 - Latch - mode 0 (Interrupt on Terminal
 :  :  :  :  :  :  :  :       Count) - Code binaire
 :  :  :  :  :  :  :  :
 0  1  1  0  0  1  0  0  64 - Cnt 1 - R/W byte de poids fort uniquement -
 :  :  :  :  :  :  :  :       mode 2 (Rate Generator) - Code binaire
 :  :  :  :  :  :  :  :
 0  1  0  1  0  1  0  0  54 - Cnt 1 - R/W byte de poids faible uniquement -
 :  :  :  :  :  :  :  :       mode 2 (Rate Generator) - Code binaire
 :  :  :  :  :  :  :  :
 ¦  ¦  ¦  ¦  ¦  ¦  ¦  ¦
 ¦  ¦  ¦  ¦  +--+--+  +-- BCD -> 1 ou Binaire -> 0
 ¦  ¦  ¦  ¦        ¦
 ¦  ¦  ¦  ¦        +----- Mode : 0 = 000   1 = 001   2 = X10
 ¦  ¦  +--+                      3 = X11   4 = 100   5 = 101
 ¦  ¦     ¦
 ¦  ¦     +-------------- Read/Write : 00 Counter Latch Command
 ¦  ¦                                  01 R/W byte de poids faible uniquement
 ¦  ¦                                  10 R/W byte de poids fort uniquement
 ¦  ¦                                  11 R/W byte de poids faible en premier
 +--+                                     puis le byte de poids fort ensuite
    ¦
    +-------------------- Sélection du compteur : 00 = Compteur 0
                                                  01 = Compteur 1
                                                  10 = Compteur 2
                                                  11 = Read Back
 
 -----------------------------------------------------------------------------}
 
{=============================================================================}
INTERFACE   {============================================== I N T E R F A C E }
{=============================================================================}
 
USES
  SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
  Forms, Dialogs, StdCtrls, Buttons, ExtCtrls, DigDisp, UAlnEdit;
 
TYPE
  TfrmFrequencemetre = CLASS(TForm)
    digdspTitre: TDigitalDisplay;
    rgrpCompteur: TRadioGroup;
    rgrpIRQ: TRadioGroup;
    rgrpDureeEchantillonage: TRadioGroup;
    bitbtnStart: TBitBtn;
    grpbxFrequence: TGroupBox;
    bitbtnOK: TBitBtn;
    bitbtnHelp: TBitBtn;
    lblHz: TLabel;
    aedtFrequence: TAlignEdit;
    bitbtnGraph: TBitBtn;
    PROCEDURE rgrpDureeEchantillonageClick(Sender: TObject);
    PROCEDURE bitbtnStartClick(Sender: TObject);
    PROCEDURE FormActivate(Sender: TObject);
    PROCEDURE FormCloseQuery(Sender: TObject; VAR CanClose: Boolean);
    procedure bitbtnGraphClick(Sender: TObject);
  PRIVATE
    { Déclarations private }
  PUBLIC
    { Déclarations public }
  END; {CLASS}
 
 
VAR
  frmFrequencemetre: TfrmFrequencemetre;
 
 
{=============================================================================}
IMPLEMENTATION   {================================= I M P L E M E N A T I O N }
{=============================================================================}
 
 
{$R *.DFM}
 
USES
  IniFiles,
  UPrincpl,
  UGraph;
 
 
CONST
  pause0_5s      = 9;                 { Durée d'échantillonnage = 1/2 seconde }
  pause1s        = 18;                { Durée d'échantillonnage = 1 seconde   }
  pause5s        = 91;                { Durée d'échantillonnage = 5 secondes  }
  pause10s       = 182;               { Durée d'échantillonnage = 10 secondes }
  cnt1setmode2   = $74;      { Cnt 1 - R/W byte de poids faible en premier
                               puis le byte de poids fort ensuite - mode 2 (Rate
                               Generator) - Code binaire }
  cnt1latchmode0 = $40;      { Cnt 1 - Latch - mode 0 (Interrupt on Terminal
                               Count) - Code binaire }
  cnt1hautmode2  = $64;      { Cnt 1 - R/W byte de poids fort uniquement -
                               mode 2 (Rate Generator) - Code binaire }
  cnt1basmode2   = $54;      { Cnt 1 - R/W byte de poids faible uniquement -
                               mode 2 (Rate Generator) - Code binaire }
 
 
 
VAR
  OnPeutFermer : BOOLEAN;         { Peut-on fermer cette boîte de dialogue ?? }
  Int7Svg      : POINTER;   { Sauvegarde du vecteur d'interruption de l'IRQ 7 }
  Int1CSvg     : POINTER;     { Sauvegarde du vecteur d'interruption du timer }
  OldInt1CProc : PROCEDURE;          { Ancienne procédure de gestion du timer }
  Compteur     : LONGINT;                       { Compteur du nombre de ticks }
  NbInter      : LONGINT;                  { Nombre d'interruptions détectées }
  AdrCrtl      : WORD;                          { Adresse de contrôle du 8254 }
  AdrCnt1      : WORD;                        { Adresse du compteur 1 du 8254 }
 
 
{-----------------------------------------------------------------------------}
{              G E S T I O N   D E S   I N T E R R U P T I O N S              }
{-----------------------------------------------------------------------------}
 
 
PROCEDURE GetProtectedModeInt(IntNo : Byte; var Handler : Pointer); ASSEMBLER;
{-----------------------------------------------------------------------------
  BUT ........... : Procédure identique à GetIntVect, mais en mode protégé
                    sous Windows
  ENTREE ........ : IntNo = Le no du vecteur d'interrupt à récupérer
                    Handler = L'adresse du vecteur d'interruption
  SORTIE ........ : --
  EFFETS DE BORDS : --
  REMARQUE(S) ... : Appel de fonctions DPMI. Voir la Bible PC pour plus de
                    détails.
 -----------------------------------------------------------------------------}
ASM
  MOV   AX, 0204h
  MOV   BL, IntNo
  INT   31h
  LES   DI, Handler
  MOV   WORD PTR ES:[DI], DX
  MOV   WORD PTR ES:[DI+2], CX
END; {PROCEDURE GetProtectedModeInt}
 
 
FUNCTION SetProtectedModeInt(IntNo : Byte; Handler : Pointer) : Word; ASSEMBLER;
{-----------------------------------------------------------------------------
  BUT ........... : Procédure identique à GetIntVect, mais en mode protégé
                    sous Windows
  ENTREE ........ : IntNo = Le no du vecteur d'interrupt à récupérer
                    Handler = L'adresse du vecteur d'interruption
  SORTIE ........ : --
  EFFETS DE BORDS : --
  REMARQUE(S) ... : Appel de fonctions DPMI. Voir la Bible PC pour plus de
                    détails.
 -----------------------------------------------------------------------------}
ASM
  MOV   BL, IntNo
  MOV   DX, WORD PTR HANDLER
  MOV   CX, WORD PTR HANDLER + 2
  MOV   AX, 0205h
  INT   31h
  JC    @@ExitPoint
  XOR   AX, AX
  @@ExitPoint:
END; {FUNCTION SetProtectedModeInt}
 
 
{-----------------------------------------------------------------------------}
{                           A C C E S   A U X   P O R T S                     }
{-----------------------------------------------------------------------------}
 
 
FUNCTION InPortB(LePort:WORD):BYTE;
{-----------------------------------------------------------------------------
  BUT ........... : Lecture de l'état d'un port
  ENTREE ........ : LePort = Port sur lequel la valeur doit être lue
  SORTIE ........ : Valeur actuellement présente sur le port
  EFFETS DE BORDS : --
  REMARQUE(S) ... : --
 -----------------------------------------------------------------------------}
VAR
  Valeur : BYTE;
BEGIN
  ASM
    PUSH DX
    MOV  DX, LePort
    IN   AL, DX
    MOV  Valeur, AL
    POP  DX
  END; {ASM}
  InPortB := Valeur;
END; {FUNCTION InPortB}
 
 
PROCEDURE OutPortB(LePort:WORD; Value:BYTE);
{-----------------------------------------------------------------------------
  BUT ........... : Ecrit une valeur sur un port
  ENTREE ........ : LePort = Port sur lequel la valeur doit être écrite
                    Value = Valeur à écrire sur le port
  SORTIE ........ : --
  EFFETS DE BORDS : --
  REMARQUE(S) ... : --
 -----------------------------------------------------------------------------}
BEGIN
  ASM
    PUSH  DX
    MOV   DX, LePort
    MOV   AL, Value
    OUT   DX, AL
    POP   DX
  END; {ASM}
END; {PROCEDURE OutPortB}
 
 
{-----------------------------------------------------------------------------}
{                                  D I V E R S                                }
{-----------------------------------------------------------------------------}
 
 
FUNCTION Delay(DelayMS:LONGINT):BOOLEAN;
{-----------------------------------------------------------------------------
  BUT ........... : Remplace la fonction DELAY qui existait sous DOS
  ENTREE ........ : DelayMS = Délai d'attente en MS
                    bit = le no du bit à changer (entre 0 et 7)
  SORTIE ........ : TRUE s'il a été demandé à l'application de quitter
  EFFETS DE BORDS : --
  REMARQUE(S) ... : --
 -----------------------------------------------------------------------------}
VAR
  ET : LONGINT;
BEGIN
  IF DelayMS = 0 THEN BEGIN
    Result := Application.Terminated;
    Exit;
  END; {IF}
  ET := GetTickCount;
  REPEAT
    Application.ProcessMessages;
  UNTIL Application.Terminated OR (GetTickCount-ET > DelayMS);
  Result := Application.Terminated;
END;{FUNCTION Delay}
 
 
PROCEDURE TfrmFrequencemetre.FormActivate(Sender: TObject);
{-----------------------------------------------------------------------------
  BUT ........... : Initialisations lors de l'activation de la fiche
  ENTREE ........ : --
  SORTIE ........ : --
  EFFETS DE BORDS : --
  REMARQUE(S) ... : --
 -----------------------------------------------------------------------------}
BEGIN
  OnPeutFermer := TRUE;         { Oui, on peut fermer cette boîte de dialogue }
  Caption := Application.Title;                          { Affichage du titre }
  IF debugmode THEN Color := debugcolor               { Couleur de la fenêtre }
               ELSE Color := clBtnFace;
  rgrpDureeEchantillonage.ShowHint := affichehint;       { Affichage des Hint }
  bitbtnStart.ShowHint := affichehint;
  bitbtnHelp.ShowHint := affichehint;
  aedtFrequence.Text := '';             { Efface l'ancienne fréquence trouvée }
END; {PROCEDURE FormActivate}
 
 
PROCEDURE TfrmFrequencemetre.FormCloseQuery(Sender: TObject;
      VAR CanClose: Boolean);
{-----------------------------------------------------------------------------
  BUT ........... : Autorise ou non la fermeture de la fiche
  ENTREE ........ : --
  SORTIE ........ : --
  EFFETS DE BORDS : --
  REMARQUE(S) ... : - La fermeture n'est possible que si OnPeutFermer est à
                      TRUE
                    - OnPeutFermer est à FALSE lorsque le fréquencemètre est
                      en cours d'exécution
 -----------------------------------------------------------------------------}
BEGIN
  CanClose := OnPeutFermer;
END; {PROCEDURE FormCloseQuery}
 
 
{-----------------------------------------------------------------------------}
{          N O U V E L L E S   F O N C T I O N S   P O U R   L E S            }
{                         I N T E R R U P T I O N S                           }
{-----------------------------------------------------------------------------}
 
 
{$F+}
PROCEDURE NewINT7; INTERRUPT;
{-----------------------------------------------------------------------------
  BUT ........... : Nouvelle fonction appelée par l'INT 7
  ENTREE ........ : --
  SORTIE ........ : --
  EFFETS DE BORDS : A chaque appel de cette procédure, la variable globale
                    NbInter est incrémentée de 1 (soit tous les 65535 pas de
                    comptage du 8254)
  REMARQUE(S) ... : ATTENTION, cette procédure doit être déclarée comme FAR
 -----------------------------------------------------------------------------}
BEGIN
  Inc(NbInter);
  OutPortB($20, $20);                   { Quittance le contrôleur d'interrupt }
END; {PROCEDURE NewINT7}
{$F-}
 
 
{$F+}
PROCEDURE NewINT1C; INTERRUPT;
{-----------------------------------------------------------------------------
  BUT ........... : Nouvelle fonction de l'INT 1C, qui est le timer
  ENTREE ........ : --
  SORTIE ........ : --
  EFFETS DE BORDS : A chaque appel de cette procédure, la variable globale
                    Compteur est décrémentée de 1
  REMARQUE(S) ... : ATTENTION, cette procédure doit être déclarée comme FAR
 -----------------------------------------------------------------------------}
BEGIN
  Dec(Compteur);
  INLINE ( $9C );                           { Mise en ordre de la pile (PUSH) }
  OldInt1CProc;                                 { Execute l'ancienne fonction }
END; {PROCEDURE NewINT1C}
{$F-}
 
 
{-----------------------------------------------------------------------------}
{ L E S   C H O S E S   S E R I E U S E S   C O M M E N C E N T   I C I . . . }
{-----------------------------------------------------------------------------}
 
 
PROCEDURE TfrmFrequencemetre.rgrpDureeEchantillonageClick(Sender: TObject);
{-----------------------------------------------------------------------------
  BUT ........... : Active le bouton Start lorsqu'une des durée
                    d'échantillonage est sélectionnée
  ENTREE ........ : --
  SORTIE ........ : --
  EFFETS DE BORDS : --
  REMARQUE(S) ... : --
 -----------------------------------------------------------------------------}
BEGIN
  bitbtnStart.Enabled := TRUE;
END; {PROCEDURE rgrpDureeEchantillonageClick}
 
 
PROCEDURE TfrmFrequencemetre.bitbtnStartClick(Sender: TObject);
{-----------------------------------------------------------------------------
  BUT ........... : Démarre le fréquencemétre...
  ENTREE ........ : --
  SORTIE ........ : --
  EFFETS DE BORDS : Voir les procédures NewINT7 et NewINT1C appelées par cette
                    procédure
  REMARQUE(S) ... : - Le vecteur d'interruption de l'IRQ 7 se trouve à:
                      7 + 8 = 15 = $0F et celui du du timer se trouve à 1C
                      (+ d'infos dans la bible PC)
 -----------------------------------------------------------------------------}
VAR
  VieuxMasque  : BYTE;                                { Ancien masque du 8259 }
  Masque       : BYTE;     { Nouveau masque du 8259 (autorise les interrupts) }
  Duree        : INTEGER; { Nombre de ticks qu'il faut attendre => soit la
                                                    durée de l'échantillonage }
  NbCycles     : LONGINT;                  { Nombre de cycles d'horloge total }
  OctetHaut,              { Octets haut/bas pour la lecture au vol à la fin.. }
  OctetBas     : BYTE;                                { ..de l'échantillonage }
  MotLectVol   : WORD;
  Frequence    : DOUBLE;
  FrequenceReelle : DOUBLE;
  Precision    : REAL;
  FicINI       : TIniFile;          { Objet permetant de lire un fichier .INI }
  SectionINI   : STRING;
  NombrePoints : INTEGER;           { Nombre de points sauvés dans le fichier }
 
BEGIN
  aedtFrequence.Text := '';             { Efface l'ancienne fréquence trouvée }
 
 
  {~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Simulation ! }
  IF debugmode THEN BEGIN
    { Désactive tous les boutons: il ne faut pas exécuter 2 fois cette
      procédure !!! et il faut diminuer les perturbations provoquées par
      l'exécution d'un autre programme (WinHelp par exemple) }
    bitbtnStart.Enabled := FALSE;
    bitbtnOK.Enabled := FALSE;
    bitbtnHelp.Enabled := FALSE;
    bitbtnGraph.Enabled := FALSE;
 
    { Pause pour simuler le temps d'échantillonnage }
    CASE rgrpDureeEchantillonage.ItemIndex OF
      0 : Delay(00500);                                      { Attend 0.5 [s] }
      1 : Delay(01000);                                        { Attend 1 [s] }
      2 : Delay(05000);                                        { Attend 5 [s] }
      3 : Delay(10000);                                       { Attend 10 [s] }
    END; {CASE OF}
 
    aedtFrequence.Text := 'Mode debug ...';    { Affiche la fréquence (bidon) }
 
    { On peut à nouveau exécuter cette procédure, ou en d'autre termes,
        cliquer sur les boutons avec le mulot }
    bitbtnStart.Enabled := TRUE;
    bitbtnOK.Enabled := TRUE;
    bitbtnHelp.Enabled := TRUE;
    bitbtnGraph.Enabled := TRUE;
 
    Exit;                              { Quitte (brutalement) cette procédure }
 
  END; {IF}
  {~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Fin de la simulation ! }
 
 
  { Interdit la fermeture de cette fenêtre et déésactive les boutons:
      - Evite d'exécuter 2 fois cette procédure !!!
      - Diminue les perturbations provoquées par l'exécution d'un autre
        programme (WinHelp par exemple) }
  OnPeutFermer := FALSE;
  bitbtnStart.Enabled := FALSE;
  bitbtnOK.Enabled := FALSE;
  bitbtnHelp.Enabled := FALSE;
  bitbtnGraph.Enabled := FALSE;
 
  { Initialisation des variables }
  NbInter := - (maxlongint) - 1;                  { Prenons un peu de marge...}
  Compteur := maxlongint;
  AdrCrtl := frmMain.PIT8254.Adresse + 3;       { Adresse de contrôle du 8254 }
  AdrCnt1 := frmMain.PIT8254.Adresse + 1;     { Adresse du compteur 1 du 8254 }
 
 
  { Initialise la durée de l'échantillonage en fonction du choix de
      l'utilisateur, ainsi que le nom de la section du fichier INI dans
      laquelle on sauve la valeur lue }
  CASE rgrpDureeEchantillonage.ItemIndex OF
    0 : BEGIN
          Duree := pause0_5s;
          SectionINI := 'GRAPH 0.5';
        END; {CASE OF}
    1 : BEGIN
          Duree := pause1s;
          SectionINI := 'GRAPH 1.0';
        END; {CASE OF}
    2 : BEGIN
          Duree := pause5s;
          SectionINI := 'GRAPH 5.0';
        END; {CASE OF}
    3 : BEGIN
          Duree := pause10s;
          SectionINI := 'GRAPH 10.0';
        END; {CASE OF}
  END; {CASE OF}
 
  { ----- ~~~~~~~~~~ ----- Déviation des interruptions ----- ~~~~~~~~~~ ----- }
  { Sauvegarde du vecteur d'interruption de l'IRQ 7 }
  GetProtectedModeInt($0F, Int7Svg);
 
  { Sauvegarde du vecteur d'interruption du timer ainsi que de l'adresse de
      l'ancienne procédure du vecteur d'interruption }
  GetProtectedModeInt($1C, Int1CSvg);
  GetProtectedModeInt($1C, @OldInt1CProc);
 
  ASM                                     { Supprime TOUTES les interruptions }
    CLI
  END; {ASM}
 
  { Met en place le nouveau vecteur d'interruption pour l'IRQ 7 ainsi que pour
      le timer }
  SetProtectedModeInt($0F, @NewINT7);
  SetProtectedModeInt($1C, Addr(NewINT1C));
 
  ASM                                  { Autorise à nouveau les interruptions }
    STI
  END; {ASM}
 
 
  { ----- ~~~~~~~~~~ -----   Masquage des interrupts   ----- ~~~~~~~~~~ ----- }
  VieuxMasque := InPortB($21);          { Lit l'état du masque d'interruption }
  Masque := VieuxMasque AND $7F;             { Ajoute l'interrupt 7 au masque }
  OutPortB($21, Masque);                                 { Applique le masque }
  OutPortB($20, $20);                   { Quittance le contrôleur d'interrupt }
 
  { Rentre dans une section critique: Arrête le multitâche de Windows.
    Pour plus d'infos, voir le CD Technet N° 4 (International) de Micro$oft }
  ASM
    PUSH AX
    MOV AX, $1681
    INT $2F
  END; {ASM}
 
 
  Compteur := Duree + 1;                     { Pour une meilleure précision.. }
  REPEAT UNTIL Compteur = Duree;            { ..attend une interrupt du timer }
 
  NbInter := 0;                                          { On échantillonne.. }
  OutPortB(AdrCrtl, cnt1setmode2);        {Initialise le compteur 1 en mode 2 }
  OutPortB(AdrCnt1, $00);             { Charge la plus grande valeur possible }
  OutPortB(AdrCnt1, $00);
 
  REPEAT UNTIL Compteur = 0;                  { ..pendant le temps qu'il faut }
 
  ASM                                     { Supprime TOUTES les interruptions }
    CLI
  END; {ASM}
 
  { ----- ~~~~~~~~~~ -----      Lecture du reste       ----- ~~~~~~~~~~ ----- }
  OutPortB(AdrCrtl, cnt1latchmode0);                   { 1) Latch le compteur }
  OctetHaut := InPortB(AdrCnt1);                 { 2) Lecture de l'octet haut }
  OctetBas := InPortB(AdrCnt1);                   { 3) Lecture de l'octet bas }
 
  { ----- ~~~~~~~~~~ --   Remet en ordre les interrupts   -- ~~~~~~~~~~ ----- }
  OutPortB($21, VieuxMasque);      { Restaure le vieux masque d'interruptions }
  SetProtectedModeInt($0F, Int7Svg);        { Remet en place les anciennes .. }
  SetProtectedModeInt($1C, Int1CSvg);            { .. routines d'interruption }
 
  ASM                                  { Autorise à nouveau les interruptions }
    STI
  END; {ASM}
 
  { Fin de la section critique. Ne pas oublier, sinon Windows PLANTE !!  }
  ASM
    PUSH AX
    MOV AX, $1682
    INT $2F
  END; {ASM}
 
  { ----- ~~~~~~~~~~ -----     Calcule la fréquence    ----- ~~~~~~~~~~ ----- }
  MotLectVol := (256 * (256 - OctetHaut)) + (256 - OctetBas);
  NbCycles  := 65536 * NbInter;                   { Nombre de cycles complets }
  NbCycles := NbCycles + MotLectVol;                       { + lecture au vol }
 
  CASE rgrpDureeEchantillonage.ItemIndex OF
    0 : Frequence := NbCycles / 00.5;
    1 : Frequence := NbCycles / 01.0;
    2 : Frequence := NbCycles / 05.0;
    3 : Frequence := NbCycles / 10.0;
  END; {CASE OF}
 
  Frequence := Frequence / 1000;                          { Conversion en KHz }
 
  aedtFrequence.Text := FloatToStr(Frequence);        { Affichage du résultat }
  {aedtFrequence.Text := aedtFrequence.Text + ' / ' + IntToStr(NbInter);}
 
 
  { ----- ~~~~~~~  Sauvegarde la valeur lue dans un fichier INI ~~~~~~~ ----- }
  TRY
    FicINI := TIniFile.Create(fichierini);
    WITH FicINI DO BEGIN
      {Lit la valeur actuelle}
      NombrePoints := ReadInteger(SectionINI, 'NombrePoints', 0);
      {Passe à la valeur suivante}
      Inc(NombrePoints);
      {Sauve la nouvelle valeur}
      WriteInteger(SectionINI, 'NombrePoints', NombrePoints);
      { Sauve la fréquence réellement générée }
      FrequenceReelle := frmMain.PIT8254.Quartz / FrmMain.PIT8254.Compteur0;
      WriteInteger(SectionINI, 'Frequence' + IntToStr(NombrePoints), Trunc(FrequenceReelle));
      { Calcule et sauve la précision }
      Precision := ((Frequence - FrequenceReelle) / FrequenceReelle) * 200;
      WriteInteger(SectionINI, 'Precision' + IntToStr(NombrePoints), Trunc(Precision));
    END; {WITH}
  FINALLY
    FicINI.Free;
  END; {TREE}
 
 
 
  { On peut à nouveau exécuter cette procédure, ou en d'autre termes, cliquer
    sur les boutons avec le mulot }
  bitbtnStart.Enabled := TRUE;
  bitbtnOK.Enabled := TRUE;
  bitbtnHelp.Enabled := TRUE;
  bitbtnGraph.Enabled := TRUE;
 
  { Et maintenant on peut fermer cette boîte de dialogue }
  OnPeutFermer := TRUE;
 
END; {PROCEDURE bitbtnStartClick}
 
 
PROCEDURE TfrmFrequencemetre.bitbtnGraphClick(Sender: TObject);
{-----------------------------------------------------------------------------
  BUT ........... : Affiche le graphe qui reprendtoutes les mesures faites...
  ENTREE ........ : --
  SORTIE ........ : --
  EFFETS DE BORDS : --
  REMARQUE(S) ... : ---
 -----------------------------------------------------------------------------}
BEGIN
  frmGraph.ShowModal;
END; {PROCEDURE bitbtnGraphClick}
 
 
{=============================================================================}
{ INITIALISATIONS ------------------------------------------- Initialisations }
{=============================================================================}
 
 
INITIALIZATION
END. {UNIT Ufreq;}