Wechselstromquelle (Teil 9)

Elektronik AC PWM Transistoren

Wir haben uns eine funktionierende, aber recht komplexe Wechselstromquelle gebaut. Unsere Lösung ist aber nicht die einzig mögliche.

Ein alternativer Weg eine Wechselstromquelle zu bauen

Auch wenn unser vorheriges Design einer Wechselstromquelle mit dem MCP4725 und dem PWM-DAC gut funktioniert hat, ist die Schaltung doch recht komplex geworden. Wir benötigten ziemlich viele Bauteile für die drei Teile unserer Schaltung: Signalerzeugung, Verstärkung mit dem Operationsverstärker und die externe Push-Pull-Stufe. Unsere Lösung war wie folgt: Wir haben ein schwaches analoges Signal erzeugt, das dann durch den Operationsverstärker und die Push-Pull-Stufe verstärkt wurde. Wenn wir PWM zur Signalerzeugung verwenden, können wir eine andere Methode anwenden. Wir können das digitale PWM-Signal direkt verstärken. Das verstärkte Ausgangssignal wird dann erst anschließend mit einem LC-Filter in eine Sinuswelle umgewandelt. Lass uns loslegen und schauen, wie diese alternative Lösung funktioniert!

Aufbau der Schaltung

Hier ist die neue Schaltung:

Diese Schaltung sieht zwar ähnlich aus wie die Push-Pull-Stufe, die wir im vorherigen Teil dieses Projekts verwendet haben, aber das Arbeitsprinzip dieser Schaltung ist anders. In dieser Schaltung werden die Transistoren als Schalter und nicht als Verstärker für ein analoges Signal verwendet. Die Schaltung wird nun direkt durch das PWM-Signal angesteuert. Der LC-Tiefpass-Filter ist es, der das digitale Signal in eine Sinuswelle umwandelt. Ein LC-Filter hat einen ähnlichen Effekt wie ein RC-Filter, mit dem Unterschied, dass sowohl die Induktivität als auch der Kondensator die hochfrequenten Anteile des Signals unterdrücken.

Ein LC-Filter ist ein Filter zweiter Ordnung und hat eine steilere Frequenzkurve mit einer Steigung von -40 dB/Dekade anstelle von nur -20 dB/Dekade. In der untenstehenden Grafik ist der Frequenzgang beider Filtertypen für eine Grenzfrequenz von 100 Hz dargestellt. Die rote Linie zeigt den Frequenzgang des RC-Filters 1. Ordnung, die blaue Linie zeigt den Frequenzgang eines LC-Filters.

Frequenzgang eines Tiefpassfilters 2. Ordnung im Vergleich zu einem Filter 1. Ordnung (fc = 100 Hz)

In der Schaltung werden eine 1 mH Spule und ein 10 uF Keramikkondensator verwendet. Dieser Filter hat die folgende Grenzfrequenz:
\(f_c = {1 \over 2 \pi \sqrt{L C}} = {1 \over 2 \pi \sqrt{1 mH \cdot 10 uF}} \approx 1,6 kHz\)

Der Vorteil dieses Filtertyps ist, dass es einfacher ist, einen Filter mit einem vergleichsweise geringen Gleichstromwiderstand zu bauen. Dies ist wichtig, da das Signal nun nach der Verstärkung gefiltert wird und somit das Filter leistungsstark genug für die angeschlossene Last sein muss. Der von mir verwendete LC-Filter hat einen Gleichstromwiderstand von 5 Ω, was recht hoch ist. Es ist jedoch möglich, einen LC-Filter mit einem kleineren Widerstand und höheren Ausgangsleistungen zu entwerfen, wenn man eine entsprechende Spule nutzt.

Zusätzlich zum LC-Filter befindet sich ein 10 uF Kondensator zwischen 5 V und GND. Dieser Kondensator stabilisiert die Versorgungsspannung des Arduinos, während die Transistoren schalten. Durch die Spule können beim Schalten hohe Stromspitzen auftreten, insbesondere während das Magnetfeld zusammenbricht. In einer professionelleren Schaltung würde man Rücklaufdioden einsetzen. Ich habe mich dagegen entschieden, um die Schaltung so einfach wie möglich zu gestalten. In unserem Fall ist das kein großes Problem, da Induktivität und Spannung vergleichsweise klein sind.

Aber schauen wir uns mal an, wie die Transistorschaltung selbst funktioniert. Wenn der PWM-Ausgang D9 auf HIGH und der PWM-Ausgang D10 auf LOW steht, werden nur der obere linke und der untere rechte Transistor über den Transistor T5 eingeschaltet. Die linke Seite der Last ist mit 5 V verbunden, während die rechte Seite mit Masse verbunden ist. Wie im Bild unten gezeigt, fließt ein Strom durch die beiden Transistoren und die Last. Der Stromfluss ist in der konventionellen Stromrichtung dargestellt.

Stromfluss, wenn D9 auf HIGH und D10 auf LOW ist (konventionelle Stromrichtung)

Wenn D10 HIGH und D9 LOW ist, fließt der Strom in die entgegengesetzte Richtung. Auf diese Weise erhalten wir eine negative Spannung.

Stromfluss, wenn D9 LOW und D10 HIGH ist (konventionelle Stromrichtung)

Die Transistorschaltung ist auch als H-Brückenschaltung bekannt und wird häufig zur Steuerung von Motoren verwendet. Bei Motoren ermöglicht sie uns, den Motor in beide Richtungen drehen zu lassen. Ein weiterer Anwendungsfall für diese Schaltung sind sogenannte Wechselrichter, die eine niedrige Gleichspannung wie 12 V in 230 V Wechselspannung umwandeln. Unsere Schaltung arbeitet im Prinzip genauso wie diese Wechselrichter. Sie ist jedoch nur für eine kleine Ausgangsspannung und Leistung ausgelegt.

Kommerzielle Wechselrichter sind wesentlich ausgereifter und beinhalten Sicherheitsmaßnahmen wie Überstrom- und Übertemperaturschutz. Bitte versuche nicht, diese Schaltung mit hohen Spannungen oder hohen Ausgangsleistungen zu verwenden.

Die endgültige Ausgangsspannung nach der Filterung hängt vom Tastgrad ab. Genau wie in unserer vorherigen Lösung können wir eine Sinuswelle erzeugen, indem wir den Tastgrad entsprechend der Sinuswerte ändern.

Der Tastgrad wird so eingestellt, dass das gewünschte Sinussignal entsteht

Beim Arbeiten mit H-Brücken ist es sehr wichtig, dass D9 und D10 nie gleichzeitig eingeschaltet sind. Wenn beide Transistoren in einem Bein der H-Brücke gleichzeitig eingeschaltet sind, erzeugt das einen Kurzschluss, der zur Zerstörung der H-Brücke führen kann.

Der Code

Das vollständige Programm sieht wie folgt aus:

#include <EEPROM.h>

// PWM update frequency is 31.25 kHz (~32 us)
const int usPerCommand = 100;

// Dead Time to prevent shoot through
const int deadTime = 5;

// 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
void setup() {
  // 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 0-5 V
  for(int i = 0; i < steps; i++) {
    int value = sin(i*3.14*2/steps) * (126.5-deadTime) + 127.5;
    voltages[i] = constrain(value, 1, 254);
  }

  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));

  // Enable the fastest possible frequency for timer 1
  // choose phase correct mode and invert output 10
  TCCR1A = (1<<WGM10)|(1 << COM1B0);
  TCCR1B = (1<<CS10);
  TIMSK1 |= (1<<TOIE1);

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

ISR(TIMER1_OVF_vect) {
  analogWrite(9, voltages[current_step]-deadTime);
  analogWrite(10, voltages[current_step]+deadTime);
}

// Output values
void loop() {
  current_step++;
  if(current_step >= steps) current_step = 0;

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

Insgesamt ist dieses Programm dem für unsere vorherige Lösung sehr ähnlich. Es gibt jedoch eine sehr wichtige Änderung. Die folgende Code-Zeile invertiert den PWM-Ausgang D10 und aktiviert den sogenannten phasenkorrekten PWM-Modus:

TCCR1A = (1<<WGM10)|(1 << COM1B0);

Diese Funktionen werden von dem auf dem Arduino Uno-Board verwendeten ATmega328P-Mikrocontroller bereitgestellt. Die Details hierzu kannst du in dessen [Datenblatt] nachlesen (https://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-7810-Automotive-Microcontrollers-ATmega328P_Datasheet.pdf).

Da sich die Hardware um die Invertierung des Ausgangssignals an D10 kümmert, können wir einfach das gleiche Tastverhältnis für beide PWM-Pins einstellen:

analogWrite(9, voltages[current_step]-deadTime);
analogWrite(10, voltages[current_step]+deadTime);

Es gibt jedoch mehrere Probleme mit dieser Methode, um die wir uns kümmern müssen. Das erste: Die Arduino-Core-Bibliotheken sind nicht für den Fall konzipiert, dass der Ausgang an D10 invertiert wird. Dies wird ein Problem für die Werte 0 und 255. Für beide Werte schaltet die Arduino-Core-Bibliothek, die die Funktion analogWrite bereitstellt, die PWM-Funktionalität ab und setzt den Pin auf HIGH oder LOW. Da sie nicht weiß, dass wir den Ausgang an D10 invertiert haben, werden beide Pins, D10 und D9, auf LOW gesetzt, wenn der Wert 0 ist und auf HIGH, wenn der Wert 255 ist. Das ist nicht das, was wir wollen. Um dieses Problem zu lösen, enthält der Code einen Workaround, der sicherstellt, dass die Werte 255 oder 0 nicht auftreten. Dies wird durch die folgenden Codezeilen erreicht:

// Precalculate Sine Values 0-5 V
for(int i = 0; i < steps; i++) {
  int value = sin(i*3.14*2/steps) * (126.5-deadTime) + 127.5;
  voltages[i] = constrain(value, 1, 254);
}

Zunächst wird die Amplitude so gewählt, dass 0 und 255 nicht vorkommen. Die zusätzliche Verwendung der Funktion constrain stellt sicher, dass auch bei einem Wert von 0 stattdessen 1 verwendet wird. Ähnlich bei Werten über 255, hier wird stattdessen der Wert 254 verwendet. Dieser Aufruf ist nicht notwendig, verhindert aber, dass man beim Experimentieren und Anpassen des Codes aus Versehen einen Kurzschluss erzeugt. Es ist quasi eine Art Safe-Guard. Durch die Konvertierung von float nach int könnte z.B. versehentlich ein Wert von 0 oder 255 entstehen, wenn die Werte anders gewählt werden.

Das zweite Problem ist, dass die Invertierung des Signals allein nicht ausreicht, um sicherzustellen, dass die beiden Ausgänge niemals gleichzeitig aktiviert sind. Das Problem ist im folgenden Bild dargestellt:

Warum es notwendig ist, eine Totzeit einzuführen

Auch wenn wir ein digitales Ausgangssignal haben, benötigt der Wechsel zwischen HIGH und LOW aufgrund von Kapazitäten in der Schaltung immer ein wenig Zeit. Während der Umschaltzeit ist es möglich, dass beide Transistoren gleichzeitig eingeschaltet sind. Um wirklich sicherzugehen, dass das nicht passiert, muss man deshalb eine sogenannte Totzeit einführen. Eine zusätzliche Zeit, in der beide Transistoren ausgeschaltet sind. Dazu verkürzen wir die Einschaltzeiten in den PWM-Signalen. Da der Ausgang an D10 invertiert ist, müssen wir in diesem speziellen Fall die Totzeit addieren.

analogWrite(9, voltages[current_step]-deadTime);
analogWrite(10, voltages[current_step]+deadTime);

Das letzte Problem, das gelöst werden muss, ist ein bisschen knifflig. Wenn wir Pech haben, könnte der Timer den Tastgrad zwischen unseren beiden analogWrite Aufrufen aktualisieren. Wenn das passiert, könnte das Tastverhältnis für D9 aktualisiert worden sein, während das Tastverhältnis für D10 nicht aktualisiert wurde. In diesem Fall können wir wieder auf das Problem stoßen, dass beide Transistoren zur gleichen Zeit eingeschaltet werden. Das zugrunde liegende Problem ist, dass die Aufrufe von analogWrite nicht mit dem Timer synchronisiert sind. Wenn wir diese Werte synchron zum Timer aktualisieren wollen, müssen wir sie innerhalb der sogenannten Interrupt Service Routine (ISR) für Timer 1 aktualisieren. Falls du dich gefragt hast, warum die analogWrite jetzt in einer anderen Methode mit dem seltsamen Namen ISR(TIMER1_OVF_vect) sind, kennst du jetzt die Antwort. Diese Methode ist genau die ISR, die ich zuvor erwähnt habe. Um die ISR nutzen zu können, müssen wir noch den zugehörigen Interrupt aktivieren. Dies geschieht durch die folgende Codezeile in setup:

TIMSK1 |= (1<<TOIE1);

Das Ergebnis

Schauen wir uns das Ergebnis an. Ohne den Lastwiderstand erhalten wir eine Sinuswelle mit 5 V Amplitude. Sehr schön.

Ausgangssignal auf dem Oszilloskop

Sobald wir jedoch den Lastwiderstand zuschalten, verringert sich die Amplitude der Sinuswelle. Das folgende Bild zeigt das Ausgangssignal mit angeschlossenem 51 Ω Lastwiderstand:

Ausgangssignal mit 51 Ω Lastwiderstand

Der Hauptgrund dafür ist der Gleichstromwiderstand der Spule. Die Spule und der Lastwiderstand bilden einen Spannungsteiler. Dies bewirkt, dass die Amplitude des Signals vom Lastwiderstand abhängig ist. Mit steigendem Strom nimmt außerdem der Spannungsabfall über den Transistoren zu. Beide Effekte bewirken eine geringere Amplitude bei größeren Lasten.

In diesem Beispiel beträgt die Ausgangsleistung 150 mW. Das ist nicht viel, aber für kleine Schaltungen ausreichend. Mit anderen Transistoren und einer anderen Spule sind leicht höhere Ausgangsleistungen möglich. Zusätzlich ist es möglich, die H-Brücke an eine höhere Gleichspannung als die 5 V vom Arduino anzuschließen. Anstatt sie an 5 V anzuschließen, kann man sie an Vin anschließen und dann den Arduino mit einer höheren Spannung versorgen.

Wie sieht es mit dem Wirkungsgrad aus? Nun, es ist nicht einfach, den Wirkungsgrad für einen Schaltkreis zu berechnen, der mit einer hohen Frequenz schaltet. Ich habe jedoch einige Messungen bei 5 V mit einer 51 Ω Last durchgeführt. Bei diesen Parametern liegt der Wirkungsgrad bei etwa 25 %. Der Grund für diesen geringen Wirkungsgrad ist der Spannungsabfall über der Spule und die Tatsache, dass in diesem Beispiel der Basisstrom für die Transistoren fast so hoch ist wie der Ausgangsstrom. Bei höheren Ausgangsspannungen steigt der Wirkungsgrad an. Theoretisch können H-Brücken-Wechselrichter einen hohen Wirkungsgrad von bis zu 99 % haben. Mit einer eigenen Schaltung und bei niedrigen Spannungen ist es jedoch nicht ohne weiteres möglich, solch hohe Wirkungsgrade zu erreichen. Strebt man einen hohen Wirkungsgrad an, ist es zudem ratsam, die bipolaren Sperrschichttransistoren durch MOSFETs zu ersetzen. Im nächsten Teil dieser Serie werden wir uns mit diesem Thema befassen und unsere selbstgebaute H-Brückenschaltung durch ein H-Brücken-Modul ersetzen, das MOSFETs nutzt.

Vorheriger Beitrag Nächster Beitrag