Drunken Sailor
Passiver Summer mit Arduino ohne tone()-Funktion
Passiver Summer
Die Funktion tone() erleichtert die Ansteuerung eines Lautsprechers. Sie blockiert allerdings einen Hardware-Timer.
Bei manchen Anwendungen ist das jedoch unerwünscht und man muss auf die Funktion verzichten. In dem folgenden Beispiel wird ein digitaler
Ausgang ohne die tone()-Funktion mit einer bestimmten Frequenz ein und ausgeschaltet und so den Lautsprecher zum Schwingen bringen. Auf
diesem Wege kann die Funktion tone() umgangen werden.
Bei den hier anfallenden Berechnungen sind zwei Größen relevant: Periode und Puls. Da wir im Programm mit delayMicroseconds()
hantieren, müssen sich die Berechnungen auf Mikrosekunden beziehen.
Bei der Note a‘ (Kammerton) beträgt die Frequenz z.B. 440 Hz. Eine Sekunde entspricht einer Million Mikrosekunden. Folglich
beträgt die Dauer einer Periode 1000000 / 440 = 2272,72 Mikrosekunden. Mit dieser Häufigkeit muss der Lautsprecher ein und ausgeschaltet
werden, damit die a‘-Note erklingen kann. Innerhalb einer Periode wird der Lautsprecher für die Hälfte der Periode ein- und für die andere
Hälfte ausgeschaltet. Die Dauer des Pulses beträgt hier also die Hälfte der Periode.
Arduino Nano
Jeder Note (Ton) ist eine bestimmte Frequenz zugeordnet. Sie gibt die Anzahl der Schwingungen z.B. einer Gitarrensaite
pro Sekunde. In der Tabelle unten sind die Frequenzen (nur Stammtöne) zusammengestellt. Auf einem Klavier entsprechen die Stammtöne
den weißen Tasten. Die vollständige Zusammenstellung findet man im
Internet unter www.wikipedia.de. Für die Erstellung des Programms wurden alle Töne nummeriert (Diese Nummern entsprechen nicht der
Nummerierung der Tasten eines Klaviers). Diese verkürzte und vereinfachte Zusammenstellung reicht aus, um einfache Melodien mit Arduino
abspielen zu können. In dem Beispiel wird ein Fragment des traditionellen Liedes „What Shall We Do with the Drunken Sailor“ (kurz:
Drunken Sailor) verwendet.
Noten-Tabelle mit zugehörigen Frequenzen
Notensystem
Der Schaltplan
Schaltplan
Die Schaltung
Neben dem passiven Summer und Arduino, die in der Schaltung die erste Geige spielen, enthält die Schaltung noch zwei Taster
und eine 7-Segmentanzeige. Auf der Anzeige werden die Frequenzwerte von den Tönen, die gerade gespielt werden, angezeigt. Mit den Tastern
wird jeweils das oben erwähnte Fragment von „Drunken Sailor“ gestartet. Eine fertig aufgebaute Testschaltung könnte dann
so aussehen:
Beim Betätigen eines der beiden Taster hören wir die Klänge von „Drunken Sailor“. Mit dem ersten Taster wird die Sequenz des
Programms gestartet, wo die Funktion tone() noch verwendet wird. Der Teil des Programms dient nur dem Vergleich. Mit dem zweiten Taster
wird ebenfalls das Musikstück gestartet, doch diesmal muss das Programm ohne tone()-Funktion auskommen. Wie man erkennen kann, besteht
zwischen den beiden Methoden kaum Unterschied. Der Programmierungsaufwand ist in beiden Fällen nahezu gleich. Ein Unterschied ist nur bei
der Ausführung feststellbar. Bei der Methode ohne tone()-Funktion spielt das Programm die Töne zunächst komplett ab, bevor Abarbeitung
anderer Programmteile erfolgen kann.
Vor dem Start des Programms muss die Bibliothek TM1637Display.h installiert werden. Sie ist für die
7-Segmentanzeige zuständig.
// *****************************************************************************
// Drunken Sailor
// Passiver Summer, Sketch ohne tone()
// Arduino Nano, IDE 1.8.13
// *****************************************************************************
#include<TM1637Display.h>#define CLK 6
#define DIO 7
// Frequenzen der Noten
int Note [] = { 27, 31,
33, 37, 41, 44, 49, 55, 62,
65, 73, 82, 87, 98, 110, 123,
131, 147, 165, 175, 196, 220, 247,
262, 294, 330, 349, 392, 440, 494,
523, 587, 659, 698, 784, 880, 988,
1047, 1175, 1319, 1397, 1568, 1760, 1976,
2093, 2349, 2637, 2794, 3136, 3520, 3951,
4186, 4699 };
TM1637Display display(CLK, DIO);
String Ausgabe;
int Summer = 3;
int Taster_S1 = 5;
int Taster_S2 = 4;
int Frequenz;
int i;
// Drunken Sailor
int Melodie [36][2] = { {28, 400}, {28, 200}, {28, 200}, {28, 400}, {28, 200}, {28, 200},
{28, 400}, {24, 400}, {26, 400}, {28, 400},
{27, 400}, {27, 200}, {27, 200}, {27, 400}, {27, 200}, {27, 200},
{27, 400}, {23, 400}, {25, 400}, {27, 400},
{28, 400}, {28, 200}, {28, 200}, {28, 400}, {28, 200}, {28, 200},
{28, 400}, {29, 400}, {30, 400}, {31, 400},
{30, 400}, {28, 400}, {27, 400}, {25, 400},
{24, 800}, {24, 800}
};
void setup() {
display.setBrightness(10);
display.clear();
pinMode (Taster_S1, INPUT_PULLUP);
pinMode (Taster_S2, INPUT_PULLUP);
pinMode (Summer, OUTPUT);
}
void loop() {
// Zum Vergleich
if (digitalRead(Taster_S1) == LOW) { // Taster S1 - mit tone()-Funktion
for (i = 0; i < 36; i++) {
int Note_Nr = Melodie [i][0]; // Nummer der Note
Frequenz = Note [Note_Nr]; // Frequenz bestimmen
Ausgabe = String (Frequenz); // Zahl in String umwandeln
int Zeit = Melodie [i][1];
display.showNumberDecEx(Ausgabe.toInt(), 0b00000000, false, 4, 4); // Ausgabe Display
tone (Summer, Frequenz, Zeit); // Ton erzeugen
delay(Zeit); // Wartezeit
}
}
if (digitalRead(Taster_S2) == LOW) { // Taster S2 - ohne tone()-Funktion
for (i = 0; i < 36; i++) {
int Note_Nr = Melodie [i][0]; // Nummer der Note
Frequenz = Note [Note_Nr]; // Frequenz bestimmen
Ausgabe = String (Frequenz); // Zahl in String umwandeln
display.showNumberDecEx(Ausgabe.toInt(), 0b00000000, false, 4, 4); // Ausgabe Display
int Periode = 1000000L / Frequenz;
int Pulse = Periode / 2;
int Zeit = Melodie [i][1]; // Zeit auslesen
for (long j = 0; j < Zeit * 1000L; j += Periode) {
digitalWrite (Summer, HIGH); // Ausgang Ein
delayMicroseconds (Pulse); // Wartezeit
digitalWrite (Summer, LOW); // Ausgang Aus
delayMicroseconds (Pulse); // Wartezeit
}
}
}
}
// **************************************************************************************