Sinus signal mit variable frequenz

hallo, ich will mich erstmal bedanken für jeden kommentar.. das ist sehr nett von euch.
ich wird jetzt nochmal von 0 anfangen.
ich will es so konfigurieren:
Phase und Frequency correct PWM (mode 8)
F(takt) = 10khz
F1 = 45hz
F2 = 55hz
ICR1 als Top setzen (F takt)
OCR1A für F1 und OCR1B für F2

Stimmt das so ertsmal?

Oder ohne 10khz takt frequenz
ich nehme mode 9 und setze:
OCR1A als Top (F1)
OCR1B für F2

Im Prinzip ja, aber dann brauchst Du 2 Tabellen, eine für jede Frequenz. Und ob das der Anforderung entspricht, mußt Du auch erst klären.

TCCR1A= (1<<COM1A1)|(1<<COM1B1)|(1<<WGM11)|(1<<WGM12); //Löschen von OC1A/OC1B bei Compare Match beim Hochzählen, Setzen von OC1A/OC1B bei Compare Match beim Runterzählen; MODE 8
TCCR1B= (1<<WGM13)|(1<<CS12); //mode 8 ; PRESCALER 256
TIMSK1= (1<<OCIE1A)|(1<<OCIE1B); // setzen von bit 1 und 2 um den Interrupt freizugeben

ich weiss nun nicht wie ich die top werte berechnen kann :

TOP = 16Mhz / 2* 256 * 45 = 694 (für F1)
TOP = 16 Mhz / 2* 256 * 55 = 568 (für F2)

Stimmt das so ?
wenn ja, was ist mit ICR1 ?

Die Werte sind schon richtig, aber für jeden Zähler gibt es nur 1 TOP. Durch Ändern des TOP kannst Du die Zähler-Frequenz umschalten. Aus der Zählerfrequenz und der gewünschten Sinus-Frequenz ergibt sich dann die Tabellenlänge - oder auch umgekehrt.

Hallo,

warum nimmst du keine Codetags?

TIMSK1 = (1<<OCIE1A)|(1<<OCIE1B); 

Das klappt nicht. Gebe nur die Interrupts frei die du benötigst. Fehlt zum Interrupt die Interrupt Routine hängt sich der µC auf. Der springt ins Nirwarna.

Dein Code etwas abgeändert damit er läuft. Damit fehlt allerdings noch die Kontrolle über die Frequenz.

const uint16_t Sinus[] = {
   127,  130,  133,  136,  139,  143,  146,  149,  152,  155,  158,  161,  164,  167,  170,  173,
   176,  178,  181,  184,  187,  190,  192,  195,  198,  200,  203,  205,  208,  210,  212,  215,
   217,  219,  221,  223,  225,  227,  229,  231,  233,  234,  236,  238,  239,  240,  242,  243,
   244,  245,  247,  248,  249,  249,  250,  251,  252,  252,  253,  253,  253,  254,  254,  254,
   254,  254,  254,  254,  253,  253,  253,  252,  252,  251,  250,  249,  249,  248,  247,  245,
   244,  243,  242,  240,  239,  238,  236,  234,  233,  231,  229,  227,  225,  223,  221,  219,
   217,  215,  212,  210,  208,  205,  203,  200,  198,  195,  192,  190,  187,  184,  181,  178,
   176,  173,  170,  167,  164,  161,  158,  155,  152,  149,  146,  143,  139,  136,  133,  130,
   127,  124,  121,  118,  115,  111,  108,  105,  102,   99,   96,   93,   90,   87,   84,   81,
    78,   76,   73,   70,   67,   64,   62,   59,   56,   54,   51,   49,   46,   44,   42,   39,
    37,   35,   33,   31,   29,   27,   25,   23,   21,   20,   18,   16,   15,   14,   12,   11,
    10,    9,    7,    6,    5,    5,    4,    3,    2,    2,    1,    1,    1,    0,    0,    0,
     0,    0,    0,    0,    1,    1,    1,    2,    2,    3,    4,    5,    5,    6,    7,    9,
    10,   11,   12,   14,   15,   16,   18,   20,   21,   23,   25,   27,   29,   31,   33,   35,
    37,   39,   42,   44,   46,   49,   51,   54,   56,   59,   62,   64,   67,   70,   73,   76,
    78,   81,   84,   87,   90,   93,   96,   99,  102,  105,  108,  111,  115,  118,  121,  124
};

const uint16_t RESOLUTION {sizeof(Sinus)/sizeof(Sinus[0])}; 

void setup()
{
  pinMode(10, OUTPUT);  // OC1B = UNO Pin 10 / Mega2560 Pin 12
  // Mode 14, Fast PWM - ICR1
  TCCR1B = 0;
  TCCR1A = 0;
  TIMSK1 = 0;
  OCR1B = 0;
  TCNT1 = 0;
  ICR1 = RESOLUTION - 1;
  TIMSK1 = _BV(OCIE1B);
  TCCR1A = _BV(COM1B1) | _BV(COM1B0) | _BV(WGM11);
  TCCR1B = _BV(WGM13)  | _BV(WGM12)  | _BV(CS10);
}

void loop()
{ }

ISR(TIMER1_COMPB_vect) // Interruptprogramm-PWM-Interrupt - läuft einmal pro PWM-Periode und holt neuen PWM-Wert
{
  static unsigned int i {0};
  i++;
  if (i >= RESOLUTION-1) i=0;
  OCR1B = Sinus[i];
}

TOP ergibt sich aus dem höchsten Wert der in der Tabelle vorkommt. Wenn man 8Bit ausreizt dann 255 oder wenn man 10Bit ausreizt dann eben 1023. Ich berechne die Tabellen in Excel, die Größe des Arrays entspricht bei mir immer der Auflösung. Der Timer sorgt am Ende dafür das zu unterschiedlichen Compare Match Zeiten (Pulsweite) immer mit dem vorher berechneten konstanten Indexabstand durch die Tabelle gesprungen wird. Änderst du die gewünschte Frequenz ändert sich der Indexabstand mit dem durch die Tabelle gesprungen wird. Das schiften begrenzt den max. Akkuwert. OCR1B darf ja nie größer TOP sein sonst gibts Aussetzer.

Ich helfe dir einmal auf die Sprünge. Damit kannste rumspielen wie du lustig bist. Als RC habe ich 2,7k und 100nF. Kannste bestimmt noch auf 10k erhöhen. Klemm ein 2 Kanal Oszi ran, einmal vorm R und einmal zwischen R und C.

/*
  Doc_Arduino - german Arduino Forum
  IDE 1.8.16
  Arduino Mega2560
  25.09.2021
  https://forum.arduino.cc/t/sinus-signal-mit-variable-frequenz/904132/31
  Übernahme im Overflow statt Compare Match
*/

const uint8_t Sinus[] PROGMEM = { // 8 Bit 
   127,  130,  133,  136,  139,  143,  146,  149,  152,  155,  158,  161,  164,  167,  170,  173,
   176,  178,  181,  184,  187,  190,  192,  195,  198,  200,  203,  205,  208,  210,  212,  215,
   217,  219,  221,  223,  225,  227,  229,  231,  233,  234,  236,  238,  239,  240,  242,  243,
   244,  245,  247,  248,  249,  249,  250,  251,  252,  252,  253,  253,  253,  254,  254,  254,
   254,  254,  254,  254,  253,  253,  253,  252,  252,  251,  250,  249,  249,  248,  247,  245,
   244,  243,  242,  240,  239,  238,  236,  234,  233,  231,  229,  227,  225,  223,  221,  219,
   217,  215,  212,  210,  208,  205,  203,  200,  198,  195,  192,  190,  187,  184,  181,  178,
   176,  173,  170,  167,  164,  161,  158,  155,  152,  149,  146,  143,  139,  136,  133,  130,
   127,  124,  121,  118,  115,  111,  108,  105,  102,   99,   96,   93,   90,   87,   84,   81,
    78,   76,   73,   70,   67,   64,   62,   59,   56,   54,   51,   49,   46,   44,   42,   39,
    37,   35,   33,   31,   29,   27,   25,   23,   21,   20,   18,   16,   15,   14,   12,   11,
    10,    9,    7,    6,    5,    5,    4,    3,    2,    2,    1,    1,    1,    0,    0,    0,
     0,    0,    0,    0,    1,    1,    1,    2,    2,    3,    4,    5,    5,    6,    7,    9,
    10,   11,   12,   14,   15,   16,   18,   20,   21,   23,   25,   27,   29,   31,   33,   35,
    37,   39,   42,   44,   46,   49,   51,   54,   56,   59,   62,   64,   67,   70,   73,   76,
    78,   81,   84,   87,   90,   93,   96,   99,  102,  105,  108,  111,  115,  118,  121,  124
};

const uint16_t RESOLUTION {sizeof(Sinus)/sizeof(Sinus[0])}; 
const uint32_t PWM {F_CPU/RESOLUTION};      // 8 Bit: 62500  / 10 Bit: 15625
const uint16_t FREQUENCY {50};              // Hz
const uint16_t PHASENDELTA {static_cast<uint16_t>((65536UL*FREQUENCY/PWM)+0.5)};  // 50Hz >> 8 Bit: 52 / 10 Bit: 210
uint16_t phasenakku;

void setup()
{
  Serial.begin(9600);
  Serial.println(F("\nuC Reset ### ###"));  
  Serial.print(F("RESOLUTION ")); Serial.println(RESOLUTION);
  Serial.print(F("PWM "));        Serial.println(PWM);
  Serial.print(F("FREQUENCY "));  Serial.println(FREQUENCY);
  Serial.print(F("PHASENDELTA "));  Serial.println(PHASENDELTA);
  Serial.print(F("TOP "));        Serial.println(RESOLUTION - 1);
  
  pinMode(12, OUTPUT);  // OC1B = UNO Pin 10 / Mega2560 Pin 12
  // Mode 14, Fast PWM - ICR1
  TCCR1B = 0;
  TCCR1A = 0;
  TIMSK1 = 0;
  OCR1B = 0;
  TCNT1 = 0;
  ICR1 = RESOLUTION - 1;
  TIMSK1 = _BV(TOIE1);    // Overflow Interrupt Enable
  TCCR1A = _BV(COM1B1) | _BV(COM1B0) | _BV(WGM11);  // Clear OCnX on compare match
  TCCR1B = _BV(WGM13)  | _BV(WGM12)  | _BV(CS10);
}

void loop()
{
    
}

ISR(TIMER1_OVF_vect) 
{
  phasenakku += PHASENDELTA;
  OCR1B = pgm_read_byte(&(Sinus[phasenakku >> 8]));  // 8 Bit
  //OCR1B = pgm_read_word(&(Sinus[phasenakku >> 6]));    // 10 Bit
}

Edit: Interrupt geändert

Mit dem COMPB Interrupt läuftst Du Gefahr, daß die Änderung des Vergleichsregisters gleich den nächsten Interrupt auslöst. Wenn der Timer hochzählt dann sollte der nächste Vergleichswert niedriger sein, beim Runterzählen höher.

Hallo,

die OCRnx sind im PWM Mode gepuffert.

Das hilft nicht. Wenn der Interrupt bei 110 auftritt, und der nächste Wert 120 ist, dann kommt eben 10 Takte später der nächste Interrupt.

Hallo,

überlege nochmal in Ruhe wie PWM funktioniert. Oder probiere es praktisch aus.

Okay, man lernt nicht aus. Gepuffert wußte ich noch, nur doppelt gepuffert war mir entgangen.

Aber warum dann kein Update beim Overflow?

Hallo,

man kann schon einmal durcheinanderkommen, ist alles kein Problem.

Umgangssprachlich gibts für mich nur entweder es ist gepuffert und damit 2x vorhanden oder es ist nicht gepuffert und damit nur einmal vorhanden. Deswegen habe ich das Wörtchen double unterschlagen. Sorry.

Overflow Interrupt. Geht auch. Der Interruptaufrufzyklus ist damit konstant. Kann man machen. :wink: Das Update erfolgt ja eh erst zu diesen Zeitpunkt. Habs oben im Code geändert.

ich habe dein programm auf ausprobiert und das kam im osci raus. Die frequenz bleibt bei 3,8hz stehen und ist leider nicht variable.

Jetzt sind wir in der Zwickmühle. Der Code ist reichlich tricky, insbesondere wegen der fehlenden Kommentare, sieht aber korrekt aus. Das Oszillogramm sagt aber tatsächlich etwas um 4Hz aus. Wo liegt der Fehler? Ein Faktor von etwa 13 zwischen Theorie und Praxis läßt sich nur schwer erklären.

Welche variable Frequenz erwartest Du, warum?

ich erwarte ein frequenz zwischen 45hz und 55hz.
und wenn er die 55hz erreicht hat muss dann zu 45hz zurück..
so ist die aufgabe

Na ja, einen kleinen Teil Deiner Aufgabe solltest Du schon selbst hinzufügen. Im Prinzip mußt Du nur aus const FREQUENCY eine Variable machen, die sich im 2s Rhythmus zwischen 45 und 55 ändert. Das geht z.B. mit BlinkWithoutDelay und einem Flag für rauf-/runterzählen.

Mein Mega gibt etwa 7Hz aus, in 8 Schritten (Phasenwinkeln). Mir scheint da irgendwas an der Initialisierung zu fehlen, und auch an der Berechnung der Schritte. Bei beiden Interruptquellen, daran liegt es nicht.

Hallo,

ich messe und sehe exakt einen 50Hz Sinus nach Filter und auch 45 oder 55Hz. Habe nochmal rückwärts den geposteten Code geladen, nicht das was verloren ging, kein Unterschied bei mir.

Habt ihr den richtigen Pin im Code gewählt? Ich habe derzeit keine Erklärung für die krasse Abweichung. Eure Arduinos laufen mit 16MHz? Obwohl das mit der Formel egal sein sollte.

Was messt ihr hiermit?

// Mega
// const byte Takt_Pin = 12;     // OC1B bzw. PB6, nicht invertiert

// Uno
const byte Takt_Pin = 10;  // OC1B bzw. PB2, nicht invertiert

void setup()
{
  pinMode(Takt_Pin, OUTPUT);
  set_Timer1();
}

void loop()
{

}  

// ****** Funktionen ******* //
void set_Timer1()   //  Fast-PWM, Mode 15
{
  TCCR1A = 0;    // Reset TCCR1A Register 
  TCCR1B = 0;    // Reset TCCR1B Register
  TIMSK1 = 0;    // Reset TIMSK1 Register (disable Timer Compare Interrupts)
  TCNT1  = 0;    // Start 0
  OCR1A = 19999; // TOP Wert bestimmt Auflösung und mit Prescaler den PWM Takt
  OCR1B = 10000; // Pulsweite, OCR1B <= OCR1A
  TCCR1A = (1<<COM1B1) | (1<<WGM11) | (1<<WGM10);  // nicht invertiert
  TCCR1B = (1<<WGM13) | (1<<WGM12) | (1<<CS11);    // Prescaler 8 
} 

Das gibt ein Rechteck mit 10ms.

Korrekt. Jetzt ist guter Rat teuer :thinking: