aiuto su codice Generatore di funzioni

Ciao, il codice che riporto non l’ho scritto io, l’ho trovato in rete ed è un generatore di funzione perfettamente funzionante, anche se il segnale è parecchio sporco, con range di frequenza 30-490Hz(testato su oscilloscopio), non ho molta esperienza ancora con Arduino ed è un mese che provo a capire come lavora questo codice, fra un informazione e un altra mi sono fatto un idea ma ancora ho diversi dubbi.
In particolare all’interno della ISR, non capisco il passaggio fondamentale dove con PhaseOut si imposta la frequenza del segnale in uscita. Perche si somma la variabile a 16 bit PhaseOut al valore di 10bit del potenziometro? Perche si fa scorrere di 8 bit la variabile PhaseOut all’iterno di RAMTable?
Scusate se sono dimande banali, se qualcuno più esperto di me mi sa rispondere. grazie

#include "avr/pgmspace.h"
#include <LiquidCrystal.h>

LiquidCrystal lcd(12, 11, 6, 5, 4, 2);

// area di memoria per contenere i dati della forma d'onda
unsigned char RAMTable[256]; 

// tabella di 256 valori seno
unsigned char sineTab[] = {
  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};

double freqOut;
const double REFCLOCK=31376.6;     // clock di riferimento
int counter=0;


volatile byte IntCNT;              
volatile byte IntCNT1;             
volatile byte counter_ms;          //counter fittizio per poter disabilitare il timer0
int statusPin1 = 0;
int buttonInput1 = 13;

void setup()
{
  lcd.begin(16, 2);
  lcd.setCursor(0,0);
  lcd.print("Generatore di ");
  lcd.setCursor(0,1);
  lcd.print("funzioni ");
  delay (3000);
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print("SINE WAVE       ");

SineWave(); // forma d'onda SINE di default

  pinMode(3, OUTPUT); // pin 3 = PWM out frequency: cambiare la corrispondente porta OCR2B e i bit COM2B0 e COM2B1 del timer2
  pinMode(buttonInput1, INPUT);

Setup_timer2();
  // disabilita gli interrupt per evitare variazioni nel timing
  bitClear(TIMSK0,TOIE0); // disabilita il Timer0 (quello della funzione delay())
  bitSet(TIMSK2,TOIE2);   // abilita il Timer2 Interrupt
}

void loop()
{
freqOut=analogRead(5);
  if (counter_ms > 100) {             // timer per evitare lo sfarfallio del display
  counter_ms=0;
  lcd.setCursor(0,1);
  lcd.print("Freq.  ");
  lcd.print(freqOut);      
        
statusPin1=digitalRead(buttonInput1);
if (statusPin1)  counter ++;
  
  if (counter > 3)
 { 
  counter = 0;
  TriangleWave();
// visualizza il tipo di forma d'onda la frequenza sul display
  lcd.setCursor(0,0);
  lcd.print("TRIANGLE WAVE   ");
  lcd.setCursor(0,1);
  lcd.print("Freq.  ");
  lcd.print(freqOut);      
  }
  
if (counter == 1)
{
  SquareWave();
// visualizza il tipo di forma d'onda la frequenza sul display
  lcd.setCursor(0,0);
  lcd.print("SQUARE WAVE     ");
  lcd.setCursor(0,1);
  lcd.print("Freq.  ");
  lcd.print(freqOut);      
  }
  
if (counter == 2)
{
  Sawtooth();
// visualizza il tipo di forma d'onda la frequenza sul display
  lcd.setCursor(0,0);
  lcd.print("SAWTOOTH WAVE   ");
  lcd.setCursor(0,1);
  lcd.print("Freq.  ");
  lcd.print(freqOut);      
  }
if (counter == 3)
{
  SineWave();
// visualizza il tipo di forma d'onda la frequenza sul display
  lcd.setCursor(0,0);
  lcd.print("SINE WAVE       ");
  lcd.setCursor(0,1);
  lcd.print("Freq.  ");
  lcd.print(freqOut);      
  }
}
}


// Imposta il timer2 con prescaler a 1, PWM mode per la correzione di fase PWM, 16.000.000/512 = 31.372 Hz di clock
void Setup_timer2() {
// Timer2 Clock Prescaler a 1
  bitSet(TCCR2B, CS20);
  bitClear(TCCR2B, CS21);
  bitClear(TCCR2B, CS22);
// Timer2 PWM Mode impostato su Phase Correct PWM sul pin 3
  bitClear(TCCR2A, COM2B0);
  bitSet(TCCR2A, COM2B1);
// Mode 1 Phase Correct PWM
  bitSet(TCCR2A, WGM20); 
  bitClear(TCCR2A, WGM21);
  bitClear(TCCR2B, WGM22);
}

// Timer2 Interrupt a 31372,550 KHz = 32uSec (REFCLOCK per il generatore)
// runtime di 8 microsecondi
ISR(TIMER2_OVF_vect) 
{
 static unsigned int PhaseOut;


PhaseOut=PhaseOut+freqOut;

OCR2B=RAMTable[PhaseOut>>8];   // uscita audio sul pin 3

   if(IntCNT1++ == 125) {  
    counter_ms++; // incremento di 4 millisecondi (per sfarfallio display)
    IntCNT1=0;
   }
}


// calcola e inserisce i valori nella tabella
void TriangleWave()
{
  for (int i = 0; i < 128; ++i) {
    RAMTable[i] = i * 2;
  }
  int value = 255;
  for (int i = 128; i < 256; ++i) {
    RAMTable[i] = value; //inserisce i valori nella tabella
    value -= 2;
  }
}

// calcola e inserisce i valori nella tabella
void SquareWave()
{
  int t;
  for (int i = 0; i < 256; ++i) 
  {
if (i == 0) t = 0;
if (i == 128) t = 255;
    RAMTable[i] = t; // // inserisce i valori nella tabella
  }
}

// inserisce i valori nella tabella
void Sawtooth()
{
  for (int i = 0; i < 256; ++i) 
  {
    RAMTable[i] = i;
  }
}


// inserisce i valori sineTab[] in RAMTable[]
void SineWave()
{
  for (int i = 0; i < 256; ++i) 
  {
    RAMTable[i] = sineTab[i];
  }
}

La discussione è simile a questa:

Ma vi siete dati l'appuntatomento con questi generatori di funzione? :wink: :wink:
In quello sketch c'è un errore su freqOut, dichiarato di tipo double, cioè float (i double in Arduino non esistono) quando poi va a contenere la lettura di un input analogico, che è un numero intero che va da 0 a 1023.

Comunque la somma fra PhaseOut e freqOut è fatta per far sì che si possa modificare tramite il potenziometro la frequenza generata. Poi si scorre a dx di 8 posizioni perché la matrice che contiene i dati per generare la curva sinusoidale è a 256 celle, quindi 2^8, ma phaseOut è a 16 bit, per cui si considerano solo gli 8 bit più significativi, perdendo quelli meno significativi.

Grazie mille della risposta.
Si questo l'avevo capito, quel che non riesco a figurarmi è come inserendo quel valore all'interno di RAMTeble modifico la frequenza del segnale.
Comunque... il potenziometro è a 10bit, scorrendo di 8 bit a dx nella variabile PhaseOut si "salvano" solo i 2 bit più significativi del potenziometro o non ci stò capendo niente?

Variando il duty cycle del segnale PWM generato dal timer vari il "finto segnale analogico" che hai in uscita sul pin.
Con la mappa in memoria si fa scorrere un indice e poi si mette nel timer il corrispondente valore della cella puntata. In uscita vedrai quindi un segnale che replica l'onda voluta. Se metti un filtro RC sul pin ammorbidisci la curva (il PWM è un'onda quadra, alla fine, non è nulla di analogico).

Ma il duty cycle cosa c'entra con la frequenza del segnale "analogico" in uscita?
Cioè il segnale "analogico" che vedo è un segnale PWM camuffato? Se si io ho un prescaler a 1 sul timer2 e avrei un segnale con frequenza altissima, 16000000/510*1=31372Hz, non può essere, non ho capito..

La frequenza dovrebbe arrivare a 32 KHz, così vedo dai commenti nel codice (non mi sono messo a fare conti né a verificare se la modalità di funzionamento è quella indicata).
Dato che l’Arduino non ha un vero output analogico, lo si simula con un segnale PWM. Alternando velocemente lo stato di un pin da off a on si può simulare una tensione analogica. Il rapporto tra stato on e off si chiama duty cycle. Questo non varia la frequenza del segnale che resta sempre quella. Ma modificando questo rapporto puoi far vedere una tensione più o meno maggiore in uscita.

Poi credo funzioni così quello sketch, non mi sono messo a studiarlo approfonditamente (non sono a casa né a mente fresca).

Ho visto in molte pagine il sistema che tu descrivi, filtrando la pwm con un passa-basso si ottiene il valor medio della pwm, il duty cycle varia il rapporto high/low e varia quindi il valor medio. Il valor medio nel tempo è il segnale analogico. Se il caso di questo codice è analogo a questo funzionamento io non ci ho capito niente e riparto da capo... grazie dei consigli, buona notte

Domani a mente fresca :wink: