c++timerarduinointerruptpulse

Arduino Timer Interrupt: Why I'm getting a small unexpected pulse upon enabling interrupt?


I made a simple code to produce 2 output lines with the same frequency (e.g. 100Hz).

Once I pressed the button (tactile switch), the 1st output line should start producing pulse, while the 2nd one should lag behind by 25% of the 1st's period.

While I'm getting the frequency I wanted, when observing the output using oscilloscope, I noticed a small pulse right after interrupt activation.

Image of continuous reading Image of the start of reading

Is this noise? or just a normal behavior? Is there any way to get rid of this?

I tried zooming & measured this and got 1.8125us pulse width @4.75V. Zoomed image & measurement

I'm worried that if for example I tried using this to a sensitive pulse counting device, it might cause counting inaccuracy.

I thought that this might be just a noise caused by the tactile switch I'm using.

As a quick tweak, I tried putting a 1 second delay before interrupt activation after pressing the button. But the problem still persists.

Perhaps I'm missing something fundamental here?

Btw, I'm using Arduino Mega 2560. I got 2 pcs, and tried on both, but they have the same behavior.

This is the code I used (the 1 sec delay is still not applied):


int initialTime = 0;
double frequency = 100; //set my desired frequency
double period = 0, timerTicks = 0;
double duty = 0.5; //set duty cycle to 50%
double pulseGap = 0.25; //offset 2nd pulse by 25%

void setup() {
  pinMode(22, INPUT); //set digitalpin 22 as INPUT (tactile switch)

  noInterrupts();
  DDRE |= (1 << PE4); //set digitalpin 2 as OUTPUT
  DDRE |= (1 << PE3); //set digitalpin 5 as OUTPUT

  //enable CTC mode
  TCCR1A = 0;
  TCCR3A = 0;
  TCCR1B = 0;
  TCCR3B = 0;
  TCCR1B |= (1 << WGM12);
  TCCR3B |= (1 << WGM32);

  TCCR1B |= (1 << CS12); //timer1 initial prescaler = 256 (switch max Frequency from 16 MHz into 62.5 Khz)
  TCCR1B &= ~(1 << CS11);
  TCCR1B &= ~(1 << CS10);
  
  TCCR3B |= (1 << CS32); //timer3 prescaler = 256  (switch max Frequency from 16 MHz into 62.5 Khz)
  TCCR3B &= ~(1 << CS31);
  TCCR3B &= ~(1 << CS30);

  period = 1 / frequency;
  timerTicks = ((period * 16000000) / 256) - 1;

  OCR1A = timerTicks;               //the time where digitalPin2 will be HIGH
  OCR1B = timerTicks * duty;        //the time where digitalPin2 will be LOW
  OCR1C = timerTicks * pulseGap;    //the time where digitalPin5 will be HIGH

  OCR3A = timerTicks;
  OCR3B = timerTicks * (duty + pulseGap); //the time where digitalPin5 will be LOW

  //set & sync Timer1 & Timer3 to 0 
  TCNT1 = initialTime;
  TCNT3 = TCNT1;

  interrupts();
}

void loop() {
  //interrupts will be enabled once the tactile switch connected in @digitalPin22 is pressed
  if(digitalRead(22) == HIGH){
    TIMSK1 = (1 << OCIE1A) | (1 << OCIE1B) | (1 << OCIE1C);
    TIMSK3 = (1 << OCIE3B);
  }
}

ISR(TIMER1_COMPA_vect) {
  PORTE |= (1 << PE4); //toggle digitalPin2 = HIGH
}

ISR(TIMER1_COMPB_vect) {
  PORTE &= ~(1 << PE4); //toggle digitalPin2 = LOW
}

ISR(TIMER1_COMPC_vect) {
  PORTE |= (1 << PE3); //toggle digitalPin5 = HIGH
}

ISR(TIMER3_COMPB_vect) {
  PORTE &= ~(1 << PE3); //toggle digitalPin5 = LOW
}


After reflecting what @dimich pointed out below, this is now the updated code. I finally got rid of those pulse! I just reformed the code but what really solved the problem is resetting their interrupt flag bits under TIFRn.


int initialTime = 0, once = 0;
int frequency = 100; //set my desired frequency
int duty = 2; //set duty cycle to 50%
int pulseGap = 4; //offset 2nd pulse by 25%

void setup() {
  pinMode(22, INPUT); //set digitalpin 22 as INPUT (tactile switch)

  noInterrupts();
  DDRE |= _BV(PE4); //set digitalpin 2 as OUTPUT
  DDRE |= _BV(PE3); //set digitalpin 5 as OUTPUT

  //enable CTC mode
  TCCR1A = 0;
  TCCR3A = 0;
  TCCR1B = 0;
  TCCR3B = 0;
  TCCR1B = _BV(WGM12) | _BV(CS12); //set to CTC Mode & timer1 prescaler to 256
  TCCR3B = _BV(WGM32) | _BV(CS32); //set to CTC Mode & timer3 prescaler to 256

  OCR1A = (16000000 / (frequency * 256)) - 1;   //the time where digitalPin2 will be HIGH
  OCR1B = OCR1A / duty;        //the time where digitalPin2 will be LOW
  OCR1C = OCR1A / pulseGap;    //the time where digitalPin5 will be HIGH

  OCR3A = OCR1A;
  OCR3B = (OCR1A / pulseGap) * 3; //the time where digitalPin5 will be LOW

  //set & sync Timer1 & Timer3 to 0 
  TCNT1 = initialTime;
  TCNT3 = TCNT1;

  interrupts();
}

void loop() {
  //interrupts will be enabled once the tactile switch connected in @digitalPin22 is pressed
  if(digitalRead(22) == HIGH && once == 0){

    noInterrupts();
    TIFR1 = _BV(OCF1A) | _BV(OCF1B) | _BV(OCF1C);
    TIFR3 = _BV(OCF3A) | _BV(OCF3B); 
    TIMSK1 = _BV(OCIE1A) | _BV(OCIE1B) | _BV(OCIE1C);
    TIMSK3 = _BV(OCIE3B);
    TCNT1 = initialTime;
    TCNT3 = TCNT1;
    interrupts();
    
    once = 1;
  }
}

ISR(TIMER1_COMPA_vect) {
  PORTE |= (1 << PE4); //set digitalPin2 = HIGH
}

ISR(TIMER1_COMPB_vect) {
  PORTE &= ~(1 << PE4); //set digitalPin2 = LOW
}

ISR(TIMER1_COMPC_vect) {
  PORTE |= (1 << PE3); //set digitalPin5 = HIGH
}

ISR(TIMER3_COMPB_vect) {
  PORTE &= ~(1 << PE3); //set digitalPin5 = LOW
}



Solution

  • There are several problems in your code. The most probable reason of observed behavior is unmasking interrups while compare match flags are already set in TIFRn registers. These flags are set by compare match event in the past even if corresponding interrupts are masked. So when you unmask interrupt, ISR is invoked immediately.

    To avoid such behaviour you should reset interrupt flags while timers are stopped (if compare match occured in the past and ISR was masked), then unmask interrupts. Then wait for external event and start timers. You can reset flags by writing 1 to corresponding OCFnX bit in TIFRn.

    Another potential issue is setting CSnn bits. In your current code there is only one CSnn bit to set, so it should work correctly. But if prescaler requires more than one CSnn bits set, you should set all them at once. While these bits are 0, timer is stopped. If you set these bits sequentially, you start timer and switch prescaller settings while timer is already running.

    To set bits atomically you can either write all bits at once, e.g.:

    TCCR1B = _BV(WGM12) | _BV(CS12) | _BV(CS11);
    

    or, if you need to preserve other bits, stop timer and run it again with new CSnn:

    TCCR1B &= ~(_BV(CS12) | _BV(CS11) | _BV(CS10);
    ...
    TCCR1B |= _BV(CS12) | _BV(CS11);
    

    Of course, you can switch prescaler bits atomically without stopping a timer by explicit read-modify-write, but it makes almost no practical sense:

    TCCR1B = (TCCR1B & ~(_BV(CS12) | _BV(CS11) | _BV(CS10)) | _BV(CS12) | _BV(CS11);
    

    Another potential issue is prescaler module shared by timers. See part 18 Timer/Counter 0, 1, 3, 4, and 5 Prescaler in the datasheet