Frequenza PWM

Dovrei usare il pwm con una frequenza di 25 kHz, ho visto quà le varie frequenze che si possono impostare: Arduino Playground - TimerPWMCheatsheet ma 25 kHz non c'è da nessuna parte e ci sono solo dei valori fissi.
Mi sembra strano che non si possa usare qualsiasi frequenza si voglia, qualcuno mi può spiegare come fare?

Visto che la frequenza deriva dal clock del ATmega per suddivisione ci sono disponibili solo certe frequenze.
Ciao Uwe

Ma ti serve una frequenza fissa o devi gestirla proprio come pwm? usando un solo pin con il relativo timer i 25KHz circa si ottengono, ma solo come frequenza fissa

d407336:
Dovrei usare il pwm con una frequenza di 25 kHz, ho visto quà le varie frequenze che si possono impostare: Arduino Playground - TimerPWMCheatsheet ma 25 kHz non c'è da nessuna parte e ci sono solo dei valori fissi.
Mi sembra strano che non si possa usare qualsiasi frequenza si voglia, qualcuno mi può spiegare come fare?

Non è facile ma neanche impossibile.
devi manipolare un timer del microcontrollore in modo da ottenere su un pin Pwm un segnale con la frequenza desiderata.
Prendiamo il timer 2, facilmente modificabile. La frequenza la ottieni con Fsys/(Presc256)
Fsys è 16MHz, Presc il prescaler, 256 il max valore del registro ad 8 bit.
Per ottenere 25 kHz devi scegliere un prescaler di /8 ed un valore del contatore del timer di 176.
16000000/(8
(256-176))=25000

Non capisco cosa intendi con frequenza fissa...
Penso che dovrei usare analogWrite() normalmente per gestire il pwm.

leo72:
Non è facile ma neanche impossibile.
devi manipolare un timer del microcontrollore in modo da ottenere su un pin Pwm un segnale con la frequenza desiderata.
Prendiamo il timer 2, facilmente modificabile. La frequenza la ottieni con Fsys/(Presc256)
Fsys è 16MHz, Presc il prescaler, 256 il max valore del registro ad 8 bit.
Per ottenere 25 kHz devi scegliere un prescaler di /8 ed un valore del contatore del timer di 176.
16000000/(8
(256-176))=25000

Allora... Fsys è la frequenza con la quale arduino esegue loop() ?
Da quanto ho capito il prescaler è un registro a 8 bit di un timer di arduino in cui si può mettere un valore massimo di 256, il relativo timer andrà alla velocità Fsys diviso il valore del prescaler?
Poi il resto non l'ho capito, /8 e 176 cosa sono?

Non sò ancora bene se farò una cosa semplice o aggiungerò più roba, ma nel caso riuscissi a stare su un Attiny85, visto che la frequenza è diversa da quella dell'Atmega328, basta modificare il prescaler e poi si può usare l'Attiny85?

Con Fsys indico la frequenza a cui lavora l'Atmega328 dell'Arduino (16000000 Hz = 16 MHz).
Il prescaler è un divisore, non è il registro contatore del timer. Serve a dividere la frequenza di sistema per un fattore predefinito preso fra quelli disponibili (riportati sul datasheet). Nell'esempio abbiamo usato un fattore 8, quindi prescaler /8 significa clock di sistema diviso 8. Per cui all'uscita del prescaler la frequenza effettiva con cui viene aggiornato il timer diventa 16000000/8=2000000 Hz = 2 MHz.
Il contatore del timer è a 8 bit, quindi 256 valori massimi. Usando la modalità FastPWM per ottenere la frequenza si usa la formula che ti ho citato:
Fsys/(Presc*256)

Sappiamo che dobbiamo ottenere una frequenza finale di 25 KHz. Se noi usassimo tutti i 256 valori del contatore e la frequenza piena di clock otterremmo una frequenza di:
16000000/(8*256)=7812,5 Hz.
Non va bene. Però noi abbiamo ridotto la frequenza a 2 MHz usando un prescaler a 8.
Quindi da 2 MHz per ottenere 25 KHz dobbiamo usare un contatore di
2000000/25000=80

Ma il nostro registro può contenere 256 valori. Come fare? Semplicemente immettendo nel registro un valore di partenza, in modo da farlo arrivare prima all'overflow:
256-80=176

Quindi ad ogni overflow dobbiamo scatenare un interrupt che chiami una routine che aggiorni il contatore del timer.

Tradotto il tutto in codice devi scrivere le seguenti righe:

void setTimer2() {
cli(); //fermo gli interrupt
TIMSK2 &= ~((1<<TOIE2) | (1<<OCIE2A) | (1<<OCIE2B)); //disattivo tutti gli interrupt sul timer 2
ASSR &= ~(1<<AS2); //clock prelevato dal clock di sistema
//imposto il timer in modalità FastPWM
TCCR2A |= ((1<<WGM21) | (1<<WGM20));
TCCR2B &= ~(1<<WGM22);
//imposto il toggle sul pin OC2A (pin D11 di Arduino)
TCCR2A &= ~(1<<COM2A1);
TCCR2A |= (1<<COM2A0);
//prescaler a /8
TCCR2B &= ~((1<<CS22) | (1<<CS20));
TCCR2B |= (1<<CS21);
TCNT2 = 176; //valore iniziale del timer
TIMSK2 |= (1<<TOIE2); //attivo un interrupt sull'overflow del timer2
sei(); //riattivo gli interrupt
}

//routine di overflow
ISR(TIMER2_OVF_vect) {
TCNT2 = 176; //reimposto il valore iniziale del timer
}

A questo punto attivi il generatore facendo un pinMode(OUTPUT) mentre per fermare il tutto metti un pinMode(INPUT).
Se hai un oscilloscopio o un DSO, prova e fammi sapere, non ho testato il codice perché non ho nessuno dei 2 apparecchi citati per controllare quindi ho scritto il codice di getto.

Ti ringrazio molto per il codice e la spiegazione, anche se non riesco a capire molto (perchè non sò come funziona, non perchè è spiegato male  :))...
Il codice posso farmelo provare con un'oscilloscopio, ma non ho capito se prima devo definire le due funzioni che hai scritto e poi mettere:
[code]
void setup(){
  pinMode(11, OUTPUT);
  analogWrite(11, 128);
}

void loop(){
}

Poi ho trovato anche questa libreria: Arduino Playground - HomePage
e questo tutorial: Gioblu.com is for sale | HugeDomains
Quindi inizio subito a leggerlo :slight_smile:

Edit:
ho provato la libreria e con due righe ho la frequenza che mi serve e in loop() posso cambiare il duty cycle come voglio (da 0 a 1023) :slight_smile: :slight_smile:

#include "TimerOne.h"

void setup(){
  Timer1.initialize(40); // 40 microsecondi, cioe 1/25000
  Timer1.pwm(9, 512);
}

void setup(){
}

solo non ho capito bene cosa fa la funzione attachInterrupt() di questa libreria...[/code]

Allora, ho capito come usare la libreria Timer1 e con poche linee di codice posso avere sui pin 9 e 10 un segnale pwm con la frequenza che mi serve e variarne il duty cycle.
Visto però che con il timer1 posso controllare solo la frequenza dei pin 9 e 10, vorrei provare a modificare anche il timer2 per poter avere anche sui pin 3 e 11 un segnale pwm con frequenza fissa e duty cycle variabile.
Il codice che mi ha scritto leo dovrebbe fare proprio questo, però non capisco come và usato...
Con la libreria timer1 è molto facile: con Timer1.initialize(500000); imposto la frequenza del pwm e con Timer1.pwm(9, 512); imposto il duty cycle, che posso cambiare quando voglio con Timer1.setPwmDuty(9, 512);
Vorrei fare la stessa cosa anche con i pin del timer2.

Riprendendo il codice che ho postato, in modalità FastPWM il duty cicle sul canale OC2A è dato dal valore del registro OCR2A + 1 fratto 256, che è il max numero di valori che può assumere il contatore del timer 2, che è ad 8 bit, per cui 2^8=256. Ecco la formula:
(OCR2A+1)/256
mentre il duty cicle sul canale OC2B è dato da quest'altra
(OCR2B+1)/256

Quindi, per avere un duty cicle del 50% su OC2A devi avere
(OCR2A+1)/256=50%
per cui
50%=0,5
(OCR2A+1)=256*0,5=128
da cui
OCR2A=128-1
Quindi mettendo OCR2A a 127ottieni un duty cicle del 50%.
Stesso dicasi su OCR2A.

In pratica devi semplicemente prendere quel codice e poi modificare questi 2 registri

non capisco come usare quel codice, devo dichiarare le due funzioni prima del setup e poi richiamarle nel setup e poi usare analogWrite()?

sarebbe possibile portare la libreria timer1 sul timer2 con le dovute modifiche o è impossibile perchè il timer1 è a 16bit e il timer2 a 8bit?

d407336:
non capisco come usare quel codice, devo dichiarare le due funzioni prima del setup e poi richiamarle nel setup e poi usare analogWrite()?

Non capisco cosa non capisci :stuck_out_tongue_closed_eyes:
Allora, abbiamo il codice per settare il timer 2, che è quello che ho messo nel post #5.
Lo devi richiamare 1 sola volta, mettilo dove vuoi: il suo posto naturale è nel setup, così com'è o tramite una funzione. L'importante è che lo richiami 1 solta volta.

Poi hai 2 pin dell'Arduino 3 e 11. Attivandoli con pinMode(OUTPUT) agganci il segnale PWM al pin. Manipolando i registri OC2A e OC2B, collegati al timer 2, cambi il duty cicle come ti ho detto.

sarebbe possibile portare la libreria timer1 sul timer2 con le dovute modifiche o è impossibile perchè il timer1 è a 16bit e il timer2 a 8bit?

Non so come funziona la Timer1, se cioè è legata indissolubilmente al timer 1 oppure no.
Andrebbe guardato il codice. O chiesto al suo autore.