Automatisches Arduino-Oszilloskop                 

von Günther Zivny                       
 
                      Elektronik-Labor  Bastelecke  Projekte  Mikrocontoller                        



Download: Arduino_Oszilloskop.zip

Obwohl das Display SSD1306 sehr klein ist, kann man damit recht brauchbare Kurven darstellen. Dieses Oszilloskop benötigt nur sehr wenig Peripherie und hat keine Bedienelemente. Einfach die Messspitze auf den Messpunkt drücken – fertig. Alternativ kann man auch die Messspitze fest anschließen und einen Hardware-Reset machen (Taste auf der Arduino-Platine). Das bringt u. U. ein besseres Ergebnis, denn die Verstärkung und der Offset werden nur in einer Richtung verstellt. Ein einmal erreichter Wert wird nicht mehr zurückgenommen. Damit auch ins Negative gehende Spannungen (Wechselspannungen) dargestellt werden können, wird nach Bedarf eine Gleichspannung (Offset) addiert. Die waagerechte Linie ist die Null-Linie. Spannungen, die nur ins Negative gehen, werden nicht erfasst.

Die roten LEDs zeigen an, welche Verstärkung gerade eingestellt ist.
Die grünen LEDs zeigen den Zeitfaktor an, sie sind als Binärzahl zu verstehen.
Beispiel: Die LEDs 1 und 4 leuchten: 1 + 4 = 5,   25 = 32:   32ms / Teilstrich.
Die Skalierung erlaubt nur eine grobe Abschätzung der Werte.
Spannungsmessbereich: 400 mV … 16 V oder +/- 200 mV … +/-5 V
Frequenzbereich: 10 Hz … 1400 Hz (Sinus).

Bei sehr kleinen Frequenzen dauert es länger, bis die richtige Einstellung gefunden ist. Es funktioniert auch nicht immer perfekt. Man kann den Einstellvorgang am Bildschirm verfolgen und durch Abheben der Messspitze auch abbrechen. In der Regel wird dann das aktuelle Bild eingefroren, manchmal verschwindet es aber auch. Wenn man über etwa 1400 Hz hinaus geht, erhält man krakelige Linien. Häufig erscheint auch ein Bild, das an eine Schwebung (schwankende Amplitude) erinnert. Erstaunlich oft erhält man aber auch schöne Sinuslinien, die auf Oversampling beruhen. Das geht bis über 10 kHz, allerdings wird die Amplitude dann durch den 0.68 nF-Kondensator schon stark abgeschwächt.

 

Prinzip des Oversampling: Es wird pro Periode nur ein Messwert ermittelt, allerdings jeweils ein wenig verschoben. Die Messwerte können sich auch auf noch mehr Perioden verteilen.

 

Beschreibung des Programms

Um ein stehendes Bild zu erzeugen, wird normalerweise gewartet, bis die zu messende Spannung einen Schwellenwert erreicht, dann startet der Strahl. Hier wird das stehende Bild auf andere Art erreicht: Es werden immer 64 Punkte gemessen, aber nur 32 pro Bildschirm verwendet. In den ersten 32 Messwerten wird der kleinste Wert ymin gesucht. Bei diesem Wert startet die Kurve am linken Bildschirmrand, unabhängig davon, wann dieser Wert im Scan auftritt. Das funktioniert nur, wenn in den ersten 32 Messwerten mindestens eine vollständige Periode eines periodischen Signals auftritt. Nichtperiodische Signale können nicht dargestellt werden.


Damit auch ins Negative gehende Spannungen (Wechselspannungen) dargestellt werden können, wird bei Bedarf eine Gleichspannung (Offset) zum Signal addiert. Die Offset-Spannung kann maximal 5 V betragen. Der Verstärkungsfaktor wird so bestimmt, dass die Kurve nicht über den Bildschirmrand hinausgeht.

Die Zeitablenkung beginnt mit dem schnellsten Wert und wird dann schrittweise reduziert, bis eine vollständige Periode auf dem Bildschirm dargestellt wird. Das funktioniert so: In der ersten Hälfte des Bildschirms wird der Maximalwert ymax gesucht. Wenn dieser auf der Trennlinie liegt, wird eine langsamere Zeitablenkung eingestellt, sodass ein größerer Zeitabschnitt dargestellt wird.

 

Wenn man am Frequenzgenerator mit einer kleinen Frequenz beginnt und die Frequenz steigert, dann wird die Zeitablenkung nicht angepasst. Wenn man dagegen mit einer hohen Frequenz startet und diese langsam verringert, dann passt die Automatik die Zeitablenkung so an, dass immer mindestens eine volle Periode dargestellt wird.

Beispielbilder:

 

Sinuswechselspannung mit Mittellinie.


Fehlabtastung durch zu kleine Abtastrate.


Eigentlich saubere Rechteckspannung wird mit schrägen Flanken und abgerundeten Ecken dargestellt.


Überwiegend im Negativen verlaufende RC-Ladelinie, aber mit kleinem positiven Anteil. Ausschließlich negative Werte werden nicht erfasst.


//automatisches Oszilloskop

#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
#define ADC_PIN A0
#define SAMPLES 64 //2*32!

uint16_t y[SAMPLES];
uint8_t trigPos = 0, V = 10, FLAG, i, ii, n, SR = 1, p = 0;
uint16_t ymin, ymax, offset = 0 ;
int x;
//............................................................

void setup() {
display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
display.clearDisplay();

pinMode(11,OUTPUT); //PWM1
pinMode(2,OUTPUT);
pinMode(3,OUTPUT);
pinMode(4,OUTPUT);
pinMode(5,OUTPUT);
pinMode(6,OUTPUT);
pinMode(7,OUTPUT);
pinMode(8,OUTPUT);
pinMode(9,OUTPUT);
}
//..........................................................
void loop() {

Mark:

FLAG = 1;

for(i = 0; i < SAMPLES; i++){ //Samples = Anzahl Messpunkte: 64
for (n = 0; n < SR; n++){ //SR = sampling rate: jede n-te Messung wird gespeichert
y[i] = analogRead(ADC_PIN);
}
}

ymax = 0;
ymin = 1024;

//......................
while (FLAG == 1) {
ymax = 0;
ymin = 1024;

for (i = 0; i < SAMPLES/2; i++) { //32 Messpunkte = Bildschirm
if (ymin > y[i]*V) {
ymin = y[i]*V; //ymin suchen
trigPos = i;
}
if(ymax < y[i]*V) { //ymax suchen
ymax = y[i]*V;
n = i; //ymax = y{n]
}
}
if (ymax < 1024) {
FLAG = 0; //while verlassen, Kurve stößt nicht oben an
}
else {
FLAG = 1; //Verstärkung reduzieren
V = V/2;
}
} //Ende while.................................

if ((y[n]<=6) & (y[n+1]<=6)) { //wenn kein input...
FLAG = 1;
V = 10; SR = 1; offset = 0; p = 0; //Zurücksetzen, wenn kein Input
analogWrite(11,0); //PWM = 0
delay(500);
goto Mark; //zurück an Anfang
}
//..............................................................
if (V==10) {digitalWrite(5,HIGH);} else {digitalWrite(5,LOW);}
if (V==5) {digitalWrite(4,HIGH);} else {digitalWrite(4,LOW);}
if (V==2) {digitalWrite(3,HIGH);} else {digitalWrite(3,LOW);}
if (V==1) {digitalWrite(2,HIGH);} else {digitalWrite(2,LOW);}
if (V==0) {V = 1;}
//...........................................

//Kurve mit neuer Verstärkung
for (i = 0; i < SAMPLES; i++) {
y[i] = y[i] * V;
}

//Kurve verschieben (offset) (Hardware!)
if (ymin <= 0) {
offset = offset + 51;
if (offset > 255) {offset = 255;}
analogWrite(11,offset);
}
delay(1000); //zur Spannungsstabilisierung nach offset

x = ymin-ymax; //bei sehr kleiner Frequenz, um von SR = 1 wegzukommen
x = abs(x);
if (x < 20 * V) {SR = SR*2; p = p+1;}
if (SR == 0) {SR = 128; p = 7;} //bei Überlauf


//Zeitablenkung optimieren
ymax = 0;
for (i = trigPos; i<=(trigPos+15); i++){ //+15 = halber Bildschirm
if (ymax < y[i]) {
ymax = y[i];
ii = i;
}
}
if (ii == (trigPos+15)) {
SR = SR*2;
p = p + 1;
}
if (SR == 0) {SR = 128; p = 7;}; //bei Überlauf

//SR mit LEDs anzeigen
if ((p & 1) == 1) {digitalWrite(6,HIGH);} else {digitalWrite(6,LOW);}
if ((p & 2) == 2) {digitalWrite(7,HIGH);} else {digitalWrite(7,LOW);}
if ((p & 4) == 4) {digitalWrite(8,HIGH);} else {digitalWrite(8,LOW);}

// Kurve zeichnen.....................................................
display.clearDisplay();

for ( i = trigPos; i < (trigPos + SAMPLES/2 - 1); i++) {
uint8_t y1 = map(y[i], 0, 1023, SCREEN_HEIGHT - 1, 0);
uint8_t y2 = map(y[i+1], 0, 1023, SCREEN_HEIGHT - 1, 0);

display.drawLine(4*(i-trigPos), y1, 4*(i-trigPos+1), y2, SSD1306_WHITE);
// 4*32 = 128 (Bildschirmbreite)
}

//Null-Linie zeichnen
int x1 = offset * 19 * V;
x1 = x1/255;
display.drawLine(0, 63-x1, 128, 63-x1, SSD1306_WHITE);

//Markierungen zeichnen, y-Ache
display.drawLine(0, 0, 3, 0, SSD1306_WHITE);
display.drawLine(0, 16, 3, 16, SSD1306_WHITE);
display.drawLine(0, 32, 3, 32, SSD1306_WHITE);
display.drawLine(0, 48, 3, 48, SSD1306_WHITE);
display.drawLine(0, 63, 3, 63, SSD1306_WHITE);
//x-Achse
display.drawLine(37, 63, 37, 60, SSD1306_WHITE);
display.drawLine(75, 63, 75, 60, SSD1306_WHITE);
display.drawLine(112,63, 112, 60, SSD1306_WHITE);
//.............

display.display(); //Display aktivieren
} //::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::



Elektronik-Labor  Bastelecke  Projekte  Mikrocontoller