From: Pat Thoyts Date: Thu, 15 Dec 2016 16:48:11 +0000 (+0000) Subject: Demonstration of using the watchdog timer to wake an ATtiny at intervals. X-Git-Url: http://privyetmir.co.uk/gitweb.cgi?a=commitdiff_plain;h=HEAD;p=avr%2Ftiny_wdtsleep.git Demonstration of using the watchdog timer to wake an ATtiny at intervals. --- 0df7a5ba6a682a1e98949eb8fc8887e84e3d483f diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ceb55a1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +*.bin +*.o +*.hex diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..465e929 --- /dev/null +++ b/Makefile @@ -0,0 +1,104 @@ +uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not') + +DEVICE=attiny85 +F_CPU=1000000 +#F_CPU=128000 +ifeq ($(uname_S),Linux) +#SERIAL:=/dev/ttyACM0 +#AVRDUDE = avrdude -c usbasp -p $(DEVICE) -b 19200 +AVRDUDE = avrdude -c atmelice_isp -p $(DEVICE) +else +SERIAL=COM5 +AVRDUDE = avrdude -c avrisp -p $(DEVICE) -P $(SERIAL) -b 19200 \ + -C/opt/arduino-1.0.3/hardware/tools/avr/etc/avrdude.conf +endif + +INC = -I. +COMPILE = avr-gcc -Wall -Os $(INC) -mmcu=$(DEVICE) -DF_CPU=$(F_CPU) + +OBJECTS = main.o + +# symbolic targets: +all: main.hex + +.c.o: + $(COMPILE) -c $< -o $@ + +.S.o: + @$(COMPILE) -x assembler-with-cpp -c $< -o $@ +# "-x assembler-with-cpp" should not be necessary since this is the default +# file type for the .S (with capital S) extension. However, upper case +# characters are not always preserved on Windows. To ensure WinAVR +# compatibility define the file type manually. + +.c.s: + @$(COMPILE) -S $< -o $@ + +flash: all + $(AVRDUDE) -U flash:w:main.hex:i + +# Fuse high byte: +# 0xdd = 1 1 0 1 1 1 0 1 +# ^ ^ ^ ^ ^ \-+-/ +# | | | | | +------ BODLEVEL 2..0 (brownout trigger level -> 2.7V) +# | | | | +---------- EESAVE (preserve EEPROM on Chip Erase -> not preserved) +# | | | +-------------- WDTON (watchdog timer always on -> disable) +# | | +---------------- SPIEN (enable serial programming -> enabled) +# | +------------------ DWEN (debug wire enable) +# +-------------------- RSTDISBL (disable external reset -> enabled) +# +# Fuse low byte: +# 0xe1 = 1 1 1 0 0 0 0 1 +# ^ ^ \+/ \--+--/ +# | | | +------- CKSEL 3..0 (clock selection -> HF PLL) +# | | +--------------- SUT 1..0 (BOD enabled, fast rising power) +# | +------------------ CKOUT (clock output on CKOUT pin -> disabled) +# +-------------------- CKDIV8 (divide clock by 8, 1 means divided) +# For 16.5MHz internal: -U hfuse:w:0xd5:m -U lfuse:w:0xe1:m +# For 12MHz crystal: -U hfuse:w:0xdd:m -U lfuse:w:0b11111111:m +# For 8MHz internal: -U hfuse:w:0xd5:m -U lfuse:w:0xe2:m +# For 1MHz internal: -U hfuse:w:0xd5:m -U lfuse:w:0x62:m +# For 128kHz clock : -U hfuse:w:0xdf:m -U lfuse:w:0xe4:m +# Note: 0xd5 is as per 0xdd but with EESAVE enabled. + +fuse: + @echo "Programming fuses for 1MHz internal oscillator (BOD disabled)" + $(AVRDUDE) -U hfuse:w:0xdf:m -U lfuse:w:0x62:m + +readcal: + $(AVRDUDE) -U calibration:r:/dev/stdout:i | head -1 + +eeread: + $(AVRDUDE) -U eeprom:r:eeprom:i + +eewrite: + $(AVRDUDE) -U eeprom:w:eeprom:i + +clean: + @rm -f main.hex main.lst main.obj main.cof main.list main.map main.eep.hex main.bin *.o + +# file targets: +main.bin: $(OBJECTS) + $(COMPILE) -o main.bin $(OBJECTS) + +main.hex: main.bin + @rm -f main.hex + @avr-objcopy -j .text -j .data -O ihex main.bin main.hex + @$(SHELL) checksize main.bin 8192 256 +# do the checksize script as our last action to allow successful compilation +# on Windows with WinAVR where the Unix commands will fail. + +eeprom: main.eep.hex +main.eep.hex: main.bin + @avr-objcopy -j .eeprom --set-section-flags=.eeprom="alloc,load" \ + --change-section-lma .eeprom=0 -O ihex main.bin main.eep.hex + @echo call configure_eeprom.py to set eeprom values + +flash_eeprom: + $(AVRDUDE) -U eeprom:w:main.eep.hex:i + +disasm: main.bin + avr-objdump -d main.bin + +cpp: + $(COMPILE) -E main.c diff --git a/checksize b/checksize new file mode 100755 index 0000000..bc8be99 --- /dev/null +++ b/checksize @@ -0,0 +1,36 @@ +#!/bin/sh +# Name: checksize +# Project: PowerSwitch/AVR-USB +# Author: Christian Starkjohann +# Creation Date: 2004-12-29 +# Tabsize: 4 +# Copyright: (c) 2005 OBJECTIVE DEVELOPMENT Software GmbH. +# Revision: $Id: checksize 83 2006-01-05 22:20:53Z cs $ + +error=0 +codelimit=2048 # default value +datalimit=96 # default value; leave 32 bytes for stack +AWK=$(which gawk || which awk) + +if [ $# -gt 1 ]; then + codelimit="$2" +fi +if [ $# -gt 2 ]; then + datalimit="$3" +fi + +set -- `avr-size -d "$1" | $AWK '/[0-9]/ {print $1 + $2, $2 + $3, $2}'` +if [ $1 -gt $codelimit ]; then + echo "*** code size $1 exceeds limit of $codelimit" + error=1 +else + echo "ROM: $1 bytes (data=$3)" +fi +if [ $2 -gt $datalimit ]; then + echo "*** data size $2 exceeds limit of $datalimit" + error=1 +else + echo "RAM: $2 bytes" +fi + +exit $error diff --git a/main.c b/main.c new file mode 100644 index 0000000..e7ae377 --- /dev/null +++ b/main.c @@ -0,0 +1,93 @@ +/* Copyright (c) 2016 Pat Thoyts + * + * Demonstration using the watchdog timer to put an ATtiny to sleep + * for long periods. Using the WDT allows us to power the device + * down completely and only wake it up once a second to update the + * count of seconds. Could use to turn on a sensor package once + * every half hour for instance. + * + * Measuring pin voltages with an oscilloscope shows it really is off + * for all but 500ns of the second. Also that the WDT timer is not + * very accurate. I measured 1.09s intervals. + * + * Fuses set to run the clock at 8MHz. Could drop to 1Mhz for more + * power saving? Acc. the datasheet, 8MHz@3V3 is 3mA but 0.7mA for 1MHz + * and around 0.1mA using the 128kHz clock source. + * For this purpose, use 128kHz. Still has same accuracy and same on + * time when measured. + * + * On my board, measured 1.48mA when asleep and around 4mA when active + * (including LED) at 3V3. Seems the same at 128kHz and 1Mhz. + */ + +#ifndef F_CPU +#error F_CPU undefined +#endif + +#include +#include +#include +#include +#include +#include + +#define SLEEP_TIME 10; +volatile uint16_t counter = SLEEP_TIME; + +ISR(WDT_vect) +{ + --counter; + if (counter == 0) + { + PORTB |= _BV(PB4); + counter = SLEEP_TIME; + } else { + PORTB &= ~_BV(PB4); + } +} + +int __attribute__((noreturn)) +main(void) +{ + int n; + + if (bit_is_set(MCUSR, WDRF)) /* if reset caused by wdt */ + { + MCUSR &= ~_BV(WDRF); /* clear wdt reset bit */ + wdt_disable(); + } + + cli(); + WDTCR |= _BV(WDCE) | _BV(WDE); /* enable change bit */ + WDTCR = _BV(WDIE) | WDTO_1S; /* enable wdt interrupt */ + sei(); + + /* set all ports output and low */ + DDRB = 0xff; + PORTB = 0x00; + + /* some fast flashes to show startup */ + for (n = 0; n < 12; ++n) + { + PORTB ^= _BV(PB3); + _delay_ms(50); + } + + power_all_disable(); + set_sleep_mode(SLEEP_MODE_PWR_DOWN); + sleep_enable(); + sei(); + + for (;;) + { + sleep_enable(); + /* testing shows awake for 500ns then sleep for 1s */ + PORTB &= ~_BV(PB3); + cli(); + sleep_bod_disable(); + sei(); + sleep_cpu(); + sleep_disable(); + PORTB |= _BV(PB3); + } +}