ATTINY85: Intercept FALLING on interrupt... is it possible?

Hi to all and… happy new year!

Testing an Attiny85, I did find the Nick Gammon sleep sketch (Gammon Forum : Electronics : Microprocessors : Interrupts)

The bootloader I did get from Arduino IDE and selected 8MHZ internal oscillator.

My issue is that loop code is executed twice (i.e. led blinks 2 times) before sleep. When I press a button (interrupt), loop is executed twice and sleep.

Where am I wrong? Thank you very much!

The code is:

// ATtiny85 sleep mode, wake on pin change interrupt demo
// Author: Nick Gammon
// Date: 12 October 2013

// ATMEL ATTINY 25/45/85 / ARDUINO
//
//                  +-\/-+
// Ain0 (D 5) PB5  1|    |8  Vcc
// Ain3 (D 3) PB3  2|    |7  PB2 (D 2) Ain1
// Ain2 (D 4) PB4  3|    |6  PB1 (D 1) pwm1
//            GND  4|    |5  PB0 (D 0) pwm0
//                  +----+

#include <avr/sleep.h>    // Sleep Modes
#include <avr/power.h>    // Power management

#define POWERON_LED 3

const byte SWITCH = 4; // pin 3 / PCINT4

ISR (PCINT0_vect) {
  // do something interesting here
}
 
void setup () {
  pinMode (SWITCH, INPUT);
  digitalWrite (SWITCH, HIGH);  // internal pull-up
  
  // pin change interrupt (example for D4)
  PCMSK  |= bit (PCINT4);  // want pin D4 / pin 3
  GIFR   |= bit (PCIF);    // clear any outstanding interrupts
  GIMSK  |= bit (PCIE);    // enable pin change interrupts 
  
}  // end of setup

void loop () {
  
  pinMode(POWERON_LED,OUTPUT);
  digitalWrite (POWERON_LED, HIGH);
  delay (500);
  digitalWrite (POWERON_LED, LOW);
  delay (500);
  pinMode(POWERON_LED,INPUT);
  goToSleep ();
}  // end of loop
  
void goToSleep () {
  
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);
  ADCSRA = 0;            // turn off ADC
  power_all_disable ();  // power off ADC, Timer 0 and 1, serial interface
  sleep_enable();
  sleep_cpu();                             
  sleep_disable();   
  power_all_enable();    // power everything back on
}  // end of goToSleep

I did just try another script.
The issue is the same. Loop is executed twice, before sleep (the script is here: ATTiny85 Wake from Sleep on Pin State Change Code Example | Big Dan the Blogging Man )

Going crazy with this :frowning:

Take pinMode() out of loop() and put it in setup() where it belongs.

Thank you for your answer. The issue I think it's not that code part. I think my issue is a 100mF capacitator that I did put in parallel with the button (to debounce hardware).

Probably when it discharge/recharge it invoke interrupt a second time.

Removed it and no issues (but a lot of debounce...). I think, at least.

100mF capacitator

That would be an [u]enormous[/u] capacitor. Normally one would use something like 10 nF.

Yes. Finished in my bag atm

(But same capacitor works very well on hardware debounce on an ATMEGA).

Btw I just trying to debounce via software. Just to understand if my issue is from capacitator...

ok, issue was the capacitator.

But, I don’t know how edit the gammon script to debounce the button via software.

Can you help me, please?

The pseudocode that I did try without success…

[...]
ISR (PCINT0_vect) {
  blinkLed();
}

void blinkLed() {
  pinMode(3,OUTPUT);
  
  for (int i=0;i<10;i++) {

  unsigned long currentMillis = millis();

  if (currentMillis - previousMillis >= interval) {
    // save the last time you blinked the LED
    previousMillis = currentMillis;

    // if the LED is off turn it on and vice-versa:
    if (ledState == LOW) {
      ledState = HIGH;
    } else {
      ledState = LOW;
    }

    // set the LED with the ledState of the variable:
    digitalWrite(3, ledState);
  }

  }
  
}
[...]

Happy new year!

I did study and I get the conclusion that INTERRUPT reacts to a PIN change (in Attiny85).

So, with a capacitator (of 100 mF in my case), i.e. code is executed twice in about 3 seconds.

First one when it's charged, second one when it's discarged.

In atmega / arduino it works because I intercept the FALLING.

So, in Attiny85, I did not understand, is there the possibility to reacts to a falling only? I.e. when capacitator is discarged?

Can you help me in this case? I need to open another forum post or I can continue here? Thank you!

The pinMode(...) calls in the loop() function are to reduce the power consumption. Nick Gannon is very clever.

I did study and I get the conclusion that INTERRUPT reacts to a PIN change (in Attiny85).

Not quite correct.

INT0 on pin PB2 can be set to respond on low, level change, rising edge or falling edge.

PCINT0 reacts to level change on any I/O pin.

jremington: [...]

INT0 on pin PB2 can be set to respond on low, level change, rising edge or falling edge.

[...]

This is my key. I need the falling edge. But... Cannot find an example sketch, to understand how intercept it...

Your very best source of information is the ATtiny85 data sheet.

IF i did understand the datasheet correctly, seems impossible to intercept the FALLING (or RISING).

Am I right?

I did try also

ISR (PCINT0_vect) {
  
  if (digitalRead(4) == LOW) {
     blinkRedLed();
  } else {
     blinkGreenLed();
  }
}

And red led and red green blink.

Help!

Thank you very much

Am I right?

No. See reply #9.

So I need to edit this from:

PCMSK  |= bit (PCINT4);  // want pin D4 / pin 3
  GIFR   |= bit (PCIF);    // clear any outstanding interrupts
  GIMSK  |= bit (PCIE);    // enable pin change interrupts

to:

PCMSK  |= bit (PCINT2);  // want pin PB2 (IT'S THE 2 )
  GIFR   |= bit (PCIF);    // clear any outstanding interrupts
  GIMSK  |= bit (PCIE);    // enable pin change interrupts

But in GIMSK I read in the comment "enable pin change interrupts" . I don't know how edit this (if I need).

And edit from

ISR (PCINT0_vect) {
  blinkLed();
}

to

ISR (INT0_vect) {
  // change from PCINT0 to INT0
  blinkLed();
}

Am I right?

Thank you

double post. sorry

ISR (PCINT0_vect) {

blinkLed();
}

Are you trying to do a delay inside an ISR?

  for (int i=0;i<10;i++) {

unsigned long currentMillis = millis();

I think you might be.

Thank you Nick Gammon. Yes, I know that delay doesn't funcion in ISR. It's only a demo...

Real code is

ISR (PCINT0_vect) {

  button_pressed = true;
}

and in loop:

[...]
if (button_pressed) {
    blinkLed();
}[...]

My big issue is edit your code to intercept the FALLING on PIN and not simple a CHANGE.

I'm deboucing push button with capacitator. A PIN CHANGE execute INTERRUPT twice: first when capacitator discharge and second one when charge. And, in effect loop code is executed twice.

I need to get only the FALLING.

So, two post previous I did try to edit your code, but I don't know if it's right (to catch FALLING) neither I Know how edit (if need!) the code in:

GIMSK  |= bit (PCIE);    // enable pin change interrupts

Your comment is PIN CHANGE... I need FALLING... Thank you very very much

Try this:

MCUCR  = (MCUCR & ~(bit(ISC01)|bit(ISC00))) | bit(ISC01);  // INT0 mode = FALLING
 GIFR |= bit(INTF0);  // clear possible pending INT0
GIMSK |= bit(INT0);   // enable INT0
PCMSK  |= bit (PCINT2);  // want pin PB2 (IT'S THE 2 )
  GIFR   |= bit (PCIF);    // clear any outstanding interrupts
  GIMSK  |= bit (PCIE);    // enable pin change interrupts

No. DON"T USE PCINT!

If you want to trigger on a [u]specific edge[/u] you have to enable and use the INT0 interrupt. See the ATtiny85 data sheet, section 9.3. Modify the MCUCR as described above.