Timer1 CTC mode and set-clear OC1A

Hi everyone,
sorry for boring with this topic but i can't find the solution at this issue from a very long time.

I'm using arduino UNO and struggling with the set and clear of digital pin 9 (OC1A).
I need to precise generate a pulse on that pin when an external event triggers the pin 2 (INT 0) only, with variable delay form trigger and variable pulse width.

Let's start with the simple variant of fixed delay and fixed pulse width.

My code is this:

volatile byte isr;

void setup()
{

TCCR1A=0;
TCCR1B=0;
TIFR1=1;
TIMSK1 =0;
TIMSK1 |= bit(OCIE1A);
TCNT1=0;
OCR1A=1000; //500us delay and pulse width

pinMode(9,OUTPUT);
pinMode(2,INPUT);

attachInterrupt(2,triggerIN,RISING);
}

void triggerIN()
{
TCCR1A |= bit(COM1A1) | bit(COM1A0); //set OC1A on compare match
TCCR1B |= bit(WGM12) | bit(CS11); //CTC mode
isr=1;
}

ISR(TIMER1_COMPA_vect)
{
if(isr==1) {TCCR1A = 0; TCCR1A = bit(COM1A1);} //clear OC1A on next compare match
if(isr==2) {TCCR1B=0; TCNT1=0;} //stop the timer
isr++;
}

I'm looking the output pulse with the oscilloscope: triggering it once i see the behavior is as expected, so at the rising edge of the signal on pin 2 the timer starts and after 500us the OC1A is high and after 500us again is low. But, on the next cycle thi is not true again and i have the OC1A set at the timer start and reset after 500us; I clear see on the scope sometimes the correct behavior and sometimes not.

Any suggestion?
Thanks

Why don't you use input capture mode instead of an external interrupt?

@OP

Please, clearly specify which one of the following you want to achieve by operating the TC1 in Mode-4 (CTC).

intzero.png
Figure-1: CTC Mode operation of TC1

1. Initially, the DPin-9 (OC1A signal/pin) will remain at LOW-state. Whenever an external interrupt signal (logic-low) will arrive at INT0-pin (DPin-2, Fig-1), the TCNT1 will start counting up; the DPin-9 will assume HIGH-state when match occurs with OCR1A Register (after the delay determined by OCR1A Register and clkTC1; say 1 sec). Connect a LED with a 2.2k series resistor at DPin-9 to see the result.

void setup()
{
  pinMode(9, OUTPUT);   //DPin-9 (OC1A signal/pin must be set as output)
  pinMode(2, INPUT_PULLUP);
  do          //check if Logic-low signal is assertd on DPin-2
  {
    bool n = digitalRead(2);
    if ( n == LOW)
    {
      break;
    }
  }
  while (1);
  //-Mode-4 CTC=TC1;LH->OC1A after match;delay 1 sec;clkTC1 = fosc/256
  TCCR1A = 0x0000;  //reset register to cancel Arduino's setting int init() function
  TCCR1A |= bit(COM1A1) | bit(COM1A0); //set OC1A on compare match
  TCCR1B = 0x0000;  //reset register
  TCCR1B |= bit(WGM12) | bit(CS12); //CTC mode; clkTC1 = 62500
  OCR1A = 62500;// 1-sec time delay
  TCNT1 = 0x0000;
}

void loop() 
{
  
}

2. Initially, the DPin-9 (OC1A signal/pin) will remain at LOW-state. Whenever an external interrupt signal (logic-low) will arrive at INT0-pin (DPin-2, Fig-1), the DPin-9 will begin toggling at interval determined by OCR1A Register and clkTC1; say 1 sec).

void setup()
{
  pinMode(9, OUTPUT);   //DPin-9 (OC1A signal/pin must be set as output)
  pinMode(2, INPUT_PULLUP);
  do          //check if Logic-low signal is assertd on DPin-2
  {
    bool n = digitalRead(2);
    if ( n == LOW)
    {
      break;
    }
  }
  while (1);
  //-Mode-4 CTC=TC1;LH->OC1A after match;delay 1 sec;clkTC1 = fosc/256
  TCCR1A = 0x0000;  //reset register
  TCCR1A |= bit(COM1A0); //toggle at compare match
  TCCR1B = 0x0000;  //reset register
  TCCR1B |= bit(WGM12) | bit(CS12); //CTC mode; clkTC1 = 62500
  OCR1A = 62500;// 1-sec time delay
  TCNT1 = 0x0000;
}

void loop() 
{
  
}

3. Same as Step-2; but, the togging interval could be dynamically changed by varying the content of OCR1A Register.

BTW: The 'theory of operation of CTC Mode' needs to be clearly understood and then the users' objectives are to be accurately specified; only then, the coding will be a straight forward task.

intzero.png

Hi gfvalvo,
is not a problem related to the use of the input capture mode instead of external interrupt. If you have a solution to my problem let me know.

Hi GolamMostafa,
i do not want to achieve none of your proposal. I need to achieve a pulse on OC1A any time there is an edge on the external trigger input.
The pulse on OC1A shall have a delay (for example 500us) from the trigger event and a width (for example 500us). After the pulse finish a new trigger event can arrive to trigger a new pulse on OC1A.
Please check my code to understand well what i need.

Thanks

But, on the next cycle thi is not true again and i have the OC1A set at the timer start and reset after 500us

I think that happens because on the "next cycle" (presumably, you mean next input trigger), the initial timer configuration from setup() has not been recreated. At the moment I don't have the means to test this theory, though.

Try this:

if(isr==2) {TCCR1B=0; TCNT1=0; TCCR1A=0;} //stop the timer

ekjk:
is not a problem related to the use of the input capture mode instead of external interrupt. If you have a solution to my problem let me know.

Basically, use Input Capture mode to capture the counter’s value and trigger an interrupt at the moment your input transitions.

In the capture ISR use the captured timer value plus a small delta to set up an Output Compare event that sets OC1A or OC1B on match and triggers an interrupt.

When the match interrupt fires, use the output match value plus the required pulse width to set up another Output Compare event that clears OC1A or OC1B on match and triggers an interrupt.

When this final interrupt fires, clean things up and set up for the next transition of your trigger input.

Ok thanks to all, i'll follow your suggestions and i'll let you know.

ekjk:
The pulse on OC1A shall have a delay (for example 500us) from the trigger event and a width (for example 500us). After the pulse finish a new trigger event can arrive to trigger a new pulse on OC1A.

Tihs may be of use to you. You provide the delay, the special timer setup does the pulse width.

.02

Hi,
i've done some trials and the nearest solution is this

void SetTimer()
{
  cli(); // disable global interrupts
  //Initialize TIMER1 for delay, input capture and compare
  TIMSK1 |= (1 << ICIE1) | (1 << TOIE1) | (1 << OCIE1A); //Enable input capture
  TCCR1B |= (1 << ICNC1); //Enable input capture noise canceller
  TCCR1B &= ~(1 << ICES1); //Enable input capture on falling edge
  TCNT1 = 0;
  TCCR1B |= (1 << CS11);// normal mode
  isr = 0;
  sei();// enable global interrupts
}
void ResetTimer()
{
  cli(); // disable global interrupts
  TCCR1A = 0; //Clear TCCR1A
  TCCR1B = 0; //Clear TCCR1B 
  TIMSK1 = 0; //Clear TIMSK1
  TIFR1 = 1; //Clear interrupt flag
  timer_set = false;
  sei();// enable global interrupts
}


ISR(TIMER1_CAPT_vect) // INPUT SIGNAL DETECTION on falling edge
{
  TCNT1 = 0;
  OCR1A=100;
  TCCR1A = (1 << COM1A1) | (1 << COM1A0); //set on compare match
  isr = 1;
}


ISR (TIMER1_COMPA_vect) // DELAY AND WIDTH GENERATION
{
  if (isr == 1)
  {
    TCNT1 = 0;
    OCR1A+=100;
    TCCR1A = (1 << COM1A1); //clear on compare match
    isr++;
  }
  if (isr == 2)
  {
    isr++;
  }
}

ResetTimer() and SetTimer() are called once during the setup().

Now the code is working but i see an output flicker on the pulse waveform: at 1 second (more or less) interval the pulse disappears for a more or less 500ms and then appears again. this behavior is cycling.

:o :o :o :o

That code doesn't compile. If you want help, post a complete code that does.

@OP

Do you think that the following timing diagram has adequately contained the objectives that you want to achieve using TC1? If so, I will try to convert it into codes with due respect to the below-stated constraints and see what the outcomes are.
pulse1.png

1. We must have clear idea about the minimum and maximum frequency of the occurrence of the external 'triggering rising edge' at DPin-2 of the UNO. If we take the example of your 2500 us (delay and then HIGH) timing, the separation time (t2 - t1) between the two rising edges of the trigger signal must be greater than 1 ms (2500 us = 1 ms). This is to say that the frequency of the trigger signal is to be less than 1 kHz.

2. The 'variable delay time + variable HIGH Period' of the OC1A signal at DPin-9 must always be less than 't2 - t1 '.

3. So please, specify the minimum and maximum separation times of the rising edges of the trigger signal at DPin-2 in order to help in coding.

pulse1.png

gfvalvo:
That code doesn't compile. If you want help, post a complete code that does.

Here the working code

volatile byte isr;

void setup()
{
  pinMode(8, INPUT);  
  pinMode(9, OUTPUT);
  ResetTimer();
  SetTimer();
}

void SetTimer()
{
  cli(); // disable global interrupts
  //Initialize TIMER1 for delay, input capture and compare
  TIMSK1 |= (1 << ICIE1) | (1 << TOIE1) | (1 << OCIE1A); //Enable input capture
  TCCR1B |= (1 << ICNC1); //Enable input capture noise canceller
  TCCR1B &= ~(1 << ICES1); //Enable input capture on falling edge
  TCNT1 = 0;
  TCCR1B |= (1 << CS11);// normal mode
  isr = 0;
  sei();// enable global interrupts
}
void ResetTimer()
{
  cli(); // disable global interrupts
  TCCR1A = 0; //Clear TCCR1A
  TCCR1B = 0; //Clear TCCR1B 
  TIMSK1 = 0; //Clear TIMSK1
  TIFR1 = 1; //Clear interrupt flag
  sei();// enable global interrupts
}


ISR(TIMER1_CAPT_vect) // INPUT SIGNAL DETECTION on falling edge
{
  TCNT1 = 0;
  OCR1A=100;
  TCCR1A = (1 << COM1A1) | (1 << COM1A0); //set on compare match
  isr = 1;
}


ISR (TIMER1_COMPA_vect) // DELAY AND WIDTH GENERATION
{
  if (isr == 1)
  {
    TCNT1 = 0;
    OCR1A+=100;
    TCCR1A = (1 << COM1A1); //clear on compare match
    isr++;
  }
  if (isr == 2)
  {
    isr++;
  }
}

void loop()
{}

GolamMostafa:
@OP

Do you think that the following timing diagram has adequately contained the objectives that you want to achieve using TC1? If so, I will try to convert it into codes with due respect to the below-stated constraints and see what the outcomes are.
pulse1.png

1. We must have clear idea about the minimum and maximum frequency of the occurrence of the external 'triggering rising edge' at DPin-2 of the UNO. If we take the example of your 2500 us (delay and then HIGH) timing, the separation time (t2 - t1) between the two rising edges of the trigger signal must be greater than 1 ms (2500 us = 1 ms). This is to say that the frequency of the trigger signal is to be less than 1 kHz.

2. The 'variable delay time + variable HIGH Period' of the OC1A signal at DPin-9 must always be less than 't2 - t1 '.

3. So please, specify the minimum and maximum separation times of the rising edges of the trigger signal at DPin-2 in order to help in coding.

HI GolamMostafa,
the time sequence you have drawn is correct and i agree with all your considerations; the min and max input signal frequency will be 10Hz-600Hz.
In the code i posted previously i use the timer 1 in capture mode, but this is not a problem or a limit.
The delay at high frequency of the input signal shall be little, like 50us maximum. The pulse width shall be 500us at any input signal frequency.
Thanks

Not a fan of constantly setting counter value to 0 like you are. Also, given your timing requirements, I think you can use pre-scaler = 1. Finally, no need to turn off interrupts once you’ve stopped the timer. The state machine code below compiles but is untested.

#include "Arduino.h"

enum State
  : uint8_t {WAITING_FOR_INPUT, WAITING_FOR_RISING, WAITING_FOR_FALLING
};

const uint16_t pulseWidth = 8000;     // 500us at 16MHz clock
const uint16_t waitTime = 160;        // 10us at 16MHz clock

volatile State current_State = WAITING_FOR_INPUT;

void setup() {
  pinMode(8, INPUT);
  pinMode(9, OUTPUT);
  digitalWrite(9, 0);

  // Set up Timer 1
  TCCR1B = 0;             // no need to disable interrupts, this stops the timer
  TCCR1A = 0;
  TCCR1B |= (1 << ICNC1);    //Enable input capture noise canceller
  TCCR1B &= ~(1 << ICES1);   //Enable input capture on falling edge
  TIFR1 = (1 << TOV1) | (1 << OCF1A) | (1 << OCF1B) | (1 << ICF1); // clear all  flags
  TIMSK1 = (1 << ICIE1);     // Enable capture interrupt
  TCCR1B |= (1 << CS10);      // start timer pre-scaler = 1
}



void loop() {
}

ISR(TIMER1_CAPT_vect) {
  uint16_t capturedValue;
  TIFR1 = (1 << TOV1) | (1 << OCF1A) | (1 << OCF1B) | (1 << ICF1); // clear all  flags
  if (current_State == WAITING_FOR_INPUT) {
    capturedValue = ICR1;
    OCR1A = capturedValue + waitTime;   // set time for rising edge
    TCCR1A |= (1 << COM1A1) | (1 << COM1A0); // output rising edge on compare match
    TIMSK1 = (1 << OCIE1A);     // Enable match interrupt
    current_State = WAITING_FOR_RISING;
  }
}

ISR (TIMER1_COMPA_vect) {
  TIFR1 = (1 << TOV1) | (1 << OCF1A) | (1 << OCF1B) | (1 << ICF1); // clear all  flags
  switch (current_State) {
    case WAITING_FOR_RISING:
      OCR1A += pulseWidth;
      TCCR1A &= ~(1 << COM1A0);          // output falling edge on compare match
      current_State = WAITING_FOR_FALLING;
      break;

    case WAITING_FOR_FALLING:
      TCCR1A &= ~(1 << COM1A1);          // leave output LOW
      TIMSK1 = (1 << ICIE1);     // Enable capture interrupt
      current_State = WAITING_FOR_INPUT;  // set for next input pulse
      break;

    default:
      break;
  }
}

ekjk:
The delay at high frequency of the input signal shall be little, like 50us maximum.

What is this high frequency range in the domain of 10 Hz - 600 Hz?

GolamMostafa:
What is this high frequency range in the domain of 10 Hz - 600 Hz?

I don't underestand your question. Anyway the delay of 50us at 600Hz is little as you ca not take into account.

gfvalvo:
Not a fan of constantly setting counter value to 0 like you are. Also, given your timing requirements, I think you can use pre-scaler = 1. Finally, no need to turn off interrupts once you’ve stopped the timer. The state machine code below compiles but is untested.

#include "Arduino.h"

enum State
  : uint8_t {WAITING_FOR_INPUT, WAITING_FOR_RISING, WAITING_FOR_FALLING
};

const uint16_t pulseWidth = 8000;    // 500us at 16MHz clock
const uint16_t waitTime = 160;        // 10us at 16MHz clock

volatile State current_State = WAITING_FOR_INPUT;

void setup() {
  pinMode(8, INPUT);
  pinMode(9, OUTPUT);
  digitalWrite(9, 0);

// Set up Timer 1
  TCCR1B = 0;            // no need to disable interrupts, this stops the timer
  TCCR1A = 0;
  TCCR1B |= (1 << ICNC1);    //Enable input capture noise canceller
  TCCR1B &= ~(1 << ICES1);  //Enable input capture on falling edge
  TIFR1 = (1 << TOV1) | (1 << OCF1A) | (1 << OCF1B) | (1 << ICF1); // clear all  flags
  TIMSK1 = (1 << ICIE1);    // Enable capture interrupt
  TCCR1B |= (1 << CS10);      // start timer pre-scaler = 1
}

void loop() {
}

ISR(TIMER1_CAPT_vect) {
  uint16_t capturedValue;
  TIFR1 = (1 << TOV1) | (1 << OCF1A) | (1 << OCF1B) | (1 << ICF1); // clear all  flags
  if (current_State == WAITING_FOR_INPUT) {
    capturedValue = ICR1;
    OCR1A = capturedValue + waitTime;  // set time for rising edge
    TCCR1A |= (1 << COM1A1) | (1 << COM1A0); // output rising edge on compare match
    TIMSK1 = (1 << OCIE1A);    // Enable match interrupt
    current_State = WAITING_FOR_RISING;
  }
}

ISR (TIMER1_COMPA_vect) {
  TIFR1 = (1 << TOV1) | (1 << OCF1A) | (1 << OCF1B) | (1 << ICF1); // clear all  flags
  switch (current_State) {
    case WAITING_FOR_RISING:
      OCR1A += pulseWidth;
      TCCR1A &= ~(1 << COM1A0);          // output falling edge on compare match
      current_State = WAITING_FOR_FALLING;
      break;

case WAITING_FOR_FALLING:
      TCCR1A &= ~(1 << COM1A1);          // leave output LOW
      TIMSK1 = (1 << ICIE1);    // Enable capture interrupt
      current_State = WAITING_FOR_INPUT;  // set for next input pulse
      break;

default:
      break;
  }
}

Thanks. I'll try it and let you know.

ekjk:
the time sequence you have drawn is correct and i agree with all your considerations; the min and max input signal frequency will be 10Hz-600Hz.

The delay at high frequency of the input signal shall be little, like 50us maximum. The pulse width shall be 500us at any input signal frequency.

The range of the trigger signal is: 10 Hz - 600 Hz. When you say 'delay at high frequency' as in the above quote, then that high frequency begins from where. Is it 500 Hz and above or what?

GolamMostafa:
The range of the trigger signal is: 10 Hz - 600 Hz. When you say 'delay at high frequency' as in the above quote, then that high frequency begins from where. Is it 500 Hz and above or what?

I mean, high frequency is 600Hz maximum. so the delay at 600Hz shall be 50us maximum and the pulse width 500us, so the total required time is not greater than the minimum time period (1/600). At low freq (e.g. 20Hz) the delay could be maximum 2ms. The range of variability of the delay should be under my control and whithin the mix-max time period of input signal.

ekjk:
Thanks. I'll try it and let you know.

Thanks very much to GolamMostafa.
Your code works great.