Forum: Mikrocontroller und Digitale Elektronik Arduino tone() ohne delay


von Nax (Gast)


Lesenswert?

Ich habe es bislang auf keiner der überlichen Wege hinbekommen,

eine Melody über den Piezo Speaker auszugeben ohne die delay Funktion zu 
nutzen.

Die delay Funktion ist während der Widergabe kontraproduktiv für mein 
Programm. Im Internet habe ich so noch keine Lösung gefunden.

Bislang versucht mit der millis() und Metro. Hat aber nicht hingehauen.

Hoffentlich kann mir einer helfen.

Des weiteren würde ich gerne, das es nur einmal durch läuft für jedesmal 
wo ich das Programm aufruf.
1
void starwars() {
2
  int i, rhythmDuration;
3
  for (i = 0; i < rhythmLength; i++) {
4
    rhythmDuration = rhythmBeats[i] * tempo; 
5
    if (rhythmNotes[i] == '0') {
6
      delay(rhythmDuration); 
7
    }
8
    else {
9
      tone(buzzerPin, rhythmNotes[i], rhythmDuration);
10
      delay(rhythmDuration);
11
    }
12
    delay(tempo/10);
13
  }
14
}

1
const int NOTE_AS3 = 233;
2
const int NOTE_C4 = 262;
3
const int NOTE_D4 = 294;
4
const int NOTE_E4 = 330;
5
const int NOTE_F4 = 349;
6
const int NOTE_G4 = 392;
7
const int NOTE_GS4 = 415;
8
const int NOTE_A4 = 440;
9
const int NOTE_AS4 = 466;
10
const int NOTE_B4 = 494;
11
const int NOTE_C5 = 523;
12
const int NOTE_CS5 = 554;
13
const int NOTE_D5 = 587;
14
const int NOTE_DS5 = 622;
15
const int NOTE_E5 = 659;
16
const int NOTE_F5 = 699;
17
const int NOTE_FS5 = 740;
18
const int NOTE_G5 = 784;
19
const int NOTE_GS5 = 831;
20
const int NOTE_A5 = 880;
21
22
const int rhythmLength = 115;
23
int buzzerPin = 3;
24
25
int rhythmNotes[] = {
26
  NOTE_A4,0,NOTE_A4,NOTE_A4,NOTE_A4,NOTE_A4,0,NOTE_A4,NOTE_A4,NOTE_A4,NOTE_A4,0,
27
  NOTE_A4,NOTE_A4,NOTE_A4,NOTE_F4,NOTE_F4,NOTE_F4,NOTE_C5,NOTE_C5,NOTE_C5,
28
  NOTE_A4,0,NOTE_A4,NOTE_A4,NOTE_A4,NOTE_A4,0,NOTE_A4,NOTE_A4,NOTE_A4,NOTE_A4,0,
29
  NOTE_A4,NOTE_A4,NOTE_A4,NOTE_F4,NOTE_F4,NOTE_F4,NOTE_C5,NOTE_C5,NOTE_C5,
30
  NOTE_A4,NOTE_A4,NOTE_A4,NOTE_F4,NOTE_C5,NOTE_A4,NOTE_F4,NOTE_C5,NOTE_A4,
31
  0,NOTE_E5,NOTE_E5,NOTE_E5,NOTE_F5,NOTE_C5,NOTE_GS4,NOTE_F4,NOTE_C5,NOTE_A4,
32
  0,NOTE_A5,NOTE_A4,NOTE_A4,NOTE_A5,NOTE_GS5,NOTE_G5,NOTE_FS5,NOTE_F5,NOTE_FS5, 
33
  0,NOTE_AS4,NOTE_DS5,NOTE_D5,NOTE_CS5,NOTE_C5,NOTE_B4,NOTE_C5, 
34
  0,NOTE_F4,NOTE_GS4,NOTE_F4,NOTE_A4,NOTE_C5,NOTE_A4,NOTE_C5,NOTE_E5,
35
  0,NOTE_A5,NOTE_A4,NOTE_A4,NOTE_A5,NOTE_GS5,NOTE_G5,NOTE_FS5,NOTE_F5,NOTE_FS5,
36
  0,NOTE_AS4,NOTE_DS5,NOTE_D5,NOTE_CS5,NOTE_C5,NOTE_B4,NOTE_C5, 
37
  0,NOTE_F4,NOTE_GS4,NOTE_F4,NOTE_C5,NOTE_A4,NOTE_F4,NOTE_C5,NOTE_A4};
38
39
int rhythmBeats[] = {
40
  1,2,1,1,1,1,2,1,1,1,1,2,1,1,1,1,1,1,1,1,1, 
41
  1,2,1,1,1,1,2,1,1,1,1,2,1,1,1,1,1,1,1,1,1,
42
  4,4,4,3,1,4,3,1,4, 
43
  4,4,4,4,3,1,4,3,1,4, 
44
  4,4,3,1,4,3,1,1,1,1, 
45
  3,2,4,3,1,1,1,1, 
46
  3,2,4,3,1,4,3,1,4, 
47
  3,4,3,1,4,3,1,1,1,1, 
48
  3,2,4,3,1,1,1,1, 
49
  3,2,4,3,1,4,3,1,4};
50
51
int tempo = 150;
52
53
void setup()
54
{
55
  
56
}
57
58
void loop()
59
{
60
  starwars();
61
}

von jojo (Gast)


Lesenswert?

> Bislang versucht mit der millis() und Metro. Hat aber nicht hingehauen.

warum nicht? guck dir den Besipielcode von Blink without delay an und 
model dir das um.

von Nax (Gast)


Lesenswert?

tone(buzzerPin, rhythmNotes[i], rhythmDuration);

Entspricht hier nicht bereits die ryhtmDuration einem delay?

von Jens K. (jens_k)


Lesenswert?

Geht ganz einfach, musste aber selbst was machen.
Mit DDS... dazu braucht man einen schnellen Timer mit Interrupt. Der 
Interrupt muss mit der doppelten Frequenz getriggert werden wie die 
maximale Frequenz die man widergeben möchte.

In der Interrupt-Routine hat man dann folgendes...

Zaehler = Zaehler + Set
If Delay > 0
    Portpin = MSB von Zaehler
    Delay = Delay - 1
Else
    Portpin = 0


Wenn man "Set" auf die hälfte von der Bitbreite von Zaehler setzt 
bekommt man am Portping 1/2 Frequenz vom Interrupt. Reduziert man den 
Wert von "Set" Sinkt auch die Frequenz mit der der Portpin toggelt...
Das Delay setzt man auf einen Wert X und der Portpin gibt bis ablauf die 
Frequenz aus.
Das schöne daran ist das kann man auf Polyphonien erweitern und braucht 
trotzdem nur einen Timer... Das einzige was beschränkt ist die 
Rechenleistung vom AVR....

von Nax (Gast)


Lesenswert?

Das versteh ich kein bisschen von :(

Ich hätte gehofft, iwas Richtung millis() wäre machbar.

Weiß aber nicht, wie ich die Verschachtelung passend umschreiben kann.

von vibrator (Gast)


Lesenswert?

Lese Dich in Timer und Interrupts ein.
Dann verstehst Du um was es geht.

von jojo (Gast)


Lesenswert?

> tone(buzzerPin, rhythmNotes[i], rhythmDuration);
>
> Entspricht hier nicht bereits die ryhtmDuration einem delay?

das müsste natürlich noch geklärt werden, währe aber sehr unschick und 
dann kannst du tone() ja eh vergessen.

hier:
http://code.google.com/p/rogue-code/wiki/ToneLibraryDocumentation#Ugly_Details

ist beschrieben wie die Timer verwendet werden, und es scheint so, als 
ob Timer0 für millis() frei bleiben würde.

> Ich hätte gehofft, iwas Richtung millis() wäre machbar.
> Weiß aber nicht, wie ich die Verschachtelung passend umschreiben kann.
was hast du denn versucht? Vestehst du den Code von blink without delay?

AVR-Tutorial: Timer

Interrupt

von Nax (Gast)


Lesenswert?

Die millis Variante kennt ich,

hab sie im Hauptsketch in verwendung an einigen Stellen

   if(aktuelleZeit - vorherigeZeit > wartezeit)
    {
      digitalWrite(pumpePin, LOW);
      pumpeStatus = digitalRead(pumpePin);
      vorherigeZeit = aktuelleZeit;
      Serial.print("Ende 0");
      i++;
    }


Weiß aber nicht, wie ich das in der verschachtelte Variante von dem 
Melody Sketch machen soll.

von jojo (Gast)


Lesenswert?

> Weiß aber nicht, wie ich das in der verschachtelte Variante von dem
> Melody Sketch machen soll.

na dann überleg's dir, scheinst ja alle Hilfsmittel zu kennen, 
verschachteln, ja, da muss man sich was überlegen

von Karl H. (kbuchegg)


Lesenswert?

Hinweis:

Das hier
1
  for (i = 0; i < rhythmLength; i++) {

geht schon mal gar nicht.
Du musst weg von dieser Überlegung, dass du hier alle Noten abspielen 
sollst.

Deine Denkweise muss so wie hier sein
1
void loop()
2
{
3
  ....
4
5
  if(aktuelleZeit - vorherigeZeit > wartezeit)
6
  {
7
8
     Zack! die vereinbarte Zeit für die Note die gerade spielt, ist um.
9
     Die nächste Note ist drann!
10
11
     Welches ist die nächste Note?
12
     Die Werte holen und mittels Dauer-tone() dudeln lassen
13
     den Wert für wartezeit bestimmen (also die Notendauer)
14
     und die die aktuelle Zeit als vorherige Zeit merken,
15
     damit obiger vergleich zum Übergang auf die nächste
16
     Note dann wieder funktioniert
17
  }
18
19
  ....
20
}

von Nax (Gast)


Lesenswert?

Bin noch nicht weitergekommen. Gibts kein fertigen Sketch? Habe bei 
github nur welche basierend auf delays gefunden. Muss man immer das Rad 
neu erfinden? Es ist eine kleine Spielerei funktion später in meiner 
Steuerung die rein soll, habe mir aber schon derzeit so den Kopf drum 
zerbrochen, dass ich fast denke, es ist so einfach nicht realisierbar. 
Denn ansonsten wären die Tuts damit  voll.

von PittyJ (Gast)


Lesenswert?

Manche Sachen sind eben komplizierter, und es gibt keine fertige Lösung 
im Internet, und auch keinen im Forum, der das mal schnell schreibt.
Da muss man dann doch selber ein paar Stunden prokeln.

Falls das zu teuer ist: die Arduinos sind doch so günstig, dass man 
einfach einen zweiten nimmt. Kommunikation über einen IO-Pin, und schon 
wird parallel die Musik gespielt.

20 Euro für einen Arduino sind billiger als eine Stunde Arbeitszeit.

von Karl H. (kbuchegg)


Lesenswert?

Nax schrieb:
> Bin noch nicht weitergekommen. Gibts kein fertigen Sketch? Habe bei
> github nur welche basierend auf delays gefunden. Muss man immer das Rad
> neu erfinden?

In deinem Fall ja.
Wer radfahren will, muss eben radfahren lernen.

Zumal das nun wirklich keine Raketentechik ist.

> zerbrochen, dass ich fast denke, es ist so einfach nicht realisierbar.

Es ist sogar recht trivial.
Aber: Vor das Wollen haben die Götter das Können gesetzt. Und das muss 
man eben lernen. Mussten wir alle. Und nein. Stundelang nach fertigem 
Code googeln hat auf diesem Schwierigkeitslevel nichts mit lernen zu 
tun. In der Zeit hättest du schon längst mit einer blinkenden Led als 
Ausgangspunkt dir die notwendige Technik selber beigebracht und Schritt 
für Schritt um die Dinge ergänzt, die anders sein sollen.
1
int notenNr = 0;
2
int noten[] = { 440, 880, 600 };
3
int dauer[] = { 1000, 500, 500 };
4
5
unsigned long aktuell = 0;
6
unsigned long tonBeginn = 0;
7
unsigned long tonDauer = 0;
8
9
void loop()
10
{
11
  aktuell = millis();
12
13
  if(aktuell - tonBeginn > tonDauer )
14
  {
15
     tone( buzzerPin, noten[notenNr] );
16
     tonDauer = dauer[notenNr];
17
18
     notenNr++;
19
     if( notenNr == 3 )
20
       notenNr = 0;
21
22
     tonBeginn = aktuell;
23
  }
24
}

und das ist jetzt vom Prinzip her so wahnsinnig schwer?
Noch dazu, wo ich es dir schon vorgekaut habe, bzw. du dir an einem 
LED-Blink Beispiel die Grundtechnik hättest anschauen können?
Den Rest musst du jetzt alleine einbauen. Wenigstens das wirst du ja 
wohl hoffentlich können.

von Karl H. (kbuchegg)


Lesenswert?

PittyJ schrieb:
> Manche Sachen sind eben komplizierter, und es gibt keine fertige Lösung
> im Internet, und auch keinen im Forum, der das mal schnell schreibt.
> Da muss man dann doch selber ein paar Stunden prokeln.
>
> Falls das zu teuer ist: die Arduinos sind doch so günstig, dass man
> einfach einen zweiten nimmt. Kommunikation über einen IO-Pin, und schon
> wird parallel die Musik gespielt.


Ich hoffe, das war als Satire gemeint.

von Peter D. (peda)


Lesenswert?

Wenn Du eine Melodie spielen willst, ohne das Main zu stoppen, mußt Du:
1. einen PWM-fähigen Ausgangspin nehmen.
2. im Timerinterrupt die Tondauer zählen und den nächsten Tonwert in die 
PWM laden.

Die Tonerzeugung über Delays ist dazu völlig ungeeignet.

von Natz (Gast)


Lesenswert?

Gibt es eine Möglichkeit einen Ton auf leise zu stellen. Habe im Sketch 
3 Pausen drin von 250ms.
Der "Ton" pause defeniert mit 0 gibt aber so ein kleines rattern auf den 
Piezo.
1
const int buzzerPin = 3;
2
3
#define c 261
4
#define d 294
5
#define e 329
6
#define f 349
7
#define g 391
8
#define gS 415
9
#define a 440
10
#define aS 455
11
#define b 466
12
#define cH 523
13
#define cSH 554
14
#define dH 587
15
#define dSH 622
16
#define eH 659
17
#define fH 698
18
#define fSH 740
19
#define gH 784
20
#define gSH 830
21
#define aH 880
22
#define pause 0
23
24
int notenNr = 0;
25
int noten[] = { 
26
  a, a, a, f, cH, a, f, cH, a, eH, eH, eH, fH, cH, gS, f, cH, a, aH, a, a, aH, gSH, gH, fSH, fH, fSH, 
27
  pause, f, gS, f, a, cH, a, cH, eH, aH, a, a, aH, gSH, gH, fSH, fH, fSH, 
28
  pause, aS, dSH, dH, cSH, cH, b, cH, 
29
  pause, f, gS, f, cH, a, f, c, a
30
};
31
32
int dauer[] = { 
33
  500, 500, 500, 350, 150, 500, 350, 150, 1000, 500, 500, 500, 350, 150, 500, 350, 150, 1000, 500, 350, 150, 500, 250,250, 125, 125, 250, 
34
  /*pause*/  250, 125, 500, 375, 125, 500, 375, 125, 1000, 500, 320, 150, 500, 250, 250, 125, 125, 250, 
35
  /*pause*/  250, 250, 500, 250, 250, 125, 125, 250, 
36
  /*pause*/  250, 250, 500, 375, 125, 500, 375, 125, 1000
37
};
38
39
unsigned long aktuell = 0;
40
unsigned long tonBeginn = 0;
41
unsigned long tonDauer = 0;
42
43
void setup() {
44
  pinMode(buzzerPin, OUTPUT);
45
}
46
47
void loop()
48
{
49
  aktuell = millis();
50
51
52
  if(aktuell - tonBeginn > tonDauer )
53
  {
54
    tone( buzzerPin, noten[notenNr] );
55
    tonDauer = dauer[notenNr];
56
57
    notenNr++;
58
    if( notenNr == 61 )
59
      notenNr = 0;
60
61
    tonBeginn = aktuell;
62
  }
63
}

von Karl H. (kbuchegg)


Lesenswert?

Siehe Doku bei Arduino.

Um tone() abzudrehen, noTone() aufrufen.
1
....
2
   if( noten[notenNr] == pause )
3
     noTone( buzzerPin );
4
   else
5
     tone( buzzerPin, noten[notenNr] );
6
7
 ...


Besser du gewöhnst dich gleich daran: Die Arduino-Doku wird deine zweite 
Bibel.
Also lies gefälligst nach. Ich hab in meinem ganzen Leben noch nie einen 
Arduino programmiert, geschweige denn einen in der Hand gehabt. Ich 
finde die Dinge raus und du nicht? Das kanns ja wohl nicht sein.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.