input capture timer

:) ciao ragazzi, e da un paio di giorni che cerco di fare andare il mio programmino di test su arduino ma non riesco ad avere valori giusti. in pratica vorrei calcolare la frequenza di un onda quadra pwm(50% dutycycle) di 38Khz, questo pwm proviene da buspirate, quindi dovrebbe essere apposto. ho impostato sul pin ICP5 un interrupt di rising edge, in pratica faccio partire il timer quando ho un fronte di salita dell'onda, e quando ho un secondo fronte di salita stoppo il timer lo riazzero vado a prendere il valore di ICR5 e poi lo stampo a schermo, solo se ho un valore. adesso quello che mi aspetto di leggere ± 416 , invece leggo 20,15,0,17,0,0,12 ecc. =( =( la frequenza dell'onda quadra e 38Khz un periodo di 26us , ho settato il prescaller a 1 = 0.0625 us => valore ICR5 ±416. questo e il programma :

uint8_t start_stop = 1; 
uint8_t disponibile = 0; 
uint16_t byte_ICR5 = 0; 

// pin ICP5 = digital pin 48 
void setup() 
{ 
  pinMode(48, INPUT); 
  Serial.begin(115200); 
// initTimer 
TCCR5A = 0; // resetto registri A e B 
TCCR5B = 0; 
//Set Initial Timer value 
TCNT5=0; 
//Enable interupt 
TIMSK5 = _BV(ICIE5); // interupt pin ICP5 
//TIMSK5 = _BV(TOIE5); interupt owerfloow 
//capture on rising edge 
TCCR5B = _BV(ICES5); 
// enable nois canceller 
TCCR5B = _BV(ICNC5); 
TCCR5B = _BV(CS50); // 1 = 0.0625 , start timer 
} 


void loop() 
{ 
   if(disponibile) 
   {
     Serial.println(byte_ICR5); 
     disponibile = 0; 
   }    
} 

// quando viene chiamata una routine ISR 
// vengono automaticamente disabilitati gli interrupts 
ISR(TIMER5_CAPT_vect) 
{ 
   if(start_stop) 
   { 
      // start time with prescaller = 1 
      TCCR5B = _BV(CS50); // 1 = 0.0625 , start timer 
      start_stop = 0; 
   } 
   else 
   { 
     TCCR5B = 0; // stop timer 
     byte_ICR5 = ICR5; 
     TCNT5 = 0; // resetto timer 
     start_stop = 1; 
     disponibile = 1; 
   } 
}

grazie in anticipo per le risposte....... :)

dimenticavo uso un arduino mega 2056 , 16Mhz

facendo delle prove ho voluto implementare col timer 1 un pwm 38Khz phase and frequency control.
adesso ho dei dati un po più decenti, ma cmq non sono come me lo aspettavo, dati: minimo di 323 max 393 ho uno sbaglio di 60 , vuol dire 60 * 0,0625 = 3.75 us un enormità.

uint8_t start_stop = 1; 
uint8_t disponibile = 0; 
uint16_t byte_ICR5 = 0; 

// pin ICP5 = digital pin 48 
void setup() 
{ 
  pinMode(48, INPUT); 
  pinMode(11, OUTPUT); // set pin 11 to output for pwm
  Serial.begin(115200); 
// initTimer 
TCCR5A = 0; // resetto registri A e B 
TCCR5B = 0; 
//Set Initial Timer value 
TCNT5=0; 
//Enable interupt 
TIMSK5 = _BV(ICIE5); // interupt pin ICP5 
//TIMSK5 = _BV(TOIE5); interupt owerfloow 
//capture on rising edge 
TCCR5B = _BV(ICES5); 
// enable nois canceller 
TCCR5B = _BV(ICNC5); 
TCCR5B = _BV(CS50); // 1 = 0.0625 , start timer 

// Initial TIMER1 Phase and frequency Correct PWM
// Frequency pwm = Fclock / (2 * N * top_value(register ICRx) ex 16000000 / ( 2 * 1 * 210) = 38095 Hz

	
	ICR1 = 210 ; // valore registro che mi da la frequenza i valori <= 2 danno come frequenza 0 Hz

	
	OCR1A = 127 ; // valore da 0-255 dutyCycle

	// Ask for non-inverted PWM on OC1A 
	TCCR1A = _BV(COM1A1);
        // per disabilitare pwm lasciando il resto come e
        // TCCR1A &= 0b00111111; // gli 1 sono il MASK
	// Configure timer 1 for Phase and Frequency Correct PWM mode, with no prescaling
	TCCR1B = _BV(WGM13) | _BV(CS10);
} 


void loop() 
{ 
   if(disponibile) 
   { 
     // preservo la variabile da modificazioni metre la sto leggendo 
     Serial.println(byte_ICR5); 
     disponibile = 0; 
   }    
} 

// quando viene chiamata una routine ISR 
// vengono automaticamente disabilitati gli interrupts 
ISR(TIMER5_CAPT_vect) 
{ 
   if(start_stop) 
   { 
      // start time with prescaller = 1 
      TCCR5B = _BV(CS50); // 1 = 0.0625 , start timer 
      start_stop = 0; 
   } 
   else 
   { 
     TCCR5B = 0; // stop timer 
     byte_ICR5 = ICR5; 
     TCNT5 = 0; // resetto timer 
     start_stop = 1; 
     disponibile = 1; 
   } 
}

e cmq se andiamo a calcolare 393 => frequenza di 40712 Hz invece nei calcoli per il pwm teoricamente dovrebbe venire 38095Hz.
facendo la misura del pwm in uscita con buspirate mi da un valore di 37952 HZ non e preciso come un oscilloscopio ma più o meno si avvicina al valore teorico del pwm.

Benvenuto. :) Ti invitiamo a presentarti qui: http://forum.arduino.cc/index.php?topic=113640.0 e a leggere il regolamento: http://forum.arduino.cc/index.php?topic=149082.0 - qui una serie di schede by xxxPighi per i collegamenti elettronici vari: http://forum.arduino.cc/index.php?topic=146152.0 - qui le pinout delle varie schede by xxxPighi: http://forum.arduino.cc/index.php?topic=151646.0 - qui una serie di link generici utili: http://forum.arduino.cc/index.php?topic=126861.0

Il codice deve essere racchiuso dai tag code (usa in inserimento o modifica il pulsante con icona #) Nel regolamento sezione 7 è spiegato meglio. Altrimenti parti del codice possono essere interpretate male nella pagina html e mal formattato (a volte vedi delle icone faccine)

Tutte le volte che modifichi i registri dovresti agire sui singoli bit e non fare assegnazioni dirette, altrimenti cancelli le precedenti impostazioni.
Ad esempio:

TCCR5B = _BV(ICES5); 
// enable nois canceller 
TCCR5B = _BV(ICNC5); 
TCCR5B = _BV(CS50); // 1 = 0.0625 , start timer

Prima setti il bit ICES5, poi vorresti settare anche il bit ICNC5 ma la seconda operazione è un’assegnazione diretta quindi vai a resettare tutti i restanti bit del registro.

Quindi questa porzione di codice diventa così:

TCCR5B = _BV(ICES5); 
// enable nois canceller 
TCCR5B |= _BV(ICNC5); 
TCCR5B |= _BV(CS50); // 1 = 0.0625 , start timer

Noti l’OR binario? In questo modo ogni operazione aggiorna la situazione precedente, non la sovrascrive.

Oppure più direttamente così:

TCCR5B = ((1<<ICES5) | (1<<ICNC5) | (1<<CS50));

grazie per la risposta, questa cosa mi e sfuggita.
cmq ritornando ai valori della seriale, sono cmq sbagliati, da un minimo di 305 a 396.
Il timer dovrebbe essere preciso no???
ho messo apposto il programma come mi hai deto tu…

uint8_t start_stop = 1; 
uint8_t disponibile = 0; 
uint16_t byte_ICR5 = 0; 

// pin ICP5 = digital pin 48 
void setup() 
{ 
  pinMode(48, INPUT); 
  pinMode(11, OUTPUT); // set pin 11 to output for pwm
  Serial.begin(115200); 
// initTimer 
TCCR5A = 0; // resetto registri A e B 
TCCR5B = 0; 
//Set Initial Timer value 
TCNT5=0; 
//Enable interupt 
TIMSK5 = _BV(ICIE5); // interupt pin ICP5 
//TIMSK5 = _BV(TOIE5); interupt owerfloow 
//capture on rising edge 
TCCR5B = _BV(ICES5); 
// enable nois canceller 
TCCR5B |= _BV(ICNC5); 

// Initial TIMER1 Phase and frequency Correct PWM
// Frequency pwm = Fclock / (2 * N * top_value(register ICRx) ex 16000000 / ( 2 * 1 * 210) = 38095 Hz

	
	ICR1 = 210 ; // valore registro che mi da la frequenza i valori <= 2 danno come frequenza 0 Hz

	
	OCR1A = 127 ; // valore da 0-255 dutyCycle

	// Ask for non-inverted PWM on OC1A 
	TCCR1A = _BV(COM1A1);
        // per disabilitare pwm lasciando il resto come e
        // TCCR1A &= 0b00111111; // gli 1 sono il MASK
	// Configure timer 1 for Phase and Frequency Correct PWM mode, with no prescaling
	TCCR1B = _BV(WGM13) | _BV(CS10);
} 


void loop() 
{ 
   if(disponibile) 
   { 
     // preservo la variabile da modificazioni metre la sto leggendo 
     Serial.println(byte_ICR5); 
     disponibile = 0; 
   }    
} 

// quando viene chiamata una routine ISR 
// vengono automaticamente disabilitati gli interrupts 
ISR(TIMER5_CAPT_vect) 
{ 
   if(start_stop) 
   { 
      // start time with prescaller = 1 
      TCCR5B = _BV(CS50); // 1 = 0.0625 , start timer 
      TCCR5B |= _BV(ICNC5); // nois canceller
      start_stop = 0; 
   } 
   else 
   { 
     TCCR5B = 0; // stop timer 
     byte_ICR5 = ICR5; 
     TCNT5 = 0; // resetto timer 
     start_stop = 1; 
     disponibile = 1; 
   } 
}

Setta le 3 variabili globali che usi nei timer come "volatile", altrimenti il compilatore potrebbe ottimizzarne l'uso e questo non va bene lavorando coi timer

volatile  uint8_t start_stop = 1; 
volatile  uint8_t disponibile = 0; 
volatile  uint16_t byte_ICR5 = 0;

:) se ho capito bene mettere volatile davanti alla variabile fa si che essa venga sempre letta dalla memoria, in ogni caso quando c'è una possibilità che il valore della variabile potrebbe cambiare al di fuori del normale flusso di esecuzione del programma. in questo caso un interrupts. grazie per la dritta ma non e questo il problema. :astonished: :astonished:

volatile uint8_t start_stop = 1; 
volatile uint8_t disponibile = 0; 
volatile uint16_t byte_ICR5 = 0; 

// pin ICP5 = digital pin 48 
void setup() 
{ 
  pinMode(48, INPUT); 
  pinMode(11, OUTPUT); // set pin 11 to output for pwm
  Serial.begin(115200); 
// initTimer 
TCCR5A = 0; // resetto registri A e B 
TCCR5B = 0; 
//Set Initial Timer value 
TCNT5=0; 
//Enable interupt 
TIMSK5 = _BV(ICIE5); // interupt pin ICP5 
//TIMSK5 = _BV(TOIE5); interupt owerfloow 
//capture on rising edge 
TCCR5B = _BV(ICES5); 
// enable nois canceller 
TCCR5B |= _BV(ICNC5); 

// Initial TIMER1 Phase and frequency Correct PWM
// Frequency pwm = Fclock / (2 * N * top_value(register ICRx) ex 16000000 / ( 2 * 1 * 210) = 38095 Hz

	
	ICR1 = 210 ; // valore registro che mi da la frequenza i valori <= 2 danno come frequenza 0 Hz

	
	OCR1A = 127 ; // valore da 0-255 dutyCycle

	// Ask for non-inverted PWM on OC1A 
	TCCR1A = _BV(COM1A1);
        // per disabilitare pwm lasciando il resto come e
        // TCCR1A &= 0b00111111; // gli 1 sono il MASK
	// Configure timer 1 for Phase and Frequency Correct PWM mode, with no prescaling
	TCCR1B = _BV(WGM13) | _BV(CS10);
} 


void loop() 
{ 
   if(disponibile) 
   { 
     // preservo la variabile da modificazioni metre la sto leggendo 
     Serial.println(byte_ICR5); 
     disponibile = 0; 
   }    
} 

// quando viene chiamata una routine ISR 
// vengono automaticamente disabilitati gli interrupts 
ISR(TIMER5_CAPT_vect) 
{ 
   if(start_stop) 
   { 
      // start time with prescaller = 1 
      TCCR5B = _BV(CS50); // 1 = 0.0625 , start timer 
      TCCR5B |= _BV(ICNC5); // nois canceller
      start_stop = 0; 
   } 
   else 
   { 
     TCCR5B = 0; // stop timer 
     byte_ICR5 = ICR5; 
     TCNT5 = 0; // resetto timer 
     start_stop = 1; 
     disponibile = 1; 
   } 
}

Prova intanto togliendo il filtro antidisturbo, che ti rallenta la lettura del segnale di 4 cicli invece che sollevare l’interrupt non appena il pin cambia stato.

:) questa cosa e proprio un rompi capo per me…….. Non riesco ad avere il risultato giusto , togliendo il filtro anti disturbo ottengo 383 :fearful: invece ho provato ad calcolare una frequenza minore 2khz ed il risultato e buono…….. invece testando un altro programma FreqMisure per arduino che fa più ho meno la stessa cosa quello funziona bene per 38 Khz mi da 416 ok ……. I registri sono settari come i miei, l'unica cosa che a di diverso e che fa lettura - lettura precedente senza azzerare ogni volta il timer, e conta anche gli interrupt di overfloow. Io non riesco proprio a capire il codice e giusto i setaggi dei registri pure , non so dove vado a sbagliare. :0

Ma tu generi il segnale con un altro timer, giusto?
Hai un oscilloscopio per controllare la frequenza generata? Sei sicuro del clock che ti esce dal pin?

Prova questo:

volatile unsigned int REG5 = 0;
volatile byte disponibile = 0;

void setup() {
    Serial1.begin(115200);
    delay(1000);
    setTimer1();
    setTimer5();
    pinMode(11, OUTPUT);
}

void loop() {
    if (disponibile) {
        Serial1.println(REG5, DEC);
        disponibile = 0;
    }
}

void setTimer1() {
    cli();
    //Phase Correct PWM con top a OCR1A e toggle del pin
    TCCR1A = ((1<<COM1A0) | (1<<WGM10) | (1<<WGM11));
    TCCR1B = ((1<<CS10) | (1<<WGM13));
    OCR1A = 105;
    sei();
}

void setTimer5() {
    cli();
    TCCR5A = 0;
    TCCR5B = ((1<<ICES5) | (1<<CS50));
    sei();
}

ISR (TIMER5_CAPT_vect) {
    if (!disponibile) {
        TCNT5 = 0;
        REG5 = ICR5;
        disponibile = 1;
    }
}

Genera un PWM con una modalità diversa da quella usata da te (attenzione! non l’ho provato, sono a lavoro ed ho scritto il codice ma non l’ho potuto provare). Aggiorna il registro REG5 solo se non lo hai già fatto, per evitare perdite di dati.

:smiley:
ciao ho testato il tuo codice, piccola parentesi avevi dimenticato attivare l’interrupts del cambio di stato logico sul timer.void

setTimer5() {
    cli();
    TCCR5A = 0;
    TCCR5B = ((1<<ICES5) | (1<<CS50));
    TIMSK5 = _BV(ICIE5);
    sei();
}

ho testato con buspirate la frequenza generata e mi da 38,002 Khz perfetto.
un altra cosa ho anche aggiunto nel setup() ….pinMode(5,INPUT);
Il mio arduino mi da come risultato tra gli 8371 e 7951 :fearful: non so se e solo sul mio che fa così!!!
Cmq non e il risultato che ci si aspetta , 416, una causa può essere che non resetti la variabile disponibile e che il timer continui a contare, poi ad un certo punto resetta la variabile e stampa il risultato.
e l’unica spiegazione che posso dare.
adesso provo a mettere prima che stampi dalla seriale noInterrupts() // stampa // interrupts(),magari visto che e una variabile a 16bit e il micro per elaborarla fa due passaggi magari a mezzo cammino viene richiamata dalla routine ISR e venga modificata, pero il problema mi sa che si pone solo quando ci sono in gioco numeri che superano i 255.
cmq provo :stuck_out_tongue_closed_eyes:
grazie molte per il tuo interessamento te ne sono grato, sono fermo ormai da 4 gg.
:slight_smile:

Come ti avevo detto, ho scritto il codice a lavoro quindi senz'altro qualcosa è rimasto fuori ;) Era anche una bozza, lo dovevi poi adattare tu alle tue esigenze.

E' vero, non azzero il registro, ma la stampa della variabile la dovrebbe non appena disponibile. Ho messo un check nella ISR in modo tale che se il dato è già stato registrato, non lo sovrascriva. In questo modo dovrebbe evitare di leggere 2 volte il registro IC del timer nel caso la CPU non riuscisse a spedire il dato prima dell'arrivo di un altro interrupt (considera che la seriale è interrupt-driven quindi potresti trovarti con un segnale di interrupt generato dal timer mentre stai spedendo un byte con la seriale).

I valori discordanti che leggi potrebbero essere dati dal prescaler. Io dovrei aver impostato il prescaler del timer 5 a /1.

Non ti porre il problema della spedizione dei 2 byte con il mio codice perché, come ti ho detto, non salvo il registro se ho già un valore memorizzato. La cosa la potresti aggirare con un buffer da 5/10 elementi in cui riversi le letture via via che arrivano gli interrupt. interrompa meno

potrei capire qualche valore che non va ogni tanto, ma questo come l'altro prog mi danno dei valori che non mi so spiegare…. 8373, con prescaller a 1 una frequenza di 38Khz dovrebbe darmi 416. potresti provarlo tu e dirmi se hai gli stessi risultati? :)

Sono a lavoro, non posso fare prove... :sweat_smile: :sweat_smile: Forse oggi pomeriggio, ma ho la famigliola da seguire, forse domani pom. se ce la faccio (e se me lo ricordo...). :sweat_smile:

ok grazie.. XD

finalmente sono riuscito a trovare il problema. ho utilizzato atmel studio per il codice , lo riscritto con le librerie dell avr, ho programmato il atmel con avrdude, cancellando il bootloader. 8) lo provato e funziona tutto correttamente adesso ho i valori giusti, e non sballano più. :stuck_out_tongue_closed_eyes: :stuck_out_tongue_closed_eyes: quindi il mio problema e con il software arduino, ma non so cosa sia…. ]:) comunque grazie ancora per l'aiuto datomi. :) buona pasqua a tutti

Sono felice per te, anche perché mi ero completamente dimenticato della cosa e non ho più fatto le prove promesse :astonished: :astonished:

Non si può sapere a cosa è dovuta la differenza di funzionamento?