PWM VELOCE

ratto93: Ho provato con la funzione delayMicroseconds(): non funzione perché abbassa notevolmente la frequenza dell'onda quadra a valori non accettabili per la mia applicazione.

Anzi, e questa è una questione che pongo un po' a tutti: la sopracitata funzione sembra non lavorare in modo opportuno per ritardi inferiori a qualche decina di microsecondi. In effetti, oltre alla frequenza che non è rispettata con precisione, si può notare sull'onda generata dell'instabilità forse dovuta alle chiamate ad interrupt interne al micro.

La cosa certa è che se si vuole viaggiare spediti è impossibile usare la funzione digitalwrite()!

Cari saluti.

Qui forse trovi del materiale che potrebbe esserti utile...

http://arduino.cc/forum/index.php/topic,41869.0.html

http://arduino.cc/forum/index.php/topic,84366.0.html

Ho usato direttamente i timer per evitare ritardi e/o altri problemi introdotti dal codice, sfruttando l'HW direttamente.
PS:
Perché mettere tutto in un loop infinito fatto con un for quando la routine loop() è essa stessa un ciclo infinito? :wink:

leo72: mah, io per non avere le mani legate e anche per questioni di controllo e leggibilità ho modificato la funzione main() togliendo il loop infinito. Naturalmente ho dovuto aggiungerlo nella procedura che ho scritto.
Oggi provo a realizzare il codice che mi hai inviato. La generazione di un'onda quadra a 100kHz e D.C. fisso è solo un primo esperimento per vedere come si comporta Arduino e se genera un'onda stabile e ben formata. In seguito devo certamente modulare il D.C. in digitale ovvero su due livelli.

Grazie Burt.

Ti consiglio questo documento:

spiega bene i vari PWM disponibili sul micro. Purtroppo si sofferma molto sui timer ad 8 bit: quelli a 16 sono molto più complessi da gestire, anche se più precisi e con maggiori potenzialità.

Ciao, è il mio primo messaggio in questo forum, abbiamo un problema in comune, generare un segnane e modularlo nelle frequenze dei 100KHz (a dir la verità a me basterbbero (10-40KHz);
Studiando le librerie ho trovato la libreria FrequencyTimer2 (Arduino Playground - FrequencyTimer2) ; nonchè Timer1 e Timer3 ( Arduino Playground - Timer1)
Con queste librerie, se ho capito bene è possibile, con la funzione setPeriod , generare una frequenza impostata su un pin, esiste anche la relativa funzione pwm (che verrà applicata su altri pin connessi al timer)
dacci un occhiata...
spero possa esserti utile

Grazie a tutti,

ho bazzicato un po' sui link che mi avete suggerito e sono veramente tutti molto interessanti.

A presto sentirci.

Burt

se vuoi questo è un programmino che genera una frequenza variabile in funzione della posizione del potenziometro, con poche modifiche lo adatti al tuo scopo

int variabile=0;
static char mode=0;
#define potPin 2 //Pin di ingresso per il potenziometro
int val = 0; //Variabile per salvare lo stato proveniente dal potenziometro
int divisore = 0; //Variabile per definire il numero di cicli di clock che comporranno un periodo d'onda

void setup()
{
// Configurazione del Timer2 per funzionare in modo fast PWM su OC2B (pin 3 di Arduino)
// set pin high on overflow, clear on compare match with OCR2B
TCCR2A = 0x23; // imposto “pin high on overflow”, e “clear on compare match with OCR2B
TCCR2B = 0x09; // seleziono come sorgente di clock I 16MHz di sistema senza prescaler
OCR2A = 1590; // inizializzo il top level match a 159 ->f_PWM=10kHz la frequenza sarà data da 16000000 : il valore di OCR2A
pinMode(3, OUTPUT); // abilito come uscita il pin 3.

}

void loop()
{
val = analogRead(potPin); //leggo il valore dal potenziometro tra 0 e 1023
divisore = 400+val; //assegno un periodo d'onda che andraà da 400 a 1423 ovvero da 40KHz a 11KHz
OCR2A=divisore; //regolo la frequenza in uscita
OCR2B=divisore/2 ; //imposto il duty cicle al 50%
delay(1000);
}

Alessiof76:

mah, io sono un po' perplesso, ho realizzato il seguente codice utilizzando l'uscita OC0B del micro ovvero la sezione B del timer 0 per avere un'onda quadra da 100 kHz con duty cycle variabile tra un minimo e un massimo:

TCC0A=B00100011; //fast PWM mode non invertito e TOP OCRA anziché FF
TCC0B=B00001001; //no prescaling TOP OCRA anziché FF

OCR0A=160; //frequenza 100 kHz

for (;:wink:
{
OCR0B=10; //duty cycle basso
delay(1000);
OCR0B=100; //duty cycle elevato
delay(1000);
}

il programma lavora correttamente ma la funzione delay non risulta operativa e il duty cycle varia tra un valore ed un altro rapidissimamente come se non ci fosse delay! In teoria dovrei avere un'alternanza di duty cycle alto e basso con un periodo di 1 secondo.
Fosse che la funzione delay utilizza lo stesso timer (timer0) e pertanto viene in qualche modo inibita?
Questo per me è un problema!

La definizione della funzione delay la trovi nel file wiring.c nella cartella hardware/arduino/cores/arduino/..
Usa il timer 0.

PaoloP:
A ecco, allora forse è per quello che non lavora correttamente. Oggi vado a vedere e provo se riesco a risolvere il problema utilizzando un altro timer.

Grazie molte e a presto.

Usa il timer 2, è a 8 bit come lo 0.

... Infatti ho provato proprio con il timer 2 e tutto funziona correttamente.

Grazie mille e a presto!

mi collego a questo topic perchè il mio problema è simile, io volevo fare dei segnali pwm ad una frequenza compresa tra 15 e 20 khz per controllare dei motori... io uso un arduino mega quindi i registri dei timer abbinati ai pin sono un po' diversi e mi sono ispirato a quello che ho trovato qui: SobiSource.com is for sale | HugeDomains per scrivere questo codice che mi da circa 31khz sul pin 9

void setup()
{
 TCCR2B = (TCCR2A & 0xF8) | 0x01 ;
 pinMode(9, OUTPUT);
 analogWrite(9,127);
}

void loop(){}

per me però sono troppi perchè ho degli integrati che sopra i 20khz non vanno e quindi quello è il mio limite massimo...
ho provato a leggere vari post per capire come "giocare" con i timer e ho provato varie cose, come dicono qua: http://it.emcelettronica.com/fast-pwm-su-arduino-valida-alternativa-all’istruzione-analogwrite#_ che cambiano la frequenza diminuendo la sensibilità del pwm, ma non sto ottenendo risultati...

Imposta il timer 2 in modalità Phase Correct PWM ad 8 bit.
La frequenza in questa modalità la calcoli così:
Fclk/(2prescalermax_val_cont)
Ora se metti un prescaler di 8 ed usi un valore massimo del contatore di 56 ottieni
160000000/(2856)=17857,qualcosa e ci saresti pienamente.

Per ottenere un valore max del contatore di 56 basta farlo partire da 200, dato che (256-56)=200.
Quindi metti una ISR che reimposta il contatore a 200 ad ogni overflow.

leo72:
Imposta il timer 2 in modalità Phase Correct PWM ad 8 bit.
La frequenza in questa modalità la calcoli così:
Fclk/(2prescalermax_val_cont)
Ora se metti un prescaler di 8 ed usi un valore massimo del contatore di 56 ottieni
160000000/(2856)=17857,qualcosa e ci saresti pienamente.

Per ottenere un valore max del contatore di 56 basta farlo partire da 200, dato che (256-56)=200.
Quindi metti una ISR che reimposta il contatore a 200 ad ogni overflow.

ok grazie mille :wink: teoricamente ho capito cosa dovrei riuscire a fare, cos' quando vado a fare l'analogWrite posso arrivare solo fino a 56? o devo partire da 200? o posso andare direttamente sui registri OCRnB?

vale lo stesso processo anche per tutti gli altri timer? per il timer 0 che ha una frequenza doppia mi basta raddoppiare il range del pwm da 56 a 112?
grazie ancora :wink:

No no...il 200 lo devi impostare come base nel timer all'interno della ISR. 56 sono gli aggiornamenti che il contatore farà prima di andare in overflow. All'overflow, rimetti il contatore a 200.

Con l'analogWrite gestisci il duty cicle e basta.

Se non hai bisogno di modificare il duty cicle, puoi semplicemente agganciare o sganciare il segnale mettendo il pin in output o input, rispettivamente.

leo72:
No no...il 200 lo devi impostare come base nel timer all'interno della ISR. 56 sono gli aggiornamenti che il contatore farà prima di andare in overflow. All'overflow, rimetti il contatore a 200.

Con l'analogWrite gestisci il duty cicle e basta.

Se non hai bisogno di modificare il duty cicle, puoi semplicemente agganciare o sganciare il segnale mettendo il pin in output o input, rispettivamente.

io devo gestire dei motori, è quindi ho bisogno di poterlo variare da 0 al massimo... nell'analogWrite mi basta mettere valori da 0 a 255 come al solito?

Sì, nell'analogWrite metti valori da 0 a 255. Il mio "no no.." era riferito alla tua precedente domanda in cui chiedevi se dovevi mettere un valore max di 56 nell'analogWrite.

leo72:
Sì, nell'analogWrite metti valori da 0 a 255. Il mio "no no.." era riferito alla tua precedente domanda in cui chiedevi se dovevi mettere un valore max di 56 nell'analogWrite.

ok grazie mille :wink: domani provo se riesco a fare qualcosa :wink: