Timer2 Compare Match overflowing

Hi all,

I am running an ATMega328P, set to use internal 8MHz clock, with timer2 asynchronously clocked via an external 32.786kHz crystal (with 18pF caps). With the following code, I am aiming to toggle an LED every second. With the clock set to 128Hz and OCR2A set to 127 (zero referenced) this should work. But it only toggles every 2s. In fact, it doesnt matter what I change OCR2A to, it always toggles at 2s. This makes me think it is interrupting on an overflow instead of compare match.

In the ISR, TCNT2 is set to zero, to effectively restart the counter. Could this be related to the issue?

As an alternative, I have tried commenting out TCNT2 = 0; and uncommenting TCCR2A = (1<<WGM21);. This should use CTC mode, which automatically clears the timer on interrupt). But then I get even stranger behavior - the LED is toggling very fast. It actually appears to be always on, but I have discovered that if I change the prescaler to 1024 (to slow the clock down to 32Hz), I can see it flashing maybe 10-15 times per second.

As an aside, it doesnt seem to make any difference if interrupts(); is commented out or not.

Ultimately I am trying to set up a timer to count around 80s, run some code, then go to sleep until another 80s is up. The 80s changes periodically.

Would appreciate any light you can shed!

// Global settings
#define LEDPin 10

// Global variables
volatile bool toggleLED = false;

// Interrupt triggered on timer2 compare match
ISR(TIMER2_COMPA_vect) {                          // Flashes LED approx (interrup triggers at 125Hz)
  TCNT2 = 0;
  // Set the flat to toggle the LED
  toggleLED = true;
}

void setup() {

  // Set up timer2
  ASSR = (1<<AS2);                          // Make Timer2 asynchronous - When AS2 is written to one, Timer/Counter2 is clocked from a crystal oscillator connected to the timer oscillator 1 (TOSC1) pin
  TCCR2A = 0;                               // Reset entire TCCR0A register
  TCCR2B = 0;                               // Reset entire TCCR0B register
  //TCCR2A = (1<<WGM21);                    // Clear Timer on Compare Match (CTC) Mode
  TCCR2B = (1<<CS22)|(1<<CS21)|(0<<CS20);   // Prescaler of 1024 = 32.768kHz/256 = 128 Hz
  OCR2A = 127;                              // Set compare register A to this value (128/128 = 1Hz)
  TIMSK2 = (1<<OCIE2A);                     // Enable compare match interrupt
  
  // Initialise variables
  pinMode(LEDPin, OUTPUT);                   

  // Enable interrupts
  //interrupts();
}

void loop() {
  
  // if flag set to toggle LED
  if (toggleLED == true){

    // Reset flag
    toggleLED = false;

    // Toggle LED
    digitalWrite (LEDPin, ! digitalRead (LEDPin));
  }
}

Too many wrong assumptions :frowning:

Why don't you use the right timer mode to blink the LED?

1 Like

It looks like the code should roughly work.

Interrupts are enabled when your sketch starts. You don't need to enable them.

You can get 8 seconds between interrupts (32 kHz w/1024 prescale and 256 counts).

It sound like you are just trying to test your interrupt rate to make sure your 128 Hz interrupt is happening at 128 Hz. I would use the overflow interrupt rather than the compare match interrupt. The overflow is after 256 counts so a toggle rate of every two seconds would be expected. That will eliminate any OCR2A issues.

Thanks for the response.

I want to use compare match, not overflow, because my application will require timing of variable periods, so I need to be able to adjust the compare match value. Just using overflow doesn’t actually solve my problem.

Any assistance would be appreciated. My program is going to the interrupt, but after 2 seconds instead of 1. Very confused.

Cheers,

DrDiettrich, not a very constructive comment. What are the wrong assumptions? What would be the correct timer mode?

Many. E.g. the prescaler is set to 1024, not 256.

Any one that automatically toggles the channel related output pin. See comment on signal generation in Normal Mode.

Here is the code I've used for years to get a 1 second interrupt from Timer2, driven by a 32 kHz crystal:

//
// initialize Timer2 as asynchronous 32768 Hz timing source
//

void timer2_init(void) {

  TCCR2A = 0;
  TCCR2B = 0;  //stop Timer 2
  TIMSK2 = 0; // disable Timer 2 interrupts
  ASSR = (1 << AS2); // select asynchronous operation of Timer2
  TCNT2 = 0; // clear Timer 2 counter
  TCCR2B = (1 << CS22) | (1 << CS20); // select prescaler 128 => 1 sec between each overflow

  while (ASSR & ((1 << TCN2UB) | (1 << TCR2BUB))); // wait for TCN2UB and TCR2BUB to be cleared

  TIFR2 = (1 << TOV2); // clear interrupt-flag
  TIMSK2 = (1 << TOIE2); // enable Timer2 overflow interrupt
}

Correct me if I am wrong but the prescaller is set to 256. CS20 is set to 0, not 1.

I’m just toggling and LED as a way to test my timing method. I don’t actually want a timer that toggles the output. I want a timer that runs some code after a certain (~80s) period.

Thanks, however this code uses the overflow method. I need to use the compare match as I want to be able to modify the period occasionally by changing the value of OCR2A.

Just count 80 of those 1 second overflows, or some other number, and take the action. In the meantime, you have the basis for a reasonably accurate RTC, as a freebie.

Here is the rest of it:

//******************************************************************
//  Timer2 Interrupt Service
//  32 kKz / 256 = 1 Hz with Timer2 prescaler 128
//  provides global tick timer and BCD ASCII Real Time Clock
//  no check for illegal values of RTC_buffer upon startup!

ISR (TIMER2_OVF_vect) {

// RTC function

    RTC_buf[7]++;    // increment second

     if (RTC_buf[7] > '9')
    {
      RTC_buf[7]='0'; // increment ten seconds
      RTC_buf[6]++;
      if ( RTC_buf[6] > '5')
      {
        RTC_buf[6]='0';
        RTC_buf[4]++;     // increment minutes
          if (RTC_buf[4] > '9')
          {
          RTC_buf[4]='0';
          RTC_buf[3]++;   // increment ten minutes

          if (RTC_buf[3] > '5')
          {
          RTC_buf[3]='0';
          RTC_buf[1]++;     // increment hours
          char b = RTC_buf[0]; // tens of hours, handle rollover at 19 or 23
            if ( ((b < '2') && (RTC_buf[1] > '9')) || ((b=='2') && (RTC_buf[1] > '3')) )
              {
            RTC_buf[1]='0';
            RTC_buf[0]++;   // increment ten hours and day number, if midnight rollover
            if (RTC_buf[0] > '2') {RTC_buf[0]='0'; dayno++;}
              }
          }
        }
      }
    }
}

Okay, the comment is misleading.

This can be done in CTC mode as well, without resetting the TCNT. Toggling the related output pin is an option.

I need to count fractions of seconds too, e.g. 83.26 seconds. I know the resolution of a 32Hz timer won’t be great but it’s close enough. A 1Hz interrupt wont be close enough though.

Yes, I tried CTC mode - in my original post I explained that the timer counted much faster than I expected as soon as CTC mode was enabled.

Have you decided what range of times do you need and what granularity? Like "75 to 85 seconds in 1/4-second intervals"?

It will likely be around 80-90s. Quarter second steps would be the minimum.

I can see where you’re going with this but I’m keen to resolve my original query rather than come up with different methods.

Can anyone see why the compare match is not working and why it runs too fast in CTC mode?

I don't think that your setting of TCNT2 = 0 in the ISR is actually happening.

The data sheet has this to say

When writing to one of the registers TCNT2, OCR2x, or TCCR2x, the value is transferred to a
temporary register, and latched after two positive edges on TOSC1. The user should not write a
new value before the contents of the temporary register have been transferred to its destination.
Each of the five mentioned registers has its individual temporary register, which means that e.g.
writing to TCNT2 does not disturb an OCR2x write in progress. The Asynchronous Status Register
(ASSR) indicates that a transfer to the destination register has taken place.

And this

Bit 4 – TCN2UB: Timer/Counter2 Update Busy
When Timer/Counter2 operates asynchronously and TCNT2 is written, this bit becomes set. When
TCNT2 has been updated from the temporary storage register, this bit is cleared by hardware. A logical
zero in this bit indicates that TCNT2 is ready to be updated with a new value.

Try this in the ISR

ISR(TIMER2_COMPA_vect) {                          // Flashes LED approx (interrup triggers at 125Hz)
while(TCN2UB != 0); //wait until update flag is cleared by hardware
  TCNT2 = 0;
  // Set the flat to toggle the LED
  toggleLED = true;
}

Sorry. I can't figure out an explanation for either behavior that doesn't involve bad hardware. I don't have an Arduino with a 32768 Hz crystal to test with.

Show your code.

Thanks for the suggestion! ..but it didnt work :frowning: The LED never came on, so I don't know if that means the TCN2UB never cleared...

I also tried while(OCR2AUB != 0); before writing to OCR2A, but same thing. LED didnt come on.

¯_(ツ)_/¯

This is my code in CTC mode. With 32.768kHz external clock, prescaler = 1024 and OCR2A = 127, I would expect the LED to toggle every 4s. But it looks like at least 8 times per second (hard to count at that speed).

// Global settings
#define LEDPin 10

// Global variables
volatile bool toggleLED = false;

// Interrupt triggered on timer2 compare match
ISR(TIMER2_COMPA_vect) {                          // Flashes LED approx (interrup triggers at 125Hz)
  
  // Set the flat to toggle the LED
  toggleLED = true;
}

void setup() {

  // Set up timer2
  ASSR = (1<<AS2);                          // Make Timer2 asynchronous - When AS2 is written to one, Timer/Counter2 is clocked from a crystal oscillator connected to the timer oscillator 1 (TOSC1) pin
  TCCR2A = 0;                               // Reset entire TCCR0A register
  TCCR2B = 0;                               // Reset entire TCCR0B register
  TCCR2A = (1<<WGM21);                      // Clear Timer on Compare Match (CTC) Mode
  TCCR2B = (1<<CS22)|(1<<CS21)|(1<<CS20);   // Prescaler of 1024 = 32.768kHz/1024 = 32 Hz
  OCR2A = 127;                              // Set compare register A to this value (32/128 = 0.25Hz)
  TIMSK2 = (1<<OCIE2A);                     // Enable compare match interrupt
  
  // Initialise variables
  pinMode(LEDPin, OUTPUT);                   
}

void loop() {
  
  // if flag set to toggle LED
  if (toggleLED == true){

    // Reset flag
    toggleLED = false;

    // Toggle LED
    digitalWrite (LEDPin, ! digitalRead (LEDPin));
  }
}