Marciokoko:
I want to run this by the forum before I do it because Im a very risk-averse person and I've read sleeping an MCU can leave it in that state forever unless its done properly (bricking it).
There is no risk in "bricking" the CPU. If you screw up (for example, use an external interrupt to wake it up then forget to enable interrupts), just re-flash the code. Sleep will not lock you out of the CPU permanently. About the only way to "brick" the CPU is to set a fuse wrong, and even that can be recovered with a high voltage programmer (such as this one):
As far as sleeping and waking up with a pushbutton, look at this code I did for an IR remote that sends a "take picture" command to my camera. Within the main loop is all you need to know to setup an interrupt handler, go to sleep and wake up from that sleep.
Register and interrupt numbers will vary depending on with timers and interrupts you choose to use, but the idea is the same. Hope this helps. Start at the "int main (void)" part for the sleep stuff.
///////////////////////////////////////////////////////////////////////////////
//
// Infrared Remote Control for SONY DSLR A-700 Digital Camera
// Copyright (c) 2015 Roger A. Krupski <rakrupski@verizon.net>
//
// Last update: 17 October 2015
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
///////////////////////////////////////////////////////////////////////////////
#define F_CPU 8000000UL // ATtiny 25 @ 8.00 mhz external crystal
#include <inttypes.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>
#include <avr/power.h>
#include <avr/pgmspace.h>
// ATtiny25/45 fuse settings (in Makefile):
// LFUSE: 0xFF (external crystal)
// HFUSE: 0xDF (SPI enabled, BOD disabled)
// EFUSE: 0xFF (self program enabled)
#define IO_PORT PORTB // (port write)
#define IO_PIN PINB // (port read)
#define IO_DDR DDRB // (port ddr)
#define IR_BIT PB0 // 2N4401 base drive for IR LED (330 ohm base resistor plus 100K pulldown)
#define MODE PB1 // mode select: select shutter0 or shutter1 (has external 10K pullup)
#define BUTTON PB2 // send IR code (red button - active low - external 10K pullup)
#define KHZ 38 // IR carrier freq
volatile uint8_t state = 0; // led on/off state
volatile uint8_t onoff = 0; // bit polarity
volatile uint8_t count = 0; // led pulse counter
// Sony camera shutter release code 0x00B4B8F (focus and shoot)
// (header + 20 bits)
static const uint8_t shutter1[] PROGMEM = {
// header
0x60, 0x18,
// command
0x30, 0x18, 0x18, 0x18, 0x30, 0x18, 0x30, 0x18, // B
0x18, 0x18, 0x30, 0x18, 0x18, 0x18, 0x18, 0x18, // 4
0x30, 0x18, 0x18, 0x18, 0x30, 0x18, 0x30, 0x18, // B
0x30, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, // 8
0x30, 0x18, 0x30, 0x18, 0x30, 0x18, 0x30, 0x18, // F
// end of command
0x00,
};
// Sony camera shutter release code 0x00ECB8F (focus, wait 2 sec, then shoot)
// (header + 20 bits)
static const uint8_t shutter2[] PROGMEM = {
// header
0x60, 0x18,
// command
0x30, 0x18, 0x30, 0x18, 0x30, 0x18, 0x18, 0x18, // E
0x30, 0x18, 0x30, 0x18, 0x18, 0x18, 0x18, 0x18, // C
0x30, 0x18, 0x18, 0x18, 0x30, 0x18, 0x30, 0x18, // B
0x30, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, // 8
0x30, 0x18, 0x30, 0x18, 0x30, 0x18, 0x30, 0x18, // F
// end of command
0x00,
};
void _delay_msec (uint8_t ms) // msec delay using the IR timer interrupt
{
while (count); // wait if IR is sending
while (ms--) { // for each millisecond
count = KHZ; // load count for 1 millisecond
onoff = 0; // insure LED is off
while (count); // wait for it
}
}
// send IR command from a PROGMEM array (terminated with 0x00)
void send_cmd (const uint8_t *command, uint8_t repeat, uint8_t delay)
{
uint8_t index;
IO_DDR |= _BV (IR_BIT); // set IR drive pin as an output
while (repeat--) {
index = 0; // reset index
state = 0; // insure state starts correctly
do { // each one is a pulse count at carrier freq
while (count); // wait for ISR to finish
count = pgm_read_byte (command + index++); // set next pulse count
onoff = (index % 2); // set which side is high
} while (count); // until we hit 0 (end of command)
_delay_msec (delay); // delay between commands
}
IO_DDR &= ~_BV (IR_BIT); // set IR drive pin back to an input
}
ISR (TIMER0_COMPA_vect) // IR carrier generator
{
if (count) { // if a count was pushed in...
state ? IO_PORT &= ~_BV (IR_BIT) : onoff ? IO_PORT |= _BV (IR_BIT) : IO_PORT &= ~_BV (IR_BIT);
count -= state; // decrement count (on times only)
state = state ? 0 : 1; // if led was on set it off, else set it on
}
}
ISR (INT0_vect) // INT0 handler - we arrive here when the button is pressed
{
GIMSK = (0x00 & ~_BV (INT0)); // disable hardware INT0
}
int main (void)
{
uint8_t deb; // debounce counter
const uint8_t *ptr; // pointer to IR command packet
cli(); // disable interrupts while setting stuff
IO_PORT = 0x00; // all pullups off
IO_DDR = 0x00; // all pins inputs (for now)
ADCSRA = 0x00; // turn off ADC
// set timer 0 (8 bit) to CTC mode, carrier freq. this times the IR pulses.
TCCR0A = (0x00 | _BV (WGM01)); // mode 2 (CTC) OCR0A = top
TCCR0B = (0x00 | _BV (CS00)); // F_CPU/1 = 8000000
OCR0A = ((F_CPU / 2000 / KHZ) - 1); // divider for IR carrier freq
TIMSK = (0x00 | _BV (OCIE0A)); // enable interrupt on OCR0A match
GIMSK = (0x00 & ~_BV (INT0)); // disable hardware INT0
MCUCR = (0x00 & ~ (_BV (ISC01) | _BV (ISC00))); // set hardware INT0 active on low LEVEL
while (1) {
cli(); // disable interrupts
IO_PORT = 0x00; // insure port is completely...
IO_DDR = 0x00; // ...off before going to sleep.
set_sleep_mode (SLEEP_MODE_PWR_DOWN); // set CPU to go into power down mode
sleep_enable(); // enable CPU to be powered down
sleep_bod_disable(); // turn off brown-out detector
GIMSK = (0x00 | _BV (INT0)); // enable INT0 active low
sei(); // enable interrupts
sleep_cpu(); // power down cpu (all clocks stopped)
/////////////// cpu is now asleep - awaiting pushbutton INT0 ///////////////
sleep_disable(); // turn off sleep mode
ptr = shutter1; // point to default command
deb = 10; // load debounce 10 msec
while (deb--) {
_delay_msec (1); // tiny delay
if (! (IO_PIN & _BV (MODE))) { // if mode is low
ptr = shutter2; // alternate mode selected
break; // quit checking
}
}
send_cmd (ptr, 3, 24); // send "shutter1" or "shutter2" depending on "mode"
// wait for button to be released (prevent repeats if button held down).
deb = 100; // init debounce
while (deb--) {
_delay_msec (1); // tiny delay
if (! (IO_PIN & _BV (BUTTON))) { // if button is pressed...
deb = 100; // ...keep checking
}
} // now button confirmed released for 100 msec
}
}
// end of sony_ir.cpp