Tonausgabe mit PWM

Hallo,

Ich bin gerade dabei einen uralten Quellcode (ca. 40 Jahre) auf aktuelle Hardware zu portieren.
Für meine ersten Tests verwende ich also einen Arduino Uno. Vom ganzen Projekt erscheint mir die Tonausgabe am schwierigsten. Habe lange versucht die ursprünglich verwendete Strategie nachzuprogrammieren. Ziemlich erfolglos. Was auch daran liegt, das für mich ein paar Zusammenhänge nicht plausibel sind. Sei's drum....

Auf der Suche nach einer Möglichkeit die Tonausgabe mit den ursprünglichen Definitionen umzusetzen bin ich auf folgende Seite gestoßen:

https://docplayer.org/21151374-Sound-mit-dem-atmega16.html

Und tatsächlich finde ich dort etwas, was der ursprünglichen Strategie ähnelt.
Leider ist sein Beispiel weder Vollständig noch stimmig.

Eine einfache Implementierung k¨onnte z.B. so aussehen:

Zitat von der betreffenden Stelle:

Auf diese Weise l¨asst sich zwar nur ein Rechtecksignal erzeugen, die Umsetzung ist dafur ¨
aber denkbar einfach und es werden keine zus¨atzlichen Anforderungen an die Hardware
gestellt. Da die Timerfrequenz der Frequenz des Tones entsprechen soll bedeutet dies dass
fur jeden Ton ein eigenes Timer-TOP definiert werden muss. ¨
Der Timer wird auf Fast PWM (WGM 14) und Compare Output Mode konfiguriert.
Damit ergibt sich als Timer-TOP das ICR1 Register. Der Wert hierfur lässt sich durch
folgende Formel errechnen.

Als Vergleichswert ergibt sich das OCR1A Register. Zu Beginn einer Timer-Periode (BOTTOM) wird der Ausgabepin OC1A auf HIGH-Pegel und bei Erreichen des ORC1A Wertes
auf LOW-Pegel gesetzt. Das OCR1A Register kann somit genutzt werden um die Hullkurve ¨
zu realisieren.
Eine einfache Implementierung k¨onnte z.B. so aussehen:


...
#define C 4778
#define D 4257
#define E 3792
...
uint8_t SONG[] = {C,D,E...,STOP}; // Array mit Noten
uint8_t LENGTH[] = {500,520,540...,STOP}; // Array mit Notenl¨angen
ISR(TIMER1_OVF_vect){
static uint8_t laenge;
static uint8_t count;
// wenn Note zu ende
if(laenge++ > notenLaenge){
// wenn Song zu ende
if(SONG[count] == STOP) {
TCCR1B &= ~(1<<CS11); // Timer aus
return;
}
// sonst neue Note
ICR1 = SONG[cnt]
OCR1A = 500; // zu beginn der note irgend eine Lautst¨arke
count++;
laenge = 0;
}
// sonst H¨ullkurve
else {
// tue irgendwas mit OCR1A
...
}
}

Habe das mal in einem Sketch ausprobiert. Leider ohne Erfolg. Entweder verstehe ich die Zusammenhänge nicht richtig, oder ich übersehe etwas. Für mich unstimmig ist folgendes:

Er möchte also, wie im Text beschrieben, mit OCR1A vergleichen. Im Quellcode verwendet er aber dann ISR(TIMER1_OVF_vect). Was meinem Verständnis nach eher für den Überlauf gedacht ist.

Weiter schreibt er, Der Timer wird auf Fast PWM (WGM 14) und Compare Output Mode konfiguriert. Ich nehme an er meint Mode 14. Meint er nun COM1A1 = 1 oder COM1A0 = 1?

Hier ist mein Sketch, den ich mal mit den vorhandenen Informationen zusammen geschrieben habe. Ich hoffe, jemand kann mir auf die Sprünge helfen...

#define speaker 9       // OC1A

#define C     4778
#define D     4257
#define E     3792
#define STOP  0


uint8_t SONG[] = {      // Array mit Noten
    C,
    D,
    E,
    STOP
    }; 




uint8_t LENGTH[] = {    // Array mit Notenlängen
    500,
    520,
    540,
    STOP
    }; 


ISR(TIMER1_OVF_vect){
  
  static uint8_t laenge;
  static uint8_t count;
  
  // wenn Note zu ende
  if(laenge++ > LENGTH[count])
  {
  
    // wenn Song zu ende
    if(SONG[count] == STOP) 
    {
      TCCR1B &= ~(1<<CS11);   // Timer aus
      return;
    }
    
    // sonst neue Note
    ICR1 = SONG[count];
    OCR1A = 500;              // zu beginn der note irgendeine Lautstärke
    count++;
    laenge = 0;
    
  }
  else                        // sonst Hüllkurve
  {
      // tue irgendwas mit OCR1A
    
  }
}

/*
gesucht: FOvf ≈ 20KHz - 50 Hz

                              FOvf
Prescaler  |   8 bit   |  9 bit    |  10 bit    |  16 bit
---------------------------------------------------------
      1    | 78,1 kHz  |   39 kHz  |  19,5 kHz  |  305 Hz
      8    |  9,8 kHz  |  4,8 kHz  |   2,4 kHz  |   38 Hz
      64   | 1221 Hz   |  610 Hz   |   305 Hz   |  4,7 Hz
      256  |  305 Hz   |  153 Hz   |  76,3 Hz   |  1,1 Hz
      1024 | 76,3 Hz   | 38,1 Hz   |    19 Hz   |  0,3 Hz

⇒ 16 bit / Prescaler 8

Mit diesen Parametern ergeben sich folgende Werte fur die Frequenzen:

  FTimer = FCpu / 8 = 2.5 MHz
  
  Fmin = FOVF = FTimer / (2^16 + 1) = 38 Hz
  Fmax = FTimer / (2 + 1) = 833 kHz
  
  ICR1 = FTimer / FTon
  
*/

// f_desired = system_clock / (prescaler * (1 + Top))
// Top = (system_clock / (prescaler * f_desired)) -1
// ICR1 = (16000000 / (8 * 1000)) -1    = 1999


void startTimer()
{
  //cli();                                            // disable interrupts
  TCCR1A  = bit(COM1A1) | bit(WGM11);                 // Toggle OC1A on compare match, FastPWM(Mode14)
  TCCR1B  = bit(WGM13) | bit(WGM12) | bit(CS11);      // FastPWM(Mode14), scale to clock / 8
  TCNT1   = 0;                                        // count back to zero
  OCR1A   = 500;
  ICR1    = SONG[0];
  TIMSK1  = bit(OCIE1A);                              // Output Compare A Match Interrupt Enable
  //sei();                                            // allow interrupts
}

void stopTimer()
{
  TCCR1B &= ~(1<<CS11);                               // stop timer
  
}

void setupTimer()
{
  cli();     // Löschen des Global Interrupt Enable Bits (I) im Status

  TCCR1A  = 0;
  TCCR1B  = 0;
  TCNT1   = 0;        
  TIMSK1  = 0;

  sei();    // Setzen des Global Interrupt Enable Bits (I) im Status  
}



void setup() {
  
  
  pinMode (speaker, OUTPUT);
  setupTimer();
  startTimer();
  
}

void loop() {
  // put your main code here, to run repeatedly:

}

VG
Matze

Hmmm....
Vermutlich meinst du:
PCM Tonausgabe

Hier ist eine Lib, die ich vor einigen Jahren mal genutzt habe, evtl. kannst du dir da was abschauen...

Erstmal vielen Dank für den Link!

Habe bisher noch immer nicht rausgefunden, was das Problem ist. Bis jetzt weiß ich nur, dass er die ISR gar nicht erst aufruft...

Hier nochmal mein aktueller Stand. Hoffe, euch fällt noch was ein.

#include "smartdebug.h"

#define speaker 9       // OC1A

#define C     4778
#define D     4257
#define E     3792

uint8_t SONG[] = {      // Array mit Noten
    C,
    D,
    E
    }; 

uint8_t LENGTH[] = {    // Array mit Notenlängen
    500,
    520,
    540
    }; 


ISR(TIMER1_COMPA_vect){
  
  static uint8_t laenge;
  static uint8_t count;
 
  DEBUG_PRINTLN("ISR");
  DEBUG_PRINTLN_VALUE("count: ", count);
  
  // wenn Note zu ende
  if(laenge++ > LENGTH[count])
  {
    DEBUG_PRINTLN_VALUE("laenge: ", laenge);
    
    // wenn Song zu ende
    if(SONG[count] == (sizeof(SONG) / sizeof(uint8_t))) 
    {
      TCCR1B &= ~(1<<CS11);   // Timer aus
      return;
    }
    
    // sonst neue Note
    ICR1 = SONG[count];
    OCR1A = 500;              // zu beginn der note irgendeine Lautstärke
    count++;
    laenge = 0;
    
  }
  else                        // sonst Hüllkurve
  {
      // tue irgendwas mit OCR1A
    
  }
}

/*
gesucht: FOvf ≈ 20KHz - 50 Hz

                              FOvf
Prescaler  |   8 bit   |  9 bit    |  10 bit    |  16 bit
---------------------------------------------------------
      1    | 78,1 kHz  |   39 kHz  |  19,5 kHz  |  305 Hz
      8    |  9,8 kHz  |  4,8 kHz  |   2,4 kHz  |   38 Hz
      64   | 1221 Hz   |  610 Hz   |   305 Hz   |  4,7 Hz
      256  |  305 Hz   |  153 Hz   |  76,3 Hz   |  1,1 Hz
      1024 | 76,3 Hz   | 38,1 Hz   |    19 Hz   |  0,3 Hz

⇒ 16 bit / Prescaler 8

Mit diesen Parametern ergeben sich folgende Werte fur die Frequenzen:

  FTimer = FCpu / 8 = 2.5 MHz
  
  Fmin = FOVF = FTimer / (2^16 + 1) = 38 Hz
  Fmax = FTimer / (2 + 1) = 833 kHz
  
  ICR1 = FTimer / FTon
  
*/

// f_desired = system_clock / (prescaler * (1 + Top))
// Top = (system_clock / (prescaler * f_desired)) -1
// ICR1 = (16000000 / (8 * 1000)) -1    = 1999


void startTimer()
{
  cli();                                              // disable interrupts
 
  // Set clock prescale to 1 for maximum PWM frequency
  TCCR1B |= bit(CS11);

  // Set to Timer/Counter1 to Waveform Generation Mode 14: Fast PWM with TOP set by ICR1
  TCCR1A |= bit(WGM11);
  TCCR1B |= bit(WGM13) | bit(WGM12);

  // Enable Fast PWM on Pin 9: Set OC1A at BOTTOM and clear OC1A on OCR1A compare
  TCCR1A |= bit(COM1A1);
  
  OCR1A   = 500;
  ICR1    = SONG[0];
  TIMSK1  = bit(OCIE1A);                              // Output Compare A Match Interrupt Enable
  
  sei();                                              // allow interrupts

  DEBUG_PRINTLN("startTimer");
}

void stopTimer()
{
  TCCR1B &= ~(1<<CS11);                               // stop timer
  
}

void setupTimer()
{
  TCCR1A  = 0;   // Timer/Counter1 Control Register A
  TCCR1B  = 0;   // Timer/Counter1 Control Register B
  TIMSK1  = 0;   // Timer/Counter1 Interrupt Mask Register
  TIFR1   = 0;   // Timer/Counter1 Interrupt Flag Register
  TCNT1   = 0;
  ICR1    = 0;
  OCR1A   = 0;   // Default to 0% PWM
  OCR1B   = 0;   // Default to 0% PWM

  DEBUG_PRINTLN("setupTimer");
  
}



void setup() {
  
  DEBUG_INIT(115200);
  
  pinMode (speaker, OUTPUT);
  setupTimer();
  startTimer();
  
}

void loop() {
  
  

}

VG

Bitte schalte in den Voreinstellungen der IDE die Warnungen ein.

Test_Forum.ino:14:5: warning: narrowing conversion of '4778' from 'int' to 'uint8_t {aka unsigned char}' inside { } [-Wnarrowing]
     };
     ^
Test_Forum.ino:14:5: warning: large integer implicitly truncated to unsigned type [-Woverflow]
Test_Forum.ino:14:5: warning: narrowing conversion of '4257' from 'int' to 'uint8_t {aka unsigned char}' inside { } [-Wnarrowing]
Test_Forum.ino:14:5: warning: large integer implicitly truncated to unsigned type [-Woverflow]
Test_Forum.ino:14:5: warning: narrowing conversion of '3792' from 'int' to 'uint8_t {aka unsigned char}' inside { } [-Wnarrowing]
Test_Forum.ino:14:5: warning: large integer implicitly truncated to unsigned type [-Woverflow]
Test_Forum.ino:20:5: warning: narrowing conversion of '500' from 'int' to 'uint8_t {aka unsigned char}' inside { } [-Wnarrowing]
     };
     ^
Test_Forum.ino:20:5: warning: large integer implicitly truncated to unsigned type [-Woverflow]
Test_Forum.ino:20:5: warning: narrowing conversion of '520' from 'int' to 'uint8_t {aka unsigned char}' inside { } [-Wnarrowing]
Test_Forum.ino:20:5: warning: large integer implicitly truncated to unsigned type [-Woverflow]
Test_Forum.ino:20:5: warning: narrowing conversion of '540' from 'int' to 'uint8_t {aka unsigned char}' inside { } [-Wnarrowing]
Test_Forum.ino:20:5: warning: large integer implicitly truncated to unsigned type [-Woverflow]

Diese Warnungen sind Fehler, die Du beseitigen mußt.

  DEBUG_PRINTLN("ISR");
  DEBUG_PRINTLN_VALUE("count: ", count);

Gehört nicht in eine Interrupt-Routine, weil es zu lange dauert!

Warum verwendest Du nicht tone()?

Die Arduinos können auch höherwertige Töne bis hin zu MP3 produzieren. Oder geht es Dir um Retro?

Ergänzung 19:14 Uhr:

Das tönt:

#define C     4778
#define D     4257
#define E     3792

const uint8_t speaker = 9;
const uint8_t ANZAHL = 3;
struct Noten 
{
  uint16_t NOTE;
  uint16_t LAENGE;
};

Noten noten[ANZAHL]=
{
  {C, 1000}, {D, 520}, {E, 540}
};

void loop() 
{
  uint32_t jetzt = millis();
  static uint32_t vorhin = -noten[0].LAENGE;
  static uint8_t zaehler = 0;

  if (jetzt - vorhin >= noten[zaehler].LAENGE)
  {
    vorhin = jetzt;
    OCR1A = noten[zaehler].NOTE;
    zaehler = ++zaehler < ANZAHL ? zaehler : 0;
  }
}

ISR(TIMER1_COMPA_vect) {
  digitalWrite(speaker, !digitalRead(speaker));
}

void startTimer()
{
  cli();
  TCCR1A = 0; // set entire TCCR1 register to 0
  TCCR1B = 0;

  OCR1A = 1000;  // compare match register
  TCCR1B |= (1 << WGM12);   // turn on CTC mode
  TCCR1B |= (1 << CS11);    // 8 prescaler: 0,5 microseconds at 16mhz
  TIMSK1 |= (1 << OCIE1A);  // enable timer compare interrupt
  sei();
}

void setup() {
  pinMode (speaker, OUTPUT);
  pinMode (LED_BUILTIN, OUTPUT);
  startTimer();
}

Eine von vielen Dokus: Timer Interrupts.

Hallo,

du hast hier mehrere Probleme.

  • falscher Datentyp wurde schon genannt
  • TOP ist kleiner als Compare (ISR1 vs. OCR1x)
  • Timer 1 wird in ISR ausgeschalten aber nie wieder ein
  • falscher Prescaler (Kommentar vs. Code)
  • Overflow ISR vorhanden aber nicht eingeschalten
  • Compare Interrupt eingeschalten aber keine ISR vorhanden

Wenn TOP immer geändert werden soll verwende einen anderen Mode mit Double Buffer Register. Bsp. Mode 15. OCR1A für TOP und OCR1B für Compare. Aber auch hier gilt das TOP nie kleiner Compare sein darf, sonst funktioniert der Timer nicht. Wie soll er seinen Wert vergleichen.
Ansonsten kommt mir es komisch vor das der Timer überhaupt gestoppt werden muss bzw. darf.

Ich würde dir empfehlen eine Nummer kleiner anzufangen. Erstmal mit dem Timer warm werden. Manual lesen. Probieren, wieder lesen, probieren, wieder lesen.

älteres Bsp. für den Einstieg

@ agmue
Das funktioniert super! Vielen Dank dafür !

Da er nur eine Melodie abspielen soll und danach nichts mehr. Habe ich den Inhalt vom Loop in eine Funktion ausgelagert. Es sollen ja verschiedene Melodien ausgegeben werden.
Das funktioniert auch weiterhin.

Ihm allerdings mitzuteilen, das nach der Anzahl von Noten Feierabend ist, ist mir noch nicht gelungen. Entweder er spielt nur 2 Noten, oder er spielt 3 Noten wobei die letzte einen Dauerton ergibt.

Hast du noch einen Tip für mich, wie ich das am elegantesten auf eine Ausgabe begrenze?

Ich kann und will nicht auf Deinen Schreibtisch schauen, bitte zeige Dein erweitertes Programm, dann kann ich darauf aufbauen.

Moin,

void playBuf()
{
  startTimer();
  bool fertig = false;
  
  while(1)
  {
      static uint8_t zaehler = 0;
      uint32_t jetzt = millis();
      static uint32_t vorhin = -noten[0].LAENGE; 

      // wenn Note zu ende
      if (jetzt - vorhin >= noten[zaehler].LAENGE)
      {
          // wenn Song zu ende
          if(fertig && jetzt - vorhin >= noten[ANZAHL-1].LAENGE)
          {
            OCR1A = 0;
            TCCR1B = 0;
            cli();
            return;
          }
          else    // sonst neue Note
          {
            vorhin = jetzt;
            OCR1A = noten[zaehler].NOTE;
            zaehler = ++zaehler < ANZAHL ? zaehler : 0;
            if(zaehler == 0) fertig = true;
            
          }
      }
   } 
}

Ich habe im Grunde nur den Inhalt der Loop in eine Funktion geschrieben. Das funktioniert auch.
Vielleicht gibt es einen besseren Weg?

Vielen Dank für deine Mühe...

Nachtrag:
Scheint noch ein Problem mit den Längen zu geben. Wenn ich die ändere, hört sich das merkwürdig an. Er spielt dann 5 Noten und in falscher Reihenfolge. Sehr komisch.

Das verbirgt sich vermutlich in dem Programmteil, den Du mir nicht zeigst. Komplette kompilierfähige Programme erleichtern mit die Hilfe :slightly_smiling_face:

sorry....

#define C     4778
#define D     4257
#define E     3792

const uint8_t speaker = 9;
const uint8_t ANZAHL = 3;
struct Noten 
{
  uint16_t NOTE;
  uint16_t LAENGE;
};

Noten noten[ANZAHL]=
{
  {C, 1000}, {D, 520}, {E, 540}
};


ISR(TIMER1_COMPA_vect) {
  digitalWrite(speaker, !digitalRead(speaker));
}

void startTimer()
{
  cli();
  TCCR1A = 0; // set entire TCCR1 register to 0
  TCCR1B = 0;

  OCR1A = 1000;  // compare match register
  TCCR1B |= (1 << WGM12);   // turn on CTC mode
  TCCR1B |= (1 << CS11);    // 8 prescaler: 0,5 microseconds at 16mhz
  TIMSK1 |= (1 << OCIE1A);  // enable timer compare interrupt
  sei();
}

void setup() {
  pinMode (speaker, OUTPUT);
  pinMode (LED_BUILTIN, OUTPUT);
  //startTimer();
  playBuf();
  
}

void playBuf()
{
  startTimer();
  bool fertig = false;
  
  while(1)
  {
      static uint8_t zaehler = 0;
      uint32_t jetzt = millis();
      static uint32_t vorhin = -noten[0].LAENGE; 

      // wenn Note zu ende
      if (jetzt - vorhin >= noten[zaehler].LAENGE)
      {
          // wenn Song zu ende
          if(fertig && jetzt - vorhin >= noten[ANZAHL-1].LAENGE)
          {
            OCR1A = 0;
            TCCR1B = 0;
            cli();
            return;
          }
          else    // sonst neue Note
          {
            vorhin = jetzt;
            OCR1A = noten[zaehler].NOTE;
            zaehler = ++zaehler < ANZAHL ? zaehler : 0;
            if(zaehler == 0) fertig = true;
            
          }
      }
   } 
}

void loop() 
{
  
/*
  uint32_t jetzt = millis();
  static uint32_t vorhin = -noten[0].LAENGE;
  static uint8_t zaehler = 0;
  
  
  if (jetzt - vorhin >= noten[zaehler].LAENGE)
  {
    vorhin = jetzt;
    OCR1A = noten[zaehler].NOTE;
    zaehler = ++zaehler < ANZAHL ? zaehler : 0;
  }
  */ 
}

Dass du garnicht Arduino - PWM verwendest (wie im Titel fälschlich steht), ist ja schonmal gut.
Dass du die Arduino IDE verwenden willst, auch.

Dass du atmega328P Code schreibst, ist allerdinngs Mist, wenn dein Ziel ist, es auf "aktuelle Hardware zu portieren". Nutze die tone() Funktion und gut ist.

ISR(TIMER1_COMPA_vect) 
{
 PINB = _BV(PB1); // Pin9
}

Das habe ich schon probiert. Bin ich auf keinen grünen Zweig mit gekommen.
Im Original Quellcode gibt es, je nach Ereignis, verschiedene Tonfolgen. Diese bestehen zwar nur aus einer überschaubaren Anzahl von Noten, aber ich war bislang noch nicht in der Lage die 23 Tonsequenzen in Noten zu "übersetzen". Also habe ich die wage Hoffnung, die ursprüngliche Deklaration wenigstens annähernd zu übernehmen. Das das nicht eins zu eins geht, ist klar.

Hier ein Beispiel:

PLYTHEM ldx   6         
	tcy   0         ; 6/0 - 7 1 8 0 11 0 8 1 11 1 8
	tcmiy 7         
	tcmiy 1         
	tcmiy 8         
	tcmiy 0         
	tcmiy 11        
	tcmiy 0         
	tcmiy 8         
	ldp   1         
	br    LC4D  
	
		
LC4D    tcmiy 1         
	tcmiy 11        
	tcmiy 1         
	tcmiy 8         
	ldx   7         
	tcy   0         ; 7/0 - 5 0 11 1 13 1 11 0 13 0 11
	tcmiy 5         
	tcmiy 0         
	tcmiy 11        
	tcmiy 1         
	tcmiy 13        
	tcmiy 1         
	tcmiy 11        
	tcmiy 0         
	tcmiy 13        
	tcmiy 0         
	tcmiy 11        
	ldx   4         
	tcy   0         ; 4/0 - 6 0 2 0 1 0 2 1 0 4 0 6
	tcmiy 6         
	tcmiy 0         
	tcmiy 2         
	tcmiy 0         
	tcmiy 1         
	tcmiy 0         
	ldp   2         
	br    LC80      

; *** Chapter 3 page 2

LC80    tcmiy 1         
	tcmiy 0         
	tcmiy 4         
	tcmiy 0         
	tcmiy 6         
	ldx   5         
	tcy   0         ; 5/0 - 0 0 2 0 10 0 10 0 4 0 0
	tcmiy 0         
	tcmiy 0         
	tcmiy 2         
	tcmiy 0         
	tcmiy 10        
	tcmiy 0         
	tcmiy 10        
	tcmiy 0         
	tcmiy 4         
	tcmiy 0         
	tcmiy 0         
	ldp   0         
	call  PLAYBUF  

Im Grunde sind das 4 arrays. 4 und 5 ist die Tondauer , 6 und 7 die Länge der Pausen.
Noch genauer, mit dem Zahlenwert 6 und 7 aneinander gereiht wird eine Schleife durchlaufen, die die Pause zwischen den Umschaltungen des Lautsprechers bildet.
Mit 4 und 5 ist es ähnlich. Wird eine Schleife durchlaufen wo der Ausgang des Speakers kurz ein und ausgeschaltet wird. Durch die mehreren Schleifendurchläufe entsteht dann die Tondauer.

Ich habe es versucht nachzuprogrammieren. Bin damit auch nicht weiter gekommen.
Liegt aber auch daran, das ich die Funktion für die Wiedergabe nicht vollends verstehe.
Wenn du Interesse hast:

;********************************************************************************
; PLAYBUF - Play the sound buffer loaded in the Scratchpad RAM region
;
; 4/Y and 5/Y have array of durations
; 6/Y and 7/Y have array of pitches
; Notes are loaded in reverse order from Y=(0-10) down to 0
;
; Called with Y register pointing to first note plus 1
;********************************************************************************
PLAYBUF tya             ; Transfer the Y register to the Accumulator
	dan             ; Subtract one from Accumulator to point to beginning of sound buffer at 4/Y
	ldx   3         
	tcy   10        
SC0F    tam             ; Save updated pointer into sound buffer into 3/10
	tmy             
	ldx   4         ; Check 4/Y
	mnez            
	br    SC3A      ; There's a non-zero value here, play it

	ldx   5         ; Check 5/Y
	mnez            
	br    SC3A      ; There's a non-zero value here, play it
	ldp   1         
	br    LC5F      ; There's a zero here... play silence (a rest)

SC39    mnea            
	mnea            
	mnea            
	mnea            
SC1D    tam             

SC3A    tcy   10        ; Select the speaker R output
	setr            ; Toggle the speaker on
	mnea            
	mnea            
	mnea            
	mnea            
	ldx   3         
	rstr            ; Toggle the speaker off
	tmy             ; Restore the note we were playing
;
; Load delay from 6/Y and 7/Y into A and Y respectively and delay for that many loops
;
; This adjusts the time between toggles of the speaker
;
	ldx   6         
	tma             
	ldx   7         
	tmy             
SC1C    dyn             
	br    SC1C      
	dan             
	br    SC1C      
;
; Restore the note we're on into the Y register
;
	ldx   3         
	tcy   10        
	tmy             
;
; Loop based on 5/Y and 4/Y, playing the note repeatedly.  This gives duration.
;
	ldx   5         
	dman            
	br    SC39      
	tam             
	ldx   4         
	dman            
	br    SC1D      
;
; Note has been played for requested duration, decrement 3/10 and loop
;
LC08    ldx   3         
	tcy   10        
	dman            
	br    SC0F      
	retn            

Was spricht gegen den atmega328? Der Ursprüngliche Mikrocontroller stammt aus dem Jahr 1977 glaube ich. War ein 4bit Typ. Das ist nicht schwer zu toppen...

Das ändert leider nichts.

Noten noten[ANZAHL]=
{
  {C, 1000}, {D, 520}, {E, 540}
};

ändere mal die 1000 in 100.  Keine Ahnung wie das zustande kommt

Natürlich nicht, ist aber eine Beschleunigung der Ausgabe um das ca 200 fache
Und damit ein besseres Timing, und mehr Nebenläufigkeit, möglich.

Aber, wenn dir das nicht schmeckt.... dann lass es im Regal liegen.

So war das nun auch wieder nicht gemeint. Ich habe deine Antwort auf das Problem mit den Längen bezogen...

Das gefällt mir nicht, weil das eigentlich loop entspricht.

Spielt drei Töne:

#define C     4778
#define D     4257
#define E     3792

const uint8_t speaker = 9;
const uint8_t ANZAHL = 3;
struct Noten 
{
  uint16_t NOTE;
  uint16_t LAENGE;
};

Noten noten[ANZAHL]=
{
  {C, 1000}, {D, 520}, {E, 540}
};

void loop() 
{
  uint32_t jetzt = millis();
  static uint32_t vorhin = 0;
  static uint8_t zaehler = 0;

  if ( (jetzt - vorhin >= noten[zaehler].LAENGE) && (zaehler < ANZAHL) )
  {
    vorhin = jetzt;
    zaehler++;
    if (zaehler < ANZAHL)
    {
      OCR1A = noten[zaehler].NOTE;
    } else {
      bitWrite(TIMSK1, OCIE1A, 0);
      digitalWrite(speaker, LOW);
    }
  }
}

ISR(TIMER1_COMPA_vect) {
  //digitalWrite(speaker, !digitalRead(speaker));
  PINB = _BV(PB1); // Pin9
}

void startTimer()
{
  cli();
  TCCR1A = 0; // set entire TCCR1 register to 0
  TCCR1B = 0;

  OCR1A = noten[0].NOTE;  // compare match register, change this
  TCCR1B |= (1 << WGM12);  // turn on CTC mode
  TCCR1B |= (1 << CS11);  // 8 prescaler: 0,5 microseconds at 16mhz
  TIMSK1 |= (1 << OCIE1A); // enable timer compare interrupt
  sei();
}

void setup() {
  pinMode (speaker, OUTPUT);
  startTimer();
}

Spielt auch drei Töne:

#define C     419
#define D     470
#define E     527

const uint8_t speaker = 9;
const uint8_t ANZAHL = 3;
struct Noten 
{
  uint16_t NOTE;
  uint16_t LAENGE;
};

Noten noten[ANZAHL]=
{
  {C, 1000}, {D, 520}, {E, 540}
};

void loop() 
{
  uint32_t jetzt = millis();
  static uint32_t vorhin = 0;
  static uint8_t zaehler = 0;

  if ( (jetzt - vorhin >= noten[zaehler].LAENGE) && (zaehler < ANZAHL) )
  {
    vorhin = jetzt;
    zaehler++;
    if (zaehler < ANZAHL)
    {
      tone(speaker, noten[zaehler].NOTE);
    } else {
      noTone(speaker);
    }
  }
}

void setup() {
  pinMode (speaker, OUTPUT);
  tone(speaker, noten[0].NOTE);
}

Wenn die Buchstaben Noten sein sollen, so stimmen die Frequenzen nicht.

Es gibt viele Arduinos mit anderen µCs, auf denen Dein hardwarenahes Programm nicht lauffähig ist. Wenn Dich das nicht stört, ist alles gut.

Nicht viel, außer dass ein Vorteil des Arduino-Konzepts die Plattform-Unabhängigkeit ist.
Die sollte man nur wegwerfen, wenn es sonst nicht geht.

Rechteck-Signale mit ca 50% Puls/Pause aber vorgegebener Frequenz (etwas hochtrabend Ton genannt) unterschiedlich lang abspielen kann die Funktion tone. Was fehlt oder welche Probleme hast du damit?

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.