Timer1 in CTC come generatore di impulsi

Ciao ragazzi,
ho ripreso in mano il progetto che avevo iniziato per generare un impulso ritardato rispetto il fronte del segnale d'ingresso (ard UNO).
Vorrei invece che tirare su un output qualsiasi quando il timer1 arriva a OCR1A con il valore di TCNT1, si alzi direttamente l'uscita collegata a quel timer, tipo il pin 9.
Allora ho scritto questo codice ma non funziona come vorrei.

void setup()
{

    pinMode(9,OUTPUT);

// initialize Timer1
    cli();          // disable global interrupts
    TCCR1A = 0;     // set entire TCCR1A register to 0
    TCCR1B = 0;     // same for TCCR1B
    // set compare match register to desired timer count:
    // turn on CTC mode:
    TCCR1B |= (1 << WGM12);
    TCCR1B &= ~(1 << WGM10);
    TCCR1B &= ~(1 << WGM11);
   
    TCCR1A |= (1<<COM1A1);
    TCCR1A |= (1<<COM1A0);
    // enable timer compare interrupt:
    TIMSK1 |= (1 << OCIE1A);
    
    // enable global interrupts:
    sei();
    
    attachInterrupt(0, SensorINPUT, FALLING);
}


void SensorINPUT()
{  
    OCR1A=1000;
    TCCR1B |= (1 << CS11);
    TCCR1A |= (1<<COM1A1);
    TCCR1A |=  (1<<COM1A0);

 
}

ISR(TIMER1_COMPA_vect)
{ 
  TCCR1A=0;

  TCCR1B &= ~(1 << CS11);

}

Quello che succede è che sul fronte di discesa del segnale d'ingresso mi si alza l'uscita 9 con durata (500us=1000*0.5, con prescaler=8).
Io vorrei invece che dopo 500us che ho avuto il fronte mi si alza l'uscita.

Cosa devo cambiare?

I maestri dei timer sono al lavoro per una soluzione ? :smiley: :smiley: :smiley: :smiley:

Avevo trovato una soluzione di questo tipo anche, ma c'è qualcosa di sbagliato. Qui setta il timer in "input captur" e poi in CTC per alzare l'uscita.

/******************************************************************** 
**       
**                  -Input Capture Interupt- 
** 
**      -Input capture interupt trigger on falling edge 
** 
**       _____ 
** <- __|     |_____________________________________ 
**            ^ 
**            ^ICR1 
** 
**     -Output, Turn LED on at falling egde of input 
**     -Turn LED off at falling edge (ICR1) + 10ms (10000 tics) 
**             _______________________________________ 
** ___________|           LED_ON                      |_______ 
**            ^                                       ^ 
**            ^ICR1                                   ^ICR1+10000 
** 
*********************************************************************/
void Init_Timer1(void) 
{ 
   TCCR1B =   (1<<ICNC1)   |          //Enable input capture noise canceler 
            (1<<CS11)   ;         //Set timer clock prescale to Fs/8 = 1us per timer tic    
    
   TIFR =      (1<<ICF1)   |         //Clear any possible pending interupts 
            (1<<TOV1)   | 
            (1<<OCF1A)   ; 
                
   TIMSK =    (1<<TICIE1)   ;         //Enable Input capture interupt 
             
} 

ISR(TIMER1_CAPT_vect) 
{    
   OCR1A = (ICR1+10000);                 //Set output compare register to turn LED off 
   TCCR1A = (1<<COM1A1) | (1<<COM1A0);   //Compare mode: Set on compare match 
   TCCR1A |= (1<<FOC1A);               //Force compare! 
   TCCR1A = (1<<COM1A1);                 //Compare mode: clear on compare match 
   TIMSK1 |= (1<<OCIE1A);               //Enable Compare ISR 
    TIFR |= (1<<OCF1A);               //Clear any pending compare ISR 
    
} 

ISR(TIMER1_COMPA_vect) 
{ 
   TCCR1A = 0;                        //Disconnect OC1A pin from timer
   TIMSK1 &=~ (1<<OCIE1A);               //Disable compare interrupt 
}

L'ho provato ma non funziona....help! :smiley:

Non ho capito cos'è che vuoi fare esattamente. :sweat_smile:
Vuoi accendere un led in ritardo rispetto al segnale che ti dà il timer?

:slight_smile: Molto semplicemente sul fronte di discesa del segnale di ingresso (o di salita non importa) vorrei che dopo per es. 500us grazie al CTC del timer1 mi andasse alta l'uscita collegata (PD9) e che poi spengo io quando voglio.

Lo sketch che ho messo per ultimo è una cosa simile a quella che vorrei e che ho trovato in rete. Se si riesce a far funzionare anche quello può andare bene. Lui sfrutta Input capture e io invece uso attachInterrupt.

A me serve valutare i tempi di risposta dei metodi, sarebbe bello provarli entrambi.

Se agganci un'uscita ad un timer, è il timer che poi pilota quel pin.
Io capisco che tu hai questo ambiente:

  1. un pin collegato ad un segnale
  2. un pin collegato ad un led
  3. quando ti arriva il segnale sul pin 1), devi accendere il led sul pin 2) dopo 500 us

Giusto?
Perché allora non fare semplicemente che nella ISR di intercettazione del segnale sul pin 1) fai allora partire il timer (TCNT1 = 0), agganciando il pin 9, programmato per tirare su l'uscita dopo 500 us? Agganci poi un'altra ISR ad esempio sul matching con OCR1A e dentro quella ISR scolleghi il timer dal pin.

Guarda il primo codice che ho postato è proprio così che va.
Cioè, arriva il segnale sul pin 2, sul fronte di discesa chiama la SensorINPUT() routine e li dentro faccio partire il timer1 e aggancio il pin 9 (uscita).
Il timer è configurato in CTC nel setup.
Quando finisce di contare mi chiama la sua routine dove azzero TCCR1A , cioè scollego il pin di uscita.

Il problema è che invece di mandare alto il pin 9 dopo 500us, lo manda alto subito (sul fronte di discesa di pin2) e dopo 500us lo tira giu.

Questo è il problema. Spero sia più chiaro ora.
Fammi sapere.
Grazie

Ce nessunooooo?? :smiley: :smiley: :smiley:

Io prima facevo così:

void SensorINPUT()
{  
    OCR1A=1000;
    TCCR1B |= (1 << CS11);
 }

ISR(TIMER1_COMPA_vect)
{ 
  digitalWrite(4,1);

  TCCR1B &= ~(1 << CS11);

}

Ovviamente utilizzavo l'uscita 4 che poi nel loop abbassavo quando volevo.
Ora vorrei solamente che il timer alzasse la sua uscita che è la 9 in modo da risparmiare tempo.

Capisco che è venerdi e si è stanchi, ma davvero nessuno riesce ad aiutarmi?
Dove sono i maghi dei timer? :grin:

Ekjk, ho mangiato, sono andato a riprendere i figlioli a scuola, li ho fatti pranzare, poi mi sono riposato ed ho dato un'occhiata ad altre cose mie (che sono dei grossi cosi nel posteriore....) :sweat_smile:

Ho dato dei suggerimenti, sinceramente non mi sono messo ad analizzare né il problema né cosa facevano i tuoi sketch...
Sorry :sweat_smile:
Insomma, se è una questione di vita o di morte, gli darò un'occhiata

azzero TCCR1A , cioè scollego il pin di uscita.

Per scollegare un pin, basta metterlo in input. Il timer continua a girare ma lo stato non viene più cambiato.

Certo ognuno ha le sue priorità, hai ragione 8)

Ti ringrazio se ci dai un occhiata.
Non fa differenza metterlo come input o azzerare TCCR1A credo..

Secondo me appena il contatore parte va subito dentro la ISR di compare.

Prova con questo codice. Non ho controllato la corrispondenza di alcuni setup, ho preso per buono ciò che era scritto nei commenti del tuo 1° sketch.

void setup() {
    setTimer1(); //set the timer 1
    attachInterrupt(0, SensorINPUT, FALLING);
    DDRB &= ~(1 << PB1); //pin D9 as input
}

void loop() {
}

// initialize Timer1    
void setTimer1() {
    SREG &= ~(1 << SREG_I); // disable global interrupts
    // set compare match register to desired timer count:
    // turn on CTC mode:
    TCCR1A = ((1<<COM1A1) | (1<<COM1A0));
    TCCR1B = (1 << WGM12);
    TCCR1B &= ~((1 << WGM10) | (1 << WGM11));
    SREG |= (1 << SREG_I);// enable global interrupts:
}


void SensorINPUT() {
    TCCR1B &= ~(1 << CS11); //stop the timer
    TCNT1 = 0; //reset the timer counter
    OCR1A=1000; //set OCR1A
    TIMSK1 |= (1 << OCIE1A);  // enable timer compare interrupt
    TCCR1B |= (1 << CS11); //restart the timer
    DDRB |= (1 << PB1); //set D9 as output
    
}


//Interrupt Service Routine called on OCR1A COMPARE MATCH 
ISR(TIMER1_COMPA_vect) {
    TCCR1B &= ~(1 << CS11); //stop the timer
    TIMSK1 &= ~(1 << OCIE1A); //disable interrupt
}

Avviato il programma, esso setta il timer 1 per funziona così com'era nello sketch iniziale, ma non fa partire il timer (non gli dà il clock). Poi attacca l'interrupt al pin 2. Quando arriva il segnale, resetto sia il contatore del timer che imposto a 1000 OCR1A (ritolgo il clock al timer perché si presuppone che esso possa stare girando, se viene chiamata un'altra volta l'ISR prima che venga chiamata la ISR "TIMER1_COMPA") e metto il pin D9 in output. Quando TCNT1 raggiunge il valore di OCR1A il timer dovrebbe settare in automatico il pin (ma ricontrolla le impostazioni in setTimer1 perché non le ho controllate, che venga messo su High il pin) dato che è stato precedentemente agganciato fisicamente al timer. Poi viene chiamata la corrispondente ISR che provvede a disattivare il segnale di interrupt ed a fermare il timer togliendogli il clock.

Ricopia il codice ora, avevo commesso 2 errori nella sua scrittura di cui non mi ero accorto.
Ah, ovviamente non so se funziona oppure no, non l'ho provato :sweat_smile: :sweat_smile:

Grazie mille, dopo provo poi ti dico! :smiley: :smiley: :smiley:

Leo non funziona.
Praticamente mi da una uscita instabile che si alterna al segnale d'ingresso in maniera complementare.
Se invece aggiungo TCCR1A=0 nella ISR compare allora ritorno al mio caso: uscita 9 che va su sul fronte di discesa di pin2 con durata 500us e poi va bassa.

Io sono convinto che la prima volta che entra nella ISR compare lui manda su l'uscita e poi ricomincia a contare tipo downcounting e quando arriva a 0 mette a 0 l'uscita......helppP!

Prova con questo di codice:

void setup() {
    setTimer1(); //set the timer 1
    attachInterrupt(0, SensorINPUT, FALLING);
    DDRB &= ~(1 << PB1); //pin D9 as input
}

void loop() {
}

// initialize Timer1    
void setTimer1() {
    SREG &= ~(1 << SREG_I); // disable global interrupts
    // set compare match register to desired timer count:
    // turn on CTC mode:
    TCCR1A = 0; //disconnect outputs
    TCCR1B = (1 << WGM12);
    TCCR1B &= ~((1 << WGM10) | (1 << WGM11));
    SREG |= (1 << SREG_I);// enable global interrupts:
}


void SensorINPUT() {
    TCCR1B &= ~(1 << CS11); //stop the timer
    TCNT1 = 0; //reset the timer counter
    OCR1A=1000; //set OCR1A
    TIMSK1 |= (1 << OCIE1A);  // enable timer compare interrupt
    TCCR1A = ((1<<COM1A1) | (1<<COM1A0)); //set OCR1A on compare
    TCCR1B |= (1 << CS11); //restart the timer
    DDRB |= (1 << PB1); //set D9 as output
    PORTB &= ~(1 << PB1)); //low on output
    
}


//Interrupt Service Routine called on OCR1A COMPARE MATCH 
ISR(TIMER1_COMPA_vect) {
    TCCR1A = 0; //disconnect the timer
    TCCR1B &= ~(1 << CS11); //stop the timer
    TIMSK1 &= ~(1 << OCIE1A); //disable interrupt
}

E' simile al precedente, solo che ho aggiunto l'azzeramento di TCCR1A dentro alla ISR ed in più ho messo l'impostazione del pin D9 a LOW nel momento in cui scatta l'interrupt. A questo punto, dovrebbe contare 500 us e poi alzare il pin D9 (lo fa il timer in automatico).

Se anche così non va, si prova una strada molto più semplice, manipolando direttamente il pin in entrambi i cambi di stato.

Ciao Leo,
non ho ancora provato l'ultima soluzione, e nel frattempo ho contattato un tizio americano che ne sa su i timer e che mi aiuto a fare un push pull pwm con il timer 2, quando nessuno diceva che era possibile farlo.
Lui mi ha spiegato come farebbe per il mio problema, ma ha detto che non mi aiuta a meno che non lo paghi $)

Questo è ciò che mi ha scritto,

I think you can do that with a single timer, but it requires two interrupt handlers:

External event triggers “Handler A” to start the timer and possibly set the output bit
Timer triggers “Handler B” at end of pulse to stop the timer and reset for next external event
You (almost certainly) want phase-and-frequency-correct PWM mode, not CTC mode, for this function, so that you can set the delay and width. The timers normally run continuously, so you must manually preset, start, and stop the timers to get the result you want.

The trick is to preload the TCNT register with the delay value relative to the TOP value in ICR. When the external event starts the timer (through Handler A), it counts upward during the initial delay time. When it hits TOP, the COM configuration sets the output bit and the timer counts downward toward BOTTOM (0×0000) during the pulse width. You’ve set the TOP value in ICR so that the delay from TOP to BOTTOM determines the pulse width. When TCNT hits BOTTOM, it clears the output bit and triggers Handler B, which stops the timer and prepares everything for the next external pulse.

Te ne intendi di phase and frequency correct pwm? io no... :smiley:

Beh, e io che sono da meno di lui?
Allora voglio essere pagato anch'io :stuck_out_tongue:

Che cagnaccio che sei :grin: :grin: :grin:

No, anzi non sei da meno (se non ti fai pagare perchè fai più bella figura :smiley: :smiley: :D)

Da quello che lui descrive vorrei capire come intercettare tramite la routine ISR(TIMER1_compa_vect) quando il TCNT1 è downcounting da TOP a BOTTOM invece che viceversa.
Perchè, penso che tale isr venga lanciata ogni volta che TCNT eguaglia OCR1A, quindi esiste un flag che mi dice che il contatore ha raggiunto lo 0? Cioè da TOP (ICR1) a BOTTOM (0).

Lui sta descrivendo una situazione differente rispetto a quella che avevi descritto tu inizialmente.
Tu dicevi che usai un segnale per attivare il timer che usavi per far portare alto un pin dopo un certo lasso di tempo e che poi era cura del codice principale rimettere basso questo pin.
Lui invece ti descrive una situazione in cui fai partire il timer, questo poi mette alto il pin raggiunto TOP e dopo tiene il pin alto finché non arriva a BOTTOM, quando lo rimette basso.

  1. Ma tu di che logica hai bisogno?
  2. lo hai provato il codice che ti avevo scritto?