In diesem Beitrag geht es darum, wie analoge Werte ausgegeben werden, um zum Beispiel die Helligkeit einer LED zu variieren oder die Farbe einer RGB-LED zu ändern. Es wird auch gezeigt, wie man mit dem Mikrocontroller Töne erzeugen kann.
Benötigte Hardware
Anzahl |
Bauteil |
Anmerkung |
1 |
|
|
1 |
9V-Blockbatterie oder 9V-Netzteil |
optional |
Analogausgänge
Eigentlich hat der ATMEGA328P nur digitale Ausgänge. Das heißt, die Spannung am Ausgang ist entweder 0 V oder 5 V. Man kann aber auch mit digitalen Ausgängen analoge Signale erzeugen, indem man sie immer wieder ein- und ausschaltet. Das Verhältnis der Einschaltdauer zur Ausschaltdauer bestimmt dann den Analogwert. Ist der Ausgang dauernd eingeschaltet, so ist die Ausgangsspannung 5 V. Ist er dauernd ausgeschaltet, so ist die Ausgangsspannung 0 V. Ist der Ausgang gleich lang ein- und ausgeschaltet, so ist der Mittelwert der Ausgangsspannung 2.5 V. Da die Pulsbreite den Analogwert bestimmt, nennt man dieses Verfahren Puls-Weiten-Modulation oder kurz PWM (pulse width modulation). Der ATMEGA328P kann mit der Funktion analogWrite(pin,value); analoge Werte an einem Pin ausgeben. Der Pin muss wie bei digitalWrite(pin,value); mit der Funktion pinMode(pin, OUTPUT); als Output definiert werden. Der angegebene Wert (value) kann zwischen 0 und 255 liegen. Die Einschaltdauer wird dabei zwischen 0 und 100% verändert.
Beim ATMEGA328P können nur sechs der Pins für PWM genutzt werden. Dies sind die Pins 5 und 6 mit einer Frequenz von etwa 1 kHz und die Pins 9, 10, 11 und 3 mit einer Frequenz von etwa 500 Hz. Zur Erzeugung des PWM-Signals werden die Timer des Mikrocontrollers benutzt. Für die Pins 5 und 6 ist das der Timer0, für die Pins 9 und 10 der Timer1 und für die Pins 11 und 3 der Timer2. Werden die Timer auch anderweitig benutzt kann es zu Konflikten führen.
Der Timer0 wird für den internen Millisekunden-Zähler genutzt, deshalb ist die PWM-Frequenz für die Pins 5 und 6 gleich 1 kHz. Solange man die PWM-Frequenz nicht verändern möchte, kann der Millisekunden-Zähler und PWM auf Pin 5 und 6 problemlos gleichzeitig genutzt werden.
Der Timer2 wird zur Tonerzeugung genutzt. Eine Beschreibung dieser Funktion erfolgt später in diesem Beitrag. Für die Tonerzeugung wird die Frequenz des Timers verändert und das Tastverhältnis sollte 50% betragen. Hier ist eine gleichzeitige Verwendung mit PWM nicht möglich.
Um eine annähernde Gleichspannung zu erhalten, muss die Energie, die während der Einschaltdauer abgegeben wird, gespeichert und während der Ausschaltdauer wieder abgegeben werden. Dies geschieht am einfachsten mit einem Widerstand und einem Kondensator. Man bezeichnet so eine Schaltung als RC-Glied
Während der Ausgang auf 5 V ist, wird der Kondensator über den Widerstand aufgeladen und während er auf 0 ist, wieder über den Widerstand entladen.
Das Bild zeigt das PWM-Signal am Eingang (gelbe Linie) und den Mittelwert am Ausgang des RC-Glieds (blaue Linie) für 25%, 50% und 75%
Das Produkt R * C nennt man Zeitkonstante des RC-Glieds. Das ist jene Zeit, die benötigt wird, um den Kondensator auf 63.2% der maximalen Spannung zu laden. Für eine gute Siebwirkung sollte die Zeitkonstante mindestens zehnmal so groß, wie die Periodendauer des PWM-Signals sein. Also für 1 kHz sind 10 ms erforderlich. Mit einem Widerstand von 10 kΩ ergibt sich eine Kapazität von
10-2 s / 104 Ω = 10-6 F oder 1 µF. die folgende Abbildung zeigt die Siebwirkung mit einem Kondensator von 0.1 µF = 1 ms und einem Kondensator von 1 µF = 10 ms.
Mit einer höheren Zeitkonstante kann die Siebwirkung weiter verbessert werden. Allerdings führt das zu einer verzögerten Reaktion auf Änderungen des Mittelwerts. Die folgende Abbildung zeigt die Ausgangsspannung, wenn alle 0.1 s abwechselnd 0 und 128 mit analogWrite(pin,value) ausgegeben wird. Man sieht deutlich den exponentiellen An- bzw. Abstieg.
Eine andere Möglichkeit den Mittelwert zu bilden ist eine Induktivität. In diesem Fall wird nicht die Spannung, sondern der Strom gespeichert. Beim Einschalten steigt der Strom exponentiell an und nimmt beim Ausschalten ebenso ab. So wie am Kondensator eine mittlere Spannung erreicht wurde, wird an der Induktivität ein mittlerer Strom erreicht. Dies macht man sich bei Gleichstrom-Motoren zu Nutze, um mit dem PWM-Signal die Drehzahl zu regeln.
Eine dritte Möglichkeit ist es, Trägheit des Auges zu nutzen, um auf diese Weise die Helligkeit zu steuern. Steuert man mit dem PWM-Signal eine Leuchtdiode an, so ist diese in der Lage, sofort zu reagieren. Das bedeutet, die Leuchtdiode erzeugt Lichtblitzte mit voller Helligkeit, wobei die Dauer des Lichtblitzes von der Pulsdauer des PWM-Signals abhängt. Das menschliche Auge kann aber diesem schnellen Wechsel nicht folgen, sondern nimmt je nach Pulsdauer ein mehr oder weniger helles Leuchten wahr.
Dimmen einer LED mit PWM
Die Schaltung ist ganz einfach. Wie bei der Ampel im ersten Teil, wird die LED über einen 1 kΩ Widerstand mit dem Ausgangspin des Mikrocontrollers verbunden. Diesmal wird Pin 5 gewählt, da dieser PWM unterstützt. Die PWM-Frequenz ist 970 Hz.
Die LED sollte langsam immer heller und nachdem Erreichen eines bestimmten Maximalwertes wieder dunkler werden. Da zwischen 50% und 100% die Helligkeitsänderung kaum wahrnehmbar ist, wird als Maximalwert 128 verwendet. Der Sketch dazu ist sehr einfach.
#define LEDPIN 5
#define MAX 128
Zuerst werden zwei Konstanten definiert. Eine für die verwendete Pin-Nummer und die andere für den maximalen Ausgabewert
int wert = 0;
boolean auf = true;
In der globalen Integer-Variablen wert wird der aktuelle Ausgabewert gespeichert. Der Anfangswert ist 0. Die boolsche Variable auf hält fest, ob der Wert erhöht werden soll, oder nicht.
void setup() {
pinMode(LEDPIN, OUTPUT);
}
In der Setup-Funktion wird nur der Pin 5 als Ausgang definiert
void loop() {
analogWrite(LEDPIN, wert);
In der Hauptschleife wird zuerst der aktuelle Wert mit analogWrite(pin, value); ausgegeben. Es folgt eine Bedingung, die mit dem Kommando if eingeleitet wird. Danach folgt in Klammern die Bedingung. Bedingungen haben immer einen boolschen Wert, das heißt, sie sind wahr oder falsch. Die globale Variable auf kann daher direkt als Bedingung verwendet werden. Alle Kommandos innerhalb der nachfolgenden geschlungenen Klammern werden nur ausgeführt, wenn die Bedingung wahr ist. In diesem Fall also, wenn der Wert erhöht werden soll. Das Kommando wert++; ist eine verkürzte Schreibweise des Kommandos wert = wert + 1; erhöht also den Wert um 1. Nun folgt eine weitere Bedingung. Wenn der Inhalt der Variablen wert den Maximalwert überschreitet, soll die Richtung umgeschaltet auf = false; und die Variable wert auf den Maximalwert gesetzt werden.
if (auf) {
wert ++;
if (wert > MAX) {
auf = false;
wert = MAX;
}
}
Mit dem Kommando else beginnt ein Programmteil, der nur ausgeführt wird, wenn die Bedingung im zugehörigen if Kommando nicht erfüllt wird. Das gilt wieder für alle Kommandos innerhalb der nachfolgenden geschlungenen Klammern. In diesem Fall also, wenn der Wert vermindert werden soll. Das Kommando wert- -; ist eine verkürzte Schreibweise des Kommandos wert = wert - 1; vermindert also den Wert um 1. Nun folgt eine weitere Bedingung. Wenn der Inhalt der Variablen wert kleiner als Null ist, soll die Richtung umgeschaltet auf = true; und die Variable wert auf 0 gesetzt werden.
else
{
wert --;
if (wert < 0) {
wert = 0;
auf = true;
}
}
Zuletzt wird noch eine Verzögerung von 50ms eingebaut und die Funktion loop() mit der geschlungenen Klammer abgeschlossen.
delay(50);
}
RGB - Leuchtdiode
Im Set ist auch eine sogenannte RGB-Leuchtdiode.
Bei einer RGB-Led sind drei Leuchtdioden in einem Gehäuse untergebracht. Eine rote, eine grüne und eine blaue. Um die Anzahl der Anschlussdrähte klein zu halten, werden entweder die Anoden oder die Kathoden aller Dioden zusammengefasst. Die RGB-Led im Set hat eine gemeinsame Kathode.
Der längste Anschlussdraht ist die Kathode, die beiden mittleren rot und grün und der kürzeste blau. Wie bei der einfachen Leuchtdiode, sind Vorwiderstände zur Strombegrenzung erforderlich.
Farbenspiel mit RGB - Led
Als Beispiel-Sketch soll eine RGB-Led so angesteuert werden, dass sich die Farbe zufällig ändert. Dazu wird die RGB-Led über drei 1 kΩ Widerstände mit den Pins 9, 10 und 11 des Mikrocontrollers verbunden. Diese drei Pins unterstützen PWM, sind also als analoge Ausgänge nutzbar. Die Schaltung sieht dann so aus.
Im Sketch werden drei globale Variablen genutzt, die die aktuellen Werte für rot, grün und blau speichern. In der Hauptschleife werden diese drei Werte auf die entsprechenden Pins ausgegeben. Danach wird zufällig eine der drei Variablen verändert.
Am Beginn werden die verwendeten Pins als Konstanten definiert. Außerdem wird die Konstante DELTA definiert. Diese wird verwendet, um den Wert einer Farbe zu ändern. Je größer dieser Wert, desto sprunghafter wird der Farbwechsel.
#define PINROT 11
#define PINGRUEN 10
#define PINBLAU 9
#define DELTA 4
Drei globale Variable für die aktuellen Farbwerte. Wertebereich 0 bis 255.
int rot;
int gruen;
int blau;
In der setup() Funktion werden die drei Pins als Ausgang definiert und die Farbwerte auf ihren Startwert gesetzt.
void setup() {
pinMode(PINROT, OUTPUT);
pinMode(PINGRUEN, OUTPUT);
pinMode(PINBLAU, OUTPUT);
rot = 128;
gruen = 128;
blau = 128+9;
}
In der Hauptschleife erfolgt die Ausgabe und die Änderung der Werte. Zuerst werden die Werte an die Pins übertragen
void loop() {
analogWrite(PINROT, rot);
analogWrite(PINGRUEN, gruen);
analogWrite(PINBLAU, blau);
Mit der Funktion random(MAX) wird eine Zufallszahl zwischen 0 und MAX – 1 erzeugt. Es gibt auch noch eine zweite Variante random(MIN, MAX) die eine Zufallszahl zwischen MIN und MAX – 1 erzeugt. Mit dem ersten Aufruf erhält man die Werte 0 oder 1. Dieser Wert wird in der lokalen Variablen auf gespeichert. Damit wird gesteuert, ob ein Wert erhöht oder vermindert werden soll. Der zweite Aufruf liefert die Zahlen 0, 1 oder 2. Das Ergebnis wird in der lokalen Variablen farbe gespeichert und dient dazu, den Farbwert auszuwählen, der geändert werden soll.
boolean auf = random(2);
int farbe = random(3);
Die folgende Programmzeile enthält eine bedingte Zuweisung:
VARIABLE = BEDINGUNG?WERT1:WERT2;
Der Variablen wird der Wert1 zugewiesen, wenn die Bedingung erfüllt ist, sonst der Wert2. Im folgenden Fall wird der lokalen Variablen d der Wert der Konstanten DELTA zugewiesen, wenn auf den Wert 1 hat, sonst der negative Wert der Konstanten DELTA
int d = (auf)?DELTA:-DELTA;
Nun soll je nach Inhalt der Variablen farbe der entsprechende Farbwert geändert werden. Dies könnte man durch mehrere Bedingungen erreichen. Eleganter und übersichtlicher ist jedoch das switch Kommando. Nach dem Schlüsselwort switch folgt in Klammern die Variable, die untersucht werden soll. Erlaubt sind hier nur ganze Zahlen oder Buchstaben. Danach folgen in geschlungenen Klammern die einzelnen Fälle. Jeder Fall beginnt mit dem Schlüsselwort case gefolgt vom Wert, den die Variable haben muss, damit der Fall eintritt. Nach dem Doppelpunkt folgen die Befehle, die auszuführen sind, wenn der Fall eintritt. Das Kommando break; am Ende bewirkt einen Ausstieg aus dem switch Kommando. Der letzte Fall kann auch an Stelle von case x : mit default : definiert werden. Das bedeutet, dass der Code nach dem Doppelpunkt immer dann ausgeführt wird, wenn keiner der davor aufgeführten Fälle eingetreten ist. Dieser Code wird auch dann ausgeführt, wenn ein vorangegangener Fall nicht mit break; abgeschlossen wurde.
Das Kommando rot += d; ist eine verkürzte Schreibweise für rot = rot + d;
switch(farbe) {
case 0: rot += d; break;
case 1: gruen += d; break;
case 3: blau += d; break;
}
Da durch die Änderungen auch Werte außerhalb des erlaubten Wertebereichs auftreten können, muss in einem solchen Fall der Farbwert auf den entsprechenden Grenzwert korrigiert werden
if (rot < 0) rot = 0;
if (rot >255) rot = 255;
if (gruen < 0) gruen = 0;
if (gruen >255) gruen = 255;
if (blau < 0) blau = 0;
if (blau >255) blau = 255;
Nach einer Verzögerung von 10 ms wird die Funktion der Hauptschleife mit einer geschlungenen Klammer abgeschlossen.
delay(10);
}
Wenn man ein Stück dünnes weißes Papier vor die Diode hält, kann man den Farbwechsel besser erkennen.
Töne erzeugen
Zum Schluss dieses Teils werde ich noch die beiden im Set vorhandenen Piezo-Buzzer beschreiben. Es handelt sich dabei um kleine Lautsprecher, die die Tatsache nutzen, dass sich ein piezoelektrischer Kristall verbiegt, wenn man Spannung anlegt. Diese Verbiegung wird auf eine Membran übertragen, die dann die Schallwellen erzeugt. Der Frequenzbereich liegt zwischen 300 Hz und 20 kHz. Am lautesten sind die Buzzer bei etwa 2 bis 3 kHz.
Einer der Buzzer, der mit dem Aufkleber, ist ein aktiver Buzzer. Er hat einen internen Tongenerator. Sobald Spannung angelegt wird, wird ein Pfeifton mit 2.5 kHz und einem Schallpegel von etwa 60 dB erzeugt. Zur Verwendung muss der Aufkleber entfernt werden. Die Polarität muss beachtet werden. Der Plus-Pol ist auf der Seite der Lasche des Aufklebers. Der Pluspol ist auch auf der Oberseite des Gehäuses markiert.
Die Ansteuerung erfolgt wie bei einer LED mit digitalWrite(PIN, WERT); wobei beim Wert 1 der Buzzer pfeift und beim Wert 0 ruhig ist. Der Beispiel-Sketch erzeugt ein Alarmsignal mit dem aktiven Buzzer. Der Buzzer wird mit dem Pluspol an Pin 4 und mit GND verbunden.
Das Programm ist einfach und enthält keine neuen Kommandos. In der Setup-Funktion wird der Pin als Ausgang definiert und auf 0 gesetzt. In der Hauptschleife wird der Buzzer für 100 ms eingeschaltet und dann für 60 ms ausgeschaltet. Danach wird er für 600 ms eingeschaltet und für 100 ms ausgeschaltet. Diese Folge wird andauernd wiederholt.
#define BUZZERPIN 4
void setup() {
pinMode(BUZZERPIN, OUTPUT);
digitalWrite(BUZZERPIN, 0);
}
void loop() {
digitalWrite(BUZZERPIN, 1);
delay(100);
digitalWrite(BUZZERPIN, 0);
delay(60);
digitalWrite(BUZZERPIN, 1);
delay(600);
digitalWrite(BUZZERPIN, 0);
delay(100);
}
Der andere Buzzer im Set ist passiv und praktisch wie ein Lautsprecher zu verwenden. Der Mikrocontroller muss hier den Ton erzeugen. Die Frequenz des Tones kann, im Gegensatz zum aktiven Buzzer, verändert werden. Um einen Ton auszugeben hat die Arduino IDE das Kommando tone(PIN, FREQUENZ); Mit diesem Kommando wird am angegebene Pin ein Rechtecksignal mit der angegebene Frequenz ausgegeben.
Als Beispiel soll eine Sirene realisiert werden, die die Frequenz bis zu einem Maximalwert stetig erhöht und danach wieder bis zu einem Minimalwert vermindert. Es wird wieder der Pin 4 verwendet. Der Anschluss erfolgt wie beim aktiven Buzzer.
Zuerst wird der verwendete Pin, die Minimalfrequenz, die Maximalfrequenz und der Wert, um den die Frequenz erhöht oder vermindert werden soll, als Konstanten definiert.
#define BUZZERPIN 4
#define FMIN 800
#define FMAX 5000
#define DELTA 10
Es folgt die Definition von zwei globalen Variablen für die aktuelle Frequenz und für die aktuelle Richtung.
int f;
boolean auf;
In der Setup Funktion wird die aktuelle Frequenz auf den Minimalwert und die Richtung auf aufwärts gesetzt.
void setup() {
f = FMIN;
auf = true;
}
In der Hauptschleife wird zuerst die aktuelle Frequenz als Ton auf den Pin ausgegeben. Dann wird mit einer bedingten Zuweisung abhängig von der aktuellen Richtung die lokale Variable d auf +DELTA oder -DELTA gesetzt. Die lokale Variable d kann nun zur aktuellen Frequenz addiert werden.
void loop() {
tone(BUZZERPIN, f);
int d = (auf)?DELTA:-DELTA;
f += d;
Nach der Änderung muss überprüft werden, ob der Maximalwert überschritten wurde. Ist das der Fall, wird die aktuelle Frequenz auf den Maximalwert gesetzt und die Richtung umgeschaltet.
if (f > FMAX) {
f = FMAX;
auf = false;
}
Es muss auch überprüft werden, ob der Minimalwert unterschritten wurde. Ist das der Fall, wird die aktuelle Frequenz auf den Minimalwert gesetzt und die Richtung umgeschaltet.
if (f < FMIN) {
f = FMIN;
auf = true;
}
Es folgt noch eine Verzögerung, damit das Ganze nicht zu schnell abläuft.
delay(3);
}
Mit diesem Beispiel ist der zweite Teil dieser Blog-Reihe abgeschlossen. Im nächsten Teil geht es um ein- und mehrstellige Siebensegment-Anzeigen.
1 comentario
james
Ottima idea il blog dei progetti peccato sia in lingua tedesca che non conosco, è possibile tradurre in italiano ? Grazie.