Go Down

Topic: Problem coding tiny85, Sleep Mode, PinChangeInterrupt, 2 buttons interrupt (Read 5042 times) previous topic - next topic

jstabile

Hi all,
Can you help with programming please. I'm using tiny85 with 2 PB switches (on PB0 & PB4), an LED on PB1.
As I'd like to run this project on a 3v coin battery as a txmitter, I'm using Sleep mode.
I'd like to wake it up with either switch and run two different functions depending which switch is pressed.

I'm trying to use PinChangeInterrupt to declare the switches but to no avail. The switches are active LOW.
I've only been working with Arduino and Atmel for 3 months and I have brickwalled.
I'm using Arduino IDE v1.01 & a MEGA as the ISP.

This is my code so far:
Code: [Select]

#include <avr/sleep.h>                      // Power Management and Sleep Modes
#include <PinChangeInterrupt.h>             // mimics attachInterrupt() but a PCI for ATtiny
#define BODS 7                              //BOD Sleep bit in MCUCR
#define BODSE 2                             //BOD Sleep enable bit in MCUCR

const int callButton = 0;                   // PB0 - Pin5 Call Switch
const int rfData = 1;                       // PB1 - Pin6 RF data & LED
const int lightButton = 4;                  // PB4 - Pin3 light Switch

void setup() {
for (byte i=0; i<5; i++) {                 //make all pins inputs with pullups enabled
    pinMode(i, INPUT);
    digitalWrite(i, HIGH);
  }

pinMode(rfData,OUTPUT);                    // rfData/LED is output
digitalWrite(rfData, LOW);                 // turn off RF data

// attachPcInterrupt(0,calFunc,FALLING);       // Catch button on PB0 with pin change interrupt
attachPcInterrupt(4,lightFunc,FALLING);       // Catch button on PB4 with pin change interrupt
}

void loop() {
    goToSleep();
    delay(1000);           //opportunity to measure active supply current
}

void ledBlink(int times, int lengthms){ // Routine for blinking a LED
  for (int x=0; x<times;x++){
    digitalWrite(rfData, HIGH);
    delay (lengthms);
    digitalWrite(rfData, LOW);
    delay(lengthms);
  }
}

void goToSleep(void){
    byte mcucr1, mcucr2;
    set_sleep_mode(SLEEP_MODE_PWR_DOWN);
    sleep_enable();
    MCUCR &= ~(_BV(ISC01) | _BV(ISC00));      //INT0 on low level
    GIMSK |= _BV(INT0);                       //enable INT0
    ADCSRA &= ~_BV(ADEN);                     //disable ADC
    cli();                                    //stop interrupts to ensure the BOD timed sequence executes as required
    mcucr1 = MCUCR | _BV(BODS) | _BV(BODSE);  //turn off the brown-out detector
    mcucr2 = mcucr1 & ~_BV(BODSE);            //if the MCU does not have BOD disable capability,
    MCUCR = mcucr1;                           //  this code has no effect
    MCUCR = mcucr2;
    sei();                                    //ensure interrupts enabled so we can wake up again
    sleep_cpu();                              //go to sleep
    GIMSK = 0x00;                             //disable INT0
    sleep_disable();                          //wake up here
}
/*
ISR(PCINT1_vect){       //external interrupt 0 wakes the MCU
    GIMSK = 0x00;       //disable external interrupts (only need one to wake up)
}*/


void callFunc(){
int bounce1 = digitalRead(callButton); // Debounce - 1st read
delay(40); // Wait
int bounce2 = digitalRead(callButton); // Debounce - 2nd read
if ((bounce1 == bounce2) && (bounce1 == LOW)) // If both bounces are the same and High

          ledBlink(2, 100);                //debugging 2 flashes
}
}

void lightFunc(){
int bounce1 = digitalRead(lightButton); // Debounce - 1st read
delay(40); // Wait
int bounce2 = digitalRead(lightButton); // Debounce - 2nd read
if ((bounce1 == bounce2) && (bounce1 == LOW)) // If both bounces are the same and High

          ledBlink(3, 100);                //debugging 3 flashes
}
}


I previously managed to use INT0 and it worked.
I've disabled PB0 switch to see if only PB4 will work, '85 not waking up.
Any help would greatly be appreciated.
Thanks, John.


Coding Badly



Coding Badly


Does the application work when powered by USB (or something other than a 3V battery)?

nickgammon

Can you give a link to the PinChangeInterrupt library please?

To summarize, you believe the processor goes to sleep and doesn't wake up? How do you know?
Please post technical questions on the forum, not by personal message. Thanks!

More info: http://www.gammon.com.au/electronics

jstabile


Coding Badly : I'm running off the USB at the moment. I haven't gone battery yet.

Hi Nick Gammon,
It's "PinChangeInterrupt-0001.zip" from https://code.google.com/p/arduino-tiny/downloads/detail?name=PinChangeInterrupt-0001.zip&can=2&q=.

I know it's going to sleep as I have a multimeter in series measuring current e.g. 0.4uA.

Coding Badly


You enable INT0 but have no interrupt service routine.  If the interrupt occurs, a "soft reset" is generated which is rarely desirable.  Either add the interrupt service routine or remove the code that enables INT0.

nickgammon

You seem to have more code than you need there. I tested the code below with my Attiny85 and it works.

Code: [Select]

#include <avr/sleep.h>                      // Power Management and Sleep Modes

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

ISR (PCINT0_vect)
{
}

void setup ()
  {
  pinMode (LED, OUTPUT);
  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 ()
  {
  digitalWrite (LED, HIGH);
  delay (500);
  digitalWrite (LED, LOW);
  delay (500);
  goToSleep ();
  }  // end of loop
 
 
void goToSleep ()
  {
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);
  sleep_enable();
  sleep_cpu();                              //go to sleep
  sleep_disable();                         
  }   


I have an LED and resistor on pin 2 (D3) and a switch wired to ground on pin 3 (D4). The rest are being used for ICSP programming.

The LED flashes twice in loop to confirm it is active. Then it sleeps and stays asleep until you ground pin 3 (D4).
Please post technical questions on the forum, not by personal message. Thanks!

More info: http://www.gammon.com.au/electronics

jstabile

Thanks Coding Badly, yeh the INT0 stuff is the left over code from my orginal testing. I'll look into it.

Thanks Nick, I'll try this out when I get a chance, weekends are hectic with my kids.
I notice you have used your own PinChangeInterrupt.
If I need to add a second switch, do I change the PCMSK to allow for 2 bits or do I call PCMSK a second time for the second switch?
Sorry for the silly questions.

nickgammon

You "or" in another bit like this:

Code: [Select]

  PCMSK = bit (PCINT3) | bit (PCINT4);  // pins 2 and 3


I changed it from |= to = because that way you know only those two pins are going to be set.
Please post technical questions on the forum, not by personal message. Thanks!

More info: http://www.gammon.com.au/electronics

nickgammon

#10
Oct 12, 2013, 02:40 am Last Edit: Oct 12, 2013, 05:19 am by Nick Gammon Reason: 1
I modified the sketch to add in the powering off of the peripherals. I set the fuses to disable the watchdog timer and brownout disable. Then I measured 500 nA of current in sleep mode. This is exactly what the datasheet predicts:



Modified sketch:

Code: [Select]

// 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>

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

ISR (PCINT0_vect)
{
// do something interesting here
}

void setup ()
 {
 pinMode (LED, OUTPUT);
 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 ()
 {
 digitalWrite (LED, HIGH);
 delay (500);
 digitalWrite (LED, LOW);
 delay (500);
 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

Please post technical questions on the forum, not by personal message. Thanks!

More info: http://www.gammon.com.au/electronics

nickgammon

See this other thread about incorporating the watchdog timer for a periodic wake-up:

http://forum.arduino.cc//index.php?topic=192888
Please post technical questions on the forum, not by personal message. Thanks!

More info: http://www.gammon.com.au/electronics

jstabile

Nick, you're a champion, thank you.
Tried it with the modified PCMSK for both switches and it interrupts on both. 

Only hassel is that it re-triggers on release of the switch, as if the interrupts are setup to detect CHANGE rather than just a LOW detection.
I'm wondering if I need to disable the interrupts when it wakes up or if I use the MCUCR reg. (ISC00 & ISC01) to just set it up for LOW?

The watchdog code looks very similar, good use.

nickgammon

#13
Oct 12, 2013, 08:16 am Last Edit: Oct 12, 2013, 09:19 am by Nick Gammon Reason: 1

Only hassel is that it re-triggers on release of the switch, as if the interrupts are setup to detect CHANGE rather than just a LOW detection.


Yep, a pin-change interrupt detects a change, hence its name. :P

However assuming you want to detect a push rather than a release, just test for it, eg.

Code: [Select]

void loop ()
 {
 goToSleep ();
 if (digitalRead (SWITCH) == LOW)
   doSomething ();
 delay (10);  // debounce
 }  // end of loop

Please post technical questions on the forum, not by personal message. Thanks!

More info: http://www.gammon.com.au/electronics

jstabile

Implemented VirtualWire. All working great. Thanks again, Nick.

Quote
Yep, a pin-change interrupt detects a change, hence its name.

I thought PinChangeInterrupt stood for allocating different pins to interrupts.


Go Up