Timer 1 funzione input capture

Ciao

stavo provando la funzione Input Capture del timer 1 di Arduino 1 per leggere un'onda quadra che gli metto in ingresso al pin ICP1.

Io devo andare a leggere il valore che mi viene salvato nel registro ICR1.

I valori che prendo da questo registro sono sul fronte di salita e poi di discesa ed infine faccio la differenza per vedere quanto dura un mezzo periodo.

Quello che non ho capito è come interpretare i valori che leggo dal registro ICR1 da un apetto temporale.

Per essere più chiaro:

Se invio un'onda, da F = 115 200 Hz e T = 8,68 us, prescaler impostato a 1 quindi uso i 16 mhz dell'arduino uno, il valore di time_diff che leggo oscilla da 2 a 3. Ma li devo considerare microsecondi, nano o cosa ??

Grazie

volatile unsigned char current_edge = 0;
volatile uint16_t starting_cnt;
volatile uint16_t ending_cnt;

uint16_t time_diff;


ISR(TIMER1_CAPT_vect) {

   if(current_edge == 0) {

     // Save timestamp
     starting_cnt = ICR1;

     //Serial.println("starting_cnt");

     //Serial.println(starting_cnt);
     
     //Switch to rising edge 
     TCCR1B |= (1 << ICES1);

     current_edge = 1;
   }
   else if(current_edge == 1) {

     //Save timestamp 
     ending_cnt = ICR1;

     //Serial.println("ending_cnt");

     //Serial.println(ending_cnt);

     //Switch to falling edge 
     TCCR1B &= ~(1 << ICES1);

     current_edge = 2;
   }

   TIFR1 |= (1 << ICF1);
}


void setup() {

  Serial.begin(9600);

  pinMode(8, INPUT);

  /*** Timer 1 overflow ***/
  
//  DDRB |= 1 << 5;
//
//  PORTB &= ~ (1 << 5);
//   
//  TCCR1A = 0x0;
//
//  TCCR1B = (1 << CS11) | (1 << CS10); // Prescaler a 64
//
//  TIMSK1 |= 1 << TOIE1; // Abilito l'interrupt Overflow
//
//  sei();


  /*** Timer 1 PWM ***/

//  TCCR1B = (1 << CS11); // Prescaler a 8
//
//  TCCR1A = (1 << WGM10) | (1 << WGM11); // Scelgo il tipo di onda PWM
//
//  TCCR1A |= (1 << COM1A1); // Scelgo il pin di uscita
//
//  OCR1A = 127 ; // Duty cycle al 50 %


  /*** Timer 1 Input Capture ***/

  TCCR1A = 0;

  TCCR1B |= (1 << CS10); // Prescaleer a 1

  TIMSK1 |= (1 << ICIE1); // Abilito l'input capture interrupt

  // Non settando il bit ICES1 di TCCR1B esso avrà valore 0 e quindi
  // la lettura dell'impulso avverrà sul FALLING
  
  sei(); // abilito i glob interrupt

}


void loop() {

  tone(4, 1000);
  
  if(current_edge == 2) {

    if(starting_cnt < ending_cnt) {

      time_diff = ending_cnt - starting_cnt;

      Serial.println("time_diff");

      Serial.println(time_diff);
    }
    else {

      //wrap around
      //(0xffff + A) - B, done without requiring signed
      time_diff = starting_cnt - ending_cnt; 
      time_diff = 0xffff - time_diff;

      //Serial.println("PLUTO");
    }

    current_edge = 0;
  }
}

p = 1/115200Hz = 0,00000868055555555556 secondi.
In millesimi di secondo 0,00000868055555555556 x 1000
In milionesimi di secondo (microsecondi) 0,00000868055555555556 x 1000000.

p è il periodo tra due fronti dello stesso verso, cioè entrambe rising o falling edge

Con il prescaler a 1, 1/16000000 = 0.0625us (62.5ns).
Il timer1 è grande 16 bit è va in overflow dopo 0.0625 x 65536 = 4096us (4.096ms)

ICR1 lo devi salvare in una variabile volatile es saveICR1.
Se il valore di saveICR1 vale 3 il tempo trascorso è 3 x 0.0625us.

Mentre se vuoi ricavare il valore di ICR1 partendo dal tempo ad esempio 1 ms, (in us 1000) 1000/0.0625 = 16000.
La riprova 16000 x 0.0625 = 1000us.

Nel tuo caso la metà del periodo vale 4,3402777us, per cui saveICR1 dovrebbe valere 4,3402777/0.0625 = 69,444.
ICR1 non può contare con la virgola per cui vale 69.

Scusa ma non avevo visto il codice. Potrebbe non funzionare poiché il tempo per eseguire il codice presente nella ISR è maggiore di 4,3402777us per cui ti perdi dei conteggi. Per verificare imposta il prescaler a 8 o più se funziona il motivo è chiaro.

PS: controlla i calcoli perché potrei avere sbagliato, anche se ho corretto più volte, con questi calcoli è facile sbagliare.

Ciao.

Buongiorno

Grazie davvero per la tua risposta esaustiva.

Infatti mi mancavano questa operazioni matematiche per capire meglio ed interpretare i valori.

Adesso ci provo e ti aggiorno.

Grazie ancora e buona giornata

>lorenrus: Quando si quota un post, NON è necessario riportarlo (inutilmente) tutto; bastano poche righe per far capire di cosa si parla ed a cosa ci si riferisce, inoltre, se si risponde al post immediatamente precedente, normalmente NON è necessario alcun "quote" dato che è sottinteso. :slight_smile:

Gli utenti da device "mobile" (piccoli schermi) ringrazieranno per la cortesia :wink:

Guglielmo

P.S.: Ho eliminato io il "quote" dal tuo post qui sopra :wink:

La formula generale sarebbe:

T = 1/(prescaler * Freq) giusto ?

Grazie

T = 1/(prescaler * Freq) giusto ?

Certo che no. Il prescaler è un divisore di frequenza, come dire riceve 8 periodi in ingresso e ne esce uno, uno ogni otto periodi è un divisore (o prescaler) per 8.

In sostanza l'asterisco deve essere '/'.

All'ingresso del divisore ci sono 16, 000, 000 periodi al secondo, dal divisore per 8 ne escono appunto 16, 000, 000 / 8 = 200, 000 Hz.

T = 1/F e F = 1/T.

Ciao.

Ciao

si scusa hai ragione. Ma per quanto riguarda quella cosa che dicevi sul primo messaggio sul fatto di aumentare il prescaler per generare un tempo di overflow ancor più piccolo.

Ma se aumento il prescaler mi aumenterà questo tempo o sbaglio ?

Fclock = 16 000 000 Hz, Prescaler a 8 --> F 8p = 16 000 000 / 8 = 2 000 000 Hz --> T 8p = 1 / 2 000 000 = 0.5 us.

Quindi T timer 1-overflow = 0.5 us * 65536 = 32768 us, quest'ultimo valore è più grande rispetto a quello che avevi calcolato nella tua prima risposta nel caso di prescaler = 1.

Se tutto questo è corretto vuol dire che ho bisogno di un micro con F clock maggiore ??

Grazie

Se tutto questo è corretto vuol dire che ho bisogno di un micro con F clock maggiore ??

Si è corretto, ma non credo che si possa risolvere solo con un clock maggiore che rimanendo su ATmega al massimo vale 20MHz.

Io al momento sono quasi alla frutta entrando su ICP1 con una frequenza di 366kHz, ogni 2.73us si verifica una IRQ e la MCU deve saltare alla ISR eseguire il codice all'interno prima di 2.73us, altrimenti se il codice interno alla ISR mi impegna in modo esclusivo la CPU per più di 2.73us mi perdo un conteggio.

La mia ISR:
// uint16_t edgeCounter;

ISR(TIMER1_CAPT_vect)  {
    edgeCounter++;
}

Appena dichiaro edgeCounter a 32-bit non funge più con prescaler a 1 (che sarebbe divisore 16 / 1 = 16), quindi aspetto TCNT1 vada in overflow 225 volte e nella ISR(OVF_vect) accumulo il valore di edgeCounter. Un overflow si presenta dopo 4096us x 225 = 921600us (921.600ms). Quindi dopo circa poco meno di un secondo termina il campionamento e ricavo la frequenza.

Per tua necessità nella ISR CAPT ci devi mettere il codice per invertire edge detector, una cosa tipo:

ISR(TIMER1_CAPT_vect)  {

    // Save timestamp
    if (starting_cnt == 0) {
        starting_cnt = ICR1;
            //Switch to rising edge 
        TCCR1B |= (1 << ICES1);
    } else {
        //Save timestamp
        ending_cnt = ICR1;    
        // spegni timer1 
    }
}

Poi nel loop if(ending_cnt != 0) ricavi la differenza tra end - starting, azzeri le variabili, inverti edge detector e riattivi il timer1.

Se il timer riceve clock ogni 0.5uS starting_cnt ad esempio vale 2 (2x0.5us), ending_cnt deve valere 10. Allora 10 - 2 = 8x0.5 = 4us. Con il prescaler a 1 si ha la massimo risoluzione di conteggio cioè al posto di 0.5 c'è 0.0625us.

Capisci che se all'interno della finestra di 0.5us invii 10 fronti il valore di ICR1 sarà sempre lo stesso.

PS: c'è un altro modo credo ma ancora non sono sicuro, comunque devo provare ad entrare con il clock sul pin T1
questo incrementa direttamente il contatore TCNT1.

Ciao.

Ciao esatto infatti ora sto provando con samd 21. E vediamo

Sisi comunque la parte per invertire i fronti l'ho messa nel codice che yho inserito nel primo messaggio della chat.

Grazie

Scusa ma quindi tu stai inviando un'onda a 336 Khz e a che freq di clock stai lavcorando e con che prescaler.
Ipotizzando che stessi a 16 Mhz e prescaler 1 vuol dire che il timer 1 va in OVF dopo 4ms, quindi non può andarti bene o sbaglio se ho capito il ragionamento dei messaggi precedenti.

Altra cosa, ma quindi quando io inserisco un'onda su ICP1 il vincolo è che il suo mezzo periodo deve essere < Tclock * 65536.

Se fosse cosi , considerando che al massimo con 16 Mhz e con il timer 1 a 16 bit riesco ad ottenere 0.0625 u * 65536 = 4 ms, vuol dire che al massimo posso leggere correttamente segnali il cui loro mezzo periodo può essere al massimo 4 ms ??

Grazie

Ipotizzando che stessi a 16 Mhz e prescaler 1 vuol dire che il timer 1 va in OVF dopo 4ms, quindi non può andarti bene o sbaglio se ho capito il ragionamento dei messaggi precedenti.

No no, mi va bene e mi va bene perché ho 4096us per eseguire il codice nella ISR OVF prima che si presenti un altro overflow.

Mentre ci sono solo 2.7us per eseguire la ISR CAPT ed è qui che c'è il rischio che un contatore a 4 byte e qualche if richiedono più tempo di 2.7us per essere eseguiti.

Se fosse cosi , considerando che al massimo con 16 Mhz e con il timer 1 a 16 bit riesco ad ottenere 0.0625 u * 65536 = 4 ms, vuol dire che al massimo posso leggere correttamente segnali il cui loro mezzo periodo può essere al massimo 4 ms ??

Ma anche peggio, quando avvii il timer1 non sai il valore che ha TCNT1 nel momento in cui su ICP1 arriva il primo fronte, per cui TCNT1 potrebbe avere valore diverso da zero (sarà diverso da zero certamente), questo valore viene copiato nel registro ICR1. Quindi ICR1 potrebbe vale 2 o 100 e se perdi tempo dopo avere avviato il timer potrebbe arrivare anche a 65535 e il colpo di clock dopo c'è overflow. Comunque si può sempre contare quante volte si va in overflow.

Per evitare ciò ho usato INT0 che riceve anche il clock da misurare e nella ISR INT0 attivo il timer1 con prescaler 1. La stessa ISR INT0 disabilita se stessa.

ISR(INT0_vect) {
    PD2_INT0_DIS();
    TCNT1 = 0;
    INT_ICP_EN();
    PD3_TOGGLE();
    TIMER_START();

}

Ma occhio che ho 2 NAND sul clock di modo che i fronti su INT0 e ICP1 risultino invertiti.

Sono riuscito a risolvere.

Passata una certa frequenza del segnale in ingresso che io gli do, oltre i 533 Mhz, non mi stampa più i valori sul plotter seriale.

Come posso ovviare/risolvere questo problema ?

Grazie

--- Ho di nuovo tolto il QUOTE ... ti prego di ricordarti di quanto ti ho detto al post #3, Grazie - gpb01