Accueil | Page d'électronique

Un oscilloscope sur votre PC

L'oscilloscope constitue une des applications que permet la réalisation du module de lecture 8 bits de l'article précédent, dans le dossier d'acquisition de données. La lecture de ce précédent article est d'ailleurs très fortement conseillée, pour une meilleure compréhension du présent circuit.

L'oscilloscope que je vous présente ici est loin de concurrencer les oscilloscopes commerciaux, qui peuvent suivre des signaux d'un fréquence de plus de 20 MHz (ou oscillations par seconde). Notre circuit a une limite physique d'environ 600 000 lectures par seconde, ce qui lui permet théoriquement de suivre un signal allant, tout au plus, à 0,6 MHz, ou 600 kHz. Toutefois, pour pouvoir distinguer quoi que ce soit, le signal devrait être, au grand maximum, de la moitié de ce 600 kHz, pour que l'onde s'étente sur un minimum de 2 lectures, à l'écran.

Et encore là, ses capacités sont grandement limitées par la vitesse de l'ordinateur. En effet, j'ai remarqué de grandes différences de vitesse, en utilisant différents systèmes d'exploitation et langages de programmation. Voici le résumé de mes observations:

Pour bien lire ce graphique, voici quelques précisions. Le premier couple de données correspond à une utilisation normale du programme Quick Basic 4.5, en appuyant sur la touche "Exécuter" (F5). Le second couple correspond au même logiciel, mais où le programme a d'abord été compilé en exécutable avant d'être exécuté. Le troisième couple correspond à l'utilisation des fonctions standard du BIOS, pour un écran VGA, écrit en Borland C++ v3.0 pour DOS, et enfin, toujours en C++, les vitesses d'acquisistions effectuées en accédant directement à la mémoire vidéo, pour les cartes SuperVGA (800 par 600, 16 couleurs).

Suite à la lecture du graphique, on constate que si l'on veut obtenir les meilleurs résultats, on a toujours avantage à démarrer son ordinateur en mode MS-DOS, au lieu de Windows 95. On voit aussi que l'accès direct à la mémoire vidéo est TRÈS rapide. Probablement que l'accès direct en mode VGA serait aussi rapide qu'en VESA pour les cartes SuperVGA, mais vu mes talents de programmeur en C++, je vais laisser la tâche de le découvrir à un(e) programmeur(e) plus expérimenté(e).

Écran de l'oscilloscope. Les traces bleues sont celles laissées par celle en blanc.

Notez que ces performances ne dépendent pas du circuit, mais bien de l'ordinateur, qui est celui qui engendre les délais les plus importants dans le fonctionnement de l'oscilloscope. Même si cette lenteur provient de l'affichage à l'écran, certaines améliorations pourraient être portées pour accélérer le tout. Souvenez-vous du module de lecture 8 bits de l'article précédent; pour transférer 8 bits dans l'ordinateur. Il y avait un goulot d'étranglement, où on devait couper les 8 bits en deux paquets, pour qu'ensuite l'ordinateur les remette ensemble. Or, toutes ces étapes de coupe-recolle pourraient être mises de côté par un accès au bus ISA. Le port parallèle, d'ailleurs, s'y connecte directement.

Le circuit intégré que j'ai d'abord utilisé est le ADC0809 (même chose que ADC0808, je crois) de National Semiconductors (équivalent au MM79C949-1, selon NS). Je l'ai choisi pour son prix (environ 6$ canadiens, ou 24 francs français), mais aussi parce qu'il comporte 8 entrées analogiques différentes. C'est avec le temps que j'ai réalisé qu'il ne suffisait pas, pour réaliser un oscilloscope. En effet, le convertisseur en question demande que le signal analogique à convertir soit stable pendant toute la durée de la convertion. Résultat: D'assez fréquentes erreurs de convertion. Même s'il comporte un petit défaut, c'est un oscilloscope basé sur ce convertisseur que je vous présente d'abord sur cette page.

La solution au problème: Un module de "Sample and Hold" (lecture et maintien). Ce circuit prend un échantillon stable du signal très changeant, et le rend disponible au convertisseur, aussi longtemps qu'il lui faut pour faire sa convertion.

Mais étant donné que le convertisseur ADC0809 comporte 8 entrées, il faudrait également 8 circuits de "Sample and Hold", du genre LF198 ou LF298 de National Semiconductors. Bien sûr, il existe un seul circuit intégré comportant à lui seul ces 8 "Sample and Hold"; le SMP08, encore de National Semiconductors (NS), mais plutôt que de se compliquer les choses, j'ai recherché, pour la seconde partie de cet article, un convertisseur qui incorporait déjà l'option "Sample and Hold". Mes critères étaient donc les suivants:

  • Largeur de 8 bits, pour le module de lecture 8 bits auquel il sera rattaché;
  • Sortie en parallèle, pour plus de rapidité, côté transfert vers l'ordinateur, car les 8 bits de sortie sont directement disponibles;
  • Très Rapide, pour qu'il puisse suffire à des taux d'échantillonnage allant jusqu'à environ 500 000 à la seconde;
  • "Sample and Hold", pour être maître de la situation même avec des signaux très changeants.

    Ce que j'ai trouvé:

  • L'AD7569, de Analog Devices, qui comporte aussi un convertisseur digital-analogique (DAC). (Le AD7669 comporte deux DACs, mais c'est inutile pour le moment);
  • L'ADC0820, un standard de NS, mais qui ne semble pas remplir le critère de rapidité;
  • L'ADC08061 de NS, comportant 1 entrée analogique (le ADC08062 en a deux);
  • Et enfin, l'ADC08161, toujours de NS, comportant une seule entrée analogique.

    Mon choix s'arrête sur l'ADC08062, puisqu'il remplis tous mes critères de sélection, en plus d'offrir deux entrées analogiques. Si le besoin d'avoir plus de deux entrées se faisait sentir, j'opterais plutôt pour l'ADC08061, travaillant de concert avec un multiplexeur analogique de 8 entrées, comme le 4051 à 60 sous. Je pourrais tout de même garder le même ADC08062 et lui adjoindre un double multiplexeur du style 4539, coûtant lui aussi à peine 60 sous.


    L'oscilloscope à ADC0809

    Cet oscilloscope suffira pour des applications où les valeurs lues sont peu changeantes, comme par exemple pour lire des sondes de température, des potentiomètres, etc. Voici son shéma:


    L'oscilloscope à ADC08062

    //////// En construction /////////


    Le code source en Borland C++ 3.0

    // Un oscilloscope sur votre PC
    // Pour les écrans VGA et même moins récents
    //
    // Il existe un fichier pour les écrans SuperVGA (voir page web)
    //
    // Par Nicolas Marchildon
    // Web: http://nicolas.marchildon.net/
    // E-Mail: nicolas@marchildon.net
    //
    // Références: (Voir section 'Programmation' de la page web)
    // ----------
    //
    // Sur internet:
    // -> "The IBM PC PROGRAMMER'S GUIDE TO C", 3rd edition, Matthew Probert.
    //
    // Livres:
    // -> "La Bible PC", 5eme édition, Micro Application.
    // -> "Crash Course in C", 2nd edition, QUE editions.
    
    
    // Fichiers à inclure
    #include <dos.h>      //Pour l'acces aux registres REGS
    
    /* Global variables */
    static unsigned char attribute;
    int a;
    int b;
    int x;
    int y;
    int Ecran[800];
    int Valeur;
    int LargeurMax;
    
    /* Maximum coordinates for graphics screen */
    static int maxx;
    static int maxy;
    
    // Déclaration des fonctions
    int ParaIn (char TypeLect);
    void plotVGA( int x, int y, unsigned char color);
    void lineVGA(int a, int b, int c, int d, unsigned char colour);
    // La fonction "lineVGA" ne sert pas dans ce programme, mais pourrait être
    // utile à certains programmeurs.
    void EntrerVGA (unsigned char mode);
    
    // Fonction Principale
    int main (void)
    {
      //EntrerVESA(0x6A);
      EntrerVGA(16);     //Voir table des modes, plus bas.
      LargeurMax = 640;  //Largeur de l'écran, en pixels; dépend du mode vidéo.
      while (!kbhit())   //Exécuter tant qu'une touche n'a pas été appuyée.
      {
    	 for (x=0; x<LargeurMax-1; x++)  //Balayer l'écran de gauche à droite.
    	 {
    		plotVGA (x, 255-Ecran[x], 2);     //Effacer l'ancien point de la position x
    												// actuelle.
    		Valeur = ParaIn(1);           //Lire la valeur donnée par l'ADC
    												//connecté au port parallèle.
    		Ecran[x] = Valeur;            //Stocker la valeur pour pouvoir ensuite
    												//effacer le point, 640 pixels plus tard.
    		plotVGA (x, 255-Ecran[x], 15);       //Afficher le point qui vient d'être lu.
    	 };
      };
      return 0;          //Fin
    
    }
    
    
    void EntrerVGA (unsigned char mode)
    {
    //		MODE	TYPE		RESOLUTION	COLOURS
    //
    //		 0	  Text		40 x 25		4 (CGA), 16 (EGA, VGA) Shades of grey
    //		 1	  Text		40 x 25		4 (CGA), 16 (EGA, VGA)
    //		 2	  Text		80 x 25		4 (CGA), 16 (EGA, VGA) Shades of grey
    //		 3	  Text		80 x 25		4 (CGA), 16 (EGA, VGA)
    //		 4	  Graphics	320 x 200	4
    //		 5	  Graphics	320 x 200	4 (grey on CGA and EGA)
    //		 6	  Graphics	640 x 200	2
    //		 7	  Text		80 x 25		Mono (EGA, VGA)
    //		13	  Graphics	320 x 200	16 (EGA, VGA)
    //		14	  Graphics	640 x 200	16 (EGA, VGA)
    //		15	  Graphics 	640 x 350	Mono (EGA, VGA)
    //		16	  Graphics 	640 x 350	16 (EGA, VGA)
    //		17	  Graphics	640 x 480	2 (VGA)
    //		18	  Graphics	640 x 480	16 (VGA)
    //		19	  Graphics	320 x 200	256 (VGA)
    //
    //The term resolution in graphics modes refers to the number of pixels across
    //and down the VDU. The larger the number of pixels, the smaller each is and
    //the sharper any displayed image appears. As you can see from the table, the
    //VGA display can produce a higher resolution than the other display cards,
    //resulting in sharper images being produced.
    //
    //The CGA display card can produce a maximum resolution of 320 x 200 pixels,
    //where as the VGA display card can produce a resolution of 640 x 480 pixels.
    //This is why writing on a VGA VDU looks so much sharper than the writing
    //displayed on a CGA VDU.
    //
    //-----Extrait de "The IBM PC PROGRAMMER'S GUIDE TO C", 3rd edition, Matthew
    //     Probert.
    //
    	union REGS inreg, outreg;
    
    	inreg.h.ah=0;
    	inreg.h.al=mode;
    	int86(0x10,&inreg, &outreg);
    }
    
    void plotVGA (int x, int y, unsigned char color)
    {
    	union REGS regs;
    
    	regs.h.ah = 12;		//BIOS display function #12
    	regs.h.al = color;
    	regs.h.bh = 0;
    	regs.x.cx = x;
    	regs.x.dx = y;
    	int86(0x10, ®s, ®s);
    }
    
    void lineVGA(int a, int b, int c, int d, unsigned char colour)
    {
    	/* Draws a straight line from (a,b) to (c,d) */
    
    	int u;
    	int v;
    	int d1x;
    	int d1y;
    	int d2x;
    	int d2y;
    	int m;
    	int n;
    	int s;
    	int i;
    
    	u = c - a;
    	v = d - b;
    	if (u == 0)
    	{
    		d1x = 0;
    		m = 0;
    	}
    	else
    	{
    		m = abs(u);
    		if (u < 0)
    			d1x = -1;
    		else
    		if (u > 0)
    			d1x = 1;
    	}
    	if ( v == 0)
    	{
    		d1y = 0;
    		n = 0;
    	}
    	else
    	{
    		n = abs(v);
    		if (v < 0)
    			d1y = -1;
    		else
    		if (v > 0)
    			d1y = 1;
    	}
    	if (m > n)
    	{
    		d2x = d1x;
    		d2y = 0;
    	}
    	else
    	{
    		d2x = 0;
    		d2y = d1y;
    		m = n;
    		n = abs(u);
    	}
    	s = m / 2;
    
    	for (i = 0; i <= m; i++)
    	{
    		plotVGA(a,b,colour);
    		s += n;
    		if (s >= m)
    		{
    			s -= m;
    			a += d1x;
    			b += d1y;
    		}
    		else
    		{
    			a += d2x;
    			b += d2y;
    		}
    	}
    }
    
    
    int ParaIn (char TypeLect)
    { // Lecture des cinq bits du port parallèle.
      // Les bits lus sont les 5 plus haut (#3 à 7).
      // Le bit 7 est à inverser si le matériel le le fait pas (avec le 74 LS 04).
      // On NE décale toutefois PAS la lecture de 3 bits pour avoir 5 bits de
      //  poids faible.
      if (TypeLect == 0)
      {
    	 //LECTURE 0
    	 //Le matériel est abscent; on doit inverser le bit 7.
    	 //EN CONSTRUCTION!!!
    	 return inportb(0x379);
      };
      if (TypeLect == 1)
      {
    	 //LECTURE 1
    	 //On a pas à inverser le bit 7 car le matériel le fait déjà.
    	 outportb(0x37A, inportb(0x37A) & !0x01);
    	 a = (inportb(0x379) & 0xFF78) >> 3;
    	 outportb(0x37A, inportb(0x37A) | 0x01);
    	 b = (inportb(0x379) & 0xFF78) >> 1;
    
    	 return (a+b);
    
      };
      return 0;
    }
    


    Fonctions pour dialoguer avec un module de lecture 8 bits en Borland C++ 3.0

    //
    // Ensemble de fonctions pour "discuter" avec le port parallèle le plus
    // simplement possible.
    // Par Nicolas Marchildon
    // Web: http://nicolas.marchildon.net/
    // E-Mail: nicolas@marchildon.net
    //
    
    // Fichiers à inclure
    #include <stdio.h>
    #include <conio.h>
    #include <string.h>
    #include <dos.h>
    #include <stdlib.h>
    #include <stdarg.h>
    #include <math.h>
    //#include <iostream.h> //Ne Pas inclure car ca marche pas!
    
    /* Global variables */
    static unsigned char attribute;
    int _dmode;
    int _lastx;
    int _lasty;
    int a;
    int b;
    double x;
    double y;
    int pcolor;
    double value;
    int Ecran[800];
    int Valeur;
    int LargeurMax;
    
    /* Maximum coordinates for graphics screen */
    static int maxx;
    static int maxy;
    
    // Déclaration des fonctions
    int ParaIn (char TypeLect);
    char ParaOut (char Endroit, char TypeLect, char Valeur);
    void EntrerVESA (int mode);
    void plot800( int x, int y, unsigned char pcolor);
    void plotVGA( int x, int y, unsigned char color);
    void line(int a, int b, int c, int d, unsigned char colour);
    void EntrerVGA (unsigned char mode);
    
    // Fonction Principale
    int main (void)
    {
      EntrerVESA(0x6A);
      //EntrerVGA(18);
      LargeurMax = 640;
      while (!kbhit())
      {
    	 for (x=0; x 0)
    			d1x = 1;
    	}
    	if ( v == 0)
    	{
    		d1y = 0;
    		n = 0;
    	}
    	else
    	{
    		n = abs(v);
    		if (v < 0)
    			d1y = -1;
    		else
    		if (v > 0)
    			d1y = 1;
    	}
    	if (m > n)
    	{
    		d2x = d1x;
    		d2y = 0;
    	}
    	else
    	{
    		d2x = 0;
    		d2y = d1y;
    		m = n;
    		n = abs(u);
    	}
    	s = m / 2;
    
    	for (i = 0; i <= m; i++)
    	{
    		plot800(a,b,colour);
    		s += n;
    		if (s >= m)
    		{
    			s -= m;
    			a += d1x;
    			b += d1y;
    		}
    		else
    		{
    			a += d2x;
    			b += d2y;
    		}
    	}
    }
    
    
    int ParaIn (char TypeLect)
    { // Lecture des cinq bits du port parallèle.
      // Les bits lus sont les 5 plus haut (#3 à 7).
      // Le bit 7 est à inverser si le matériel le le fait pas (avec le 74 LS 04).
      // On NE décale toutefois PAS la lecture de 3 bits pour avoir 5 bits de
      //  poids faible.
      if (TypeLect == 0)
      {
    	 //LECTURE 0
    	 //Le matériel est abscent; on doit inverser le bit 7.
    	 return inportb(0x379);
      };
      if (TypeLect == 1)
      {
    	 //LECTURE 1
    	 //On a pas à inverser le bit 7 car le matériel le fait déjà.
    	 outportb(0x37A, inportb(0x37A) & !0x01);
    	 a = (inportb(0x379) & 0xFF78) >> 3;
    	 outportb(0x37A, inportb(0x37A) | 0x01);
    	 b = (inportb(0x379) & 0xFF78) << 1;
    
    	 return (a+b);
    
      };
      return 0;
    }
    
    char ParaOut (char Endroit, char TypeLect, char Valeur)
    {
      if (Endroit == 0)
      {
    	 //Ecrire sur le port de données (Adresse de base)
    	 outportb(0x378, Valeur);
      };
      if (Endroit == 2)
      {
    	 //Ecrire sur le port de commande (Adresse de base + 0)
    	 //Bientot disponible dans un cinéma pres de chez vous...
    	 if (TypeLect == 0)
    	 {
    		//Ecriture SANS matériel pour inverser les bits 0, 1, et 3.
    	 };
    	 if (TypeLect == 0)
    	 {
    		//Ecriture AVEC matériel pour inverser les bits 0, 1, et 3.
    	 };
      };
      return 0;
    }
    

    Accueil | Page d'électronique


    Page écrite à l'aide du célèbre Notepad.

    Cette page a été entièrement crée et est gérée par Nicolas Marchildon