Dear all, I was going to test the pin change interrupt of an Arduino Nano. I think i did all as described in the datasheet, but seems i made a very stupid mistake - i just can't find it - maybe you see it immidiately? I would expect that the LED blinks, as soon as i connct a jumper wire between D12 (PB3) and GND / 5V and back. Seems the interrupt did not fire or was unable to wake it up..?
#include <avr/sleep.h>
#include <avr/power.h>
// bit manipulation macros
#define clear_bit(a,z) (a &= ~_BV(z))
#define set_bit(a,z) (a |= _BV(z))
#define LED_PIN (13)
#define BTN_PIN (12)
volatile int ready2sleep=1;
ISR(PCINT0_vect)
{
clear_bit(PCICR,PCIE0); // disable pin change int
}
void enterSleep(void)
{
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
set_bit(PCIFR,PCIF0); // clear any pending pin change int
set_bit(PCICR,PCIE0); // enable pin change int (again)
sleep_enable();
sleep_mode();
//wakeup point is here
sleep_disable();
wakeup_action();
}
void setup()
{
pinMode(LED_PIN,OUTPUT);
pinMode(BTN_PIN, INPUT); // used for interrupt
set_bit(PCICR,PCIE0); // setup PINCHANGE int
set_bit(PCMSK0,PCINT3); // PB3 = interrupt pin
// enable interrupts
sei();
}
void loop()
{
if(ready2sleep == 1)
{
ready2sleep = 0;
enterSleep();
}
}
void wakeup_action()
{
for (int i=0;i<3;i++) {
digitalWrite(LED_PIN, HIGH);
delay(60);
digitalWrite(LED_PIN, LOW);
delay(60);
}
delay(500);
ready2sleep=1;
}
The code does something. You expect it to do something. We don't know what it does, or what you expect it to do. Hard to help, with so little information.
Seems the interrupt did not fire or was unable to wake it up..?
I think you need to build a far simpler test case to determine if the pin change interrupt happens. Once you KNOW that the pin change interrupt happens, because you have configured the interrupt vector correctly, THEN you can proceed to testing whether the pin change interrupt happens while the Arduino is sleeping, and whether it wakes the Arduino up.
arduarn:
Looking at the Nano schematic, I believe pin 12 aligns with PB4, which would correspond with PCINT4.
WOW - this is true - i had a wrong schematic with PB3 and PB4 inverted!!!
Now i can triger the interrupt, but LED blinks only 1x instead of 3x - why this? When i call "wakeup_action();" in the loop, it blinks 3x as it should..?
new, simplier code (without sleep..)
// bit manipulation macros
#define clear_bit(a,z) (a &= ~_BV(z))
#define set_bit(a,z) (a |= _BV(z))
#define LED_PIN (13)
#define BTN_PIN (12)
volatile int i;
void setup()
{
pinMode(LED_PIN,OUTPUT);
pinMode(BTN_PIN, INPUT); // used for interrupt
set_bit(PCICR,PCIE0); // setup PINCHANGE int
set_bit(PCMSK0,PCINT4); // PB4 = interrupt pin
// enable interrupts
sei();
}
void loop()
{
// nothing to do here
}
ISR(PCINT0_vect)
{
clear_bit(PCICR,PCIE0); // disable pin change int
wakeup_action();
}
void wakeup_action()
{
// blink 3 times and then wait a bit
for (i=0;i<3;i++) {
digitalWrite(LED_PIN, HIGH);
delay(100);
digitalWrite(LED_PIN, LOW);
delay(100);
}
delay(500);
set_bit(PCIFR,PCIF0); // clear any pending pin change int
set_bit(PCICR,PCIE0); // enable pin change int (again)
}
Because you're calling wakeup_actino() inside the interrupt and that function uses delays. You should never use delays in interrupts.
Have the interrupt set a variable and then make the loop inspect that variable and run the function. That will also allow the interrupt to ignore further interrupts during the blinky cycle. Remember it's a pin CHANGE so you will get a minimum of 2 interrupts per button-press, more like 20 with normal contact-bounce.
ok i changed the program like you say and it works (see code below). I used delay in the isr - yes - but i disable the interrupt directly after the fire - is delay at all not possible inside an ISR? (seems, i didn't know)
(Before i wrote:)
Finding: When i just set a flag in the ISR, it works as it should when i trigger the interrupt - byt why is this behavong different / what's wrong with the code in post #7 above??
// bit manipulation macros
#define clear_bit(a,z) (a &= ~_BV(z))
#define set_bit(a,z) (a |= _BV(z))
#define LED_PIN (13)
#define BTN_PIN (12)
volatile int i;
volatile int btn_int=0;
void setup()
{
pinMode(LED_PIN,OUTPUT);
pinMode(BTN_PIN, INPUT); // used for interrupt
set_bit(PCICR,PCIE0); // setup PINCHANGE int
set_bit(PCMSK0,PCINT4); // PB4 = interrupt pin
// enable interrupts
sei();
}
void loop()
{
if (btn_int==1)
{
btn_int=0;
wakeup_action();
}
}
ISR(PCINT0_vect)
{
clear_bit(PCICR,PCIE0); // disable pin change int
btn_int=1;
}
void wakeup_action()
{
// blink 3 times and then wait a bit
for (i=0;i<3;i++) {
digitalWrite(LED_PIN, HIGH);
delay(100);
digitalWrite(LED_PIN, LOW);
delay(100);
}
delay(500);
set_bit(PCIFR,PCIF0); // clear any pending pin change int
set_bit(PCICR,PCIE0); // enable pin change int (again)
}
Finding: When i just set a flag in the ISR, it works as it should when i trigger the interrupt - byt why is this behavong different / what's wrong with the code in post #7 above??
The code in post #7 calls wakeup_action(), a function using delay(), from within the ISR. Within an ISR, interrupts are disabled, and delay() requires interrupts to be enabled.
ok, now everything works fine - directly from my very first code i posted. The real only mistake came from my bad schematics and was really PB3 / PB4 confusion..
But i learned about the delay issue in ISRs. And don't worry about contact bounces: I disable the pin change as the first activity in the ISR - so no problem about twice calling it before finish blinking etc etc.