Oscilloscope

Introduction

Premier montage
      Câblage
      Codes pour générer les signaux

L'oscilloscope
      Version "basique"
      Version "à fréquence maximale"
      Version "à temps contrôlé"

Premiers résultats
      Observations
      Courbes obtenues

Montée en fréquence
      Signal rectangulaire
      Signal MLI ou PWM

Mesures simultanées

Mesures en temps réel

Conclusion

Introduction

L'objectif de ce projet est :

Premier montage

Câblage

Les premiers essais sont effectués en basse fréquence : 0,5 Hz. Pour tester l'oscilloscope, un générateur de signaux rectangulaire ou sinusoïdal est bien utile. Ce n'est pas évident de réaliser un tel générateur de signaux, avec une carte Arduino. Par contre, il est facile de faire varier l'intensité lumineuse d'une DEL. De couleur blanche, placée a proximité immédiate d'une photorésistance, elle permettra de recueillir un signal à afficher sur l'ordinateur. Le montage est positionné dans un endroit sombre de préférence.

On utilise donc deux cartes Arduino. Une carte fait varier l'intensité lumineuse d'une DEL. L'autre carte récupère le signal pour l'envoyer à l'ordinateur. Un programme en Python affiche les données du signal.

Certains numéros associés aux connecteurs d'une carte Arduino sont précédés du signe "~". Cela signifie qu'ils peuvent fonctionner selon deux modes :

C'est ce type de connecteur qui est choisi. Le câblage est le suivant :

La résistance qui protège la DEL ne doit pas avoir une valeur trop importante, pour éviter de limiter son intensité lumineuse. Les données sont les suivantes :

La résistance minimale se calcule avec la loi d'Ohm :

Codes pour générer les signaux

Signal rectangulaire :

#define DEL 6

void setup() {
  pinMode(DEL, OUTPUT);
}
void loop() {
  digitalWrite(DEL, HIGH);
  delay(500);
  digitalWrite(DEL, LOW);
  delay(1500);
}

Signal sinusoïdal :

#define DEL 6
float periode=2;

void setup() {
  pinMode(DEL, OUTPUT);
}
void loop() {
  for (float t=0; t<periode; t=t+0.01) {
    analogWrite(DEL, (int)(127.5+127.5*sin(t*2*PI/periode)));
    delay(10);
  }
}

L'oscilloscope

Version "basique"

Code sur la carte Arduino

Le signal lu (0 à 1023) est envoyé à l'ordinateur toutes les 0,05 s.

void setup() {  
  Serial.begin(115200); 
}
void loop() {
  Serial.println(analogRead(0));
  delay(50);
}

Tracé sur une durée choisie

La bibliothèque matplotlib est mise à contribution. La cinquième ligne du programme est à adapter selon le nom du port USB. Les valeurs sont lues toutes les 0,05 s, pendant 8 s.

#!/usr/bin/python3
import time
import serial
import matplotlib.pyplot as plt
portUSB=serial.Serial('/dev/ttyUSB0', 115200)

temps=[]
tension=[]
duree=8
t=0
dt=0.05

while (t<duree) :
  temps.append(t)
  tension.append(0.004887*float(portUSB.readline()))
  t=t+dt
  time.sleep(dt)

plt.figure("Oscilloscope")
plt.xlabel("Temps en s")
plt.ylabel("Tension en V")
plt.grid(True)
plt.plot(temps,tension)
plt.show()

Version "à fréquence maximale"

Code sur la carte Arduino

Le nombre maximal de mesures par unité de temps s'obtient... avec le code le plus simple!

void setup() {  
  Serial.begin(115200); 
}
void loop() {
  Serial.println(analogRead(0));
}

Tracé sur une durée choisie

Le code est presque identique au précédent. Le temps dt entre deux mesures est déterminé empiriquement. La première valeur envoyée à l'ordinateur engendre parfois une erreur, ne pouvant être convertie en nombre. Ce problème est ici corrigé.

#!/usr/bin/python3
import serial
import matplotlib.pyplot as plt
portUSB=serial.Serial('/dev/ttyUSB0', 115200)

temps=[]
tension=[]
duree=8
t=0
dt=0.000422

while (t<duree) :
  try :
    temps.append(t)
    tension.append(0.004887*float(portUSB.readline()))
    t=t+dt
  except :
    pass

plt.figure("Oscilloscope")
plt.xlabel("Temps en s")
plt.ylabel("Tension en V")
plt.grid(True)
plt.plot(temps, tension)
plt.show()

Version "à temps contrôlé"

Code sur la carte Arduino

Le temps s'obtient avec la fonction micros(). La fonction millis() est utilisable, mais les résultats obtenus sont moins bons lorsque la fréquence du signal à mesurer augmente.

void setup() {  
  Serial.begin(115200); 
}
void loop() {
  Serial.print(micros());
  Serial.print(" ");
  Serial.println(analogRead(0));
}

Tracé sur une durée choisie

Le code ressemble au précédent. La première valeur envoyée à l'ordinateur engendre parfois une erreur. Quand ce n'est pas le cas, elle est parfois fausse. La première valeur lue n'est donc pas traitée. Une première valeur de temps est crée artificiellement pour permettre à la boucle de fonctionner. Elle est détruite une fois l'ensemble des valeurs récupérées.

#!/usr/bin/python3
import serial
import matplotlib.pyplot as plt
portUSB=serial.Serial('/dev/ttyUSB0', 115200)

temps=[]
tension=[]
duree=0.05

lu=portUSB.readline()
temps.append(0)
while (temps[-1]<duree) :
  lu=portUSB.readline().split()
  temps.append(0.000001*float(lu[0]))
  tension.append(0.004887*float(lu[1]))
del temps[0]

plt.figure("Oscilloscope")
plt.xlabel("Temps en s")
plt.ylabel("Tension en V")
plt.grid(True)
plt.plot(temps, tension)
plt.show()

Premiers résultats

Observations

Les essais

Les premiers essais, effectués en basse fréquence, engendrent des courbes similaires avec les trois types d'oscilloscope. On remarque que la résistance de la photorésistance :

Les oscilloscopes

Remarque

Dans la troisième version de l'oscilloscope, le temps est contrôlé par la carte Arduino. Cependant, il est aussi possible de le contrôler par l'ordinateur, avec le code ci-dessous. Le Python n'est pas assez rapide, les essais ont montré des résultats... moins réguliers.

import time
...
debut=time.time()
while (temps[-1]<duree) :
    temps.append(time.time()-debut)
...

Courbes obtenues

Résultat avec le signal rectangulaire :

Et avec le signal sinusoïdal :

Montée en fréquence

Signal rectangulaire

Un signal rectangulaire est crée et lu directement, avec la troisième version de l'oscilloscope, sans passer par la photorésistance. Les résultats obtenus sont corrects jusqu'à une fréquence de 100 Hz environ (les 8 s sont remplacés par 0,05 s).

Code pour obtenir un signal rectangulaire à 100 Hz :

#define DEL 6

void setup() {
  pinMode(DEL, OUTPUT);
}
void loop() {
  digitalWrite(DEL, HIGH);
  delay(5);
  digitalWrite(DEL, LOW);
  delay(5);
}

Résultat obtenu :

Signal MLI ou PWM

Un signal MLI (modulation de largeur d'impulsions) ou, en anglais, PWM (pulse width modulation) permet de commander un servomoteur. L’intervalle de temps entre chaque impulsion, autrement dit la période, est généralement de 20 ms. Les impulsions ont une durée de 1 ms pour la position angulaire 0°, et une durée de 2 ms pour la position angulaire 180°. On est à la limite de ce que peut visualiser l'oscilloscope.

Code pour générer le signal MLI :

#include <Servo.h>
Servo leServo;

void setup() {
  leServo.attach(6);
}
void loop() {
  leServo.write(70); // Position angulaire de 70°
}

Résultat obtenu pour la position 180° :

Et la position 0° :

Mesures simultanées

Une carte Arduino possède six entrées analogiques. Il est donc possible d'effectuer six mesures en même temps. On va ici relever quatre tensions qui correspondent à la commande d'un moteur pas à pas.

Code qui génère les signaux

// Numéros des connecteurs
#define C1 8
#define C2 9
#define C3 10
#define C4 11
// Paramètres du moteur pas à pas
#define nbPas 20
#define vitesseRotation 250
// Objet associé au moteur
#include <Stepper.h>
Stepper moteur(nbPas, C1, C2, C3, C4);

void setup() {
  pinMode(C1, OUTPUT);
  pinMode(C2, OUTPUT);
  pinMode(C3, OUTPUT);
  pinMode(C4, OUTPUT);
  moteur.setSpeed(vitesseRotation);
}
void loop() {
  moteur.step(20000);
}

Code adapté à la mesure de quatre tensions

void setup() {  
  Serial.begin(115200); 
}
void loop() {
  Serial.print(micros());
  Serial.print(" ");
  Serial.print(analogRead(0));
  Serial.print(" ");
  Serial.print(analogRead(1));
  Serial.print(" ");
  Serial.print(analogRead(2));
  Serial.print(" ");
  Serial.println(analogRead(3));
}

Code adapté à l'affichage des quatre tensions

#!/usr/bin/python3
import serial
import matplotlib.pyplot as plt
portUSB=serial.Serial('/dev/ttyUSB0', 115200)

tension1=[]
tension2=[]
tension3=[]
tension4=[]
temps=[]
duree=0.05

lu=portUSB.readline()
temps.append(0)
while (temps[-1]<duree) :
  lu=portUSB.readline().split()
  temps.append(0.000001*float(lu[0]))
  tension1.append(0.004887*float(lu[1]))
  tension2.append(0.004887*float(lu[2]))
  tension3.append(0.004887*float(lu[3]))
  tension4.append(0.004887*float(lu[4]))
del temps[0]

plt.figure("Oscilloscope")
plt.xlabel("Temps en s")
plt.ylabel("Tension en V")
plt.grid(True)
plt.plot(temps, tension1, color='#2277bb') # Bleu
plt.plot(temps, tension2, color='#ff7700') # Orange
plt.plot(temps, tension3, color='#22aa22') # Vert
plt.plot(temps, tension4, color='#dd2222') # Rouge
plt.show()

Courbes obtenues

Mesures en temps réel

Il s'agit de créer une animation montrant l'évolution de la tension en fonction du temps. Cette animation peut se présenter de multiples manières.

En théorie, les données devraient s'afficher au fur et à mesure qu'elles arrivent. En pratique, la mise à jour de l'affichage prend du temps, il est difficile de produire une animation fluide avec un tracé précis. L'idée est de récupérer plusieurs valeurs entre entre deux images consécutives, en cherchant le meilleur compromis.

Coté Arduino, la tension est envoyée dix fois par seconde :

void setup() {  
  Serial.begin(115200); 
}
void loop() {
  Serial.print(micros());
  Serial.print(" ");
  Serial.println(analogRead(0));
  delay(100);
}

Dans le code ci-dessous, la première lecture n'est pas prise en compte. La deuxième lecture permet à la boucle de fonctionner. Ensuite, deux lectures sont faites avant chaque mise à jour de l'affichage. Les essais ont montré une animation de cinq images par seconde environ.

#!/usr/bin/python3
import serial
import matplotlib.pyplot as plt
portUSB=serial.Serial('/dev/ttyUSB0', 115200)

temps=[]
tension=[]
duree=25

plt.figure("Oscilloscope")

lu=portUSB.readline()
lu=portUSB.readline().split()
temps.append(0.000001*float(lu[0]))
tension.append(0.004887*float(lu[1]))
while (temps[-1]<duree) :
    lu=portUSB.readline().split()
    temps.append(0.000001*float(lu[0]))
    tension.append(0.004887*float(lu[1]))
    lu=portUSB.readline().split()
    temps.append(0.000001*float(lu[0]))
    tension.append(0.004887*float(lu[1]))
    plt.ylim(-0.2,5.2)
    plt.xlabel("Temps en s")
    plt.ylabel("Tension en V")
    plt.grid(True)
    plt.plot(temps, tension, color='#2277bb')
    plt.pause(0.000001)

Exemple de charges et décharges d'un condensateur (attention à la polarité) de 2200 μF ralenties par une résistance de 270 Ω :

Conclusion

Cet oscilloscope présente des avantages et des inconvénients.

Avantages

Inconvénients