Skip to content

Instantly share code, notes, and snippets.

@electronut
Last active October 25, 2022 18:53
Show Gist options
  • Save electronut/5648690 to your computer and use it in GitHub Desktop.
Save electronut/5648690 to your computer and use it in GitHub Desktop.
Putting the ATmega168 into Power Save mode, and then waking it with a pin-change interrupt.
//**********************************************************************
//
// Putting the ATmega168 into Power Save mode, and then waking it
// with a pin-change interrupt.
//
// electronut.in
//**********************************************************************
/*
build commands on OS X with CrossPack:
avr-gcc -Wall -Os -DF_CPU=8000000 -mmcu=atmega168 -c main.c -o main.o
avr-gcc -Wall -Os -DF_CPU=8000000 -mmcu=atmega168 -o main.elf main.o
rm -f main.hex
avr-objcopy -j .text -j .data -O ihex main.elf main.hex
avr-size --format=avr --mcu=atmega168 main.elf
avrdude -c usbtiny -p atmega168 -U flash:w:main.hex:i
avrdude -c usbtiny -p atmega168 -U lfuse:w:0xe2:m -U hfuse:w:0xdf:m -U efuse:w:0x01:m
*/
#include <avr/io.h>
#include <string.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>
#define F_CPU 8000000
// for sleep
volatile int goToSleep = 0;
int main (void)
{
// Put an LED on PD4 (pin 6)
// PD4 as output
DDRD |= (1<<4);
//Set high
PORTD |= (1<<4);
//
// 16-bit timer setup:
//
// turn off interrupts
cli();
// 16 bit timer - every 3 seconds:
// Count cycles - 3*8000000/1024
OCR1A = 23437;
// Put Timer/Counter1 in CTC mode
TCCR1B |= 1<<WGM12;
// enable 16-bit timer interrupt
TIMSK1 |= 1<<OCIE1A;
// start 16-bit timer if PCINT0 pin-14 PB0 is low
if((!PINB & (1 << PB0))) {
TCCR1B |= (1<<CS12) | (1<<CS10); // Divide by 1024
}
// enable Pin change interrupt enable 0
// this is PCINT0 - pin 14
PCICR |= (1 << PCIE0);
PCMSK0 |= (1 << PCINT0);
// turn on interrupts
sei();
// loop
while (1) {
// delay
_delay_ms(100);
// toggle led pin on/off
PORTD ^= (1<<4);
// sleep section
if(goToSleep) {
// turn LED pin off
PORTD &= ~(1<<4);
// set sleep mode
set_sleep_mode(SLEEP_MODE_PWR_SAVE);
// disable global interrupts
cli();
// enable sleep flag
sleep_enable();
// enable global interrupts
sei();
// actually sleep
sleep_cpu();
// ...
// just awake here
// ...
// disable global interrupts
cli();
// enable Pin change interrupt enable 0
// this is PCINT0 - pin 14
PCICR |= (1 << PCIE0);
PCMSK0 |= (1 << PCINT0);
// unset sleep flag
goToSleep = 0;
// disable sleep flag
sleep_disable();
// enable global interrupts
sei();
}
}
return 1;
}
// pin change interrupt
ISR (PCINT0_vect)
{
// read PCINT0 (PB0 - pn 14):
if(PINB & (1 << PB0)) {
// rising edge
// disable sleep - wake up!
sleep_disable();
// stop clock for 16-bit timer
TCCR1B &= ~((1<<CS12) | (1<<CS11) | (1<<CS10));
}
else{
// falling edge
// clear counter in 16-bit timer
TCNT1 = 0;
// start 16-bit timer
TCCR1B |= (1<<CS12) | (1<<CS10);
}
}
// 16-bit timer CTC handler
ISR(TIMER1_COMPA_vect)
{
// set sleep flag
goToSleep = 1;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment