start:eti:carte:sources:ufreqt
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;}
start/eti/carte/sources/ufreqt.txt · Last modified: 2016/07/24 02:52 by admin