Arduino è in grado di gestire segnali PWM attraverso le sue uscite digitali, ma è anche in grado di regolare la frequenza pwm di tali pin:
In molti articoli ho utilizzato e descritto i segnali PWM e come utilizzarli parlando spesso di Duty Cycle ossia del ciclo di lavoro in cui hai uno stato attivo ( HIGH ) all’interno di un periodo ti tempo stabilito.
Ti invito a leggere l’introduzione sul’argomento in questo articolo.
Qualche giorno fa ho letto che la frequenza di lavoro dei pin PWM su Arduino non è la medesima per tutti i pin.
Prendi ad esempio l’Arduino Uno ( o qualsiasi altro Arduino basato sull’Atmega328 ) sono presenti 6 uscite, che puoi individuare grazie al simbolo ~, utilizzabili come PWM:
ma non tutte lavorano alla stessa frequenza.
Incuriosito ho voluto approfondire questa informazione e partendo dal sito ufficiale ho trovato questa decrizione.
In pratica arduino dispone di tre timer distinti definiti Timer0, Timer1 e Timer2 gestiti dall’Atmega328 a ciascun Timer sono collegati 2 comparatori ( Output Compare – OC ) definiti OCxA e OCxB, in cui x vale 0,1 e 2 ciascun numero corrisponde ad uno dei timer, ad ogni comparatore è collegato un pin arduino, ed ecco i 6 pin PWM:
Guardando la tabella noti innanzitutto che i 3 timer sono riportati con la disitura TCCRx in cui x vale 0,1 e 2 e che il Timer0 ha una Frequenza doppia rispetto agli altri due che si ripercuote alla fine della tabella sulla Frequanza PWM di Default in cui la Frequenza PWM dei pin 5 e 6 è doppia rispetto a quella dei pin 3,9,10 e 11.
Differenza di Frequenza PWM
La differenza di Frequenza PWM tra la coppia di pin 5 e 6 e gli altri ti porta a fare delle considerazioni:
- se vuoi collegare due motori o servo motori da controllare in PWM scegli i pin con frequenza uguale;
- la frequenza pwm di default è alquanto bassa;
se consideri che la frequenza PWM del Timer è ad esempio 31250Hz come è possibile che la Frequenza PWM di default sia di soli 488Hz?
La risposta è nel fatto che ciascun Timer ( TCCRx ) possiede due registri denominati A e B, rispettivamente per il Timer0 sono TCCR0A e TCCR0B il cui compito è definire le modalità di funzionamento e altri parametri dei Timer stessi. In particolare il Registo B ( TCCR0B, TCCR1B, TCCR2B ) ha anche il compito di impostare un prescaler che puoi immaginare come una sorta di dividendo della frequenza PWM.
I prescaler sono 5 per i pin connessi ai Timer 0 ed 1: 1,8,64,256,1024 e 7 per il Timer2: 1,8,32,64,128,256,1024 ed il valore di prescaler di default è 64 per tutti i Timer da cui:
31250Hz / 64 = 488Hz
62500Hz / 64 = 976Hz
Modificando quindi il prescaler di un Timer puoi variare la Frequenza PWM dei pin collegati ad esso.
Important!
Ricorda che la frequenza non è il Duty Cycle, variando la frequenza PWM otterrai un periodo inferiore ma non un duty cycle differente.
In questo modo la variazione del Timer 1 connesso ai pin 9 e 10 ti permette di continuare a impostare il duty cycle differente sui due pin con il comando analogWrite( pin, val );
Il Timer0 di Arduino
Il Timer0, a cui sono collegati i pin 5 e 6, è collegato anche alle funzioni di ritardo interne al micro controllore come ad esempio delay() e millis() ed è riferimento per la classe Servo ed altre librerie che sfruttano il Timer dell’Atmega328/168.
E’ quindi sconsigliato l’uso di questo Timer a frequenza differenti da quelle impostate dal costruttore per evitare comportamenti imprevisti delle funzioni citate.
Modificare la frequanza PWM
Hai visto che è possibile modificare la Frequenza PWM variando il prescaler dell’Output compare corrispondente ad un pin, per farlo è disponibile sul playground arduino questa semplice funzione:
void setPwmFrequency(int pin, int divisor) { byte mode; if(pin == 5 || pin == 6 || pin == 9 || pin == 10) { switch(divisor) { case 1: mode = 0x01; break; case 8: mode = 0x02; break; case 64: mode = 0x03; break; case 256: mode = 0x04; break; case 1024: mode = 0x05; break; default: return; } if(pin == 5 || pin == 6) { TCCR0B = TCCR0B & 0b11111000 | mode; } else { TCCR1B = TCCR1B & 0b11111000 | mode; } } else if(pin == 3 || pin == 11) { switch(divisor) { case 1: mode = 0x01; break; case 8: mode = 0x02; break; case 32: mode = 0x03; break; case 64: mode = 0x04; break; case 128: mode = 0x05; break; case 256: mode = 0x06; break; case 1024: mode = 0x7; break; default: return; } TCCR2B = TCCR2B & 0b11111000 | mode; } }
in cui i pin 5,6,9,10 accettano solo cinque valori di prescaler che puoi impostare, mentre i pin 3 e 11 accettano anche tagli di prescaler a 32 e 128.
La funzione presentata puoi utilizzarla con la seguente sintassi:
setPwmFrequency(3, 8);
in cui il primo parametro rappresenta il pin su cui vuoi impostare il prescaler ed il secondo parametro è il prescaler da impostare.
Nell’esempio il pin su cui andrai a modificare la frequenza PWM è il pin 3, collegato a OC2B e Timer TCCR2 e la frequanza PWM impostata sarà:
31250Hz / 8 = 3904Hz = 3,9KHz
Tutte queste informazioni le ho apprese su molti siti che ho sfogliato e letto prima di capire come funzionassero i Timer e i prescaler, ma un ringraziamento speciale voglio rivolgerlo ad un blogger che ha scritto un articolo davvero interessante e ben fatto: settorezero.
Buona sperimentazione !!!
38 commenti
2 ping
Vai al modulo dei commenti
Salve Maur, avrei da chiederti una info, sto realizzando un progettino con un at mega 2560, il quale dovrebbe avere 15 pwm di cui ne sto utilizzando 6 precisamente il pin 3, 5, 6, 9, 10, 11. Ma il contatto 9 e 10 funzionano da contatti digitali e non pwm quindi arrivato il valore a 255 in uscita a questi due pin c’è la 5v al di sotto di 255 ci sono zero volt. Sapresti darmi qualche dritta? Grazie e saluti Cesare Mariani
Autore
Ciao Cesare,
la Mega 2560 ha i pin PWM compatibili con l’arduino uno per cui i pin 9 e 10 sono PWM se non funzionano in questo modo potrebbe dipendere dallo sketch che hai scritto.
In modo specifico sul sito arduino riportano le specifiche della Mega 2560 ed in particolare per i PWM c’è scritto:
PWM: 2 to 13 and 44 to 46. Provide 8-bit PWM output with the analogWrite() function.
Mauro
Ciao Mauro,
trovo i tuoi articoli sempre molto interessanti e di preziosissimo aiuto.
Nello specifico volevo porgerti una domanda, ho provato a generare segnali pwm con diverse frequenze di lavoro. La cosa mi è riuscita senza alcun intoppo grazie a quanto hai scritto, ma se io avessi, come di fatto ho, la necessità di utilizzare una frequenza ben delineata che non fa parte delle varie possibilità ottenibili modificando i vari prescaler? Si può ottenere quanto chiesto attraverso la modifica dello sketch oppure l’unica soluzione è di integrare nel circuito componenti hardware in grado di fare ciò?
Ti ringrazio anticipatamente,
saluti
Emiliano.
Autore
Ciao Emiliano,
penso che tu debba scegliere la seconda starda per realizzare ciò che ti occorre.
Mauro
Ciao Mauro,
complimenti per l’ottima spiegazione.
Ho avuto necessita di modificare la frequenza di pwm dei pin 3 e 9 con la funzione:
setPwmFrequency(3, 1) e setPwmFrequency(9, 1)
Ora è sorto un problema enorme:
Col pin 5 ho necesità di pilotare un servomotore. Ma non appena inserisco anche solo “#include ” il monitor seriale impazzisce, la ethernet shield anche.
Ho letto su internet:
“Ho dato un’occhiata alla Servo ma sull’Atmega328 usa un interrupt agganciato al timer 1”
Quindi non è associato al timer 0 come hai scritto?
Come posso fare per modificare il timer associato alla classe Servo.h?
In alternativa per muovere un servo si può usare la libreria Stepper.h? Perche se includo tale libreria non mi da problemi.
Non so a chi rivolgermi, ho una consegna a breve e non so più che fare.
Grazie mille, spero tu possa aiutarmi
Alessandro
Evidem
Scusa Mauro ho copiato male il messaggio.
Volevo scrivere : Ma non appena inserisco anche solo “#include Servo.h ” il monitor seriale impazzisce, la ethernet shield anche.
Mauro ho scoperto che la libreria Servo.h e la libreria OneWire.h vanno in conflitto fra loro!
Ecco perche il mio arduino impazziva anche cambiando i timer o la frequenza pwm!
Non credevo esistessero anche problemi di questo tipo
Un modo per risolvere c’è ed è usare la libreria pwmServo
fonte:
http://www.pjrc.com/teensy/td_libs_Servo.html
Autore
Grazie 1000 Alessandro ne terrò conto, anche io ignoravo il conflitto tra queste due librerie, so che alcune librerie possono essere incompatibili tra loro tuttavia non mi era mai capitato un caso pratico come il tuo.
La condivisione sarà di aiuto a tanti appassionati.
Salve Mauro,
volevo condividere con voi una mia scoperta (magari per voi più esperti è un’ovvietà ma a me ha fatto perdere un paio d’orette). Cambiando la frequenza di PWM del pin 9 del mio arduino uno, il servomotore collegato al pin 6 (sul timer 0) è letteralmente impazzito fino a bruciarsi non potendo ruotare fino al suo fine corsa.
Ne deduco quindi che la classe servo si avvale del timer 1 e non del timer 0, altrimenti in che modo si spiega questo evento?
Mauro vorrei chiederti un favore, dato che siamo in tema PWM, vorresti per caso fare un’approfondimento sui filtri RC passa basso? In rete si trova tutto spiegato in modo molto complicato, niente a che vedere con la tua chiarezza.
Alessandro
Autore
Ciao Alessandro, mi incuriosisce la tua affermazione sui timer perché non immaginavo la classe servo usasse il TIMER di un pin a cui non é collegato un servo, devo verificare.
Per i filtri RC non saprei semplificare quello che si trova su internet, é un argomento complesso e chi lo conosce meglio di me sono certo lo spieghi più chiaramente possibile.
Grazie Mauro! Aspetto le tue verifiche! Per quanto riguarda i filtri, capisco cosa intendi.
Grazie ancora!
Alessandro
Salve Mauro,
Volevo chiederti una cosa: hai scritto che cambiando frequenza di PWM non cambio il duty culle ma solo il periodo. Dato che la frequenza è data da 1/periodo ed il duty cycle è dato dal numero dei picchi alti / periodo. Mi sembra di capire che cambiando frequenza di PWM ottengo anche un altro duty cycle. Puoi chiarirmi questo dubbio? Grazie!
Autore
Ciao Ale,
il duty cycle è dato dalla differenza tra il segnale basso e alto all’interno del periodo, per capirlo meglio ti consiglio di leggere il mio articolo dedicato ai segnali analogici e digitali presente nel corso on-line gratuito.
La frequenza determina il periodo.
Per chiarirti il senso dell’articolo se hai un duty cycle 50% vuol dire che hai un segnale per il 50% a livello alto e 50% a livello basso indipendentemente da quanto dura in frequenza il periodo.
ciao mauro volevo chiederti se è possibile pilotare in pwm con arduino un convertitore dc dc per fargli cambiare corrente e tensione gradualmente?
Autore
Ciao Antonio,
dipende dal convertitore che stai utilizzando.
Solitamente ho usato driver che accettano in ingresso un segnale PWM e variano la tensione mantenendo costante la corrente, un esempio è il driver per led realizzato da Luca e che trovi tra i miei tutorial.
Ciao Mauro,
quando cerco info su arduino mi imbatto sempre sui tuoi articoli, complimenti per
la chiarezza.
Leggendo come configurare la “frequenza” delle porte
pwm mi è venuto un dubbio e forse puoi aiutarmi: sto provando, con scarsi successi,
a pilotare dei motorini per vibrazioni cellulari LRA, a differenza di normali motorini,
utilizzano la frequenza per variare l’intensità di vibrazione, piuttosto che le variazioni di
duty cycle(sempre che io abbia capito bene datasheet e documentazione).
Mi chidevo, sarebbe sano e possibile, fare una funzione che, fornendo sempre
lo stesso duty, cambi di continuo la frequenza delle porte onde ottenere X tipi di vibrazione
dove X è il numero di scaler per la frequenza ?
Si possono quindi cambiare le frequenze a Caldo sui pin quando arduino ci
sta gia scrivendo un duty cycle senza deattacchare riconfigurare attacchare i pin ogni volta?
Spero di essere stato chiaro!
Autore
Ciao Tommaso,
la tua richiesta è interessante e posta in modo chiaro. Francamente non ho mai provato a far variare di continuo la frequenza dei pin arduino in corso d’opera e potresti sperimentare comportamenti anomali.
In ogni caso mi sorge il dubbio che avresti comunque un numero limitato di cambi in quanto i prescaler sono fissi e definiti ed oltre quelli non puoi andare.
Sei certo che variando semplicemente il duty-cycle sia impossibile variare la vibrazione? Di solito i sistemi a vibrazione dei cellulari sono realizzati con semplici motorini a cui viene applicato una puleggia sbilanciata in modo da creare vibrazione.
Ciao Mauro ,
ti aggiorno perchè docu su LRA se ne trova poca, o meglio , tanta teoria, nulla di specifico
con arduino, però dagli esempi e dai datasheet ho capito come funziona un LRA(credo).
prima mentre scrivevo il commento mi sono reso conto proprio del limite di range,
così mi sono scritto una funzione che emula il pwm:
(scrivo al volo a memoria, occhio agli errori)
//pin= pin di uscita segnale “pwm”
//freq= frequenza voluta
//dutyperc= 0-100 , pow percentuale del dutycyclye
int fakepwmwrite(int pin, int freq, int dutyperc) {
int cycletime= 1000000/ freq; //durata in micros di un ciclo
int wtime= cycletime * (dutyperc / 100); //tempo del ciclo HIGH
int nwtime= cycletime – wtime; //tempo del ciclo LOW
digitalWrite(pin, HIGH);
delayMicroseconds(wtime);
digitalWrite(pin, LOW);
delayMicroseconds(nwtime);
}
Con questa funzione sono riuscito a farlo partire! ma “l’algoritmo” è frutto di pura deduzione
e matematica dell’ultimo minuto, nulla di scientifico e certo, anzi lo darei per “spannometrico ed approsimativo”..
tanto è vero che con un tester ai capi dell LRA è evidente che a seconda della frequenza che si simula ,
a parità di duty, varia la tensione ottenuta, quindi frequenza ok, tensione no(però è vivo!). Qualche idea per
ottenere i buoni e vecchi 5 v all’uscita mantenendo la frequenza immutata?
(ps: gli LRA hanno assorbimenti superiori a 40ma quindi io il segnale pwm lo mando ad un driver per motori)
Grazie!
*ps: si riesce così a farlo partire e gestire la vibrazione variando la frequenza, come sideffect però si ha una variazione della tensione al variare della frequenza e relativa diminuzione della “forza” di vibrazione
Autore
Ciao Tommaso,
non conoscendo ed avendo provato degli LRA non riesco ad aiutarti più che consigliandoti questo articolo in cui si parla chiaramente di questo tipo di componenti: http://www.precisionmicrodrives.com/application-notes-technical-guides/application-bulletins/ab-003-how-to-drive-linear-resonance-actuators-lra-vibrating-motors
Ed è anche consigliato un drive adatto in quanto dubito che un driver per motori possa darti buoni risultati non essendo stato pensato a tale scopo.
Ciao Marco ,
ho presente l’articolo, l’integrato della T.I. che pilota gli lra, ma non trovo in commercio board o driver
di dimensioni minime come servono a me.
Proprio per questo con un banale driver per motori 1A max e due canali
pwm che scrivono come mostrato sopra sono riuscito ad ottenere le vibrazioni,
anche se non potenti e precise come con tale integrato.
Cmq funziona tutto ora(x quel che serve a me), senza integrato della T.I. 🙂
Continua così!!
Autore
Bene Tommaso,
sono contento che tu abbia trovato un buon metodo per ottenere quello che ti serve.
La publicazione è stata utile ovviamente. Rimane da essere “digerita” meglio con i datasheet del 328.
Ciao Mauro, volevo sapere se c’era la possibilità di impostare la frequenza del segnale PWM a 2Khz, mi servirebbe per poter pilotare un ESC brushed.
Grazie in anticipo per l’interesse.
Autore
Ciao Stefano, come hai letto nell’articolo i fattori di divisione sono al massimo 7 per i pin. 3 e 5 e facendo i calcoli avresti bisogno di un fattore di divisione 16 per giungere ad un valore prossimo ai 2KHz, ma tale valore non è disponibile.
Diciamo pure che l’articolo è praticamente copiato.
E poi è SETTOREZERO e non sottozero.
Autore
Ciao Giovanni,
il mio articolo vuole solo essere una citazione ed occasione per riportare il codice con cui modificare le frequenze pwm.
Ho aggiunto il ringraziamento ed il link al tuo articolo perché tutti potessero leggere l’articolo ispiratore e, a mio avviso, scritto in modo eccellente.
Se reputi che l’articolo sia scorretto nei tuoi confronti lo elimino o sostituisco con un articolo che parli del pwm, duty cycle e frequenza arduino in modo differente.
P.S.: ho corretto in settorezero, scusa errore di digitazione, il link era corretto.
Salve a tutti, sono nuovo del sito, o meglio ho corrisposto via mail e devo dire che sono contento di aver ricevuto delle risposte. Ringrazio Mauro che mi ha subito risposto.
Comunque vorrei spostare i miei dubbi qui nel blog. Come dicevo, voglio impare a programmare un arduino, quindi volevo chiedere se è possibile “fargli fare” quell oche io chiedo. Mi servirebbe analizzare N.6 segnali in ppm (radiocomando da aereo…) e generare N.2 PWM a fr <7/8 KHz e N.4 seganli da servo comando (ppm). questo perchè vorrei pilotare un cingolato che stò costruendo, ma soprattutto vorrei passare per un arduino per poter miscelare i comandi inizialmente, metter un giroscopio e un GPS in seguito.
Quindi riassumendo, è possibile avere queste uscite dall'arduino?
grazie
Autore
Ciao Giuliano,
penso che tu possa scontrarti con alcuni limiti dell’arduino uno come il numero di pin disponibili e la capacità di memoria a disposizione per elaborare lo sketch.
Io proverei a verificare se una MEGA possa essere più indicata, come scheda, o passare all’arduino due conoscendo i limiti di quest’ultimo, riportati sul sito ufficiale arduino.
Ciao Mauro, avrei un banalissimo dubbio da neofita che ti espongo:
Se decido di cambiare la frequenza di lavoro di un pin pwm lancio il programmino che hai postato inserendo io gli appositi paramentri e fin qui ok, ma dopo aver lanciato tale programma posso cancellarlo ed inserirne un’ altro senza che la ‘nuova’ modifica venga cancellata? grazie
Autore
Ciao Giuseppe,
la modifica dei registri di interrupt, quelli su cui si basano anche i PWM penso sia permanente ma non ho mai eseguito un vero e proprio test, dovresti fare qualche prova.
Salve, ho notato una cosa alquanto strana collegando un oscilloscopio (da due soldi) al pin pwm di arduino: aumentando il duty cycle la tensione dello stato ON non è 5 v ma decresce, arrivando a 1 v circa verso il massimo, questo sia con i pin a vuoto che non. Di cosa è la colpa? Grazie mille
Autore
Ciao Nico,
il problema potrebbe essere dovuto alla cattiva qualità dell’oscilloscopio o all’alimentazione che stai fornendo ad arduino.
deriva dal fatto che l’oscilloscopio non ha elevato impedenza per quella frequenza che stai generando e sta facendo cadere la tensione che visualizzi.
Autore
Grazie Sante .. ignoravo questo aspetto.
Salve,
vorrei usare un segnale pwm per la generazione di un’onda quadra.
Scriverei: analogWrite(pwm pin, 128) in modo che il duty cycle sia pari al 50%.
Vorrei leggere istante per istante i valori del segnale in uscita, cioè sapere se il segnale è alto o basso in modo da poter individuare i fronti di salita o discesa.
Come posso fare? La funzione digitalRead funziona correttamente solo se il duty cycle è 0 o 100, altrimenti restituisce sempre 0.
Grazie.
Autore
Ciao Alberto,
a quale frequenza di PWM stai lavorando ?
Nel blog trovi un articolo che dettaglia come funzionano i TIMEr di arduino uno e come questi possono essere impostati in funzione dei pin.
La lettura dovrai eseguirla con un oscilloscopio in grado di campionare alla frequenza generata.
Ciao Mauro, questo articolo è molto interessante.
Ho bisogno di generare con Arduino un PWM con frequenza 107Khz con 75% duty (del Ton).
Come posso fare?
Autore
Ciao Maurizio,
leggendo l’articolo e la tabella ti sarai reso conto che tale frequenza è superiore alla frequenza base massima sui pin Arduino.
In ogni caso con i prescaler puoi dividere tale frequenza non incrementarla.
[…] DSO leggendo il segnale PWM generato con Arduino. Qualche mese fa ho pubblicato questo articolo: Frequenza PWM su Arduino e Duty Cycle in cui hai letto come funzionano i Timer arduino e come siano connessi ai pin arduino. In […]
[…] PWM di arduino, operazione pericolosa che trovi descritta in un mio articolo di qualche tempo fa: Frequenza PWM su Arduino e Duty Cycle che nello sketch di Patrick rappresenta l’unica parte più complessa e le trovi alle linee […]