Oscilloscope sur PC

 

 

 

Remerciement à Mr Buzer, Professeur à l'ESIEE et notre tuteur pour ce projet, pour son aide et sa sympathie.

1) La carte d'acquisition :

-Le circuit électrique

-Le programme du PIC18F452

2) Le Programme PC :

-Gestion du port série

-Fonctions Graphiques

Conclusion

Bibliographie

Documentations/PDFs

Fichiers du Projet

 

Le projet réalisé est un oscilloscope sur PC qui est composé d’une carte d’acquisition munie d’un microcontrôleur qui communique avec l’ordinateur grâce au port série.

La carte d’acquisition est gérée par un Pic18f452 (pdf) de la marque Microchip cadencé à 20MHz. L’acquisition est faite grâce à un ADC 0820 8 bits (pdf) et à plusieurs étages électroniques permettant d’adapter la tension entre 0 et 5 Volts pour l'ADC.

 

La carte d'acquisition

Le circuit électrique

Le circuit électrique est inspiré du magasine ELECTRONIQUE PRATIQUE HS n°9.

L’ADC0820 (pdf) ne mesurant des tensions qu’entre 0V et 5V, plusieurs adaptations du signal sont nécessaires.

1)Diviser la tension

Pour pouvoir avoir une tension d’entrée importante ( entre +15V et -15V), il est important de diviser la tension. Pour cela, on utilise un pont de résistances toutes identiques. Avec 8 résistances en série, on peut obtenir une division par 2, 4 et 8. La valeur de la division est sélectionnée grâce à 3 relais qui commutent en fonction des ordres du pic. Un AOP (LM324 pdf) monté en suiveur situé en aval des relais permet de ne pas modifier les valeurs des divisions.



Les tensions d’alimentation des AOPs sont de +9V et -9V. Cette tension est obtenue grâce à un MAX232.

Lors des phases d'expérimentations, nous avons pu remarquer que la tension mesurée avait tendance à diminuer lorsque la fréquence du signal était supérieure à 6 kHz. La source de ce problème n'a pas pu être identifiée. Cependant, la cause la plus probable semble être l'effet capacitif de la plaquette d'essai utilisée.

2) La tension de référence de l’ADC

L’ADC possède une tension de référence qui permet d’améliorer la précision pour les petites tensions.
Avec le même principe que précédemment, un pont de résistance fournit les tensions de référence 5V, 2.5V, 1.25V et 0.625V. Ces tensions sont obtenues grâce à un composant (LM336Z-5.0 pdf) qui fournit un 5V de référence, c’est à dire un 5V très stable qui ne varie pas ou très peu en fonction de la température ou d’autres facteurs. Les tensions sont sélectionnées ensuite par un multiplexeur analogique 74HC4052 (pdf) car les tensions sont suffisament faibles.

 

3) Les tensions négatives

L’ADC ne peut prendre des valeurs qu’entre 0V et 5V or un signal peut être en partie négatif, comme les signaux sinusoïdaux par exemple. Ainsi, un signal de 5V d’amplitude, entre –2.5V et +2.5V doit être ramener entre 0V et 5V.
Pour cela, on ajoute la moitié de la tension de référence de l’ADC au signal d’entrée.
Par exemple : un signal sinusoïdal variant entre –2.5V et +2.5V. Avec une tension de référence de 5V, on revient entre 0V et +5V.

L'AOP du haut permet de prendre la moitié de la tension de référence créée par les résistances. Le montage du deuxième AOP permet de réaliser un additionneur.

4) Schéma général de la carte

 

Le programme du PIC18F452

Le Pic18F452 (pdf) est un microcontroleur de la famille des PICs.. Il est programmable en assembleur RISC ou en C et possede 32K de memoire FLASH et 1534 bits de memoire RAM.

1) La Conversion

La liaison avec le port série se faire à 115200 bauds. Une trame est constitué de 10 bits, soit 1 bit de start, 8 bits de données et 1 bit de stop. L’envoie d’une trame prend donc 86.8 µs. Ce temps est beaucoup plus long que le temps de conversion de l’ADC (1.5µs) c’est pourquoi, le PIC stocke d’abord 1024 valeurs avec un temps entre chaque échantillon allant de 3µs à 200ms. Il envoie ensuite toutes les valeurs d’un seul coup, en arrêtant de prendre des valeurs.
L’ADC est contrôlé par le PIC uniquement grâce au bit WR. Ensuite, une attente de plus de 1.5µs permet d’être sur que la conversion est finie. Ce procédé permet d’avoir des temps de conversion rigoureusement identique car si pour une raison inconnue, l’ADC a un problème lors de la conversion, le PIC ne tiens pas compte du INTR. De plus, pendant la prise des valeurs, les interruptions sont désactivées car elles pourraient fausser la base de temps.
Le PIC est programmé uniquement en assembleur ce qui permet de connaître exactement le temps pris par chaque instruction.
Pour faire varier les temps de conversion, c’est le PC qui envoie les informations au PIC. Ainsi, le PIC possède 4 modes de conversion différents.

- 1 Mode ultra-rapide à 3µs (le PC envoie la valeur « m » comme minimum)
- 1 Mode rapide à 5µs ( valeur « f » comme fixe)
- 1 Mode variable de 10 µs à 1ms (Le PC envoie « v » puis une valeur entre 0 et 255)
- 1 Mode variable lent de 2ms à 250ms ( Le PC envoie « l » puis une valeur entre 0 et 255)

Temps/Divisions Mode Valeur à envoyer après "mode" Tps entre chaque échantillon en µs

0,15 ms/div m   3 µs
0,25 ms/div f   5 µs
0,5 ms /div v 1 10 µs
1 ms /div

v

3

20 µs

2,5 ms/div v 9 50 µs
5 ms/div v 19 100 µs
10 ms/div v 36 200 µs
25 ms/div v 99 500 µs
50 ms/div v 199 1 ms
100 ms/div l 1 2 ms
250 ms/div l 4 5 ms
500 ms/div l 9 10 ms
2,5 s/div l 49 50 ms
5 s/div l 99 100 ms
7,5 s/div l 149 150 ms
10 s/div l 199 200 ms

 

2) Stockage des données

Le Pic 18F452 possède 1536 bits de mémoire RAM dans laquelle les valeurs prises par l’ADC sont stockées. C’est une mémoire rapide dont l’accès se fait grâce à des registres post ou pré-incrémentable ou post-décrémentable (POSTINC0, PREINC0 ou POSTDEC0). Ceci rend la formation du tableau de valeurs beaucoup plus simple.

  movff ADC_DATA, POSTINC0 ; met la valeur de l’ADC dans le registre POSTINC0

La mémoire RAM est divisée en 16 Banks de 256 valeurs chacune dont seulement 5 banks sont exploitables par l’utilisateur. L’adresse du pointeur mémoire est contenue dans FSRL (FSR Low) et FSRH (FSR High). En effet, les 1536 bits nécessitent une adresse de 12 bits or, le Pic ne fonctionne qu’en 8 bits, donc deux registres sont nécessaires.
Etant donné que le pic envoie 1024 valeurs au PC, il faut utilisé 4 banks. Il suffit donc juste de tester FSRH. La première bank de travail est la bank 1.

  movf NB_VAL_HIGH, W
cpfseq FSR0H, 0
bra loop
bra envoie
; W est le register de travail du pic
; Compare FSRH et NB_VAL_HIGH = 5
; Continue a prendre des valeurs
; Envoyer le tableau de valeurs

 

Organisation mémoire

 

3) Envoie et Réception des données

Le Pic18f452 est équipé d’un module de gestion du port série (USART) . Un MAX232 (pdf) est nécessaire pour convertir les tensions du PIC en tension compatible avec le port série.

Le registre TXREG envoie les données sur le port série lorsqu’il est remplit. Un flag indique lorsque la transmission est terminé.

  movlw 'm'
movwf TXREG
btfss TXSTA,TRMT
bra $-1


; envoie ‘m’ sur le port série
; attend que l’envoie soit fini

Le registre RCREG active également un flag lorsqu’un bit est reçu. Il est remis à zéro lors de la lecture du registre.

  btfss PIR1, RCIF
bra $-1
movff RCREG, RC_REG_TMP
; attend une réception

; met RCREG dans un registre temporaire

 

4) Le Trigger du PIC

Le programme est équipé d’un trigger qui envoie les données uniquement lorsque la valeur de la tension dépasse un certain seuil défini par l’utilisateur. Le trigger boucle tant que la valeur n’est pas atteinte, cependant, il peut être désactivé grâce a une interruption sur le port série. Le port série peut provoquer une interruption lorsqu’il est plein. On vérifie alors la donnée reçue. Si celle-ci est « n » alors, le trigger se désactive et le programme prend les valeurs de la sonde.

  bsf PIE1, RCIE

loop_trig:
call Get_Value
movff ADC_DATA, VALUE1
movf VAL_TRIG, W

cpfslt VALUE1, 1
return

; Si oui,
btfsc FLAG_TRIG,0
bra loop_trig
return
;Enable interruption pour que le trigger puisse être stopper.






; Test : VALUE1 < VAL_TRIG
; Si non commence a prendre les valeurs

; Si oui,
; regarde si une interruption a changée le flag
; si non, attend encore
; si trigger désactivé, retour

 

5) Organisation du programme

Le programme est organisé de manière centralisée. A la fin de chaque action, le programme retourne en état d’attente d’une instruction du PC. Le programme effectue tout d’abord l’initialisation et la configuration des entrées/sorties ainsi que des registres. Ensuite, il se synchronise avec le PC en réceptionnant « OK » puis en envoyant « O ». Ceci permet notamment au programme PC de savoir si la carte est correctement branchée. Si la carte ne répond pas pour une raison autre que le branchement, le PC a le pouvoir de « reseter » la carte grâce a la patte MCLR du pic. Ceci permet d’éviter tout plantage du programme.

Organigramme du programme du Pic18F452

 

Le programme PC

Organisation du programme

 

1)Description générale

Le programme se compose d'un processus principal et de quatre threads :

· Le processus principal pour gérer l'affichage de la fenêtre et les saisies utilisateur.
· Un thread de synchronisation pour gérer les lectures/écritures sur le port série et l'affichage de la courbe.
· Un thread pour lire des données sur le port série.
· Un thread pour écrire sur le port série.
· Un thread pour afficher le signal.

Cette architecture permet de garder le programme réactif face à l'utilisateur. Les opérations d'entrée sortie sur le port série sont transparentes et un problème avec la carte ne bloque pas tout le programme car le thread de synchronisation intercepte l’erreur. Les threads communiquent entre eux à l'aide de signaux (events selon les API Windows). Le programme n'est pas temps réel et lit des échantillons de valeurs numérisés avec la carte puis les traite dans la partie graphique. La lecture et l'affichage étant dans deux threads séparés, ces deux opérations peuvent se faire simultanément.
Le programme a été développé avec Borland C++ Builder 5. Ce logiciel comporte une librairie de composants visuels nommée VCL. Cette bibliothèque associe à chaque objet une classe qui simplifie grandement son utilisation. L'utilisation de cette bibliothèque nous a permis de ne pas trop nous embarrasser avec la gestion des messages Windows, et nous a grandement simplifié l'interface graphique et la gestion des contrôles. Il suffit juste d'ajouter les composants sur une fiche (une fenêtre Windows vierge). Il faut ensuite associer à chacun d'eux des valeurs adéquates et des fonctions pour qu'ils réagissent à certains évènement, comme le clic d'un bouton par exemple. Néanmoins, la gestion du port série ainsi que les threads sont des API Windows standards, et ne font pas appels aux composants de la VCL.

2)Le Processus Principal

A la création de la fenêtre, toutes les ressources nécessaires sont allouées. On crée en premier lieu les évènements pour que les threads communiquent entre eux, puis les différents buffers nécessaires et enfin les threads. En dernier lieu, on initialise tout ce qui est nécessaire. A la fermeture du programme, on attend que les threads se terminent et on libère la mémoire.
Le processus principal est chargé de la partie graphique, il gère l'interface utilisateur avec les boutons et les cases à cocher. Il communique avec le thread de synchronisation à l'aide de signaux. Quand l'utilisateur change quelque chose, un signal est envoyé au thread de synchronisation qui va s'occuper des lectures, écritures et affichage nécessaire ou encore de la fermeture des autres threads.


3)Le Thread de Synchronisation


Le thread de synchronisation est le centre du programme. Ce thread reçoit les signaux du processus principal et gère les threads de lecture/écriture et affichage. Ses principales fonctions sont :


- Initialiser le port série et la carte : L'initialisation de la carte commence par un reset en utilisant la ligne RTS du port série, puis en envoyant la chaîne "OK". Si la carte répond 'O', elle est bien présente et prête à recevoir des instructions. Dans le cas contraire, on informe l'utilisateur.


- Lire les valeurs en continu sur le port série: Selon le calibre de temps, le thread synchro ordonnera la lecture de 1 octets pour les calibres lents ou de paquets de 1024 octets pour les plus rapides. Ces octets sont ensuite placés dans un buffer de lecture. A la fin de la lecture, le buffer de lecture est transféré dans un buffer d'affichage. Pour ne pas créer de conflits en lecture/écriture sur ces buffers, ils sont protégés par des mutex qui autorisent un seul accès à la fois. En utilisant deux buffers, on peut afficher et lire des valeurs en même temps dans deux threads différents. La lecture et l'affichage étant assez lents, cela permet de gagner en fluidité. Le thread vérifie que tout se passe bien et informe l'utilisateur en cas de problème. Il détecte par exemple si la carte est débranchée et demande à l'utilisateur d'agir en conséquence.


- Changer les calibres de mesures, pour modifier les volts par division ou les temps par divisions.


- Activer ou désactiver le trigger: Le trigger permet de lire un paquet de 1024 valeurs et de l'analyser. La carte envoie ce paquet quand la tension a atteint le niveau du trigger du programme. Le programme affiche ces valeurs et attend que l'utilisateur lui dise de reprendre un affichage normal des valeurs. Le trigger est très utile pour voir des envoies de données par le port série par exemple ou encore après une antenne. Ainsi dès que le message arrive, le pic enregistre la trame et le pc l’affiche.


4)Le Thread de Lecture

Ce thread est composé d'une boucle qui attend deux signaux. Le premier signal sert à fermer le thread et l'autre à commencer la lecture. Quand il reçoit ce dernier, le thread appelle une fonction qui va lire des octets sur le port série. Cette lecture non bloquante : si le thread attend de lire quelque chose mais ne reçoit rien, il se fermera s'il reçoit le signal qui lui en donne l'ordre, ou se terminera après un temps d'attente définit à l'avance. La méthode d'attente sur le port est passive, par exemple si le thread doit lire 1024 octets, il sera "endormi" jusqu'à ce qu'il soit réveillé par un signal de Windows quand la lecture sera terminée. Pendant ce temps, le reste du programme continue de s'exécuter normalement.
Cette méthode de lecture asynchrone est plus compliquée que la méthode synchrone. Néanmoins elle est beaucoup plus sûre et plus souple. Le programme nécessite beaucoup d'opérations d'entrée/sorties et cette méthode est plus appropriée. Le principe général est de lancer une opération de lecture/écriture puis d'attendre que Windows envoie un signal quand cette opération est finie. Il faut ensuite vérifier si tout s'est bien passé et retourner dans le thread principal pour attendre un nouveau signal du thread de synchronisation.


5)Le Thread Ecriture

Ce thread a exactement la même structure que le thread de lecture, il est aussi asynchrone et non bloquant. Il est néanmoins plus simple car il n’écrit qu'un seul octet à la fois.

6)Le Thread Affichage

Tout comme les autres threads, il possède une boucle qui attend deux signaux, un pour se terminer et l'autre pour afficher le dessin. Pour dessiner, ce thread attend d'avoir le mutex pour pouvoir lire le buffer d'affichage, et le libère quand il a finit. Suite à quelques problèmes à cause de la VCL de Borland, la fonction de dessin est dans une section critique. C'est à dire qu'aucun autre processus ne peut l'interrompre, il n'y a donc pas d'interaction avec la VCL et moins de risque de conflit avec les hardwares descriptors utilisé dans l’affichage. La fonction de dessin sera étudiée plus loin.

Fonctions Graphiques

 

1)Affichage du signal

On affiche les signaux en utilisant le Hardware Descriptor, car l’affichage est alors beaucoup plus rapide qu’en utilisant les fonctions de Borland.
On définit une fenêtre d’affichage sur laquelle le signal sera représenté.
A chaque appel de la fonction de dessin, aussi bien pour une courbe que pour une FFT, on commence par remplir la zone d’affichage en blanc avec un brush. Puis on dessine les lignes horizontales et verticales des divisions à l’aide de crayons.
On sélectionne ensuite un crayon de couleur différente pour dessiner la courbe en reliant entre eux les différentes valeurs du tableau (ou en dessinant des raies verticales dans le cas de la FFT).
Un bouton permet de basculer entre l’affichage de la courbe et l’affichage de la FFT.


Un autre bouton permet de geler l’affichage.


2)Fonctionnement des curseurs

Lorsque le curseur est au dessus de la courbe, il prend la forme d’une croix. Nous avons choisi la forme à donner au curseur en définissant une zone TImage transparente sur laquelle le curseur change lorsque il passe dessus. La zone Timage recouvre exactement la fenêtre d’affichage de la courbe.
Lorsque c’est necessaire, deux zones de texte indiquent la position du curseur X et Y, c’est-à-dire les coordonnées en temps et en valeur de la tension où l’on pointe le curseur.

La selection des curseurs se fait à l’aide de trois boutons :

Lorsque l’un des curseurs est sélectionné, on dessine la croix qui le pointe grâce à un crayon et l’autre curseur devient inactif.

Les Deltas :
L’utilisateur déplace son curseur et peut, s’il souhaite mesurer une différence de temps ou de tension, sauvegarder deux points dans X1Y1 et X2Y2. Pour cela, il lui suffit de déplacer l’un des curseurs sur la courbe, de cliquer pour enregistrer sa position, et de recommencer avec le second.
Il existe deux booléens click_save1 et click_save2 qui nous indiquent si l’un des curseurs a déjà été sauvegardé. Si c’est le cas, la croix signalant ce point reste affichée.
Si l’un des curseurs est sauvegardé et que le second est sélectionné par l’utilisateur, les cases DX et DY afficheront directement les différences entre la valeur sauvegardée et la valeur courante.

Si l’utilisateur choisit « No Curseur », alors les booléens sont resetés et les croix effacées.
Lorsque l’on change d’affichage (courbe ou FFT) les curseurs sont supprimés.


3)Fonctionnement du trigger

Le trigger permet de stabiliser le signal. En effet, comme l’affichage ne se fait pas en temps réel, le tableau des 1024 valeurs peut commencer à n’importe quel endroit de la courbe. Le trigger permet d’indiquer à partir de quel valeur de tension, le signal doit être afficher. Ainsi, quelques soient les valeurs du tableau, le signal sera toujours affiché à partir de la même valeur.
L’affichage d’une ligne horizontale liée à la position d’une scrollbar verticale visualise la position du trigger.
On affichera la courbe à partir de sa première valeur si le trigger est positionné en dehors de la courbe.
Dans le cas contraire, on parcourt la courbe pour trouver l’abscisse correspondant au trigger (ordonnée) sélectionné.
On choisit d’afficher à partir d’une pente positive. On vérifie donc que le point obtenu répond bien à ce critère en regardant l’ordonnées de l’une des abscisses suivantes : si celle-ci est supérieure à celle du point choisi, on est bien sur une pente positive, dans le cas contraire on continue de parcourir le tableau.
On a également la possibilité de définir la sensibilité du trigger : dans le cas où le signal est bruité, on peut augmenter la sensibilité pour être sûr de la position de début d’affichage. La recherche de l’abscisse s’effectue alors sur un intervalle dépendant de la sensibilité sélectionnée.


4)Les Opérations sur la courbe

La fonction MinMax parcourt le tableau de valeurs pour trouver les extremum de la courbe.

La fonction Période parcourt le tableau de valeurs pour calculer la valeur moyenne du signal. Pour calculer sa période, on parcourt le tableau de valeurs et on repère les points pour lesquels les valeurs passent la barre de la moyenne. Une période correspond à trois passages de cette barrière. On effectue cette opération sur toute la longueur du signal, et on effectue une moyenne de toutes ces périodes calculées, car les résultats peuvent parfois être faussés dans le cas où le signal est très bruité. Ce système permet d’avoir un résultat relativement satisfaisant, les erreurs étant peu nombreuses.
L’opération renvoie –1 si le signal récupéré ne permet pas de mettre une période en évidence.

Il est également possible d'effectuer une trasformée de Fourier du signal. La fonction nous a été fournie par notre tuteur, Mr Buzer. Pour calculer les fréquences en abscisse, il faut multiplier le nombre d'échantillons numérisés par le temps d'échantillonage. Nous obtenons un temps T0 = N * Temps_echantillion. La fréquence F0 = 1/T0 correspond au pas entre deux fréquences. La première valeur du tableau de FFT est égale à 0*F0, c'est la composante continue du signal. La deuxième valeur correpond à F0, la troisième à 2*F0 et ainsi de suite.


5) Les Calibres

On crée des structures qui contiennent les différents calibres par pixels (c’est-à-dire par points relevés, pour l’affichage) en temps et en Voltage, ce qui nous fournit des coefficients par lesquels multiplier les valeurs avant de les afficher (la période, les minimum, maximum et deltas). Les calibres seront choisis parmi les valeurs proposées et modifiés par l’utilisateur.

Conclusion

Ce projet nous a permis de mettre en oeuvre des connaissances acquises lors de ces 3 ans d'études à l'ESIEE. L'aspect pluridisciplinaire de ce projet nous a donné l'occasion d'aborder des problèmes d'informatique, d'électronique analogique, d'électronique numérique ainsi que des problèmes de mathématiques et d'algorithmique. La plupart des problèmes rencontrés au cours de ces 3 semaines de travail ont été resolus, mais il reste quelques problemes électronique et informatique que nous n'avons pas pu résoudre par faute de temps. Nous avons également appris sur des domaines que nous ne connaissions pas bien tels que la gestion du port serie ou encore la programmation Windows grâce au logiciel Borland C++ Builder 5.

Ce projet a également été l'occasion d'améliorer nos capacités de travail en équipe. La séparation des tâches, le suivi des membres et le regroupement des parties de chacun ont été a été faits de manière organisée tout au long de ce projet

 

 

Bibliographie

Electronique Pratique Hors-Serie n°9, Juin 2001.

Aide de Borland

Win32 Developer's Reference : Aide du SDK de Windows

Guide du développeur - Borland C++ Builder 5

MSDN Library : http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnfiles/html/msdn_serial.asp