Interrupt Hell...

Go To Last Post
7 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi, I'm using an ATTINY85 with INTRC @ 8MHz, AVR-G++, and AVR-DUDE.

My goal is to get an interrupt function to fire every time TOV0 gets set.

Globally I have a variable called idx declared:

volatile uint8_t idx = 0;

In my main loop I can basically do the following just fine, and I can see a square wave on my scope:

// INIT CHIP
wdt_disable();
TCCR0A = 0;
TCCR0B = 5;  // 1:1, 2:8, 3:64, 4:256, 5:1024

// INIT PORTS
DDRB = 0xff;
PORTB = 0x00;

while(true)
{
	if ((TIFR & (1 << TOV0)) != 0)
	{
		if (idx == 0)
		{
			PORTB = 0x00;
			idx = 1;
		}
		else
		{
			PORTB = 0xff;
			idx = 0;
		}
	}
}

But when I switch to using an interupt, PORTB goes dead (and the program doesn't run anymore... do I have the vector wrong? how can I check?). Full program:

#define F_CPU 8000000UL  // 8 MHz

#include 
#include 
#include 
#include 

volatile uint8_t idx = 0;

SIGNAL(SIG_OVERFLOW0)
{ 
	if (idx == 0)
	{
		PORTB = 0x00;
		idx = 1;
	}
	else
	{
		PORTB = 0xff;
		idx = 0;
	}

	reti();
}


int main()
{
	// INIT CHIP
	wdt_disable();
	TCCR0A = 0;
	TCCR0B = 5;  // 1:1, 2:8, 3:64, 4:256, 5:1024
	sei();
	TIMSK = (1 << TOIE0);

	// INIT PORTS
	DDRB = 0xff;
	PORTB = 0x00;

	while(true)
	{
		/*if ((TIFR & (1 << TOV0)) != 0)
		{
			if (idx == 0)
			{
				PORTB = 0x00;
				idx = 1;
			}
			else
			{
				PORTB = 0xff;
				idx = 0;
			}
		}*/
	}

	return 0;
}

Also, I'm not sure if it's relevant, but I use the following lines (command line batch) to compile and program my .hex file, is it possible that the interrupt function lives outside of the .text section? How can I copy other sections out of the elf file into the .hex file?

avr-g++ main.cpp -mmcu=attiny85 -Os -o main.elf
avr-objcopy -j .text -O ihex main.elf main.hex

avrdude -p attiny85 -c stk500v2 -P COM5 -C c:\avrdude\avrdude.conf -e -B 50 -U flash:w:main.hex:i
avrdude -p attiny85 -c stk500v2 -P COM5 -C c:\avrdude\avrdude.conf -B 50 -U lfuse:w:0xe2:m -U hfuse:w:0xdf:m -U efuse:w:0xff:m

Thanks for any help you guys can provide.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Remove the reti() - the compiler will do it for you, but after it has restored the registers the could have been changed inside your ISR (and your reti() could easily called before restoring the registers).
BTW, SIGNAL is an obsolete syntax, use ISR instead - http://www.nongnu.org/avr-libc/user-manual/group__avr__interrupts.html

/Martin.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Also note your "non-interrupt" code won't work as you intend. From the datasheet:

Quote:
• Bit 1 – TOV0: Timer/Counter0 Overflow Flag

The bit TOV0 is set when an overflow occurs in Timer/Counter0. TOV0 is cleared by hardware
when executing the corresponding interrupt handling vector. Alternatively, TOV0 is cleared by
writing a logic one to the flag. When the SREG I-bit, TOIE0 (Timer/Counter0 Overflow Interrupt
Enable), and TOV0 are set, the Timer/Counter0 Overflow interrupt is executed.

Thus once the interrupt is set once, your main loop will continuously execute the bit toggle. Hence your output will be a lot faster than the 15 Hz it will be with the interrupt!

-Colin

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
Thus once the interrupt is set once, your main loop will continuously execute the bit toggle.
;No it won't since that code is commented out.

You can simplify your ISR code to:

PORTB ^= 0xff;

Regards,
Steve A.

The Board helps those that help themselves.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

No it won't since that code is commented out.

I have to admit that I almost replied to this thread to ask why OP was doing the same thing in mainline and the ISR then I spotted the "/*" and "*/". I'd suggest that to disable blocks of code it's far easier to read if one uses:

   while(true) 
   { 
#if 0
      if ((TIFR & (1 << TOV0)) != 0) 
      { 
         if (idx == 0) 
         { 
            PORTB = 0x00; 
            idx = 1; 
         } 
         else 
         { 
            PORTB = 0xff; 
            idx = 0; 
         } 
      }
#endif //0 
   } 

or perhaps even:

#define TOGGLING_IN_ISR

...

   while(true) 
   { 
#ifndef TOGGLIN_IN_ISR
      if ((TIFR & (1 << TOV0)) != 0) 
      { 
         if (idx == 0) 
         { 
            PORTB = 0x00; 
            idx = 1; 
         } 
         else 
         { 
            PORTB = 0xff; 
            idx = 0; 
         } 
      }
#endif // TOGGLING_IN_ISR 
   } 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

;No it won't since that code is commented out.

Note my comment specifically qualified what I was talking about:

Quote:

Also note your "non-interrupt" code won't work as you intend. From the datasheet:

The OP had posted two sets of code. One not using interrupts (which "worked") and one using interrupts (which "didn't work"). When he gets the interrupt code working, he needs to be aware why the timing will be so different between the two.

My comment was pointed towards the non-interrupt code, aka the code without interrupts in it.

-Colin

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Okay, so I changed the non-interrupt code to this, and I do not notice a speed decrease, so still a little confused there:

// INIT CHIP
wdt_disable();
TCCR0A = 0;
TCCR0B = 5;  // 1:1, 2:8, 3:64, 4:256, 5:1024

// INIT PORTS
DDRB = 0xff;
PORTB = 0x00;
while(true)
{
	// check for bit set
	if ((TIFR & (1 << TOV0)) != 0)
	{
		// unset bit
		TIFR &= ((1 << TOV0) ^ 0xff);
		if (idx == 0)
		{
			PORTB = 0x00;
			idx = 1;
		}
		else
		{
			PORTB = 0xff;
			idx = 0;
		}
	}
}

As for the interrupt code, I removed the reti(); and changed signal to ISR and that did the trick, however the code is MUCH slower as you guys predicted it would be, it's visually noticeable.

Upon doing the math this makes sense (8000000 hz / (1024 * 256)) = ~ 30 hz. I was just surprised, because it didn't go this slow even while I unset the bit.