Attiny85 Interrupt Service Routine not calling function.

Hello
This is my first post so bear with me :slight_smile: Also my apologies for the somewhat vague subject. I couldn’t think of anything better for this enigma of mine.

Now my issue. As the code stands it runs perfectly. When powered, it blinks then sits and waits till I initiate an interrupt on pin 2(INT0). after that it blinks5 and goes back to waiting. Now when I comment out the blink5 in the setup() blink5 does not get called when the ISR is called. To me it seems like when the program is being complied, if blink5 is not in setup it gets ‘optimized’ lower in the assembly language and is not known to the ISR or something close to that nature. When blink5 is called in setup() first, the program works. I don’t understand nor see why this would affect each other.

Summary
in set-up:
(Scenario A) If blink5 is in setup the program runs as expected it would. sets-up blinks5…waits @int blinks5… .waits
(Scenario B) If blink 5 is commented out, blink5 won’t be called by the ISR. sets-up…waits @int does nothing.

Things I’ve tried:
I’ve tried every permutation of order for setup, loop, blink5, and ISR.
I’ve tried making my own prototype for blink5 “void blink5();” As I understand it, the arduino IDE auto-generates prototypes to simplify code making.
I’ve tried adding additional libraries in case i was missing one.(said libraries are commented out now)
Lastly prayed to the coding gods… they did not answer.

Attiny85 Datasheet if needed:

//#include <avr/sleep.h>
//#include <util/delay.h>
#include <avr/interrupt.h>
//#include "avr/interrupt.h"   //Would this make a difference? Seen it in a few examples in this form
//#include <avr/io.h>  //Do I need this to define the ISR vector? IRS seems to function without it.

#define LedPin 0
#define InputPin 2   //Pin that looks at the interupt signal.
                     //Not used in code directly. used by ISR INT0

void setup() {
  blink5();  //<-----Here is my question and problem
  /* when this is commented the blink code will Not run when the ISR is triggered.
  * however if it is included in the setup it works as expected.
  * blink5 would contain code that I dont want to run in setup.
  */
  
  
  //Interrupt related registers
  SREG |= (1<< SREG_I); //enable global interrupts
  GIMSK |= (1<<INT0);   //enable external interrupts
  MCUCR |= (1<<ISC00);  //Bit0. config of ext interrupt
  MCUCR |= (1<<ISC01);  //Bit1.
}
  
void loop() {
    //Sit and eat power
}

void blink5(){  // I blink...Just blink
  int i = 300;
  for (int j = 0; j < 5; j++){
    digitalWrite(LedPin, HIGH);
    delay(i);
    digitalWrite(LedPin, LOW);
    delay(i);
  }
}

ISR(PCINT0_vector) {
  blink5();        //I used to have the blink code in here. Same outcome-did not blink
}

This code will be implemented into my IR project. The interrupt comes from pin2(INT0) on the attiny85 chip which is connected to an IR Receiver. I’m confident that this part of the circuit works as it preforms as expected with other code and triggers the Interrupt in Scenario A.
So how do I correct this and why does this happen…Or politely show me the painfully obvious solution lol.

Examples I referenced from:

http://gonium.net/md/2006/12/20/handling-external-interrupts-with-arduino/

Many thanks in advance

the problem is your ISR which uses delay.

Inside the attached function, delay() won't work and the value returned by millis() will not increment. Serial data received while in the function may be lost. You should declare as volatile any variables that you modify within the attached function. See the section on ISRs below for more information.

instead of running that function, create a flag in the ISR, then conditionally run the function if the flag is triggered, then reset the flag

void myISR()
{
  myFlag = true;
}
void loop()
{
  if (myFlag)
  {
    myFunction();
    myFlag = false;
  }
}

also, you may find it easier to simply use the attachInterrupt() function.

BulldogLowell:
also, you may find it easier to simply use the attachInterrupt() function.

True and eventually ill try the library method. However im going for computer electrical engineering and i’m interested in learning how interrupts actually work. Then I’ll add some abstraction for convenience. Though one might suggest to try that first before diving deeper.

BulldogLowell:
the problem is your ISR which uses delay.

instead of running that function, create a flag in the ISR, then conditionally run the function if the flag is triggered, then reset the flag

Good suggestion and good practice. I just revised my code to include that. However upon running this change the overall behavior is the same.

#include <avr/interrupt.h>

#define LedPin 0
#define InputPin 2   //Pin that looks at the interupt signal.
                     //Not used in code directly. used by ISR INT0
volatile int InterruptFlag = 0;

int firstblink = 0;

void setup() {
 // blink5();  //<-----Here is my question and problem
  /* when this is commented the blink code will Not run when the ISR is triggered.
  * however if it is included in the setup it works as expected.
  * blink5 would contain code that I dont want to run in setup.
  */
  
  pinMode(LedPin, OUTPUT);

  //Interupt related registers
  SREG |= (1<< SREG_I); //enable global interrupts
  GIMSK |= (1<<INT0);   //enable external interrupts
  MCUCR |= (1<<ISC00);  //Bit0. config of ext interrupt
  MCUCR |= (1<<ISC01);  //Bit1.
}
  
void loop() {
  /*instead of blink5() being in setup i tried moving it down. set up as
  *if (firstblink ==0) the program runs blinks once then waits for interrupt.    When interrupt, it blinks
  *if (firstblink ==5)  some number that it can never logically be true.    When interrupt, it never blinks
  */
  if (firstblink ==5) { 
    blink5();
    firstblink =1;
  }
  
  if (InterruptFlag) {
     blink5();
     InterruptFlag = 0;
  }
}

void blink5(){  // I blink...Just blink
  int i = 300;
  for (int j = 0; j < 5; j++){
    digitalWrite(LedPin, HIGH);
    delay(i);
    digitalWrite(LedPin, LOW);
    delay(i);
  }
}

ISR(PCINT0_vector) {
  InterruptFlag =1;
}

I just had an idea an moved the first call of blink5() into main with logical one time if statement… with this it runs as it did before. blinks once and waits for interrupt whereupon it would call blink5() and blink.
Now when I create an if statement that is always false

if (false) {}

the blink5() function is never called when the ISR is called.

It really feels like the compiler is ‘optimizing’ code that it thinks could never be ran, and only including that function if it can be ran normally without the ISR.

I think you need pinMode(InputPin, INPUT);

...R

Right now i"m 80-90% certain that the compiler is optimizing out the blink5 function when its not called by normal code flow.

So my question now is, assuming I'm on the right track, is how to I change the optimization to prevent my functions from being left out?

This error has become increasingly frustrating as this code is so simple and my project is almost done apart from this issue. I'm literally at a standstill.

CyberZoid: Right now i"m 80-90% certain that the compiler is optimizing out the blink5 function when its not called by normal code flow.

So my question now is, assuming I'm on the right track, is how to I change the optimization to prevent my functions from being left out?

Why would the compiler pick on your poor blink5 function.

Did you add in Robin2's bit?

pinMode(InputPin, INPUT);

For the sake of argument, I just did. same behavior. I admit it should be in but I didn't add it before because in Scenario A the interrupt works just fine. It's in there now. It's simple code. I don't understand this monster i've created

did you try attachInterrupt() ?

I did try to make a version with attachInterrupt(). However this code doesn’t work. Granted I haven’t looked into this method much yet.

#include <avr/interrupt.h>

#define LedPin 0
#define InputPin 2   //Pin that looks at the interupt signal.
                     //Not used in code directly. used by ISR INT0
volatile int InterruptFlag = 0;

int firstblink = 0;

void setup() {
 // blink5();  //<-----Here is my question and problem
  /* when this is commented the blink code will Not run when the ISR is triggered.
  * however if it is included in the setup it works as expected.
  * blink5 would contain code that I dont want to run in setup.
  */
  
  pinMode(LedPin, OUTPUT);
  pinMode(InputPin, INPUT);

  //Interupt related registers
 // SREG |= (1<< SREG_I); //enable global interrupts
 // GIMSK |= (1<<INT0);   //enable external interrupts
 // MCUCR |= (1<<ISC00);  //Bit0. config of ext interrupt
 // MCUCR |= (1<<ISC01);  //Bit1.
  attachInterrupt(2,myInt, CHANGE);   //tried both 2 and INT0 for the pins. as well as 'CHANGE' and 'LOW'
}
  
void loop() {
  /*instead of blink5() being in setup i tried moving it down. set up as
  *if (firstblink ==0) the program runs blinks once then waits for interrupt.    When interrupt, it blinks
  *if (firstblink ==5)  some number that it can never logicaly be true.    When interrupt, it never blinks
  */
  if (firstblink ==0) { 
    blink5();
    firstblink =1;
  }
  sei();
  
  if (InterruptFlag) {
     blink5();
     InterruptFlag = 0;
  }
}
void myInt(){
  InterruptFlag = 1;
}

void blink5(){  // I blink...Just blink
  int i = 300;
  for (int j = 0; j < 5; j++){
    digitalWrite(LedPin, HIGH);
    delay(i);
    digitalWrite(LedPin, LOW);
    delay(i);
  }
}


//ISR(PCINT0_vector) {
//  InterruptFlag =1;
//}

try adding a little rebound on the interrupt like this:

//#include <avr/interrupt.h>

#define LedPin 0
#define InputPin 2   //Pin that looks at the interupt signal.
volatile int InterruptFlag = 0;
int firstblink = 0;
//
void setup() 
{
  pinMode(LedPin, OUTPUT);
  pinMode(InputPin, INPUT);
  attachInterrupt(2,myInt, CHANGE);   //tried both 2 and INT0 for the pins. as well as 'CHANGE' and 'LOW'
}
//
void loop() 
{
  if (firstblink == 0) 
  { 
    blink5();
    firstblink =1;
  }
  
  if (InterruptFlag) {
     blink5();
     InterruptFlag = 0;
  }
}
//
void myInt()  //interrupt with debounce
{ 
  static unsigned long last_interrupt_time = 0;
  unsigned long interrupt_time = millis();
  if (interrupt_time - last_interrupt_time > 200)
  {
    InterruptFlag = 1;
  }
  last_interrupt_time = interrupt_time;
}
//
void blink5()
{ 
  for (int j = 0; j < 5; j++)
  {
    digitalWrite(LedPin, HIGH);
    delay(300);
    digitalWrite(LedPin, LOW);
    delay(300);
  }
}

I just tried your code version. with the attachinterrupts I couldnt get it to work. However I inserted your ISR

ISR(PCINT0_vector) {
  static unsigned long last_interrupt_time = 0;
  unsigned long interrupt_time = millis();
  if (interrupt_time - last_interrupt_time > 30000)
  {
    InterruptFlag = 1;
  }
  last_interrupt_time = interrupt_time;
}

into my old code and it works still with the same behavior. When blink5 is called somewhere else in the code everything works. But when blink5 is not called or logicaly can never be called (if firstblink ==42 for example) the code will never blink from the ISR

try running this sketch EXACTLY as it is…

Pin2 is interrupt 0 Pin3 is interrupt 1… I didn’t notice that mistake.

#define LedPin 0
#define InputPin 2   //Pin that looks at the interupt signal.
volatile int InterruptFlag = 0;
int firstblink = 0;
//
void setup() 
{
  pinMode(LedPin, OUTPUT);
  pinMode(InputPin, INPUT);
  attachInterrupt(0,myInt, CHANGE);   //
}
//
void loop() 
{
  if (InterruptFlag) 
  {
     blink5();
     InterruptFlag = 0;
  }
}
//
void myInt()  //interrupt with debounce
{ 
  static unsigned long last_interrupt_time = 0;
  unsigned long interrupt_time = millis();
  if (interrupt_time - last_interrupt_time > 200)
  {
    InterruptFlag = 1;
  }
  last_interrupt_time = interrupt_time;
}
//
void blink5()
{ 
  for (int j = 0; j < 5; j++)
  {
    digitalWrite(LedPin, HIGH);
    delay(300);
    digitalWrite(LedPin, LOW);
    delay(300);
  }
}

Funny I just discovered that out myself. Which explains why my attachInterrupt version never worked. Thankyou

I'm grateful that there is a solution now; however, I don't like the idea of forgetting the issue. I mean someday I might be working on a microcontroller that doesn't have a easily available interrupt library to work with, I want to customize it, for timing optimization or something else. Do you have any idea (even longshots) that would explain my problems when I wasn't using the library as It did work for me in one scenario. So I know I'm doing it right, or at least mostly right.

Btw thank you for the help and guidance.

ISR(PCINT0_vector) {
  InterruptFlag =1;
}

That's not the name for the vector. It's PCINT0_vect.

If you get the ISR vector name wrong it will silently fail. That is, there is no error message, and the ISR will not be called.

http://www.gammon.com.au/interrupts

Vector names for the Attiny85:

/* Interrupt vectors */
/* Interrupt vector 0 is the reset vector. */
/* External Interrupt 0 */
#define INT0_vect           _VECTOR(1)
#define SIG_INTERRUPT0          _VECTOR(1)

/* Pin change Interrupt Request 0 */
#define PCINT0_vect         _VECTOR(2)
#define SIG_PIN_CHANGE          _VECTOR(2)

/* Timer/Counter1 Compare Match 1A */
#define TIM1_COMPA_vect         _VECTOR(3)
#define TIMER1_COMPA_vect       _VECTOR(3)
#define SIG_OUTPUT_COMPARE1A        _VECTOR(3)

/* Timer/Counter1 Overflow */
#define TIM1_OVF_vect           _VECTOR(4)
#define TIMER1_OVF_vect         _VECTOR(4)
#define SIG_OVERFLOW1           _VECTOR(4)

/* Timer/Counter0 Overflow */
#define TIM0_OVF_vect           _VECTOR(5)
#define TIMER0_OVF_vect         _VECTOR(5)
#define SIG_OVERFLOW0           _VECTOR(5)

/* EEPROM Ready */
#define EE_RDY_vect         _VECTOR(6)
#define SIG_EEPROM_READY        _VECTOR(6)

/* Analog comparator */
#define ANA_COMP_vect           _VECTOR(7)
#define SIG_COMPARATOR          _VECTOR(7)

/* ADC Conversion ready */
#define ADC_vect            _VECTOR(8)
#define SIG_ADC             _VECTOR(8)

/* Timer/Counter1 Compare Match B */
#define TIM1_COMPB_vect         _VECTOR(9)
#define TIMER1_COMPB_vect       _VECTOR(9)
#define SIG_OUTPUT_COMPARE1B        _VECTOR(9)

/* Timer/Counter0 Compare Match A */
#define TIM0_COMPA_vect         _VECTOR(10)
#define TIMER0_COMPA_vect       _VECTOR(10)
#define SIG_OUTPUT_COMPARE0A        _VECTOR(10)

/* Timer/Counter0 Compare Match B */
#define TIM0_COMPB_vect         _VECTOR(11)
#define TIMER0_COMPB_vect       _VECTOR(11)
#define SIG_OUTPUT_COMPARE0B        _VECTOR(11)

/* Watchdog Time-out */
#define WDT_vect            _VECTOR(12)
#define SIG_WATCHDOG_TIMEOUT        _VECTOR(12)

/* USI START */
#define USI_START_vect          _VECTOR(13)
#define SIG_USI_START           _VECTOR(13)

/* USI Overflow */
#define USI_OVF_vect            _VECTOR(14)
#define SIG_USI_OVERFLOW        _VECTOR(14)

#define _VECTORS_SIZE 30

When powered, it blinks then sits and waits till I initiate an interrupt on pin 2(INT0). after that it blinks5 and goes back to waiting. Now when I comment out the blink5 in the setup() blink5 does not get called when the ISR is called. To me it seems like when the program is being complied, if blink5 is not in setup it gets 'optimized' lower in the assembly language and is not known to the ISR or something close to that nature. When blink5 is called in setup() first, the program works. I don't understand nor see why this would affect each other.

I can explain this. But first I'll comment that you are overthinking this. If you couldn't get ISRs to work reliably these chips would be useless. And so would the compiler.

You added a blink5() call to setup, right? So every time the chip resets it blinks? Now with the ISR name misspelt, it generates a "bad interrupt" jump in the ISR jump table. And that "bad interrupt" goes back to running the program again. So, it calls blink5 (from setup) and the LED flashes. That's because of the bad interrupt, not because you somehow fixed the compiler's optimizations. That was a complete red herring.

What would have tipped you off would have been if you did something different in setup (eg. blink 2 times). Then you would see it blinking 2 times every time you caused the interrupt, and not 5 times.

And to show you the technique, I’ll describe how I found it. I must admit I didn’t initially spot your variation on the ISR spelling. So I compiled your sketch and disassembled it:

 avr-objdump -S /tmp/build42728228547944288.tmp/sketch_oct06c.cpp.elf > nick.txt

You can get the .ELF name from doing a verbose compile, and copying and pasting.

Then at the start of nick.txt:

Disassembly of section .text:

00000000 <__vectors>:
   0:	26 c0       	rjmp	.+76     	; 0x4e <__ctors_end>
   2:	40 c0       	rjmp	.+128    	; 0x84 <__bad_interrupt>
   4:	3f c0       	rjmp	.+126    	; 0x84 <__bad_interrupt>
   6:	3e c0       	rjmp	.+124    	; 0x84 <__bad_interrupt>
   8:	3d c0       	rjmp	.+122    	; 0x84 <__bad_interrupt>
   a:	67 c0       	rjmp	.+206    	; 0xda <__vector_5>
   c:	3b c0       	rjmp	.+118    	; 0x84 <__bad_interrupt>
   e:	3a c0       	rjmp	.+116    	; 0x84 <__bad_interrupt>
  10:	39 c0       	rjmp	.+114    	; 0x84 <__bad_interrupt>
  12:	38 c0       	rjmp	.+112    	; 0x84 <__bad_interrupt>
  14:	37 c0       	rjmp	.+110    	; 0x84 <__bad_interrupt>
  16:	36 c0       	rjmp	.+108    	; 0x84 <__bad_interrupt>
  18:	35 c0       	rjmp	.+106    	; 0x84 <__bad_interrupt>
  1a:	34 c0       	rjmp	.+104    	; 0x84 <__bad_interrupt>
  1c:	33 c0       	rjmp	.+102    	; 0x84 <__bad_interrupt>

I looked at that and thought “hmm, isn’t there a missing interrupt?”. After all the external interrupts are early ones (vector 1, not vector 5).

And if you look down to __vector_5 you see:

000000da <__vector_5>:
#if defined(__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__)
ISR(TIM0_OVF_vect)
#else
ISR(TIMER0_OVF_vect)
#endif
{
...

So clearly that is the timer overflow interrupt and not the external interrupt. So the question arises: “why no external interrupt vector?”. And the obvious answer is: “misspelt ISR name”. And there it goes …