Wechselstromquelle (Teil 2)

DAC AC Elektronik

Weiter geht's: Dieses Mal erzeugen wir ein Sinussignal für unsere Wechselstromquelle. Unser erster Ansatz: die Verwendung eines DACs.

Erzeugen einer Sinusschwingung mit einem DAC

Im ersten Teil dieses Projekts haben wir uns mit den Grundlagen der Erzeugung eines Wechselstromsignals beschäftigt. Es ist zwar nicht unbedingt notwendig, dass ein Wechselstromsignal eine Sinusform hat, aber es wäre schön, so nah wie möglich an das heranzukommen, was wir von der Netzspannung kennen. Deshalb werden wir uns nun ansehen, wie man ein solches Signal erzeugen kann. Unser erster Ansatz: die Verwendung eines DACs.

Digital-Analog-Wandler (DACs) sind eine naheliegende Wahl, wenn es darum geht, analoge Signale mit einem Mikrocontroller zu erzeugen. Leider hat der Arduino keinen eingebauten DAC und deshalb müssen wir ein externes DAC-Modul wie den MCP4725 verwenden. Ich habe bereits ein zweiteiliges Tutorial zu diesem DAC-Modul gemacht, daher werde ich hier nicht weiter ins Detail gehen. Wenn du wissen willst, was du mit diesem Modul machen kannst und wo seine Grenzen liegen, schau dir das MCP4725 Tutorial an. Heute werden wir uns auf die Erzeugung von Sinussignalen mit diesem DAC konzentrieren.

Wie machen wir das? Nun, der DAC erlaubt es uns, beliebige analoge Spannungen zwischen 0V und ungefähr 5V auszugeben. Das bedeutet, dass wir die Ausgangsspannung mithilfe der sin-Funktion berechnen können und die berechneten Werte nur noch mit dem richtigen Timing ausgeben müssen, um ein Sinussignal mit der gewünschten Frequenz zu erhalten. Das klingt zwar recht trivial, aber der Teil mit dem korrekten Timing hat seine Tücken. Schauen wir uns dies also Schritt für Schritt an, beginnend mit einer recht einfachen Testschaltung.

Aufbau der Schaltung

Die Schaltung für unsere kleine Wechselstromquelle ist nicht sonderlich komplex. Wenn du dich an die abstrakte Schaltung aus dem letzten Teil dieses Projekts erinnerst, können wir anstelle der Signalerzeugungsschaltung einfach das MCP4725 DAC-Modul einsetzen. Abstrakter Schaltplan mit MCP4725 DAC

Das Bild unten zeigt, wie alles verdrahtet wird. Der MCP4725 wird über I2C angeschlossen. Für den Spannungsteiler, der unsere neue Massereferenz erzeugt, habe ich zwei 220 Ω Widerstände verwendet. Der zusätzliche 10 kΩ Widerstand ist unsere Last, über der wir das Wechselstromsignal mit einem Oszilloskop oder einem Multimeter messen können. Schaltung auf einem Breadboard

Der Spannungsteiler begrenzt unseren maximalen Strom auf \(I_{s} = {U_{s} \over R} = {2,5 V \over 220 Ω} = 11,4 mA\). Wenn wir einen höheren Strom benötigen, müssten wir die Widerstandswerte verringern. Dies ist jedoch ebenfalls keine wirkliche Lösung, da der Strom, der direkt über die beiden Widerstände fließt, dann ebenfalls steigt und so ein immer größerer Anteil an Gesamtleistung unnötig als Wärme verloren geht.

Spannungsteiler sind nicht dazu gedacht, irgendetwas mit Strom zu versorgen. In dieser Schaltung missbrauchen wir gewissermaßen einen Spannungsteiler. Allerdings liegt der maximale Strom, den der DAC liefern kann, während er noch ein stabiles Signal ausgibt, ohnehin unter 10 mA. Unsere Wechselstromquelle ist also nicht gerade besonders leistungsfähig. Um dieses Problem werden wir uns jedoch erst in einer späteren Phase dieses Projekts kümmern.

Hinweise zur Messung des Signals mit einem Oszilloskop
Um unser Wechselstromsignal zu messen, müssen wir den Tastkopf an eine Seite des Widerstands und die Masseleitung an die andere Seite anschließen. Das ist nichts Besonderes, aber es gibt eine Sache zu beachten: Die Massen aller Kanäle sind untereinander und mit dem Schutzleiter verbunden. Dies ist normalerweise kein Problem, aber unser Fall ist etwas speziell, da wir den 2,5-V-Ausgang unseres Spannungsteilers als Masse für den Wechselstromteil unserer Schaltung verwenden. Es gibt zwei Dinge, die man deswegen vermeiden sollte:

  • Kurzschließen des Ausgangs des Spannungsteilers zur Masse über einen zweiten Tastkopf
    Beim Anschluss einer zweiten Sonde für eine weitere Messung ist zu beachten, dass die Massen alle miteinander verbunden sind. Wenn man die Masseleitung eines Kanals mit den vom Spannungsteiler erzeugten 2,5 V und die Masseleitung eines anderen Kanals mit der Masse des Arduino verbindet, erzeugt man einen Kurzschluss.
  • Kurzschließen des Spannungsteilerausgangs zur Masse über den Schutzleiter
    Beachte, dass die Masseleitungen des Oszilloskops über den Schutzleiter mit der Haupterde verbunden sind. Wenn die Masse des Arduinos ebenfalls mit der Haupterde verbunden ist (z. B. über den Computer), schließt man beim Anschließen des Tastkopfs den Ausgang des Spannungsteilers mit der Masse des Arduinos kurz. Ein Strom kann durch das Oszilloskop, die Stromleitung, den Computer und das USB-Kabel bis zur Masse des Arduino fließen. Prüfe daher mit einem Multimeter, ob die Masse des Arduinos mit der Haupterde verbunden ist, bevor du den Tastkopf anschließt. Messe dazu z. B. die Kontinuität zwischen dem äußeren Ring der BNC-Buchsen des Oszilloskops und der Metallabschirmung der USB-Buchse des Arduino. Wenn sie verbunden sind, betreibe den Arduino über eine Batterie und zieh das USB-Kabel vor der Messung ab, um einen Kurzschluss zu vermeiden. Alternativ kann man einen USB-Isolator benutzen.

Glücklicherweise begrenzen die Widerstände des Spannungsteilers den Strom im Falle eines Kurzschlusses. Dies verhindert ernsthafte Schäden.

Die Theorie: Ausgabe von Analogsignalen

Bevor wir den Code zur Erzeugung der Sinuswelle anschauen, sollten wir uns erst mit einigen Grundlagen vertraut machen. Man kann nicht einfach eine Sinuswelle mit einem DAC ausgeben, so wie man auch nicht einfach ein analoges Signal mit einem ADC messen kann. Wenn wir mit einem Mikrocontroller mit analogen Signalen arbeiten wollen, müssen wir uns Gedanken machen, wie wir das analoge Signal digital darstellen können.

Auflösung und Abtastrate

Auflösung

Falls du das Tutorial über den MCP4725 DAC gelesen hast, weißt du bereits, dass wir eine Zahl zwischen 0 und 4096 verwenden, um seine Ausgangsspannung darzustellen. Für den ADC des Arduino wird die gemessene Spannung in einer Zahl zwischen 0 und 1023 dargestellt. In der analogen Welt gibt es eine unendliche Anzahl von möglichen Spannungen zwischen 0 V und 5 V, ADCs und DACs haben jedoch eine begrenzte Auflösung. Sie können nur zwischen einer begrenzten Anzahl von verschiedenen Analogwerten unterscheiden. Der ADC des Arduino hat eine 10-Bit-Auflösung und kann zwischen 1024 verschiedenen Spannungen unterscheiden. Der MCP4725 DAC kann zwischen 4096 Ausgangswerten differenzieren. Bei 5 V Versorgungsspannung können wir so die Ausgangsspannung in 1,22 mV Schritten einstellen. Wenn wir größere Schritte oder mit anderen Worten eine geringe Ausgangsauflösung verwenden, werden diese im erzeugten Signal als Treppenstufen sichtbar. Das Extrembeispiel ist ein digitaler Ausgang, der eine 1-Bit-Auflösung hat und nur zwei Werte kennt: an und aus.

Abtastrate

Wenn wir ein Signal erzeugen wollen, das sich über die Zeit verändert, ist die Abtastrate eine weitere wichtige Größe. Sie gibt an, wie oft wir pro Sekunde einen neuen Spannungswert messen oder ausgeben. Um ein Signal digital darzustellen, müssen wir den kontinuierlichen Signalverlauf in eine Reihe von diskreten Abtastwerten umwandeln. Wenn die Abtastrate jedoch zu niedrig ist, können wir die ursprüngliche Wellenform nicht mehr aus den diskreten Abtastwerten rekonstruieren. Um eine Sinusschwingung zu rekonstruieren, benötigen wir mindestens zwei Samples pro Periode, um ihre Frequenz und Amplitude zu bestimmen. Wir müssen mit einer Frequenz abtasten, die mindestens doppelt so hoch ist wie die tatsächliche Frequenz der Sinusschwingung. Dies ist auch als Nyquist-Shannon-Abtasttheorem bekannt.

Der Zusammenhang zwischen Auflösung und Abtastrate

Das Abtasttheorem besagt, dass wir mindestens 100 Samples pro Sekunde benötigen, um eine 50-Hz-Sinuswelle rekonstruieren zu können. Es gibt jedoch einen entscheidenden Haken. Mit nur 2 Samples pro Periode können wir nur zwei verschiedene Spannungen messen oder ausgeben, was einer Auflösung von nur 1 Bit entspricht. Das reicht zwar aus, um das Signal mathematisch zu rekonstruieren, wenn wir wissen, dass es sich um ein Sinussignal mit einer niedrigeren Frequenz als unsere Abtastfrequenz handelt, aber das hilft uns nicht viel, wenn wir ein Sinussignal ausgeben wollen. Das ausgegebene Signal würde einem 50-Hz-Rechtecksignal entsprechen, das auch durch einfaches An- und Ausschalten eines Pins oder mittels PWM erzeugt werden könnte. Ein solches Signal könnte auch in ein Sinussignal umgewandelt werden, aber das wird das Thema für das nächste Mal sein. Bei einer solch geringen Abtastrate nützt es also nichts einen DAC mit 12-bit Auflösung zu haben. Die Abtastrate ist hier der limitierende Faktor, nicht die Auflösung des DACs. Wenn wir mit unserem DAC eine gescheite Sinuswelle erzeugen wollen, benötigen wir eine höhere Abtastrate.

Um die Auflösung des DACs auszunutzen zu können, sollten wir die Abtastrate so hoch wie möglich wählen. Allerdings gibt es für die Abtastrate eine obere Grenze. Unsere maximale Abtastrate wird maßgeblich durch die Geschwindigkeit der I2C Kommunikation begrenzt. Wenn wir ein Sinussignal mit hoher Frequenz ausgeben möchten, müssen wir Kompromisse bei der Auflösung eingehen. Wir können nur wenige Sample pro Periode ausgeben, da die Periodendauer kurz ist. Ist die Frequenz hingegen niedrig, können wir eine höhere Auflösung erzielen. Bei sehr niedrigen Frequenzen wird dann die Auflösung des DACs zum limitierenden Faktor. Darüber sollten wir uns in diesem Projekt Sorgen allerdings nicht sorgen.

Die Praxis: Auf das richtige Timing kommt es an

Genug der Theorie fangen wir an. Um die Sinusschwingung mit einer bestimmten vorgegebenen Frequenz zu erzeugen, müssen wir sicherstellen, dass die Samples im richtigen zeitlichen Abstand zueinander ausgegeben werden. Bevor wir das tun, sollten wir aber zunächst die maximal mögliche Abtastrate bestimmen. Damit werden wir also beginnen.

Bestimmen der minimalen Abtastzeit

Der einfachste Weg, die maximale Abtastrate zu bestimmen, ist, einfach zu versuchen, so viele Samples wie möglich auszugeben und die benötigte Zeit pro Sample zu messen. Der folgende Code gibt 10 Samples in einer Schleife aus und ermittelt die durchschnittliche Zeit, die dies benötigt. Für eine genaue Messung müssten wir genau wissen, welche Befehle wir in welcher Reihenfolge in unserem endgültigen Code ausführen werden. Das ist nichts, was wir im Vorfeld tun könnten. Wir können die benötigte Zeit jedoch näherungsweise bestimmen und später zur Sicherheit etwas zusätzliche Zeit zur Messung hinzufügen. Die Verwendung einer Schleife zur Ausgabe von Samples aus einem Puffer sollte unserem endgültigen Programm nahe genug kommen, um uns diese Näherung zu ermöglichen.

#include <Adafruit_MCP4725.h>

Adafruit_MCP4725 dac;
const int address = 0x60;
unsigned short values[10] = {};

void setup() {
  // Setup DAC
  dac.begin(address);

  // Measure time needed to write a value
  long start_time = micros();
  for(int i = 0; i < 10; i++) {
    dac.setVoltage(values[i], false);  
  }
  long end_time = micros();

  // Send value to computer
  Serial.begin(9600);
  Serial.print("Microseconds per Iteration: ");
  Serial.println((end_time-start_time)/10);
}

void loop() {
}

Ich habe 157 us pro Iteration gemessen und du solltest einen ähnlichen Wert erhalten, wenn du einen Arduino Uno verwendest. Um sicherzugehen, werden wir mit 200 us pro Iteration weiterarbeiten. Auf diese Weise können wir eine Abtastrate von 5000 Samples pro Sekunde erreichen. Wenn du ein anderes Arduino-Board als den Arduino Uno oder eine andere Version der Bibliotheken verwendest, erhältst du möglicherweise ein anderes Ergebnis. In diesem Fall passe einfach den Wert in meinem Code an deine eigene Messung an.

Die Geschwindigkeit der Sinusberechnungen

Um ein Sinussignal auszugeben, müssen wir die entsprechenden Samples erzeugen. Dazu werden wir die Funktion sin verwenden. Es gibt jedoch ein kleines Problem: Der Arduino ist nicht sehr schnell, wenn es um Fließkomma-Arithmetik geht. Versuche einmal, in dem Code, den wir gerade benutzt haben, um die maximale Abtastrate zu bestimmen, values[i] durch sin(i) zu ersetzen. Wenn man dies tut, kann man sehen, dass sich die benötigte Zeit pro Sample um etwa 100 us erhöht. Die Berechnung zur Ermittlung der wirklichen Werte ist noch einmal etwas komplizierter und würde die Zeit, die wir für die Ausgabe eines Samples benötigen, auf etwa 350 us ansteigen lassen.

Das Gute ist, dass eine Sinuswelle keine zufällige Folge von Samples ist. Jede Periode ist gleich. Das heißt, wenn wir die Abtastfrequenz so einstellen, dass sie ein Vielfaches der Frequenz der Sinusschwingung ist, können wir in jeder Periode die gleichen Daten ausgeben. Sobald wir die gewünschte Frequenz kennen, können wir diese Daten vorausberechnen und in einem Array speichern. Auf diese Weise kann unsere Ausgabeschleife einfach die Werte aus dem Array lesen und sie mit dem richtigen Timing an den DAC senden.

Vorausberechnung der Samples

Wenn wir unsere maximale Abtastrate von 5000 Samples pro Sekunde verwenden, können wir in jeder Periode einer 50-Hz-Sinuswelle bis zu 100 Samples ausgeben. Dies ist die Anzahl der Samples, die wir vorausberechnen und in unserem Puffer ablegen müssen. Um die eigentliche Berechnung durchzuführen, können wir die 100 Schritte auf Zahlen zwischen \(0\) und \(2 \cdot \pi\) abbilden und das Ergebnis in die sin-Funktion stecken. sin gibt immer eine Zahl zwischen -1 und 1 zurück. Um dieses Ergebnis mit dem DAC auszugeben, müssen wir es in den Bereich zwischen 0 und 4095 transformieren. Unser neuer Mittelpunkt ist dabei 2048, was 2,5 V entspricht. Da wir mit dem DAC nicht die vollen 5 V ausgeben können, müssen wir einen Kompromiss eingehen und stattdessen 4,989 V verwenden. Dies entspricht 4095, was unser Maximalwert ist. Aus Gründen der Symmetrie sollten wir dann 1 als unseren niedrigsten Wert verwenden. Dies entspricht 1,22 mV. Wenn wir all dies berücksichtigen, erhalten wir das folgende Stück Code, um unsere Samples vorauszuberechnen:

// Precalculate Sine Values (1 - 4095)
for(int i = 0; i < steps; i++) {
   voltages[i] = sin(i*3.14*2/steps) * 2047 + 2048;
}

Wir sind allerdings noch nicht fertig. Für unsere Schleife müssen wir stets die Anzahl der Samples pro Periode kennen. Wie bereits gesagt, kommen wir bei einer 50 Hz-Sinuswelle und einer Abtastrate von 5000 Samples pro Sekunde auf maximal 100 Samples pro Periode. Um diese Zahl auch für andere Frequenzen zu berechnen, können wir einfach die Abtastfrequenz durch die Frequenz der Sinusschwingung dividieren:
\(n_{steps} = {f_{samp} \over f_{sine}}\)

Es gibt jedoch noch ein zusätzliches Problem: Um unseren Puffer mit vorberechneten Werten verwenden zu können, muss die Abtastfrequenz ein echtes Vielfaches der Sinusfrequenz sein. Zusätzlich wäre es gut, sicherzustellen, dass bestimmte charakteristische Punkte der Sinuswelle immer ausgegeben werden. Zumindest sollte sichergestellt werden, dass das Maximum und das Minimum exakt übereinstimmen. Ich habe zusätzlich die Nulldurchgänge in diese Menge von charakteristischen Punkten aufgenommen. Das heißt, wir brauchen mindestens 4 Samples pro Periode. Wie im Bild unten zu sehen, können wir die Sinuskurve noch weiter unterteilen und weitere Samples hinzufügen. Die Anzahl der Schritte muss jedoch immer ein Vielfaches von vier sein, damit die charakteristischen Punkte erhalten bleiben. Sinuswelle mit markierten charakteristischen Punkten

Für 50 Hz passt alles auf Anhieb. Bei einem Wert wie 21,7 Hz ist das jedoch anders. Unsere Abtastfrequenz muss nun ein Vielfaches von 21,7 Hz sein. Die maximal mögliche Abtastfrequenz, um dies mit unserem DAC zu erreichen, ist 4991 Hz. Dies würde jedoch bedeuten, dass wir 230 Schritte pro Periode haben. 230 ist kein Vielfaches von 4. Wir müssen also die Abtastfrequenz etwas weiter absenken, damit die Anzahl der Samples pro Periode ein Vielfaches von 4 ist. Ein möglicher Wert wäre 4947,6 Hz, was 228 Schritte pro Periode ergibt.

Programmatisch lässt sich dies wie folgt lösen:

// Calculate number of possible steps
int possible_steps = 1000000/usPerCommand/frequency;

// Steps need to be a multiple of 4 to keep the sine form
steps = (possible_steps  / 4) * 4;
if(steps > BUFFER_SIZE) steps = BUFFER_SIZE;
if(steps < 4) steps = 4;

// Time per Step
usPerStep = 1000000 / (frequency * steps);
if(usPerStep < usPerCommand) usPerStep = usPerCommand;

Der Code macht sich die Tatsache zunutze, dass bei der Arbeit mit ganzen Zahlen der Rest einer Division verworfen wird. Indem wir zuerst durch vier dividieren und dann mit vier multiplizieren, erhalten wir die höchstmögliche Anzahl von Samples pro Periode, die ein Vielfaches von 4 ist. Die zusätzlichen Überprüfungen stellen sicher, dass die Anzahl der Schritte nie kleiner als 4 und nie größer als die Größe unseres Puffers ist. Dies begrenzt die maximale Frequenz unserer Wechselstromquelle auf 1,25 kHz. Für diese hohe Frequenz erhalten wir jedoch nur das Minimum von 4 Samples pro Periode und können nur zwischen 3 verschiedenen Spannungswerten unterscheiden. Für niedrigere Frequenzen erhalten wir eine deutlich bessere Auflösung. Dies ist genau das, was wir erreichen wollten.

Um später das richtige Timing zu ermöglichen, verwendet der Code die ermittelte Anzahl der Samples pro Periode auch zur Berechnung der realen Abtastfrequenz oder genauer gesagt der Zeit, die vergehen muss, bevor das nächste Sample ausgegeben werden soll. In unserer Ausgabeschleife werden wir diesen Wert verwenden, um entsprechend lange zu warten. So können wir ein korrektes Timing erzwingen und somit die gewünschte Frequenz erreichen.

Alles zusammen

Nun setzen wir alles zusammen. Der erste Schritt besteht darin, den DAC zu initialisieren. Danach müssen wir die gewünschte Frequenz vom Benutzer abfragen. Dies kann mit Serial.parseFloat() gemacht werden. Damit unsere Wechselstromquelle später auch ohne angeschlossenen Computer genutzt werden kann, habe ich noch ein bisschen Code hinzugefügt, der die Frequenz im EEPROM des Arduinos speichert. Der EEPROM ist ein kleiner Speicherbereich, dessen Inhalt auch dann erhalten bleibt, wenn die Stromversorgung unterbrochen wird. Er wird oft verwendet, um Konfigurationswerte wie in unserem Fall die gewünschte Frequenz zu speichern. Wenn der Benutzer nicht innerhalb von 10 s eine Zahl eingibt, verwendet das Programm die gespeicherte Frequenz. Um die Frequenz zu ändern, muss nur der Reset-Knopf am Arduino Uno gedrückt werden, und man wird aufgefordert, die Frequenz erneut einzugeben.

Sobald wir die Frequenz kennen, wird eine geeignete Abtastrate bestimmt und die Samples werden vorausberechnet. In der Loop-Prozedur geben wir dann diese Samples aus. Wenn wir das Ende unseres Sample-Puffers erreichen, fangen wir einfach wieder am Anfang an. Um das richtige Timing zu gewährleisten, wird eine leere while-Schleife verwendet, die mit der Funktion micros prüft, ob die Zeit bis zur Ausgabe des nächsten Samples bereits verstrichen ist. Nachdem wir lange genug gewartet haben, inkrementieren wir die Startzeit für die nächste Iteration. Es ist wichtig, dass wir nicht einfach start_time = micros() schreiben, da zwischen den beiden Aufrufen von micros ebenfalls eine gewisse Zeit vergeht. Diese Zeit würde für uns verloren gehen, und wir würden somit die Samples zu langsam ausgeben. Außerdem hat die Funktion micros nur eine Genauigkeit von 4 us. In manchen Fällen werden wir mit unserer Schleife die geforderte Wartezeit nicht exakt einhalten können. Da wir die start_time immer um die Anzahl der Mikrosekunden inkrementieren, die wir hätten warten sollen, und nicht um die Zeit, die wir tatsächlich gewartet haben, ist die Wartezeit in der nächsten Iteration etwas kürzer, falls wir ein bisschen zu lange warten. Auf diese Weise summieren sich die verbleibenden Timing-Fehler nicht auf und sind daher nicht sichtbar.

Nachdem das geklärt ist, hier nun der vollständige Code:

#include <Adafruit_MCP4725.h>
#include <EEPROM.h>

// DAC
const int I2C_ADDR = 0x60;
Adafruit_MCP4725 dac;

// Time needed for each iteration
const int usPerCommand = 200;

// Precalculated Voltage Buffer
const int BUFFER_SIZE = 256;
unsigned int voltages[BUFFER_SIZE];
unsigned int steps;
unsigned int usPerStep;

unsigned int current_step = 0;
unsigned long start_time;

// Setup frequency and DAC
void setup() {
  // Init DAC
  dac.begin(I2C_ADDR);
  
  // Read desired frequency
  Serial.begin(9600);
  Serial.print("Enter Frequency (Hz): ");

  // Wait 10s for input otherwise take stored value
  Serial.setTimeout(10000);
  float frequency = Serial.parseFloat();
  if(frequency == 0) EEPROM.get(0, frequency);
  else EEPROM.put(0, frequency);

  Serial.println(frequency);

  // Calculate number of possible steps
  int possible_steps = 1000000/usPerCommand/frequency;

  // Steps need to be a multiple of 4 to keep the sine form
  steps = (possible_steps  / 4) * 4;
  if(steps > BUFFER_SIZE) steps = BUFFER_SIZE;
  if(steps < 4) steps = 4;

  // Time per Step
  usPerStep = 1000000 / (frequency * steps);
  if(usPerStep < usPerCommand) usPerStep = usPerCommand;

  // Precalculate Sine Values (1 - 4095)
  for(int i = 0; i < steps; i++) {
    voltages[i] = sin(i*3.14*2/steps) * 2047 + 2048;
  }

  Serial.print("Number of output steps: ");
  Serial.println(steps);

  Serial.print("Microseconds per step: ");
  Serial.println(usPerStep);

  Serial.print("Archieved Frequency (Hz): ");
  Serial.println(1000000.0/float(steps)/float(usPerStep));

  // Initially set start time
  start_time = micros();
}

// Output values
void loop() {
  dac.setVoltage(voltages[current_step], false);
  current_step++;
  if(current_step >= steps) current_step = 0;

  while(micros()-start_time < usPerStep);
  start_time += usPerStep;
}

Das Ergebnis

Offen gesagt ist der Code dieses Mal etwas komplexer, aber keine Sorge, falls du ihn nicht ganz verstehst. Sobald du das Programm auf den Arduino hochgeladen hast, kannst du sehen, was es macht. Es begrüßt uns mit einer Aufforderung, die Frequenz in den seriellen Monitor einzugeben. Gib hier einfach deine gewünschte Frequenz ein. Die maximale Frequenz beträgt 1250 Hz. Wenn du kein Oszilloskop hast, kannst du auch eine sehr langsame Frequenz wie 0,01 Hz wählen und das Ausgangssignal mit einem Multimeter untersuchen.

Frequenzabfrage im seriellen Monitor

Nach Eingabe der Frequenz gibt das Programm die berechnete Anzahl der Schritte pro Periode und die Wartezeit bis zum nächsten Sample aus und beginnt mit der Ausgabe des Sinussignals. Das folgende Bild zeigt das mit meinem Oszilloskop gemessene Sinussignal.

Gemessenes Sinussignal

Das Signal hat eine Spitze-zu-Spitze-Spannung von 5 V, eine Amplitude von 2,5 V und eine Effektivspannung von 1,77 V. Der maximale Strom ist auf ein paar Milliampere begrenzt, nicht nur durch den Spannungsteiler, sondern auch durch den DAC, dessen Ausgang instabil wird, sobald ein höherer Strom fließt.

Obwohl ihre Leistung stark begrenzt, würde es uns diese Wechselstromquelle schon erlauben, mit einigen einfachen Gleichrichterschaltungen zu experimentieren. Wir werden uns in einem der zukünftigen Teile dieses Projekts ansehen, wie man die Leistung erhöhen kann. Vorher werde ich aber noch einige weitere Techniken zur Erzeugung einer Sinuswelle mit dem Arduino Uno zeigen.

Vorheriger Beitrag Nächster Beitrag