2

My ATtiny 85 won't wake up!

The purpose of the device is to monitor a reed switch and give a 20 ms output pulse on every alternate positive-going transition of the reed switch input. The device should go to sleep after 30 s of inactivity. It should wake up on reed switch activity (rising or falling edge).

/*
          ATMEL ATTINY 85
              +-\/-+
        PB5  1|    |8  Vcc
        PB3  2|    |7  PB2 (INT0) reed switch input (1 MΩ ext. pull-up)
        PB4  3|    |6  PB1        output
        GND  4|    |5  PB0 
              +----+
*/
#include <avr/sleep.h>
#include <avr/power.h>
#include <elapsedMillis.h>

const int hallPin = 2;            // Reed switch in this case.
const int magnetPin =  1;         // Output.
const long debouncing_time = 15;  // Debouncing time in milliseconds.

volatile int hallState = 0;       // variable for storing the hall counter
volatile bool hallMem = 0;        // One-shot memory.
volatile unsigned long last_micros;

elapsedMillis timer;

void setup(){
  pinMode(magnetPin, OUTPUT);
  pinMode(hallPin, INPUT);
  // Attach an interrupt to the ISR vector. DO I NEED THIS?
//  attachInterrupt(0, pin_ISR, RISING); //default interupt pin is always 0 on ATtiny85 (physical pin 7).
  timer = 0;                             //Reset the timer
}

void loop(){
  if(digitalRead(hallPin) && !hallMem){
    hallMem = true;               // One-shot memory.
    hallState++;
    delay(debouncing_time);       // Keep it simple.
    timer = 0;
  }
  if(!digitalRead(hallPin)){
    hallMem = false;              // Allow re-trigger.
  }
  if(hallState > 1) {
      digitalWrite(magnetPin, HIGH);        // Send 20 ms pulse to sensor activation coil.
      delay(20);
      digitalWrite(magnetPin, LOW);
      hallState = 0;                        // Reset the trigger.
  }
  if(timer >= 30000) {                      // No activity.
    system_sleep();
  }
}

// From http://interface.khm.de/index.php/lab/experiments/sleep_watchdog_battery/
void system_sleep() {
  GIMSK |= _BV(PCIE);                     // Enable Pin Change Interrupts
  PCMSK |= _BV(INT0);                     // Use INT0 (PB2) as interrupt pin
  ADCSRA &= ~_BV(ADEN);                   // ADC off
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);    // replaces above statement

  sleep_enable();                         // Sets the Sleep Enable bit in the MCUCR Register (SE BIT)
  sei();                                  // Enable interrupts
  sleep_cpu();                            // sleep

  cli();                                  // Disable interrupts
//  PCMSK &= ~_BV(INT0);                  // Turn off INT0 (PB2) as interrupt pin
  sleep_disable();                        // Clear SE bit
  ADCSRA |= _BV(ADEN);                    // ADC on

  sei();                                  // Enable interrupts  
  
  timer = 0;          // Reset the sleep timer on wake-up.
}

ISR(PCINT0_vect) {
}

Can anyone spot my error that is preventing a wake-up?

What is the ADC enabling instruction doing? Do I need it?

Many thanks.

2
  • Try to replace the PCMSK |= _BV(INT0); with PCMSK |= _BV(PCINT2);
    – gbg
    Commented Sep 11, 2021 at 22:05
  • @gbg, that seems to be doing something, thanks. I'm going to add in an "awakeLED" so I can monitor the sleep status. It's bedtime in Ireland.
    – Transistor
    Commented Sep 11, 2021 at 23:04

1 Answer 1

3

I believe your problem lies here:

PCMSK |= _BV(INT0);  // Use INT0 (PB2) as interrupt pin

You seem to have mixed up two sorts of interrupts:

  1. INT0 (External Interrupt), which only works on pin INT0 (i.e. PB2) and can sense low-level, rising edge, falling edge, or any change.

  2. PCINT0 (Pin Change Interrupt), which works on any IO pin, but can only sense “any change”.

Since you are sleeping in PWR_DOWN mode, pin change interrupt is the appropriate choice. All your program is consistent with this choice but for the line above.

The INT0 macro has the value 6. It represents bit 6 of GIMSK, which is used to enable the INT0 interrupt. Not what you want. Bit 6 of PCMSK is reserved, so you are not supposed to touch it. If you want the pin change interrupt to sense changes in pin PB2 (aka PCINT2), you instead have to:

PCMSK |= _BV(PCINT2);  // sense changes in PCINT2 = PB2
2
  • 1
    Thanks for the clear explanation. It works now. Can I leave out the ADCSRA lines as I'm not using any analog functions?
    – Transistor
    Commented Sep 12, 2021 at 13:50
  • 1
    @Transistor: Indeed you can. Commented Sep 12, 2021 at 14:40

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.