Setting Timer1 Overflow ISR crashes program without defined ISR

Recently I helped someone on a sketch involving configuring Timer1 on the Atmega328. In that sketch I enable a Timer1 Overflow Interrupt, but then did not actually need that ISR, so did not specify the ISR itself. No harm done, everything worked fine.
Then I added printing for debugging purpose and noticed that printing output stopped when Timer1 overflow occurred. The rest of the functions (ISR driven input capture and PWM generation) of the sketch continue to work.
I found that the lacking definition of the ISR was the problem. Where I always assumed you don't need to define the ISR. You can just enable the ISR flag to be set up and then check if that ISR flag is set yourself and clear it afterwards. That assumption may be wrong?

I simplified it to below small demonstrator sketch

This sketch will start Timer1 and let it overflow every 4 seconds, while setting an interrupt flag when it reaches it's TOP value as set by OCR1A.
The sketch crashes and resets the Atmega328 after 4 seconds and starts again.

void setup(){
  Serial.begin(57600);
// configure timer to Fast PWM mode 15 (Table 15-5 datasheet page 109)
// TOP is defined by OCR1A and TOV1 flag set at TOP
// Overflow after 4 seconds.
  
  TCCR1A = 1 << WGM11 | 1 << WGM10;
  TCCR1B = 1 << WGM13 | 1 << WGM12 | 1 << CS12 | 1 << CS10;
  TIMSK1 = 1 << TOIE1; // setting a timer1 overflow interrupt
  OCR1A = 62499;
}

void loop() {
  static uint8_t ticks;
  ticks++;
  delay(200);
  Serial.println(ticks);
}

//ISR (TIMER1_OVF_vect){} // uncomment this empty ISR and everything works fine.

When defining the empty ISR, by uncommenting it, everything works fine.

Why does the program crash and reset?

If you enable the OVF ISR the processor will jump to the address set in TIMER1_OVF_vect if the timer overflows. But if that vector isn't set properly it crashes/resets. The statement

ISR (TIMER1_OVF_vect){}

sets this vector properly - and if its only to an RETI instruction.

1 Like

If the ISR is not defined for appropriate interrupt, compiler places the default service routine
__bad_interrupt. It contains a jump to the address 0 aka reset vector.

Any ATmega has fixed addresses for each interrupt and each one is defined as jump to specific address. Example:

00000000 <__vectors>:
       0:	0c 94 84 09 	jmp	0x1308	; 0x1308 <__dtors_end>
       4:	0c 94 ae 09 	jmp	0x135c	; 0x135c <__bad_interrupt>
       8:	0c 94 ae 09 	jmp	0x135c	; 0x135c <__bad_interrupt>
       c:	0c 94 c5 35 	jmp	0x6b8a	; 0x6b8a <__vector_3>
      10:	0c 94 ae 09 	jmp	0x135c	; 0x135c <__bad_interrupt>
      14:	0c 94 ea 35 	jmp	0x6bd4	; 0x6bd4 <__vector_5>

The _vector_3 is defined ISR in this case.

2 Likes

In that case, just check the overflow flag bit (TOV1 in register TIFR1) in software. Don't enable the interrupt, then you don't need to define the ISR.

2 Likes

Thanks all for the prompt and valid input. All clear now.

I knew a) I had used the approach of just checking the TOV1 bit before and also thought b) I did not define the associated ISR in that code. I found my old code and it seems a) was true and b) was not true.
I had defined the associated ISR to increase a counter byte.

I will forget about why the sketch I helped someone with did continue to run for 50%. Timer1 and the associated Input Capture ISR continued to work, but the main loop no more.

[EDIT] I now realize where my thought of not needing the ISR came from. The modern Attinies (e.g. Attiny412) seem to not need the ISR. At least thats where I have done some timer input capture by only checking if the CAPT bit in TCB INTerruptFLAGS register is set, without defining the ISR.

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