Mikrocontroller sind eine sehr interessante Möglichkeit, sich näher mit Elektronik zu beschäftigen. Mit diesen kann auch Musik erzeugt werden, was viel einfacher ist, als ich selbst lange dachte. In diesem Artikel soll es um die Klangerzeugung mit MicroPython gehen.
Für dieses kleine Projekt benötigen wir nur ganz wenig Material; wer schon einmal mit Mikrocontrollern zu tun hatte, wird vermutlich das Meiste bereits zu Hause haben. Wir benötigen natürlich einen Mikrocontroller, hier wird ein Raspberry Pi Pico verwendet, jeder andere sollte es aber genauso tun, wenn er PWM unterstützt. Aber dazu später mehr. Außerdem brauchen wir natürlich Jumper-Kabel und einen Lautsprecher mit Klinkeneingang. Am besten verwenden wir einen alten, der sonst nicht mehr gebraucht wird, damit es keinen Ärger gibt, wenn dieser nach dem Projekt nicht mehr funktioniert. Auch von Kopfhörern würde ich abraten, falls etwas mit der Lautstärke durcheinander gerät. Für die grundsätzliche Tonerzeugung wird nicht mehr Material benötigt. Um einen Anschluss an die Lautsprecher zu vereinfachen, sind Krokodilklemmen oder ein Audiojack Breakout Board empfehlenswert. Außerdem wird es mit ein paar Dreh- oder Schiebereglern interessanter, um interaktiv "Melodien" zu spielen.
Das wichtigste ist natürlich die Audioausgabe. Wie die meisten schon oft gesehen haben dürften, besitzen die hier verwendeten Klinkenstecker mehrere Kontakte. Die Spitze wird für die linke, der Kontakt darüber für die rechte Seite der Ausgabe verwendet. Wenn wir auf diese Pins entsprechende Signale legen, können wir Töne erzeugen. Hier werden wir das gleiche Audio für beide Seiten verwenden, dies kann natürlich noch auf Stereo ausgeweitet werden. Nun können noch ein bis zwei Kontakte am Stecker übrig sein. Einer wird als Ground-Verbindung genutzt (entsprechender Pin am Pico), der eventuell vorhandene andere Kontakt ist für ein Zusatzsignal, zum Beispiel ein Mikrofon, gedacht.
Außer der Audioausgabe können wir auch noch die Regler verwenden. Diese besitzen drei Pins. Durch die zwei äußeren muss elektrischer Strom fließen. Dafür können wir den 3.3Volt-Ausgang und einen Ground-Pin verwenden. Durch das Verstellen des Reglers wird der eingebaute Widerstand des Potenziometers verändert. Der mittlere Pin wird an einen analogen Pin (ADC) angeschlossen, womit der Widerstand ausgelesen werden kann.
Jetzt können wir mit dem Verkabeln beginnen. Ich benutze zwei Potenziometer, eines für die Lautstärke, das andere für die Tonhöhe. Die mittleren Pins verbinden wir mit den ADC-Pins 0 und 1. Die äußeren Pins verbinden wir mit dem 3.3Volt-Pin und einem Ground-Pin. Für den Lautsprecher-Ausgang habe ich Pin 13 verwendet. Die folgende Grafik verdeutlicht dies. Dabei wird das weiße Kabel mit dem innersten Klinken-Kontakt, auch Sleeve genannt, verbunden, das schwarze stellt die zwei Audiosignalverbindungen dar.
Mit diesem Aufbau können wir nun Töne erzeugen. Zunächst initialisieren wir die Pins. Für Pin 13, die Audioausgabe, verwenden wir Pulse Width Modulation, um mit einem digitalen Ausgang analoge Signale zu simulieren. Hierbei können wir eine Frequenz einstellen, welche dem ausgegebenen Ton entspricht, außerdem den Tastgrad, welcher der Lautstärke entspricht. Darauffolgend können wir eine Endlosschleife starten. Darin lesen wir den Wert des ersten Potenziometers aus, skalieren ihn und weisen dem Tastgrad diese Zahl zu. Dabei müssen wir skalieren, da wir einen Wert in 16Bit-Auflösung auslesen, was 65536 verschiedenen Möglichkeiten entspricht. Der Tastgrad lässt allerdings nur Werte bis 1000 zu. Auf die gleiche Weise verfahren wir mit dem zweiten Potenziometer und der Frequenz, welche wir auf beliebige Werte skalieren, es sollte nur die 8 nicht unterschreiten, da kleinere Frequenzen nicht unterstützt werden. Im Beispiel werden Frequenzen zwischen 105 und 2099 verwendet. Die Endlosschleife können wir noch mit einem try-except-Block umschließen, um den Ton zu beenden, wenn das Programm geschlossen wird. So sieht der fertige Code aus:
from machine import Pin, ADC, PWM
speaker = PWM(Pin(13))
height = ADC(0)
volume = ADC(1)
try:
while True:
new_vol = int(volume.read_u16() / 65553 * 1000)
speaker.duty_u16(new_vol)
new_freq = int(height.read_u16() / 65553 * 2000) + 100
speaker.freq(new_freq)
except KeyboardInterrupt:
speaker.duty_u16(0)
Jetzt haben wir es schon mal geschafft, Töne zu erzeugen. Leider ist es ziemlich schwierig, richtige Melodien zu erzeugen. Wenn man in die Schleife noch ein Warten von 0.2 Sekunden oder so einbaut, kann man schon den Eindruck erzeugen, tatsächlich verschiedene Töne zu erzeugen, aber schön wird es leider immer noch nicht. Da bleibt noch die Möglichkeit, Melodien einzuprogrammieren. Im Folgenden ist der Code für ein zur Jahreszeit passendes Lied abgebildet. Vielleicht erkennt es ja der ein oder andere.
from machine import Pin, ADC, PWM
from time import sleep
speaker = PWM(Pin(13))
height = ADC(0)
volume = ADC(1)
def song():
def playNote(f, d):
speaker.duty_u16(500)
speaker.freq(f)
sleep(d)
speaker.duty_u16(0)
sleep(0.02)
playNote(391, 1)
playNote(440, 1)
playNote(391, 0.75)
playNote(349, 0.25)
playNote(329, 0.5)
playNote(349, 0.5)
playNote(391, 1)
playNote(440, 1)
playNote(391, 0.75)
playNote(349, 0.25)
playNote(329, 0.5)
playNote(349, 0.5)
playNote(391, 1)
playNote(391, 1)
playNote(440, 1)
playNote(493, 0.5)
playNote(523, 0.5)
playNote(493, 1)
playNote(440, 1)
playNote(391, 2)
playNote(293, 0.75)
playNote(329, 0.25)
playNote(293, 0.5)
playNote(329, 0.5)
playNote(349, 0.75)
playNote(391, 0.25)
playNote(349, 1)
playNote(329, 0.75)
playNote(349, 0.25)
playNote(329, 0.5)
playNote(349, 0.5)
playNote(391, 0.75)
playNote(440, 0.25)
playNote(391, 1)
playNote(523, 0.5)
playNote(493, 0.5)
playNote(440, 0.5)
playNote(391, 0.5)
playNote(523, 0.5)
playNote(443, 0.5)
playNote(391, 0.5)
playNote(349, 0.5)
playNote(329, 1)
playNote(293, 1)
playNote(261, 2)
speaker.duty_u16(0)
song()
Das war es auch schon mit dieser kleinen Einführung in die Tonerzeugung mit Mikrocontrollern. Ich hoffe, ich konnte dazu motivieren, das selber einmal auszuprobieren, oder immerhin für interessanten Lesestoff sorgen. Es bleiben natürlich wie immer noch viele Möglichkeiten zur Verbesserung und Erweiterung des Projekts. So könnte man beispielsweise die Bestimmung der Tonhöhe mit Tastern erledigen und damit vielleicht eine Art Klaviertastatur herstellen. Was habt ihr für Ideen, Anregungen oder sonstige Verbesserungsvorschläge? Schreibt doch gerne was dazu, wenn es euch interessiert.