Go Down

Topic: PWM VELOCE (Read 5578 times) previous topic - next topic

Burt

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.



ratto93

Prova con il dealayMicroseconds dentro i for...
Se corri veloce come un fulmine, ti schianterai come un tuono.

leo72

#2
Jan 09, 2012, 08:25 pm Last Edit: Jan 09, 2012, 08:37 pm by leo72 Reason: 1
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)

uwefed

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

leo72

PS:
cmq ho fatto male i conti.. quel codice genera un PWM a 2 MHz....  :smiley-sweat:
Ora lo correggo...


Burt

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.





Burt

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.

ratto93

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
Se corri veloce come un fulmine, ti schianterai come un tuono.

leo72

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?  ;)

Burt

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.

leo72

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à.

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

Il primo aereo a volare fu costruito da due meccanici di biciclette!

Burt

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);
}
Il primo aereo a volare fu costruito da due meccanici di biciclette!

Burt

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!

Go Up