interrupt timer problem

Can someone tell me why the following code doesn't print "done"? This code illustrates the problem I'm having with a larger Arduino sketch.

volatile char done = 0;

ISR(TIMER1_COMPA_vect)
{
  done = 1;
}

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

   TIMSK1 = 1; // OCIE1A: Timer/Counter1, Output Compare A Match Interrupt Enable

   unsigned char oldSREG = SREG; // Save global interrupt flag
   cli();
   OCR1A = 1000;
   SREG = oldSREG;  // Restore global interrupt flag

   TCCR1A =
0b00; // WGM11:0 = 0, CTC (Top = OCR1A) (see TCCR1B)

   TCCR1B =
0b01 << 3 | // WGM13:2 = 1 CTC (Top = OCR1A) (see TCCR1A)
0b001; // CS12:0 = 1, clkI/O/1 (No prescaling)

  while (!done);

  Serial.println("done");
}

void loop() {
}

Maybe it's because you didn't turn interrupts back on.

Pete

The "SREG = oldSREG" statement re-enables interrupts.

OK. What is your code supposed to do?

Pete

mkwired:
Can someone tell me why the following code doesn't print "done"? ...

...

TIMSK1 = 1; // OCIE1A: Timer/Counter1, Output Compare A Match Interrupt Enable

...

That line is setting "Bit 0 – TOIE1: Timer/Counter1, Overflow Interrupt Enable", but you have an interrupt on "compare" not "overflow". Change that to 2 and you see "done".

You could make your code more readable, and avoid these mistakes, by using the symbolic constants, eg.

volatile boolean done = 0;

ISR(TIMER1_COMPA_vect)
{
  done = true;
}

void setup() {
  Serial.begin(9600);
  OCR1A = 1000;   // compare A register value
  TCCR1A = 0;  // normal operation
  TCCR1B = _BV(WGM12) | _BV(CS10);   // CTC, no prescaling
  TIMSK1 = _BV (OCIE1A); // interrupt on Compare A Match

  while (!done)
    {}   // wait for interrupt
  Serial.println("done");
}

void loop() { }

Your comment said "OCIE1A" but that wasn't what you used.

Also, if you turn on interrupts for the compare last, you don't need to worry about doing a cli() earlier on, avoiding more confusion.

A rookie mistake :frowning: I should have noticed that. It still doesn't work though.

If I upload either the corrected sketch, or the one I posted just above, to my Uno, it prints "done". Maybe more details?

I'm using a Uno R2 with the Arduino IDE 1.0.

Arduino IDE 1.0 eh?

Yes, it doesn't print "done" for me either using that IDE. Never was a saying more apt:

Upgrade: Replace your old bugs with new ones.

OK, try this:

volatile boolean done = 0;

ISR(TIMER1_COMPA_vect)
{
  done = true;
}

void setup() {
  for (byte i = 2; i <= 13; i++)
    pinMode (i, OUTPUT);
  Serial.begin(115200);
  OCR1A = 1000;   // compare A register value
  TCCR1A = 0;  // normal operation
  TCCR1B = _BV(WGM12) | _BV(CS10);   // CTC, no prescaling
  TIMSK1 = _BV (OCIE1A); // interrupt on Compare A Match

  while (!done)
    {}   // wait for interrupt
  Serial.println("done");
  for (byte i = 2; i <= 13; i++)
    {
    digitalWrite (i, HIGH);
    delay (200);
    }
}

void loop() { }

With my LED board plugged in, LEDs 2 to 13 do indeed light up after a brief delay, so the timer is firing. Why the Serial.println is not working I can't say. But it convinces me to stay with 0022 of the IDE for a while longer.

The timer interrupts cause the problem. The LED will blink faster if the interrupts occur. Weird

ISR(TIMER1_COMPA_vect)
{
}

void setup() {
  pinMode (13, OUTPUT);
  OCR1A = 65535;   // compare A register value
  TCCR1A = 0;  // normal operation
  TCCR1B = _BV(WGM12) | _BV(CS10);   // CTC, no prescaling
  TIMSK1 = _BV (OCIE1A); // interrupt on Compare A Match
}

void loop() {
  delay(1000);
  digitalWrite(13, 1);
  delay(1000);
  digitalWrite(13, 0);
}

The IDE 1.0 uses interrupts for Serial.print, so the interrupts are affecting that somehow. I can't say why right now, but at least that explains it partly. The version 0022 of HardwareSerial didn't rely on interrupts.

People got what they wanted. Although I think interrupt based Serial is a good thing, it causes what the Arduino team wanted to avoid in the beginning, which is software stopping because of interrupts. By using interrupts you do need to know how things work instead of copying and pasting from examples. Maybe giving the option for the user to choose interrupt based or polled Serial would be best.

Regarding this issue:

Without looking into the files from the Arduino IDE, I'd make a suggestion for you guys to put your interrupt based code in the loop() section of the program. It's just a hunch. But if we were using C, the software would be written like this:

#include <stdio.h> 

#define EVER (;;)

int main () {

//do your hardware setups here... 

sei();//notice this... in the end of setups and before infinite loop. 

for EVER {
  //your cyclic code goes in here. 
}

}

Having said this, in the first example, no one actually writes the sei() instruction in any of your programs. Just a hunch, but I may be right. Can someone have a look at the IDE internals to look for this?

I changed the code to this:

volatile boolean done = 0;

ISR(TIMER1_COMPA_vect)
{
  PORTB = 0x20;
  done = true;
  PORTB = 0;
}

void setup() {
  for (byte i = 2; i <= 13; i++)
    pinMode (i, OUTPUT);
  Serial.begin(115200);
  Serial.println ("A");
  Serial.println ("B");
  OCR1A = 3000;   // compare A register value
  TCCR1A = 0;  // normal operation
  TCCR1B = _BV(WGM12) | _BV(CS10);   // CTC, no prescaling
  TIMSK1 = _BV (OCIE1A); // interrupt on Compare A Match

  while (!done)
    {}   // wait for interrupt
    
   
  Serial.println ("C");
  Serial.println ("D");
  Serial.println("done");
  for (byte i = 2; i <= 12; i++)
    {
    digitalWrite (i, HIGH);
    delay (200);
    }
}

void loop() { }

Sticking the logic analyzer on pin D13 (turned on and off by the ISR) I see this:

So the interrupt is firing every 2.25 uS. Looking at the code for the compare ISR:

ISR(TIMER1_COMPA_vect)
 118:	1f 92       	push	r1 (2)
 11a:	0f 92       	push	r0 (2)
 11c:	0f b6       	in	r0, 0x3f	(1)
 11e:	0f 92       	push	r0 (2)
 120:	11 24       	eor	r1, r1 (1)
 122:	8f 93       	push	r24 (2)
{
  PORTB = 0x20;
 124:	80 e2       	ldi	r24, 0x20	(1)
 126:	85 b9       	out	0x05, r24	(1)
  done = true;
 128:	81 e0       	ldi	r24, 0x01	(1)
 12a:	80 93 1e 01 	sts	0x011E, r24  (2)
  PORTB = 0;
 12e:	15 b8       	out	0x05, r1	(1)
}
 130:	8f 91       	pop	r24 (2)
 132:	0f 90       	pop	r0 (2)
 134:	0f be       	out	0x3f, r0	(1)
 136:	0f 90       	pop	r0 (2)
 138:	1f 90       	pop	r1 (2)
 13a:	18 95       	reti (4)

That's 29 clock cycles, plus 7 to enter the ISR giving 36. Multiply by 62.5 nS per cycle, and it would take 2.250 uS to execute that. So clearly, very clearly, this is the only interrupt firing. The interrupt for serial transfers is lower priority, so the Serial.println never gets a chance to empty its buffer.

I'm not sure why the timer keeps firing, but I am no expert on timers. A different mode, perhaps?

And I can't say I am very impressed with main.cpp in version 1.0:

#include <Arduino.h>

int main(void)
{
	init();

#if defined(USBCON)
	USB.attach();
#endif
	
	setup();
    
	for (;;) {
		loop();
		if (serialEventRun) serialEventRun();
	}
        
	return 0;
}

Yuck! A little test every time you run loop. Too bad if you aren't using serial events, there is still a test there that has to be done. And is there a warning anywhere, that if you choose to "do your own" loop, that the test won't be done, and maybe it is important?

Compare to the old main.cpp:

#include <WProgram.h>

int main(void)
{
	init();

	setup();
    
	for (;;)
		loop();
        
	return 0;
}

At least you get what you expect: initialize and then loop.

bubulindo:
Having said this, in the first example, no one actually writes the sei() instruction in any of your programs. Just a hunch, but I may be right. Can someone have a look at the IDE internals to look for this?

Init sets interrupts up:

void init()
{
	// this needs to be called before setup() or some functions won't
	// work there
	sei();
...

HardwareSerial.cpp has some pretty dodgy code in it:

struct ring_buffer
{
  unsigned char buffer[SERIAL_BUFFER_SIZE];
  volatile int head;
  volatile int tail;
};

...

inline void store_char(unsigned char c, ring_buffer *buffer)
{
  int i = (unsigned int)(buffer->head + 1) % SERIAL_BUFFER_SIZE;

  // if we should be storing the received character into the location
  // just before the tail (meaning that the head would advance to the
  // current location of the tail), we're about to overflow the buffer
  // and so we don't write the character or advance the head.
  if (i != buffer->tail) {
    buffer->buffer[buffer->head] = c;
    buffer->head = i;
  }
}

They are using 2-byte fields for the ring buffer, but no protection when accessing them (eg. clearing interrupts), so from time to time they will become corrupted. I would not use this for production code.

For example:

  // if we should be storing the received character into the location
  // just before the tail (meaning that the head would advance to the
  // current location of the tail), we're about to overflow the buffer
  // and so we don't write the character or advance the head.
  if (i != buffer->tail) {
 4ba:	80 91 6a 01 	lds	r24, 0x016A     // started accessing the buffer
 4be:	90 91 6b 01 	lds	r25, 0x016B     // <--- hope no interrupt here
 4c2:	28 17       	cp	r18, r24  // <--- or here
 4c4:	39 07       	cpc	r19, r25  // <--- or here
 4c6:	59 f0       	breq	.+22     	; 0x4de <__vector_18+0x50>

I'm having a lazy morning at work...

So, sei() is actually called before setup(), so that is not the reason why the string isn't printed.

One thing I have noticed in wiring.c is this:

	// timers 1 and 2 are used for phase-correct hardware pwm
	// this is better for motors as it ensures an even waveform
	// note, however, that fast pwm mode can achieve a frequency of up
	// 8 MHz (with a 16 MHz clock) at 50% duty cycle

#if defined(TCCR1B) && defined(CS11) && defined(CS10)
	TCCR1B = 0;

	// set timer 1 prescale factor to 64
	sbi(TCCR1B, CS11);
#if F_CPU >= 8000000L
	sbi(TCCR1B, CS10);
#endif
#elif defined(TCCR1) && defined(CS11) && defined(CS10)
	sbi(TCCR1, CS11);
#if F_CPU >= 8000000L
	sbi(TCCR1, CS10);
#endif
#endif
	// put timer 1 in 8-bit phase correct pwm mode
#if defined(TCCR1A) && defined(WGM10)
	sbi(TCCR1A, WGM10);
#elif defined(TCCR1)
	#warning this needs to be finished
#endif

so there are some settings done beforehand that should be override before setting all up.

My suggestion is to clear all the TCC1 registers and then set them up.

I'd be happy to set the timers up, but I have no idea what is intended with it. So I guess that's a bit hard.

It just keeps running, nothing ever stopped it, but this is not a bad thing or even unusual. But the interrupt should not be occurring every 2.25µs, it should be every every 62.5µs with the original 1000 in OCR1A, or 187.5µs with 3000. bubulindo's suggestion to clear all the registers first (not knowing what init does to them) is a very good one, but it seems to me that's been done. Hmmmm...