Libreria per modificare Frequenza PWM Arduino

Buongiorno a tutti.
Avrei bisogno di modificare la frequenza di un singolo Pin di uscita PWM per un Arduino Uno, senza se possibile modificare i timer interni.
Ho letto un articolo che utilizzava il comando senza indicare eventuali librerie aggiuntive:

setPwmFrequency(9, 1);

Per dividere la frequenza di base del Pin 9 che dovrebbe essere nell'Arduino Uno per il Pin 9 a 31250Hz dividendo per 1 invece che per 64 ed avere quindi una Frequenza di 31250Hz al posto di 488Hz.
Mi serviva per provare ad eliminare il fischio per comandare un Motore Dc.
Ho provato ad inserire il comando nel void setup ma mi viene dato l'errore di comando non dichiarato.
Credo quindi che ci sia bisogno di una libreria esterna.
Leggendo su google ho trovato il nome di questa libreria "PWM-Frequency-library" ma non riesco a trovarla per aggiungerla.

Non mi pare sia possibile. Si basano sui timer.
Qui una libreria (anzi una funzione) per la Uno:
"GitHub - fizcris/PWM_frequency_Arduino_change: Functions to change PWM frequency in Arduino UNO & MEGA."
con tutti gli avvisi del caso, leggi sezione " DO NOT MODIFY pin 6 as..."

Grazie mille per la risposta.
credo che questa soluzione sia più che sufficiente in quanto ora ho capito che i tempi sono appunto gestiti da due pin che non tocco. A me serviva un uno qualsiasi quindi posso utilizzare il Pin 9 o 10 che utilizzano lo stesso timer e sono apposto.
Come mai non riesco a trovarla direttamente dall'IDE? forse sbaglio modo di cercare, io cerco
PWM_frequency_Arduino_change

Non è un comando base di Arduino core.
E a quel link che ti ho postato, non sembra una libreria ma solo un semplice pezzo di codice.

P.S. ricordo che su Arduino Uno/nano (atmega 328p):

  • Pins 5 and 6: controlled by Timer 0
  • Pins 9 and 10: controlled by timer 1
  • Pins 11 and 3: controlled by timer 2

Timer 0 usato da delay, timer1 e timer2 usate da alcune librerie, ad esempio la servo usa di default timer1, IrRemote il timer2. Sono solo esempi

E' un pezzo di codice anche poco furbo, perche i case vanno a usare esattamente gli stessi valori argomenti dello switch! :slight_smile:

Inoltre "Divisor" in realtà non è il divisore ma, appunto, il numero da inserire per ottenere il divisore desiderato, che è una potenza di due.

Sono anche sbagliate le frequenze dei pin 11 e 3, probabilmente misurate su un Arduino Uno che ha un oscillatore ceramico molto approssimativo, anziché calcolate come per quelle degli altri pin.

Quella funzione equivale a questa:

void setPwmFrequencyUNO(uint8_t pin, uint8_t n)
  {
  if(pin==5 || pin==6 || pin==9 || pin==10) 
    {
    if(pin==5 || pin==6)
      {
      TCCR0B = TCCR0B & 0b11111000 | n; // 5 o 6.
      } 
    else 
      {
      TCCR1B = TCCR1B & 0b11111000 | n; // 9 o 10.
      }
    }
  else if(pin==3 || pin==11) 
    {
    TCCR2B = TCCR2B & 0b11111000 | n; // 3 o 11.
    }
  }
    Pin 5&6; 9&10:

n     Div  Frequenza
1     256  62500,0Hz
2    2048   7812,5Hz
3   16384    976,5625Hz
4   65536    244,140625Hz
5  262144     61,03515625Hz
        Pin 5&6 (Timer 0, come millis()!):
TCCR0B = TCCR0B & 0b11111000 | n; // Cambia solo i 3 LSB.
        Pin 9&10 (Timer 1):
TCCR1B = TCCR1B & 0b11111000 | n; // Come sopra.


    Pin 3&11 (Timer 2):

n     Div  Frequenza
1     512  31250,0Hz
2    4096   3966,25Hz
3   16384    976,5625Hz
4   32768    488,28125Hz
5   65536    244,140625Hz
6  131072    122,0703125Hz
7  524288     30,517578125Hz

TCCR2B = TCCR2B & 0b11111000 | n; // Come sopra.

Quindi dovrei inserire tutto il codice e la
Funzione per modificare la frequenza del pin 9 e di conseguenza del pin 10?

Effettivamente, un pò prolissa e con codice inutile.
Però rispetto alla tua, c'é l'uscita senza fare nulla se divisor o n non sono validi

Sì, hai ragione. La modifico:

void setPwmFrequencyUNO(uint8_t pin, uint8_t n)
  {
  if((pin==5 || pin==6 || pin==9 || pin==10) && n>0 && n<6) 
    {
    if(pin==5 || pin==6)
      {
      // Azzera i 3 LSB e li sostituisce:
      TCCR0B = TCCR0B & 0b11111000 | n; // 5 o 6.
      } 
    else 
      {
      TCCR1B = TCCR1B & 0b11111000 | n; // 9 o 10.
      }
    }
  else if((pin==3 || pin==11) && n>0 && n<8) 
    {
    TCCR2B = TCCR2B & 0b11111000 | n; // 3 o 11.
    }
  }

Occhio comunque a modificare le frequenze dei Timers ... ricordade sempre che il "core" Arduino per la UNO (ATmega328P) li imposta e li usa così:

Timer0

  • Usato da millis() e delay() e per il PWM sui pin 5 e 6
  • fast hardware pwm
  • (default 976.5625 Hz)
  • Pin 5 è anche usato per il “pulse counting
  • Pin 8 è usato per il “input capture

Timer1

  • Usato dalla libreria Servo e per il PWM sui pin 9 e 10. Libreria e PWM mutuamente esclusivi.
  • 8-bit phase correct pwm mode
  • (default 488.28125 Hz)

Timer2

  • Usato per il PWM sui pin 3 e 11
  • 8-bit phase correct pwm mode
  • (default 488.28125 Hz)

Su Leonardo e MEGA vengono usati in modo leggermete differente ...

Guglielmo

1 Like

Grazie mille a tutti.
Solo non so come inserire quel codice per farlo diventare una libreria...

Non ne fai una libreria, lo copi così com'è dentro al tuo .ino

altro esempio:
"Modificare frequenza del PWM di Arduino | danielealberti.it"

Copio questa parte solo?
E poi devo richiamarla alla fine?

Copi quella funzione nel tuo programma e la chiami, inserendo i due parametri, per modificare la frequenza.

RISOLTO

Ho seguito ciò che mi hai detto e richiamato la funzione in questo modo per il pin 9 e di conseguenza il pin 10

setPwmFrequencyUNO(9, 1);

Ho impiegato giorni di ricerche e per cercare di capire e invcece in giornata avete ricreato una funzione, corretta come io sbadatamente correggerei un digitalwrite con la w minuscola...
Che dire, siete davvero grandi.
Grazie mille, ora il motore non fischia più :slight_smile:
Sto per acquistare un arduino nano every che come sapete ha un processore diverso, questa funzione potrebbe lavorare allo stesso modo o non è compatibile?

No, la mcu è diversa, quella funzione lavora con i registri della mcu montata su Arduino uno/nano, atmega328p

Ok, grazie mille!

Buonasera a tutti.
Ho scritto nuovamente qui, senza aprire un altro post in quanto il fine è uguale.
Se ho sbagliato chiedo scusa. :grimacing:
Volevo trovare una soluzione analoga anche per l' Arduino Nano Every.
Cercando in rete, ho trovato questo codice che seppur non dia errori, pare non modificare la frequenza in uscita a quel valore.
Collegando un oscilloscopio, il valore rimane sempre a 1.000Khz circa.
Sò che la frequenza dell'Arduino Nano Every è di 20Mhz ma ho lasciato ugualmente 31250Hz (che non credo sia raggiungibile con qualsiasi divisore) ma ho voluto lasciare il codice come l'ho trovato.
Grazie in anticipo

void setup() {
  // Imposta il pin 9 come output
  pinMode(9, OUTPUT);

  // Imposta la frequenza del PWM sul pin 9
  setPin9PWMFrequency(31250);
}

void loop() {
  analogWrite(9, 128);  // Imposta il duty cycle del PWM al 50% (valore tra 0 e 255)
}

void setPin9PWMFrequency(long frequency) {
  // Calcola il valore del prescaler in base alla frequenza desiderata
  long prescalerValue = F_CPU / (frequency * 2) - 1;

  // Imposta il prescaler del Timer/Counter 2 (TC2)
  TCB2.CTRLA = TCB_CLKSEL_CLKDIV2_gc | TCB_ENABLE_bm;

  // Imposta il periodo del Timer/Counter 2 (TC2)
  TCB2.CCMPL = 0xFFFF;

  // Imposta il prescaler del Timer/Counter 3 (TC3)
  TCB3.CTRLA = TCB_CLKSEL_CLKDIV2_gc | TCB_ENABLE_bm;

  // Imposta il periodo del Timer/Counter 3 (TC3)
  TCB3.CCMPL = prescalerValue;
}

In aggiunta ho trovato anche questa stringa di codice che da sola modificherebbe la frequenza dei pin associati 9 e 10 impostando il divisore a 128 nel caso dell' 8 , (0xF8).
Anche questa non dà errore ma non modifica la frequenza come desiderato.

TCB2.CTRLA = (TCB2.CTRLA & 0xF8) | 0x01; 

io ho risolto seguendo questo