Arduino Forum

International => Italiano => Software => Topic started by: Burt on Jan 09, 2012, 05:51 pm

Title: PWM VELOCE
Post by: Burt on Jan 09, 2012, 05:51 pm
Buongiorno a tutti,
sono nuovo del forum e vorrei porre una questione che mi assilla. Ho la necessità di realizzare un PWM con frequenza superiore a 150 kHz al fine di comunicare informazione sulla rete di alimentazione. Le funzioni scrittura sui port digitali di Arduino non riescono a realizzare onde quadre di quella frequenza e di utilizzare il PWM presente sulla scheda non se ne parla in quando produce frequenze ben più basse.
Per velocizzare il tutto ho pensato di scrivere direttamente sul port utilizzando i registri del micro a questo dedicati. In questo modo ottengo una frequenza superiore a 2 MHz che però adesso è troppo veloce e va rallentata. Per rallentarla ho inserito un ciclo for annidato che esegue un'operazione qualunque. Dopo aver settato il pin 2 del portD come uscita, segmento saliente di codice è:

for(;;)
{
PORTD=PORTD|B00000100;
Time=0;
  FOR(i=1;i<=1000;i++)
   {
     FOR(j=1;j<=1000;j++)
    {
      Time=Time+1;
     }
   }
  PORTD=PORTD&B11111011;
Time=0;
   FOR(i=1;i<=1000;i++)
     {
      FOR(j=1;j<=1000;j++)
     {
       Time=Time+1;
      }
   }
}

Naturalmente il port D è stato settato come uscita.
Guardando la forma d'onda generato con l'oscilloscopio sembra che i cicli di attesa non influenzino per nulla la frequenza; come se non fossero stati inseriti.
Non riesco a capire perché e non so come diminuire la frequenza dell'onda.
Ringrazio anticipatamente per le eventuali risposte e porgo cordiali saluti.


Title: Re: PWM VELOCE
Post by: ratto93 on Jan 09, 2012, 05:58 pm
Prova con il dealayMicroseconds dentro i for...
Title: Re: PWM VELOCE
Post by: leo72 on Jan 09, 2012, 08:25 pm
Non ho capito una cosa, ma il segnale lo devi modulare visto che parli di trasportare informazioni, oppure ti basta un segnale PWM ad una determinata frequenza?

Ad esempio, il codice qui sotto genera un segnale PWM sul pin 3 a 200 kHz esatti con duty cicle 50% (se non ho sbagliato i calcoli):

Code: [Select]
pinMode(3, OUTPUT);
TCCR2A |= ((1<<COM2B1) | (1<<WGM21) | (1<<WGM20));
TCCR2B |= ((1<WGM22) | (1<<CS21));
TCCR2B &= (~(1<<CS22) | ~(1<<CS20));
OCR2A = 9;
OCR2B = 4;


Per gestirlo, ti basta mettere in output in pin quando devi iniziare la trasmissione, e metterlo in input per terminarla.

EDIT: questo dovrebbe andare - 200 kHz/50% (se può servire a qualcuno)
Title: Re: PWM VELOCE
Post by: uwefed on Jan 09, 2012, 08:28 pm
Non si modulano in PWM per mandare informazione sulla rete 230V. Sono sistemi diversi.
Un segnale PWM ha una frequenza fissa di base, ma come durata del impulso in certi casi corrisponde a delle frequenze molto piú alte.
per esempio un segnale PWM al 50% corrisponde alla frequenza di ripetizione. Un PWM di 1/256 corrisponde a una frequenza 128 volta piú alta di quella base. Hai della atenuazioni variabili a seconda della percentuale PWM.

Inoltre hai il problema che devi isolare il sitema dalla tensione di rete.

Ci sono varie discussione riguardante powerline. per esempio:  
arduino.cc/forum/index.php/topic,85592.0.html

Ciao Uwe
Title: Re: PWM VELOCE
Post by: leo72 on Jan 09, 2012, 08:34 pm
PS:
cmq ho fatto male i conti.. quel codice genera un PWM a 2 MHz....  :smiley-sweat:
Ora lo correggo...

Title: Re: PWM VELOCE
Post by: Burt on Jan 10, 2012, 05:57 pm
Anzitutto grazie per le risposte tutte interessanti.

leo 72: uhm... Mi par bene che tu abbia agito direttamente sui timer ed è una buona idea sicuramente risolutiva. Mi chiedevo però, tornando alla procedura che ho scritto, come mai un loop annidato dopo una scrittura diretta su registro non producesse alcun ritardo... Come se non esistesse, come se non venisse tradotto in linguaggio macchina.

uwefed: mah, quello che sto cercando di fare è un giochetto: il segnale PWM modulato in digitale (0 logico 20% 1 logico 80%) corre assieme all'alternata di alimentazione a 12 volt 50Hz dopo il secondario di un trasformatore che alimenta dei faretti in bassa tensione. Su ogni punto luce viene utilizzata la 50Hz per accendere la lampada e viene filtrata l'onda quadra modulata in PWM con un passaalto (l'onda quadra è appunto a 100kHz) Di quest'onda quadra viene ricavato il valor medio e dunque l'informazione che serve ad accendere, con un codice, una determinata lampada. Così in effetti le lampade si trovano su un bus che porta sia l'alimentazione sia il segnale.

Buona giornata a tutti e a presto.




Title: Re: PWM VELOCE
Post by: Burt on Jan 10, 2012, 06:04 pm
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.
Title: Re: PWM VELOCE
Post by: ratto93 on Jan 10, 2012, 06:15 pm
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
Title: Re: PWM VELOCE
Post by: leo72 on Jan 10, 2012, 06:27 pm
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?  ;)
Title: Re: PWM VELOCE
Post by: Burt on Jan 13, 2012, 05:27 am
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.
Title: Re: PWM VELOCE
Post by: leo72 on Jan 13, 2012, 09:26 am
Ti consiglio questo documento:
http://www.arcfn.com/2009/07/secrets-of-arduino-pwm.html
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à.
Title: Re: PWM VELOCE
Post by: alessiof76 on Jan 13, 2012, 11:01 am
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 (http://arduino.cc/playground/Code/FrequencyTimer2) ; nonchè Timer1 e Timer3 ( http://arduino.cc/playground/Code/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

Title: Re: PWM VELOCE
Post by: Burt on Jan 15, 2012, 04:01 pm
Grazie a tutti,

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

A presto sentirci.

Burt
Title: Re: PWM VELOCE
Post by: alessiof76 on Jan 15, 2012, 06:59 pm
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);
}
Title: Re: PWM VELOCE
Post by: Burt on Jan 24, 2012, 05:21 pm
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 (;;)
{
  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!
Title: Re: PWM VELOCE
Post by: PaoloP on Jan 24, 2012, 09:53 pm
La definizione della funzione delay la trovi nel file wiring.c nella cartella hardware/arduino/cores/arduino/..
Usa il timer 0.
Title: Re: PWM VELOCE
Post by: Burt on Jan 25, 2012, 11:05 am
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.
Title: Re: PWM VELOCE
Post by: leo72 on Jan 25, 2012, 01:46 pm
Usa il timer 2, è a 8 bit come lo 0.
Title: Re: PWM VELOCE
Post by: Burt on Jan 27, 2012, 10:53 am
... Infatti ho provato proprio con il timer 2 e tutto funziona correttamente.

Grazie mille e a presto!
Title: Re: PWM VELOCE
Post by: er_like_no_other on Oct 12, 2012, 04:07 pm
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: http://sobisource.com/?p=195 per scrivere questo codice che mi da circa 31khz sul pin 9
Code: [Select]
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%E2%80%99istruzione-analogwrite#_ che cambiano la frequenza diminuendo la sensibilità del pwm, ma non sto ottenendo risultati...
Title: Re: PWM VELOCE
Post by: leo72 on Oct 12, 2012, 04:47 pm
Imposta il timer 2 in modalità Phase Correct PWM ad 8 bit.
La frequenza in questa modalità la calcoli così:
Fclk/(2*prescaler*max_val_cont)
Ora se metti un prescaler di 8 ed usi un valore massimo del contatore di 56 ottieni
160000000/(2*8*56)=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.
Title: Re: PWM VELOCE
Post by: er_like_no_other on Oct 12, 2012, 05:40 pm

Imposta il timer 2 in modalità Phase Correct PWM ad 8 bit.
La frequenza in questa modalità la calcoli così:
Fclk/(2*prescaler*max_val_cont)
Ora se metti un prescaler di 8 ed usi un valore massimo del contatore di 56 ottieni
160000000/(2*8*56)=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 ;) 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 ;)
Title: Re: PWM VELOCE
Post by: leo72 on Oct 12, 2012, 06:09 pm
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.
Title: Re: PWM VELOCE
Post by: er_like_no_other on Oct 12, 2012, 06:26 pm

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?
Title: Re: PWM VELOCE
Post by: leo72 on Oct 12, 2012, 07:05 pm
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.
Title: Re: PWM VELOCE
Post by: er_like_no_other on Oct 12, 2012, 08:44 pm

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 ;) domani provo se riesco a fare qualcosa ;)
Title: Re: PWM VELOCE
Post by: er_like_no_other on Oct 13, 2012, 11:49 am
purtroppo ho appena scoperto che il mio sistema non funziona sopra gli 8khz, adesso devo capire perchè e cercare di risolvere questo problema prima di poter pensare di alzare ulteriormente la frequenza... :(
Title: Re: PWM VELOCE
Post by: leo72 on Oct 13, 2012, 03:35 pm

purtroppo ho appena scoperto che il mio sistema non funziona sopra gli 8khz, adesso devo capire perchè e cercare di risolvere questo problema prima di poter pensare di alzare ulteriormente la frequenza... :(

Beh, la formula l'hai capita.  ;)
Per ottenere altre frequenze giochi col prescaler e con il valore iniziale del contatore.
Title: Re: PWM VELOCE
Post by: er_like_no_other on Oct 13, 2012, 03:37 pm


purtroppo ho appena scoperto che il mio sistema non funziona sopra gli 8khz, adesso devo capire perchè e cercare di risolvere questo problema prima di poter pensare di alzare ulteriormente la frequenza... :(

Beh, la formula l'hai capita.  ;)
Per ottenere altre frequenze giochi col prescaler e con il valore iniziale del contatore.

si, l'ho capita, anche se non ho proprio le idee chiare sui registri, magari provando riesco a fare qualcosa, grazie mille! ;)
Title: Re: PWM VELOCE
Post by: leo72 on Oct 13, 2012, 04:11 pm
Se mi dici la frequenza che ti serve ed il timer da usare, te la posso passare io la configurazione per i registri.  ;)
Title: Re: PWM VELOCE
Post by: er_like_no_other on Oct 13, 2012, 04:22 pm
il timer sarebbe il 3 dell'atmega2560, volevo provare ad una frequenza di 3khz, perchè a 8 sembra troppo alta...
Title: Re: PWM VELOCE
Post by: leo72 on Oct 13, 2012, 04:57 pm
Secondo i miei calcoli, basta mettere un prescaler di /8 e si ottiene una frequenza di 3049 Hz senza dover modificare il contatore, quindi senza ISR aggiuntive.

Fai così:
Code: [Select]
cli(); //fermi gli interrupt
//prescaler a /8
TCCR3B |= (1<<CS31);
TCCR3B &= ~((1<<CS30) | (1<<CS32));
sei();

Adesso basta fare un analogWrite(pin, 127) per avere un'onda a 3 Khz circa con duty cicle al 50%. Prova e fammi sapere.
Title: Re: PWM VELOCE
Post by: er_like_no_other on Oct 13, 2012, 08:52 pm

Secondo i miei calcoli, basta mettere un prescaler di /8 e si ottiene una frequenza di 3049 Hz senza dover modificare il contatore, quindi senza ISR aggiuntive.

Fai così:
Code: [Select]
cli(); //fermi gli interrupt
//prescaler a /8
TCCR3B |= (1<<CS31);
TCCR3B &= ~((1<<CS30) | (1<<CS32));
sei();

Adesso basta fare un analogWrite(pin, 127) per avere un'onda a 3 Khz circa con duty cicle al 50%. Prova e fammi sapere.

grazie mille! appena ho tempo provo ;)
Title: Re: PWM VELOCE
Post by: er_like_no_other on Oct 20, 2012, 11:48 pm
finalmente l'ho provato, mi viene una frequenza di 3,92khz
una curiosità, il pezzo di programma che hai fatto tu equivale a scrivere
Code: [Select]
TCCR3B = (TCCR3B & 0xF8) | 0x02 ; che è quello che fanno anche qui: http://sobisource.com/?p=195 no? si cambia solo il prescaler?
Title: Re: PWM VELOCE
Post by: leo72 on Oct 21, 2012, 12:15 am

finalmente l'ho provato, mi viene una frequenza di 3,92khz
una curiosità, il pezzo di programma che hai fatto tu equivale a scrivere
Code: [Select]
TCCR3B = (TCCR3B & 0xF8) | 0x02 ; che è quello che fanno anche qui: http://sobisource.com/?p=195 no? si cambia solo il prescaler?

Sì, si cambia solo il prescaler. Solo che io l'ho fatto manipolando i singoli bit, ottenendo un codice più leggibile.

PS:
avevo scritto che avresti ottenuto una frequenza di circa 3 KHz perché ho invertito i numeri, avevo letto 3,092 invece di 3,902 Khz. Scusa  :smiley-sweat:
Ti va bene lo stesso oppure vuoi 3 KHz precisi?
Title: Re: PWM VELOCE
Post by: er_like_no_other on Oct 21, 2012, 12:35 am
per adesso va bene, devo ancora capire cosa non va sul driver motori quindi mi serve solo per provare :smiley-yell:
Title: Re: PWM VELOCE
Post by: er_like_no_other on Dec 01, 2012, 07:44 pm
adesso sto riprovando, il mio driver non riesce neanche a stare dietro ai 980hz di default del pwm abbinato al TIMER0... se abbasso la frequenza di quel pwm rischio di avere problemi con i delay, millis, pulseIn e tutte le altre funzioni che si basano su quel timer... c'è una soluzione "indolore" per diminuire la frequenza di quel PWM senza andare a dare problemi alle altre funzioni?
grazie
Title: Re: PWM VELOCE
Post by: leo72 on Dec 01, 2012, 11:23 pm

adesso sto riprovando, il mio driver non riesce neanche a stare dietro ai 980hz di default del pwm abbinato al TIMER0... se abbasso la frequenza di quel pwm rischio di avere problemi con i delay, millis, pulseIn e tutte le altre funzioni che si basano su quel timer... c'è una soluzione "indolore" per diminuire la frequenza di quel PWM senza andare a dare problemi alle altre funzioni?
grazie

Premesso che il timer 0 non va toccato più di tanto perché è configurato in modo da lavorare con quella frequenza (976 Hz) pena l'alterazione delle funzioni temporali (come hai giustamente evidenziato), ci potrebbe essere una soluzione "rustica".
Si potrebbe modificare la ISR dell'overflow del timer 0 ed inserire alla fine la generazione di un segnale PWM su un pin predeterminato con frequenza dimezzata (quindi 488 Hz).
Per semplificare molto la cosa affinché la routine non pesi sul resto della ISR così che i tempi rimangano il più possibile esenti da alterazioni si dovrebbe rendere il codice molto snello, quindi niente impostazioni avanzate selezionabili da codice ecc.. giusto le istruzioni per fare ciò che deve su un pin preimpostato.
Title: Re: PWM VELOCE
Post by: er_like_no_other on Dec 02, 2012, 02:38 am
ogni volta che parli di modificare l'ISR non capisco mai dove è il codice nel quale devo andare a mettere le mani... :smiley-sweat:

ero partito con l'idea del pwm veloce e adesso mi trovo a dover fare un pwm lento, se avessi a disposizione un generatore di funzione vorrei proprio fare uno sweep per vedere a che frequenza taglia questo driver motori, sul datasheet non l'ho visto... domani provo a farlo con l'arduino...

ho un'altra domanda: se io vario la frequenza di un pwm di conseguenza vario anche quella di tutti i pwm legati a quel timer? o dipende come lo vario?
Title: Re: PWM VELOCE
Post by: leo72 on Dec 02, 2012, 11:38 am

ogni volta che parli di modificare l'ISR non capisco mai dove è il codice nel quale devo andare a mettere le mani... :smiley-sweat:

Beh, avendo passato un pò di ore a guardare tutti i file del core di Arduino, poi le cose so dove cercarle  ;)

Quote

ho un'altra domanda: se io vario la frequenza di un pwm di conseguenza vario anche quella di tutti i pwm legati a quel timer? o dipende come lo vario?

Partiamo a monte. C'è un timer. Un timer è un circuito che contiene un contatore aggiornato da un circuito collegato ad un prescaler, ossia un divisore di clock posto sull'ingresso. Grazie a questo divisore, posso variare il segnale d'ingresso per cui posso diminuire a mio piacimento la frequenza con la quale viene aggiornato il contatore. Il contatore poi è un registro in RAM per cui posso anche variarne il contenuto. In questo modo posso ottenere overflow molto rapidi oppure molto lenti, a seconda di come imposto il tutto.

Ora vediamo come possiamo usare il timer. Lo possiamo usare essenzialmente in 3 modi: per generare un segnale di interrupt, per cambiare lo stato di un pin oppure per sollevare entrambi gli eventi.
Se lo uso solo per sollevare un interrupt, il timer diventa un semplice contatore. Se invece lo collego materialmente ad un pin esso diventa un generatore di segnale PWM, la cui frequenza e duty cicle varia in base a come lo impostiamo (se in modalità FastPWM, Phase Correct PWM, con controllo sul valore massimo e/o sul valore minimo ecc.... ci sono una dozzina di modi differenti per sistemarlo).

E' ovvio che il timer 0 è "delicato", nel senso che essendo già stato impostato dal core di Arduino per determinate funzioni, se vai a toccare il prescaler, il valore del contatore, il duty cicle o la modalità di generazione del segnale PWM, alteri tutto quello che c'è a valle.

Fin qui è chiaro, no?

Adesso come possiamo variarlo? Se noi inseriamo una piccola routine alla fine della funzione che intercetta l'overflow del timer 0, che sull'Arduino è usata per la gestione del contatore di millisecondi, possiamo fargli eseguire delle operazioni. Ad esempio, possiamo variare lo stato di un pin a nostro piacimento, alternandolo LOW/HIGH con una frequenza dimezzata rispetto a quella del timer. Per far ciò basta cambiare lo stato di questo pin ogni 2 chiamate della ISR così che se la ISR lavora a 976 Hz il nostro generatore di PWM lavori ad una frequenza di (976/2)=488 MHz.

Fai questo test. Apri il file /arduino-1.0.2/hardware/arduino/cores/arduino/wiring.c
Intorno alla posizione 44 la funzione che intercetta l'overflow del contatore del timer (che usa una parola chiave, SIGNAL, che sui compilatori avr-gcc sta andando in "deprecato", sostituita appunto dalla parola chiave ISR)

Il codice è questo:
Code: [Select]

#if defined(__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__)
SIGNAL(TIM0_OVF_vect)
#else
SIGNAL(TIMER0_OVF_vect)
#endif
{
// copy these to local variables so they can be stored in registers
// (volatile variables must be read from memory on every access)
unsigned long m = timer0_millis;
unsigned char f = timer0_fract;

m += MILLIS_INC;
f += FRACT_INC;
if (f >= FRACT_MAX) {
f -= FRACT_MAX;
m += 1;
}

timer0_fract = f;
timer0_millis = m;
timer0_overflow_count++;
}


Tu modificalo così:
Code: [Select]

#if defined(__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__)
SIGNAL(TIM0_OVF_vect)
#else
SIGNAL(TIMER0_OVF_vect)
#endif
{
volatile static uint8_t _tempBitStatus = 0; //MOD

// copy these to local variables so they can be stored in registers
// (volatile variables must be read from memory on every access)
unsigned long m = timer0_millis;
unsigned char f = timer0_fract;

m += MILLIS_INC;
f += FRACT_INC;
if (f >= FRACT_MAX) {
f -= FRACT_MAX;
m += 1;
}

timer0_fract = f;
timer0_millis = m;
timer0_overflow_count++;

if (_tempBitStatus & 1) { //MOD
PORTD |= (1<<7); //MOD
} else { //MOD
PORTD &= ~(1<<7); //MOD
} //MOD
_tempBitStatus++; //MOD
}


Adesso carica questo sketch sul tuo Arduino:
Code: [Select]

void setup() {
}

void loop() {
    //accende e spenge il PWM sul pin 7 ogni 5 sec
    pinMode(7, OUTPUT);
    delay(5000);
    pinMode(7, INPUT);
    delay(5000);
}

Controlla il segnale sul pin 7: avrai un PWM con frequenza dimezzata rispetto a quella solita generata dal timer 0, che viene accesa e spenta ogni 5 secondi.
Title: Re: PWM VELOCE
Post by: er_like_no_other on Dec 02, 2012, 01:22 pm
ok grazie mille per la spiegazione! ;) ora ho capito :)

nella prova pratica però ho dei dubbi, premetto che ho sistemato i codice perchè io ho solo un arduino mega e il pin 7 della porta D è il pin 38, comunque la frequenza è giusta, circa 476Hz, ho provato col mio oscilloscopio... ma c'è una cosa che non capisco: quando metto com input resta comunqe acceso un po' il led... :smiley-roll-blue:
ma così non posso variare il pwm, no?
Title: Re: PWM VELOCE
Post by: leo72 on Dec 02, 2012, 04:21 pm

nella prova pratica però ho dei dubbi, premetto che ho sistemato i codice perchè io ho solo un arduino mega

Scusa, ma hai finito i pin PWM Per caso?
Perché altrimenti non capisco per quale motivo vuoi alterare il timer 0.
Lo sai, vero, che a parte il timer 0 gli altri timer sono impostati per avere una frequenza di ~488 Hz e che solo il timer 0 è impostato per avere ~976 Hz? Se il tuo problema era avere un PWM a bassa frequenza, allora è un "non problema".  ;)

Quote

e il pin 7 della porta D è il pin 38, comunque la frequenza è giusta, circa 476Hz, ho provato col mio oscilloscopio...

Non avevi specificato della Mega, per cui ho preso per buono il fatto di una Arduino Uno/2009. Comunque il tutto è facilmente adattabile, basta modificare il bit ed il registro della porta.

Quote

ma c'è una cosa che non capisco: quando metto com input resta comunqe acceso un po' il led... :smiley-roll-blue:

Mettendo il pin in input, nel momento in cui viene scritto il segnale HIGH si accende la pull-up interna, che viene spenta quando il segnale viene messo su LOW.
Come ti avevo spiegato, per ridurre al minimo l'impatto sui calcoli del tempo, ho messo una routine più semplice possibile.
Volendo si può rivedere il codice affinché stacchi proprio il segnale dal pin quando lo metti in input. Però si torna a quanto ti ho chiesto prima, e cioè: ma hai finito i pin PWM?

Quote

ma così non posso variare il pwm, no?

Col codice che ti ho dato no. E' minimale che di più non si può. Fa solo ON/OFF con frequenza dimezzata rispetto a quella impostata dal timer, tutto qui.
Per alterare il PWM devi alterare le impostazioni del timer, e si torna al discorso che ti facevo nel precedente post.
Title: Re: PWM VELOCE
Post by: er_like_no_other on Dec 02, 2012, 05:09 pm
ok ok no problem, grazie delle spiegazioni
purtroppo non pensavo di avere questi problemi con le frequenze dei pwm e avendo il circuito su pcb non mi resta che mettere mano al software :(
allora se non è possibile modificare la frequenza del pwm del timer 0 devo fare in modo che anche gli altri pwm abbiano lo stesso comportamento, cioè raddoppio le frequenze degli altri pwm che mi interessano, e questo dovrei riuscire a farlo da solo ;)
non mi è chiara ancora una cosa, se io nel timer 2 voglio che un pwm vada a 980hz e un'altro a 18KHz, si può o devo fare una cosa tipo quella che suggerivi di fare per il timer 0?
Grazie ;)
Title: Re: PWM VELOCE
Post by: leo72 on Dec 02, 2012, 05:18 pm

allora se non è possibile modificare la frequenza del pwm del timer 0 devo fare in modo che anche gli altri pwm abbiano lo stesso comportamento,

In teoria il timer 0 puoi modificarlo come ti pare. Ricordati solo che poi non hai più le funzioni temporali dell'Arduino.
Se la cosa non è fondamentale, puoi farlo.

Quote

cioè raddoppio le frequenze degli altri pwm che mi interessano, e questo dovrei riuscire a farlo da solo ;)

Ma il problema è avere un PWM con frequenza bassa o alta?  :smiley-sweat:
Prima dici che vuoi una frequenza alta, poi dici che i 976 Hz del timer 0 sono troppi e vuoi dimezzarli, ora invece vuoi raddoppiare la frequenza degli altri timer... deciditi  :smiley-yell:
Spiega per bene cosa vuoi fare, vediamo se riesco a darti una mano concreta  ;)

Quote

non mi è chiara ancora una cosa, se io nel timer 2 voglio che un pwm vada a 980hz e un'altro a 18KHz, si può o devo fare una cosa tipo quella che suggerivi di fare per il timer 0?
Grazie ;)

Non si può fare. O vai a 18 KHz o vai a 980 Hz.
Però puoi usare un "trucco" come quello che ti ho illustrato per inserire un prescaler software ed avere su un pin un segnale con una frequenza inferiore.
Title: Re: PWM VELOCE
Post by: er_like_no_other on Dec 02, 2012, 06:10 pm
ok, il problema è che ho un motore che ha un comportamento diverso dagli altri 3...
però non ho chiaro quale sia la souluzine ideale, vorrei che sul pin 2 ci fosse un pwm con una frequenza di quasi 20KHz, mentre sul 3,4,5 e 6 un pwm di frequenza arbitraria uguale per tutti questi 4 pin... ma da questo link http://sobisource.com/?p=195 vedo che i pin 2, 3 e 5 sono tutti collegati al timer 3 e quindi mi crea problemi :smiley-yell:
oggi volevo provare a fare lo sweep per vedere a che frequenza taglia ma non mi va più lo schermo del robot e sto ancora lavorando su quello... :smiley-yell: in un altro topic mi suggerivi di andare a fare un giro a Lourdes, forse sarebbe proprio il caso...
Title: Re: PWM VELOCE
Post by: leo72 on Dec 02, 2012, 06:30 pm
Facciamo il punto delle "certezze":
1) hai un PCB già fatto per cui non puoi modificare le piste
2) hai necessità di un PWM sul pin 2 diverso da quello che il timer 3 ti genera

Allora ho una buona notizia per te  ;)
Un pin genera un segnale PWM corrispondente alle impostazioni del timer solo se lo agganci al timer stesso. Se lo lasci sganciato, è un pin normale e lo puoi usare per generare un segnale PWM usando la tecnica bit-banging via SW che ti ho illustrato io.
Quindi, se tu piloti il pin 2 via SW senza agganciare il pin al timer usando l'analogWrite, puoi generare sul pin 2 un segnale PWM con una frequenza differente (solo minore, ovviamente) rispetto a quella generata sugli altri 2 pin agganciati al timer  ;)
Che ne dici?  :D
Title: Re: PWM VELOCE
Post by: er_like_no_other on Dec 02, 2012, 09:02 pm
un'altra certezza è che i pwm sui pin 3,4,5 e 6 devono avere la stessa frequenza e di duty cycle indipendenti fra loro...
poi ci sarebbe che siccome il pwm del pin 2 può essere di frequenza molto più alta mi piacerebbe sfruttare questa cosa, però per le frequenze precise prima devo fare dei test per vedere a che frequenze taglia
nei prossimi giorni faccio dei test, poi ci risentiamo, intanto grazie mille ;)
Title: Re: PWM VELOCE
Post by: leo72 on Dec 02, 2012, 11:24 pm
Scrivi e vediamo se si tira fuori qualcosa.
Title: Re: PWM VELOCE
Post by: er_like_no_other on Dec 03, 2012, 09:48 pm
allora sul pin 2 posso arrivare tranquillamente a 20KHz di frequenza con duty cycle variabile, mentre sui pin 3,4,5 e 6 avrei bisognpo di una frequenza molto bassa, sui 200Hz...
per i pin con il pwm "lento" si possono usare gli altri timer che non sono direttamente collegati ai pin indicati per fare questi pwm andando a modificare l'ISR come proponevi di fare con il timer 0?
per il pwm "veloce" invece mi basta andare a mettere le mani sui registri come mi avevi già spiegato
Title: Re: PWM VELOCE
Post by: leo72 on Dec 03, 2012, 10:16 pm
Nonostante siano in sequenza, i pin 2,3,4,5,6 sono agganciati a diversi timer:
timer 3: pin 2, 3, 5
timer 0: pin 4
timer 4: pin 6

Il timer 3 lo possiamo perciò impostare come vogliamo, e possiamo avere sul pin 2 i tuoi 20 kHz con duty cicle variabile.
Sui pin 3 e 5 potremmo usare la tecnica del bit-banging sul timer 3, dividendo la frequenza del timer di un fattore 100 per avere proprio 200 Hz. Avresti però lo stesso duty cicle, se non è un problema.

Il timer 0 non lo possiamo modificare altrimenti si alterano le funzioni temporali per cui il pin 4 lo dobbiamo pilotare col bit-banging, magari agganciandolo al timer 4, che pilota il pin 6.

Che ne dici?
Title: Re: PWM VELOCE
Post by: er_like_no_other on Dec 04, 2012, 10:12 am
sul pin 2 siamo d'accordo, sugli altri se non si riesce a variare il duty cycle bisogna pensare ad una alternativa...

Curiosità, la funzione tone si basa sul timer 0, fa con la stessa tecnica?
Title: Re: PWM VELOCE
Post by: leo72 on Dec 04, 2012, 11:12 am

sul pin 2 siamo d'accordo, sugli altri se non si riesce a variare il duty cycle bisogna pensare ad una alternativa...

Si può fare a livello di codice ma diventa complicato con la tecnica del bit-banging.
Si potrebbe usare un ulteriore timer non usato ed agganciare i pin 3, 4 e 5 ad esso (es. timer 2). Però tutti e 3 i pin avrebbero stessa frequenza e stesso duty cicle.

Quote

Curiosità, la funzione tone si basa sul timer 0, fa con la stessa tecnica?

No, viene fatto fare in HW, impostando il timer a seconda del tipo e della frequenza da generare. Il timer usato cambia a seconda del microcontrollore. Sull'Atmega1280/2560 viene usato il timer 2. Ma questa scelta può essere cambiata, basta modificare il file Tone.cpp del core.
Title: Re: PWM VELOCE
Post by: er_like_no_other on Dec 04, 2012, 02:39 pm
ok ma a me servirebbe avere almeno 3 livelli di duty cycle tra cui scegliere e che i 4 canali possano essere controllati individualmente :~
però se mettiamo il duty cycle sempre al 50%, a frequenza di circa 200Hz, si risce ad avere un controllo di quei 4 pin in modo indipendente tra loro? cioè se c'è il pwm al 50% poi anche se faccio digitalWrite il pin non resta sempre a livello alto o basso, no? è un casino...  :smiley-yell:
Title: Re: PWM VELOCE
Post by: leo72 on Dec 04, 2012, 03:12 pm

ok ma a me servirebbe avere almeno 3 livelli di duty cycle tra cui scegliere e che i 4 canali possano essere controllati individualmente :~

Non è impossibile ma si complica un po' il codice da scrivere. Ammettendo 3 livelli di duty cicle al 25/50/75% (poi c'è lo 0% che possiamo far corrispondere ad un segnale LOW fisso, ed il 100%, che possiamo far corrispondere ad un segnale HIGH fisso), si hanno un po' di switch..case o di if..else.
Tutto fattibile, ma con un po' di codice in più.

Quote

però se mettiamo il duty cycle sempre al 50%, a frequenza di circa 200Hz, si risce ad avere un controllo di quei 4 pin in modo indipendente tra loro?

Beh, cerchiamo di accendere/spengere il PWM sui pin indicati in modo indipendente.
Si può creare una funzione activatePwm(pin, dutyCicle) che attivi il PWM software sul pin indicato col duty cicle indicato.
Tutto si può fare, va vista la complessità del resto del codice per capire le risorse a disposizione e se c'è qualcosa che va in conflitto con qualcos'altro.
Allo stato attuale, per far ciò che ti ho detto, serve avere libero accesso ad almeno 3 timer: t2, T3 e t4.

Quote

cioè se c'è il pwm al 50% poi anche se faccio digitalWrite il pin non resta sempre a livello alto o basso, no? è un casino...  :smiley-yell:

Il finto PWM surclasserebbe il digitalWrite. Cioè se attivi un PWM al 25% su un pin, e poi su quello stesso pin ci fai un digitalWrite, il segnale imposto col digitalWrite dura fino al successivo richiamo dell'interrupt del timer che controlla quel pin. Poi il codice nella ISR reimposterebbe il pin secondo le sue impostazioni, quindi il digitalWrite se ne va. Va prima disattivato il segnale PWM software e poi si può riutilizzare il pin col digitalWrite.
Title: Re: PWM VELOCE
Post by: er_like_no_other on Dec 04, 2012, 05:24 pm
per semplificare ulteriormente a me andrebbe bene anche solo 0% 50% e 100%,  basta che siano indipendenti tra loro... :)
Title: Re: PWM VELOCE
Post by: leo72 on Dec 04, 2012, 08:50 pm

per semplificare ulteriormente a me andrebbe bene anche solo 0% 50% e 100%,  basta che siano indipendenti tra loro... :)

OK.
Allora, riassumiamo cosa vuoi e su quali pin:
pin 2: 20 KHz
pin 3, 4, 5, 6: 200 Hz

Duty cicle (ogni pin indipendente):
0-50-100%

Timer che utilizzerò: 3 (forse anche il 4)

OK? Se va bene, domani inizio a scrivere qualcosa.
Title: Re: PWM VELOCE
Post by: er_like_no_other on Dec 04, 2012, 09:27 pm
si, e sul pin 2 a 256 livelli impostabili
grazie infinite per l'aiuto e scusa per il disturbo, fai pure con calma che non ho fretta ;)
Title: Re: PWM VELOCE
Post by: leo72 on Dec 04, 2012, 09:40 pm

si, e sul pin 2 a 256 livelli impostabili

Sì, ovvio. Essendo un pin pilotato direttamente dal timer, puoi avere i 256 livelli ddi duty cicle possibili.

Quote

grazie infinite per l'aiuto e scusa per il disturbo, fai pure con calma che non ho fretta ;)

OK.
Title: Re: PWM VELOCE
Post by: er_like_no_other on Dec 04, 2012, 10:26 pm
grazie mille! ;)
ormai mi sento in debito con te per tutte le volte che mi hai aiutato :smiley-sweat:
Title: Re: PWM VELOCE
Post by: leo72 on Dec 04, 2012, 10:35 pm

grazie mille! ;)
ormai mi sento in debito con te per tutte le volte che mi hai aiutato :smiley-sweat:

Avrai modo di sdebitarti  ]:D
Vorrei costruire il mio primo robottino per cui, avendo bisogno di un esperto in robot, ti romperò i maroni io, tra un pò  ;)
Anzi, appena pronta vorrei sottoporti la "lista della spesa" per consigli, suggerimenti e critiche  :P :P
Title: Re: PWM VELOCE
Post by: er_like_no_other on Dec 04, 2012, 10:44 pm


grazie mille! ;)
ormai mi sento in debito con te per tutte le volte che mi hai aiutato :smiley-sweat:

Avrai modo di sdebitarti  ]:D
Vorrei costruire il mio primo robottino per cui, avendo bisogno di un esperto in robot, ti romperò i maroni io, tra un pò  ;)
Anzi, appena pronta vorrei sottoporti la "lista della spesa" per consigli, suggerimenti e critiche  :P :P

va bene ;)