Timer1 Interrupts and external interrupts. weird register behavior?

Hi all!

I've been digging deeper into the Atmega registers lately and I'm having a problem setting up my interrupts.

I'm building a frequency detector (guitar in => volt/octave out via R-2R 10bit DAC on 10 pins, 6 from PORTC and 4 from PORTB, with some other cool features). Using Arduino UNO for now but will migrate to the standalone MCU chip.

To measure the period of my signal (frequency between 40Hz and 1400Hz), I want to use pin D8 (also called PB0 or ICP1) to detect a rising edge (from an external analog comparator) and launch an interrupt (reading the buffered Timer1 and reseting it). Also I want to use D2 and D3 (also called PD2 and PD3 or INT0 and INT1) to do other stuff. INT0 would detect a falling edge on the comparator and INT1 would detect a rising edge from another part of the circuit in order to run some computation and track the pitch of the guitar, in an optimized way.

If I strip all my program from it's actual content and just keep the bare minimum to set up the IN/OUTs, the timers and the interrupts, while monitoring an LED on pin D1 (also called PD1 or TX) to check if everything is OK, I can clearly see it isn't.

What I figured is basically two things:

1°) When I enable INT0 and INT1 via the EIMSK register, my Timer1 Compare A interrupt doesn't trigger. My Timer1 Input Capture Interrupt doesn't either.

2°) When I disable Timer Output Compare B Interrupt or Timer 1 Overflow Interrupt via the TIMSK1 register, my Timer1 Compare A interrupt doesn't trigger. My Timer1 Input Capture Interrupt doesn't either. This is less of a problem but I wonder why.

It is to be noted that I know there would be an easier way to code this by poling the inputs and using micros() rather than using interrupts. First of all I'm going to do some "heavy" computing in parallel and second of all I want to learn! :slight_smile:

Is this behavior normal?
Thanks for reading here is my code.

void setup() {
  pinMode(1,OUTPUT);
  pinMode(2,INPUT_PULLUP);
  pinMode(3,INPUT_PULLUP);
  pinMode(8,INPUT_PULLUP);
  
  
  //EIMSK |= (1 << INT0);//these two bits enable external interrupts and are causing my first problem
  //EIMSK |= (1 << INT1);
  
  TCCR1A &= (0 << COM1A1); //these 2 bits disable OC1A when not in PWM mode
  TCCR1A &= (0 << COM1A0); //
  TCCR1A &= (0 << COM1B1); //these 2 bits disable OC1B when not in PWM mode
  TCCR1A &= (0 << COM1B0); //
  
  TCCR1A |= (1 << WGM11); // these 4 bits set the Timer1 to FAST PWM mode with OCR1A as TOP value. For some reason right now this is the only mode I could use Compare Out A interrupt in.
  TCCR1A |= (1 << WGM10); // 
  TCCR1B |= (1 << WGM13); // 
  TCCR1B |= (1 << WGM12); //
  
  TIMSK1 |= (1 << OCIE1A); ////enable output compare A Interrupt
  //TIMSK1 &= (0 << OCIE1B); //disable output compare B Interrupt. Cause of my 2nd problem
  TIMSK1 |= (1 << ICIE1); //enable input capture interrupt
  //TIMSK1 &= (0 << TOIE1); //disable overflow interrupt. Cause of my 2nd problem
  TCCR1B |= (1 << ICES1); //input capture on rising edge (or comparator out i guess)
  OCR1A = 65000;

}

//HERE ARE MY ISRs, THIS IS WHERE I USE THE LED TO CHECK IF IT WORKS. 

ISR (TIMER1_CAPT_vect) {
  digitalWrite(1,1);
}

ISR (TIMER1_COMPA_vect) {
  //digitalWrite(1,1);
}

ISR (TIMER1_OVF_vect) {
  //digitalWrite(1,1);
}

ISR (INT0_vect) {
  //digitalWrite(1,1);
}

ISR (INT1_vect) {
  //digitalWrite(1,1);
}

void loop() {
}

The ATmega is a mature product, so be assured that ALL the interrupts mentioned in the data sheet work as advertised, if used properly. The Input Capture mode of Timer1, used to time external events, has nothing to do with the external interrupts INT0 and INT1.

Without knowing what is connected to your Uno, it is impossible to debug that program.

  digitalWrite(1,1);

Not a good idea to use the serial port pin for anything other than program uploading or serial monitor.

There are easier ways to do almost everything you have mentioned in your post. Did you know that the ATmegas have a built in comparator?

Nick Gammon has a great tutorial on timers and timer interrupts.

Thanks for the reply!

I know there are easier ways but I want to step up my knowledge of the Atmega. not using poling and micros() also allow me to compute other stuff fast enough for my design requirements.

I know there is an internal comparator but I didn't like the fact that I can't attach different interrupts for respectively rising and falling edges. (thus I can't do some analog hysteresis, nor can i do what I intended to do with the INT0 interrupt).

I solved the first question of my initial post: I didn't declare the inputs with PULLUP resistors during my tests.(unlike the code i posted). the INT0 and INT1 were probably floating and causing problems. Now it works!

I guess I could write in the comparator rising edge interrupt to set the interrupt on falling edge and other way around.
Still using an external LM311 is usefull in this case, my guitar is heavily amplified (clipping +/- 12v) so the open collector output as well as the access to the emitter pin are quite usefull to drive the Atmega with 0v to 5v without using shottky protection diodes. I'm also using the LM311 strobe pin.

2°) When I disable Timer Output Compare B Interrupt or Timer 1 Overflow Interrupt via the TIMSK1 register, my Timer1 Compare A interrupt doesn't trigger. My Timer1 Input Capture Interrupt doesn't either. This is less of a problem but I wonder why.

 //TIMSK1 &= (0 << OCIE1B); //disable output compare B Interrupt. Cause of my 2nd proble
  //TIMSK1 &= (0 << TOIE1); //disable overflow interrupt. Cause of my 2nd problem

Your syntax for clearing a bit in a register is incorrect, and actually clears the entire register.

You want to use

  TIMSK1 &= ~(1 << OCIE1B); //disable output compare B Interrupt. Cause of my 2nd proble
  TIMSK1 &= ~(1 << TOIE1); //disable overflow interrupt. Cause of my 2nd problem
void setup() {
 Serial.begin(115200);
 byte value = B11111111;
 Serial.println(value,BIN); 
 value &= ~(1<<2);//clears one bit
 Serial.println(value,BIN);
 value &= (0<<4);//clears all
 Serial.println(value,BIN);
}
void loop() {}

See this reference

You can always use the Arduino functions bitSet() and bitClear()

Thank you very much!
I am new to bit manipulation and am obviously still learning.
What amazes me is that this very wrong syntax I'm using for clearing a bit didn't disturb my perfectly working program i wrote last week for another project ahah, must be a miracle.
I'll dig into it.