Sleeping and Waking up

Ok after a few days of looking into sleeping Arduino and Waking it up, I have a basic understanding of the subject, enough that Im willing to try out a couple of examples.

I will be using an UNO, IDLE MODE (because it seems safer) and Waking up External Interrupt, Serial and WDT.
circuit_picture_sm.png
I found this as an example for an IDLE_MODE & Interrupt scenario:

//http://donalmorrissey.blogspot.com/2010/04/putting-arduino-diecimila-to-sleep.html
//Sleeps and waits for interrupt

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

int pin2 = 2;

void pin2Interrupt(void){
  /* This will bring us back from sleep. */
  
  /* We detach the interrupt to stop it from 
   * continuously firing while the interrupt pin
   * is low.
   */
  detachInterrupt(0);
}

void enterSleep(void){
  
  /* Setup pin2 as an interrupt and attach handler. */
  attachInterrupt(0, pin2Interrupt, LOW);
  delay(100);
  
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);
  sleep_enable();
  sleep_mode();
  /* The program will continue from here. */
  /* First thing to do is disable sleep. */
  sleep_disable(); 
}

void setup(){
  Serial.begin(9600);
  /* Setup the pin direction. */
  pinMode(pin2, INPUT);
  Serial.println("Initialisation complete.");
}

int seconds=0;
void loop() {
  delay(1000);
  seconds++;
  
  Serial.print("Awake for ");
  Serial.print(seconds, DEC);
  Serial.println(" second");
  
  if(seconds == 3) {
    Serial.println("Entering sleep");
    delay(200);
    seconds = 0;
    enterSleep();
  }
  
}

Then for the Serial Input the code is the same but the wiring is different. I should connect a 220Ohm Resistor to the Tx (pin1) and the INT0 (pin2) on the UNO. I understand that in this case, since the Tx pin is pulled high when no data is moving, the minute anything comes thru the port, it will go LOW, firing the same interrupt as before. In the first sketch that LOW condition was achieved by connecting a button to the same INT0 and since pin2 is also pulled high, it would go LOW when we pushed the button. This would, in effect wake up the MCU in both cases.

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).

Questions:

  1. Shouldn't the sketch call for IDLE_MODE for the UART awakening, since I/O is only available if in IDLE_MODE and not in POWER_DOWN mode?

  2. If I wanted to wake it up based on a certain value, I would have to actually wake it up anyway, parse the value received thru the serial and put it right back to sleep if it wasn't the correct wakeup value, right?

  3. As for WDT, I understand this is more commonly used for when we put Arduino in deep sleep with POWER_DOWN. I want to test out the differences in consumption between idle, power_down and power_save. How would I go about doing that?

Have you read - Gammon Forum : Electronics : Microprocessors : Power saving techniques for microprocessors ?

Thanks!

I read down near the bottom where he has the last sketch. But once again he puts it to sleep using power_down mode and wakes it up based on Serial input. So I must be reading this chart wrong:

wakeupsarduino.png

From what I understood, the Serial USB was I/O and therefore only available as a Wakeup Source in IDLE_MODE.

wakeupsarduino.png

  1. So my first doubt remains, why isnt the serial disabled in power_down?

  2. and my other doubt was if that sketch above was safe to try if I wire up my Arduino as shown in the image and press the button?

Finally I have a wdt sketch question but I dont want to complicate the thread or confuse myself until I clear these 2 doubts above?

Ill wait for answers before trying it because Im worried about irreversibly damaging my MCU.

Thanks guys

Marciokoko:

  1. So my first doubt remains, why isnt the serial disabled in power_down?

It is.

Marciokoko:
I read down near the bottom where he has the last sketch. But once again he puts it to sleep using power_down mode and wakes it up based on Serial input. So I must be reading this chart wrong...

You're reading Gammon wrong.

Nick Gammon's power web page:
...the sketch below uses a pin-change interrupt to wake the processor from sleep when it receives incoming serial data on the Rx pin (D0).

He's not waking the processor with serial i/o, he's waking it with a pin change interrupt on the serial RX pin. It's a clever way to do what the processor appears to not allow you to do: wake up from power down mode when there's serial input.

OK but the serial USB is indeed disabled. I understand the pin change interrupt is what's doing the waking on int0 but its coming in from the Serial Rx, right?

Nick described it in about six sentences, in plain English.
I don't think I could improve upon it.

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

OK and one last thing. I have a Tosduino mcu and I want to know if this code would work. According to the specs say it's still an atmega328 chip.