Weird problem with ATmega328p (Uno) timer2

Weird problem with ATmega328p (Uno) timer2.

I'm using timer 1 to switch on a LED then trigger timer 2 to switch it off again after a certain elapsed period. In reality, it is much more complicated that that but I have reduced the core problem to this for demonstration purposes.

It appears to fail with at least 2 prescaler settings, that is /256 and /1024 of timer 2. With some lower prescaler settings, it functions OK. The failure case is that the timer 2 ISR is never entered with these higher prescaler values. Obviously, when the prescaler division value is increased, the compare output value (OCR2A) must be reduced correspondingly to maintain the same period and clearly the timer2 ISR must have completed before the next cycle of timer1.
So timer1 runs with a 4ms period and sets up timer2 for a 1ms period. This works if the prescaler is /128 and OCR2A is set to ~62 and the ISR is called correctly. I've chosen 1ms to be well within the 4ms period of timer1 so there is no clash. If, however, I select a prescaler of /256 and an OCR2A value of 31 then it fails in that the ISR is never entered although the elapsed time period should be the same.

I'm probably missing something very basic. Here is the code sample. For anyone who wishes to try it, simply swap the comments on the three marked lines in the timer 1 ISR.

const uint8_t led = 13  ;  // was 13
volatile uint32_t isrEntered = 0 ;

ISR(TIMER2_COMPA_vect) {
  // switches led off and terminates itself.
  TCCR2B = 0 ;  // stop timer 2
  digitalWrite( led , LOW ) ;
  isrEntered++ ;
}


ISR(TIMER1_COMPA_vect) {

  // runs every 4 ms
  // set up timer2 to trigger its ISR after a defined period

  TCCR2A = WGM21 ;  // CTC

  /* this fails */
  /*
  OCR2A = 31 ;     // 62 = ~1ms with ps = /256
  TCNT2 = 0 ;
  TCCR2B = (1 << CS21) | (1 << CS22)  ; // prescaler /256
  */
  
  /* this works */
   
    OCR2A = 62 ;     // 62 = ~1ms with ps = /256
    TCNT2 = 0 ;
    TCCR2B = (1 << CS20) | (1 << CS22)  ; // prescaler /128
   

  digitalWrite( led , HIGH ) ;
}


void setup() {
  Serial.begin( 115200 ) ;

  pinMode( led, OUTPUT ) ;


  //deep clean timer 2
  TCCR2A = 0 ;
  TCCR2B = 0 ;
  TCNT2 = 0;

  // timer2 set up - but started by timer1
  TCCR2A = WGM21 ; // CTC
  OCR2A = 255 ;
  TIMSK2 = (1 << OCIE2A);  // enable interrupts


  //deep clean timer 1
  TCCR1A = 0 ;
  TCCR1B = 0 ;
  TCNT1 = 0;

  // timer1 setup CTC, 4ms Period, interrupts
  TCCR1B = (1 << WGM12) | (1 << CS12) ; // CTC ps= /256
  OCR1A = 250 ; // 4ms with ps= /256
  TIMSK1 = (1 << OCIE1A);  // enable interrupts
}

void loop() {

  static uint32_t lastRunAtMs = 0 ;
  if ( millis() - lastRunAtMs > 1000 ) {  // 1 seconds

    noInterrupts() ;
    uint32_t isrEnteredCopy = isrEntered  ;
    isrEntered = 0 ;
    interrupts() ;

    Serial.print( "Timer 2 ISR entered in this timeslice = " ) ;
    Serial.println( isrEnteredCopy ) ;
    lastRunAtMs = millis() ;
  }

}

Did you mean this:
TCCR2A = 1 << WGM21 ; // CTC
or this:
TCCR2A = _BV(WGM21) ; // CTC

1 Like

Yes of course. That was it. Well done. I knew it had to be a basic error but was staring at it too long to see it.

Bingo! That fixes the bug in the TIMER1_COMPA ISR :slight_smile:

One more note: The table in my Mega controller data sheet replaces WGM20..22 by WGM0..2! Another pitfall :frowning:

Also the syntax WGM21:0 is questionable. Does it mean WGM00..21 or WGM20..21?

I observe few anomalies in your codes (the codes does not agree with description) which I wish to pinpoint based on the answers of the following queries: (When I run your corrected sketch with TCCR2A = 1 << WGM21, I see no activity (ON/OFF) of L; however, there is this message: "Timer 2 ISR entered in this timeslice = 250" on the Serial Monitor.)

1. You want to run TC1 at 250 Hz (4 ms period) CTC Mode-4. Correct?
2. When Compared Match occurs for TC1, you want to turn on L (built-in LED of Arduino). Correct?

3. When Compared Match occurs for TC1, you want to turn on TC2 at 1000 Hz (1 ms period) CTC Mode-4 (Fig-1). Correct?

wave1
Figure-1:

4. Once TC2 has started, then how long you want to wait before turning off L?

It's turned off with the first T2 interrupt.

Once TC2 has started by TC1, after that TC1 and TC2 are not synchronized. They are totally free-running square wave oscillators.

Let TC1 allow to turn on L at it first compare match interrupt and also to turn on TC2. Let allow TC2 to be running for 1-sec and then allow TC2 to turn off L during its last clock cycle.

Please start a new topic if you want to discuss something different from the code of this topic.

The OP says that the LED will go off after certain amount of time; but, the LED is not going OFF. Therefore, I can always bring this incident to the kind attention of the OP. It is up to him to care it or not!

There is RONG calculation in the above for OCR1A:
1. For 4 ms period, the frequency is: 250 Hz (1000/4).
2. Therefore, the value of OCR1A is 124 and not 250 with prescaler 256!

f = 16x10^6/(2N(1+OCR1A))
==> OCR1A + 1 = 16x10^6/(2Nf)    //N= 256, f = 250 Hz
==> OCR1A + 1 = 16000000/(2x256x250)   //
==> OCR1A + 1 = 125                                
==> OCR1A = 125 - 1
==> OCR1A = 124

The above calculation is also RONG!
For exactly 1 ms period of the CTC wave, the value of prescaler (N ) and OCR2A should
64 and 124 respectively and not 128 and 62?

With N = 128 and OCR2A = 62, we get the following fractional frequency which is no good!
f = 992.06 Hz

Note: There is a good chance for two waves to remain in synchronism and locked if one is multiples of the other.

Of course it goes OFF with the first T2 interrupt. T2 will continue to run but the LED is only turned ON on the next T1 interrupt.

You had observed that behavior if you run the code and watch the LED.

Thanks once again to all for for looking into this.
@GolamMostafa
OK. Thanks for the additional detailed analysis and discussion of the code sample provided in the OP. Obviously it was a basic error resulting in a timer overrun because the wave mode erroneously selected did not respect the selected match value in OCR2A. However, that code was lifted out of a much larger development and then prepared as a minimal example which demonstrates the main problem and ,hence, some of the comments may not have been well maintained to reflect later changes in the code. I'm sorry if these were a distraction.
It will be used to adjust the brightness of a vacuum fluorescent display which is multiplexed at ~4ms (that is approximately). Some time into the ~4ms period, depending on the output of an LDR, the display will be blanked and that is the purpose of starting a second timer. It is not a high precision application.
There may also be other ways of achieving the same thing, even with just one timer, but this ( now ) appears to work as intended.

@DrDiettrich
I guess the data sheets are rather prone to such copy paste type errors probably because when these are updated, say to reflect new corporate identity standards such as new layouts, fonts, colours etc., the work is done not by engineers but by office assistants.

@6v6gt

Not visible. To make it visible, the timing and frequency of the signals needs to be adjusted. If I could know how much later the LED will go OFF, I would try to adjust the codes to make the LED's activity visible.

Congratulation for the good job!

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.