attachInterupt wont work on RISING

Hello,

I have an ATTiny85 with a small reed relay connected to physical pin 5 (digital pin 0), I have an external pull-down resistor connected to physical pin 7 (external interupt INT0), and when an external voltage source goes HIGH (+5v) on this pin I want to wakeup the microcontroller, this should then cycle the relay for one second, before going back to sleep.

However, this will not work with attachInterupt set to ‘RISING’. If I change the attachInterrupt to ‘LOW’ instead, the relay operates for one second as it should, the microcontroller switches off, then back on again because of the external pull-down resistor, this then cycles continually. And if I hold the interupt pin high when attachInterupt is set to ‘LOW’, it stops cycling. All of this is as I would expect.

But this never wakes when attachInterupt is set to ‘RISING’ and the external source is supplied. It cycles once, goes to sleep, and that’s it.

Any ideas with what I’m doing wrong?

#include <avr/sleep.h>
#include <avr/power.h>

int RelayControlPin = 0; //relay

void wake ()
{
  sleep_disable();
  detachInterrupt (0);
}

void setup() {
  pinMode(RelayControlPin, OUTPUT);
}

void loop() {
  digitalWrite(RelayControlPin, HIGH);
  delay(1000);
  digitalWrite(RelayControlPin, LOW);

  set_sleep_mode (SLEEP_MODE_PWR_DOWN);  
  sleep_enable();

  noInterrupts ();

  attachInterrupt (0, wake, RISING);

  interrupts();
  sleep_cpu();
}

I really appreciate any advice that can be given.

Couple things to try. Works on the 328.

First, move your set_sleep_mode to setup.

Get rid of all your sleep_xxx calls and just use sleep_mode(); as the last line in your loop. No need to disable/enable and all that.

Move your attachInterrupt to setup as calling it each time though the loop, resets everything.

There really is no need for your wake() ISR if all you want to do is run once through the loop. So long as the interrupt is active (which you can do without attachInterrupt) the processor will wake up and carry on from where it went to sleep. I use this all the time waiting for terminal input.

int main()
{
    // Initialize ======================
    
    init_main();
    //binC(PRR);
    set_sleep_mode(SLEEP_MODE_IDLE);
    sprint(VERSION);colon();
    prtB(DEVICE_ID);lf();
    enablePCI();
    
    // Execute =========================
    while (1)
    {
        if (usb.msg_waiting) processUSB(usb.buffer);
        if (soft.msg_waiting) procesSoft(soft.buffer);
        sleep_mode();
        PORTB |= 0B00100000;
        _delay_ms(30);
        PORTB &= 0B11011111;
    }
}

The above snippet goes to sleep after hitting sleep_mode. Both serial ports (one hard, one soft) are interrupt driven, the USART by RXCIE and the soft by a PCI. Either hit will wake the processor, flash the LED for 30ms (just to let whoever’s watching that it woke up) and then loops back to the top where it checks to see which port has a message waiting. After processing the message, back to sleep.

Check the datasheet - a RISING or FALLING interrupt can only work if the pins input flipflop is being clocked. Perhaps you have the wrong sleep level for this? Perhaps the ATtiny85 doesn't support input clocking during sleep.

Note this paragraph from the ATmega328 datasheet:

Note that recognition of falling or rising edge interrupts on INT0 or INT1 requires the presence of an I/O clock, described in ”Clock Systems and their Distribution” on page 26. Low level interrupt on INT0 and INT1 is detected asynchro- nously. This implies that this interrupt can be used for waking the part also from sleep modes other than Idle mode. The I/O clock is halted in all sleep modes except Idle mode.

The ATtiny may be very similar, or not - check the datasheet always for this sort of low-level detail

Maybe this is it:

[edit]
and the explanation why it works on the ATMEGA328P despite what it says in the data sheet:

Thank you for the replies, I've checked the datasheet and found this:

"Note that recognition of falling or rising edge interrupts on INT0 requires the presence of an I/O clock, described in “Clock Systems and their Distribution” on page 23."

However, this clock (labelled 'clkIO') is only on when the device is fully awake or in IDLE mode:

|500x240

So is this basically not possible?

I believe what is be said there is that for INT0, in modes other than Idle, RISING and FALLING will not trigger the interrupt but a level will and the only level is LOW. Try changing the attach to LOW, make pin 7 input pullup and find some way to invert your signal. As I understand it, the level needs to be valid for about 4 clock cycles for the trigger to actually work.

You cannot using a rising edge to initiate a wakeup on an Attiny85 but you can use a pin change interrupt to do so.

Here is an extract of some code (for an Attiny84) so you'll have to look at the data sheet for any necessary conversions.

There is a dummy ISR. When sleep is forced and later the configured pin changes state, the device will wake up. You can test in the loop the status of the pin(s) and determine why it woke and act accordingly.

ISR( PCINT0_vect )
{
  // pin change interrupt
  // concept change. This is used soley to wake the processor.
  // we derive zero information here. everything is doen in the loop by polling the trigger pin
  // we do not need to protect against multiple spurious triggers here.

}


void setup() {

. . . 
  // enable pin change interrupt on PCINT7 to wake processor ( D7 - attiny84)
  GIMSK |= bit( PCIE0 ) ;   // pins PA0 to PA7 as pin change interrups
  PCMSK0 = bit( PCINT7) ;   // only accept PA7 interrupt
. . .

}

As 6v6gt suggests. Only one port so GIMSK |= 1 << PCIE; and pin 7 which is INT0 is PCINT2 so it would be PCMSK |= 1 << PCINT2.

If it wasn’t clearly stated, this is a change interrupt, any change. You cannot specify RISING or FALLING so you need to test, perhaps in the ISR.

Thank you for the replies. However, I have to say that I'm getting nowhere with this.

Would it be possible for someone to reply with the full code based upon my basic example?

I get the idea that we're basically just going to wake it, and then decide for what reason it woke up, but I'm really struggling to understand how I implement this?

All I can ever achieve is the device switching off, but never back on again.

Thanks once again.

#include <avr/sleep.h>
//#include <avr/power.h>

int RelayControlPin = 0; //relay

ISR(PCINT0_vect)
{
	cli(); //turn interrupts off
	if (bitRead(PORTB, 2)
	{
		// if your control pin is high, assume
		// it triggered the interrupt so do
		// whatever it is you do here.
	}
}

void setup() 
{
	pinMode(RelayControlPin, OUTPUT);
	set_sleep_mode (SLEEP_MODE_PWR_DOWN);
	GIMSK |= 1 << PCIE;		// enable pin change interrupts
	PCMSK |= 1 << PCINT2;	// enable interrupt in pin7
}

void loop() 
{
	digitalWrite(RelayControlPin, HIGH);
	delay(1000);
	digitalWrite(RelayControlPin, LOW);
	sei();	// turn interrupts on before going to sleep
	sleep_mode();
}

Sketch uses 708 bytes (8%) of program storage space. Maximum is 8192 bytes.
Global variables use 9 bytes (1%) of dynamic memory, leaving 503 bytes for local variables. Maximum is 512 bytes.

DKWatson:

#include <avr/sleep.h>

//#include <avr/power.h>

int RelayControlPin = 0; //relay

ISR(PCINT0_vect)
{
cli(); //turn interrupts off          //???
if (bitRead(PORTB, 2)
{
// if your control pin is high, assume
// it triggered the interrupt so do
// whatever it is you do here.
}
}

Do you need to turn off interrupts in an ISR?

No. By default they are turned off at the start and then back on at the end. I just do it out of habit, kind of like initializing variables to zero or putting parentheses around everything - It didn't used to be like that.

DKWatson:

#include <avr/sleep.h>

//#include <avr/power.h>

int RelayControlPin = 0; //relay

ISR(PCINT0_vect)
{
cli(); //turn interrupts off
if (bitRead(PORTB, 2)
{
// if your control pin is high, assume
// it triggered the interrupt so do
// whatever it is you do here.
}
}

void setup()
{
pinMode(RelayControlPin, OUTPUT);
set_sleep_mode (SLEEP_MODE_PWR_DOWN);
GIMSK |= 1 << PCIE; // enable pin change interrupts
PCMSK |= 1 << PCINT2; // enable interrupt in pin7
}

void loop()
{
digitalWrite(RelayControlPin, HIGH);
delay(1000);
digitalWrite(RelayControlPin, LOW);
sei(); // turn interrupts on before going to sleep
sleep_mode();
}



Sketch uses 708 bytes (8%) of program storage space. Maximum is 8192 bytes.
Global variables use 9 bytes (1%) of dynamic memory, leaving 503 bytes for local variables. Maximum is 512 bytes.

Thank you so much, I have adapted this into my design and it now works perfectly, the whole thing draws just 800uAmps when in standby. Better than the 6mA it was drawing.