PWM 38Khz

Ciao a tutti,
per una barriera IR, sto cercando di ottenere un segnale pwm a 38Khz.
Sto utilizzando un Arduino Mega.
Ho trovato questo codice per ottenere tale segnale sul pin digitale 10, lavorando a livello dei registri.

const byte LED = 10;  // Timer 2 "A" output: OC2A

void setup() {
  pinMode (LED, OUTPUT);
  
  // set up Timer 2
  TCCR2A = _BV (COM2A0) | _BV(WGM21);  // CTC, toggle OC2A on Compare Match
  TCCR2B = _BV (CS20);   // No prescaler
  OCR2A =  209;          // compare A register value (210 * clock speed)
                         //  = 13.125 nS , so frequency is 1 / (2 * 13.125) = 38095

}  // end of setup

void loop() { }

Non ho dimestichezza con la gestione dei registri, così al fine di ottenere il segnale sul pin 6 ho visto che bisogna agire sul timer4.
Ho quindi modificato il codice banalmente come segue, ma non funziona.
Potete aiutarmi?

const byte LED = 6;  

void setup() {
  pinMode (LED, OUTPUT);
  
  // set up Timer 4
  TCCR4A = _BV (COM4A0) | _BV(WGM41);  
  TCCR4B = _BV (CS40);   // No prescaler
  OCR24 =  209;          // compare A register value (210 * clock speed)
                         //  = 13.125 nS , so frequency is 1 / (2 * 13.125) = 38095
}  // end of setup

Ma che frequenza ottieni?
Se cerchi "barriera ad infrarossi" trovi un mio Topic con il suggerimento di Astro che con tre righe mi ha fatto ottenere i 38KHz (circa... precisi non li avrai mai), però mi pare si trattasse del timer 2.

Etan:
Ciao a tutti,
per una barriera IR, sto cercando di ottenere un segnale pwm a 38Khz.
Sto utilizzando un Arduino Mega.
Ho trovato questo codice per ottenere tale segnale sul pin digitale 10, lavorando a livello dei registri.

const byte LED = 10;  // Timer 2 "A" output: OC2A

void setup() {
 pinMode (LED, OUTPUT);
 
 // set up Timer 2
 TCCR2A = _BV (COM2A0) | _BV(WGM21);  // CTC, toggle OC2A on Compare Match
 TCCR2B = _BV (CS20);   // No prescaler
 OCR2A =  209;          // compare A register value (210 * clock speed)
                        //  = 13.125 nS , so frequency is 1 / (2 * 13.125) = 38095

}  // end of setup

void loop() { }




Non ho dimestichezza con la gestione dei registri, così al fine di ottenere il segnale sul pin 6 ho visto che bisogna agire sul timer4.
Ho quindi modificato il codice banalmente come segue, ma non funziona.
Potete aiutarmi?



const byte LED = 6;

void setup() {
 pinMode (LED, OUTPUT);
 
 // set up Timer 4
 TCCR4A = _BV (COM4A0) | _BV(WGM41);  
 TCCR4B = _BV (CS40);   // No prescaler
 OCR24 =  209;          // compare A register value (210 * clock speed)
                        //  = 13.125 nS , so frequency is 1 / (2 * 13.125) = 38095
}  // end of setup

non funziona ma cosa fa?
la cosa che mi viene in mente è che il timer 2 è a 8 bit mentre il 4 è a 16...

poi l'ocr24... forse intendevi ocr4a?

Probabile che intendesse OCR4A.

Cmq prova queste righe di codice messe nel setup.
Dovrebbero impostare il timer 4 dell'Atmega2560 in modalità Phase Correct PWM ad 8 bit con top fissato da OCR4A. se non ho fatto male i conti, hai i tuoi 38095 Hz sul pin D6.

TCCR4A = ((1<<COM4A0) | (1<<WGM40));
TCCR4B = (1<<CS40);
OCR4A = 105;

TCCR4A = ((1<<COM4A0) | (1<<WGM40));
TCCR4B = (1<<CS40);
OCR4A = 105;

Grazie mille!
Li proverò domattina appena rimetto le mani sul sistema.

Ti andrebbe di dettagliare meglio il codice in modo da capire i valori che metti nei registri e come hai fatto i conti?

Grazie ancora

Grazie Michele per il suggerimento.
Sono a corto di pin ed il 6 era perfetto.
In ogni caso mi metto a studiarmi anche il tuo post.

Etan:

[quote author=Michele Menniti link=topic=141298.msg1061383#msg1061383 date=1357580646]
Ma che frequenza ottieni?
Se cerchi "barriera ad infrarossi" trovi un mio Topic con il suggerimento di Astro che con tre righe mi ha fatto ottenere i 38KHz (circa... precisi non li avrai mai), però mi pare si trattasse del timer 2.

Grazie Michele per il suggerimento.
Sono a corto di pin ed il 6 era perfetto.
In ogni caso mi metto a studiarmi anche il tuo post.
[/quote]
ho trovato il mio sketch di prova: (c'è anche il metodo per il calcolo, il suggerimento è di Astrobeed, non mio :))

#define TX 11
void setup()
{
pinMode(TX, OUTPUT);

OCR2A = 209;
TCCR2A = 0b01000011;
TCCR2B = 0b00001001;
/*
Ad OCR2A si deve associare il risultato di
(clock/frequenza_da_ottenere/2)-1
Esempio (Arduino a 16MHz)
per 38000Hz: 16000000/38000/2=210-1=209
per 40000Hz: 199
*/
}

void loop()
{
}

Etan:

TCCR4A = ((1<<COM4A0) | (1<<WGM40));

TCCR4B = (1<<CS40);
OCR4A = 105;




Grazie mille!
Li proverò domattina appena rimetto le mani sul sistema.

Ti andrebbe di dettagliare meglio il codice in modo da capire i valori che metti nei registri e come hai fatto i conti?

Grazie ancora

La prima cosa da fare è scegliere la modalità di generazione dell'onda.
In questo caso ho scelto la Phase Correct PWM ad 8 bit del timer 4, che, ricordo, è a 16 bit ma può essere configurato appunto per lavorare anche ad 8/9/10 bit in alcune modalità. La Phase Correct PWM, rispetto alla Fast PWM che viene spesso scelta, permette di avere un segnale perfettamente quadro, con le fasi High e Low perfettamente simmetriche.
Per far ciò ho settato ad 1 il bit WGM40 nel registro TCCR4A.
Scelta la modalità, ho scelto il canale ed il comportamento. Volevi agire sul pin D6, che è il pin OC4A, per cui ho scelto di cambiare lo stato del pin (da alto a basso e viceversa) ogni volta che il valore del contatore del timer raggiungesse il valore del registro OCR4A. Per fare ciò ho settato ad 1 il bit COM4A0 sempre nel registro TCCR4A.
Adesso arriviamo al valore di OCR4A.

Per la modalità Phase Correct PWM, la formula da usare per calcolare la frequenza è
Fpwm = Fsys/(2PRESCALERVALORE_MAX*2)

Il PRESCALER è il fattore di divisione del clock di sistema. Ho usato un fattore /1, quindi nessuna divisione del clock. Per selezionare questa voce ho settato ad 1 il bit CS40 nel registro TCCR4B.
VALORE_MAX è il valore massimo del registro o, in alternativa, il valore di OCR4A.
Avendo messo PRESCALER ad 1, con Fsys pari a 16 MHz, la formula è
Fpwm = 16.000.000/(2OCR4A2)
Da cui
38000 = 16.000.000/(2OCR42)
Da cui
OCR4A = 16.000.000/(38000*4)

Ed ecco OCR4A a 105.

Seguendo il tuo ragionamento con il datasheet dell'ATmega2560 sono riuscito a capire un pò meglio la sequenza di istruzioni.

Domani potrò caricare il nuovo firmware e verificare con l'oscilloscopio cosa accade.

grazie ancora.

Leo, prova a riguardare le ultime tre formule, a me pare di vedere almeno due errori :cold_sweat:

A parte l'aver tralasciato una "A (OCR4 al posto di OCR4A) e l'aver approssimato 38095 a 38000? :sweat_smile:

sì, un ulteriore inghippo è strano per me ma non per lui, evidentemente, inutile parlarne :smiley:

Michele non ho risposto per mancanza di interesse ma per assenza di connessione.
E' ovvio che ho posto il quesito e che mi interessano molto i vostri commenti e aiuti.
Purtroppo non sono riuscito a rilevare gli inghippi di cui parlavi, ti sarei grato se me li facessi rilevare.
Anche perchè il codice di Leo non mi funziona.

TCCR4A = ((1<<COM4A0) | (1<<WGM40));
TCCR4B = (1<<CS40);
OCR4A = 105;

Sul pin 6, dichiarato come output non accade nulla e non riesco a spiegarmi il perchè.

Spero ancora nel vostro aiuto.
Grazie

XD XD :sweat_smile: ma no Etan, mica mi riferivo a te! il "lui" è il programma che si comporta a prescindere dal mio ragionamento. Spiego l'errore che ho fatto: dalle mie reminiscenze elementari ricordavo che una serie di * e / portevano essere invertire tra loro senza che cambiasse il risultato, invece era una mia confusione; allora facevo 16.000.000/38000*4 ignorando la parentesi e non mi usciva (OVVIAMENTE) 105, poi ci sono arrivato, volevo evitare di parlarne ma ora mi hai costretto ad uscire allo scoperto :smiley:
Sul resto devi aspettare Leo, il mago è lui in queste cose....

Etan:
Anche perchè il codice di Leo non mi funziona.

TCCR4A = ((1<<COM4A0) | (1<<WGM40));

TCCR4B = (1<<CS40);
OCR4A = 105;




Sul pin 6, dichiarato come output non accade nulla e non riesco a spiegarmi il perchè.

Spero ancora nel vostro aiuto.
Grazie

Hai messo il pinMode(6, OUTPUT) ?

leo72:
Hai messo il pinMode(6, OUTPUT) ?

Si ma niente! Sull'oscilloscopio ho sempre un segnale a 0V.
Tu l'hai provato?

Scusa Michele non avevo capito. Grazie per il tuo prezioso aiuto.

Etan:

leo72:
Hai messo il pinMode(6, OUTPUT) ?

Si ma niente! Sull'oscilloscopio ho sempre un segnale a 0V.
Tu l'hai provato?

Ho provato sull'Arduino UNO, usando i corrispondenti pin OC0A e OC1A, ed a me i led si accendono. A questo punto ho dei dubbi sulla corrispondenza fra canali dei timer e pin dell'Arduino MEGA dello schema che ho allegato alcuni post fa.

e questa mi sembra la stessa storia dell'altro Topic, evidentemente il problema è proprio la non corrispondenza dei timer :disappointed_relieved:

Boh. Ho ricontrollato ora, OC4A è su D6, sulla MEGA.