Ton erzeugen

Programmieren

Zeit für etwas Musik! In diesem Tutorial erzeugen wir Töne mit einem Piezo-Lautsprecher und unserem Arduino Uno.

Ton erzeugen

Letztes Mal haben wir uns mit der PWM befasst, bei der wir den Tastgrad eines Signals mit fester Frequenz ändern konnten. Um verschiedene Töne zu erzeugen, müssen wir jedoch ein Signal mit einstellbarer Frequenz erzeugen. Die Arduino-Bibliothek stellt dafür eine praktische Prozedur mit dem Namen tone zur Verfügung, mit der wir Töne mit einer bestimmten Frequenz erzeugen können. Die verwendete Technik ähnelt in gewisser Weise der PWM, aber die Arduino-Implementierung verwendet die PWM-Peripherie nicht direkt zur Klangerzeugung. Das Signal wird stattdessen durch schnelles an und ausschalten eines Pins mithilfe eines Hardware-Timers erzeugt. Der Vorteil gegenüber der direkten Verwendung der PWM-Hardware besteht darin, dass wir jeden Pin und nicht nur PWM-fähige Pins verwenden können. Da derselbe Timer normalerweise für die PWM an den Pins 3 und 11 verwendet wird, können wir jedoch nicht gleichzeitig PWM an diesen Pins verwenden.

Genug Theorie, fangen wir an. Ich habe ein kleines Musikbeispiel ausgewählt, das wir mit dem Arduino und einem Piezo-Lautsprecher nachspielen werden. Natürlich wird das Ergebnis nicht so perfekt und harmonisch klingen wie ein Klavier, aber es macht Spaß, mit Klängen zu spielen, und vielleicht erkennst du ja das Musikstück, das ich ausgewählt habe.

Erste Takte der Europahymne

Aufbau der Schaltung

Verbinden des Piezo-Lautsprechers mit dem Arduino Uno

Über die Schaltung gibt es nicht viel zu sagen. Schließe einfach den Piezo-Lautsprecher an Pin 8 des Arduino an. Du solltest dich jedoch vergewissern, dass du einen Piezo-Lautsprecher und nicht einen Summer nutzt. Ein Summer erzeugt nur einen Ton mit einer festen Frequenz. Er besitzt eine interne Schaltung zur Erzeugung dieses Tons und kann mit einer festen Spannung über einen einfachen digitalen Ausgang betrieben werden. Dies ist jedoch nicht das Ziel dieses Tutorials.

Der Arduino ist in der Lage, den Piezo-Lautsprecher direkt anzusteuern, für echte Lautsprecher oder einen großen Piezo-Lautsprecher benötigst du einen Verstärker. Aber um ehrlich zu sein, ein kleiner Piezo-Lautsprecher ist nervig genug. Du kannst einen 220 Ω Widerstand in Reihe schalten, um die Lautstärke zu reduzieren.

Der Code

In unserem Code werden wir die folgenden neuen Funktionen benötigen:

  • tone(pin, frequency)
    • pin: Nummer des zu verwendenden Pins
    • frequency: Frequenz des zu erzeugenden Tons
  • noTone(pin)
    • pin: Nummer des zu verwendenden Pins

Mit der tone -Prozedur können wir einen Ton mit einer bestimmten Frequenz erzeugen. Mit noTone können wir den Lautsprecher wieder zum Schweigen bringen. Es gibt auch eine Version der Funktion tone, der man als drittes Argument eine Dauer für den Ton übergeben kann. Das Problem ist, dass diese nicht blockiert, sondern stattdessen die Dauer mithilfe des Hardware-Timers misst. Wenn man nun mehrere Töne direkt hintereinander erzeugen will, führt dies dazu, dass der erste Ton durch den zweiten unterbrochen wird, bevor er vollständig gespielt wurde. Infolgedessen hört man von jedem Ton nur einige Klickgeräusche. Um die Dauer der Töne festzulegen, werden wir einfach die delay-Prozedur nutzen. Nun müssen wir Code schreiben, um jede einzelne Note im Musikbeispiel zu erzeugen. Dazu müssen wir die Noten in eine Frequenz übersetzen. Um uns das Leben zu erleichtern, können wir für jede Note Variablen definieren und die entsprechende Frequenz als Wert zuweisen.

const int F = 349;
const int G = 392;
const int A = 440;
const int B = 466;
const int C = 523;

Vielleicht ist dir aufgefallen, dass ich das Schlüsselwort const in der Variablendeklaration verwendet habe. Eine als const deklarierte Variable kann nicht zur Laufzeit geändert werden, man spricht deshalb auch von einer Konstanten.

Der vollständige Code ist immer noch etwas umfangreich. Fühl dich frei, ihn einfach zu kopieren. Ich habe alles in der setup-Prozedur implementiert, da ich die Melodie nur einmal spielen möchte. Auch wenn wir keinen Code in der loop-Prozedur benötigen, kann diese Prozedur trotzdem nicht einfach weggelassen werden.

const int F = 349;
const int G = 392;
const int A = 440;
const int B = 466;
const int C = 523;

void setup() {
  tone(8, A);
  delay(250);
  tone(8, A);
  delay(250);
  tone(8, B);
  delay(250);
  tone(8, C);
  delay(250);

  tone(8, C);
  delay(250);
  tone(8, B);
  delay(250);
  tone(8, A);
  delay(250);
  tone(8, G);
  delay(250);

  tone(8, F);
  delay(250);
  tone(8, F);
  delay(250);
  tone(8, G);
  delay(250);
  tone(8, A);
  delay(250);

  tone(8, A);
  delay(375);
  tone(8, G);
  delay(125);
  tone(8, G);
  delay(500);

  tone(8, A);
  delay(250);
  tone(8, A);
  delay(250);
  tone(8, B);
  delay(250);
  tone(8, C);
  delay(250);

  tone(8, C);
  delay(250);
  tone(8, B);
  delay(250);
  tone(8, A);
  delay(250);
  tone(8, G);
  delay(250);

  tone(8, F);
  delay(250);
  tone(8, F);
  delay(250);
  tone(8, G);
  delay(250);
  tone(8, A);
  delay(250);

  tone(8, G);
  delay(375);
  tone(8, F);
  delay(125);
  tone(8, F);
  delay(500);

  noTone(8);
}

void loop() {
}

Ein erstes Ergebnis

Sobald der Code auf das Arduino hochgeladen ist, beginnt die Melodie zu spielen. Das Ergebnis mag gut genug sein, um die Melodie zu erkennen, aber es ist definitiv nicht toll. Es fehlen Pausen, um die einzelnen nacheinander gespielten Noten unterscheiden zu können. Dies wird unser nächster Schritt sein. Übrigens ist die Melodie der Anfang von Beethovens Ode an die Freude, die auch als Europahymne bekannt ist.

Einzelne Anschläge

Um die einzelnen Töne zu trennen, müssen wir nach jedem Ton einen Aufruf von noTone und eine kleine Verzögerung hinzufügen, um etwas Stille zwischen den Anschlägen zu erzeugen. Dies für jeden einzelnen Ton zu tun, wäre jedoch eine ziemlich mühsame Aufgabe. Deshalb möchte ich dir zeigen, wie du deine eigenen Funktionen schreiben kannst. Es ist überhaupt nicht schwer. Um deine eigene Funktion zu deklarieren, notiere einfach den Rückgabetyp, einen Funktionsnamen und die Liste der Argumente mit ihren Typen. Wir können die Argumente verwenden, um Werte zu übergeben, die wir in der Funktionsimplementierung benötigen. Es ist wahrscheinlich am besten, wenn ich dir einfach den Code zeige:

void playNote(int freq, int duration) {
  tone(8, freq);
  delay(duration-25);

  noTone(8);
  delay(25);
}

Das Argument freq wird benutzt, um die Frequenz festzulegen, und wir benutzen duration, um die Länge unserer Verzögerung zu definieren. Um die einzelnen Töne zu trennen, habe ich 25 ms von der Gesamtlänge abgezogen und diese 25 ms für eine kurze Zeit der Stille direkt nach dem Ton verwendet. Dadurch erhalten wir unterscheidbare Anschläge, auch wenn derselbe Ton zweimal gespielt wird. Ein wichtiges Detail bei der Definition eigener Funktionen ist es, sie vor den Funktionen hinzuzufügen, in denen sie verwendet werden. Der Compiler interpretiert die Datei von oben nach unten. Wenn du also die Funktion am Ende der Datei einfügst, kannst du sie nicht verwenden.

Mit der neu definierten Funktion können wir die Erzeugung unserer Melodie auf sauber und klar verständlich umsetzen:

const int F = 349;
const int G = 392;
const int A = 440;
const int B = 466;
const int C = 523;

void playNote(int freq, int duration) {
  tone(8, freq);
  delay(duration-25);

  noTone(8);
  delay(25);
}

void setup() {
  playNote(A, 250);
  playNote(A, 250);
  playNote(B, 250);
  playNote(C, 250);

  playNote(C, 250);
  playNote(B, 250);
  playNote(A, 250);
  playNote(G, 250);

  playNote(F, 250);
  playNote(F, 250);
  playNote(G, 250);
  playNote(A, 250);

  playNote(A, 250);
  playNote(G, 250);
  playNote(G, 500);

  playNote(A, 250);
  playNote(A, 250);
  playNote(B, 250);
  playNote(C, 250);

  playNote(C, 250);
  playNote(B, 250);
  playNote(A, 250);
  playNote(G, 250);

  playNote(F, 250);
  playNote(F, 250);
  playNote(G, 250);
  playNote(A, 250);

  playNote(G, 250);
  playNote(F, 250);
  playNote(F, 500);
}

void loop() {
}

Das Endergebnis

Unser Endergebnis ist eine große Verbesserung gegenüber dem, was wir zuvor erreicht haben. Um die Wahrheit zu sagen, der Klang eines Piezo-Lautsprechers ist nicht erstaunlich, aber man kann die Melodie klar erkennen.

Vorheriger Beitrag Nächster Beitrag