Demonstration of using the watchdog timer to wake an ATtiny at intervals. master
authorPat Thoyts <patthoyts@users.sourceforge.net>
Thu, 15 Dec 2016 16:48:11 +0000 (16:48 +0000)
committerPat Thoyts <patthoyts@users.sourceforge.net>
Thu, 15 Dec 2016 16:48:11 +0000 (16:48 +0000)
.gitignore [new file with mode: 0644]
Makefile [new file with mode: 0644]
checksize [new file with mode: 0755]
main.c [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..ceb55a1
--- /dev/null
@@ -0,0 +1,3 @@
+*.bin
+*.o
+*.hex
diff --git a/Makefile b/Makefile
new file mode 100644 (file)
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 (executable)
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 (file)
index 0000000..e7ae377
--- /dev/null
+++ b/main.c
@@ -0,0 +1,93 @@
+/* Copyright (c) 2016 Pat Thoyts <patthoyts@users.sourceforge.net>
+ *
+ * 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 <avr/interrupt.h>
+#include <avr/io.h>
+#include <avr/power.h>
+#include <avr/sleep.h>
+#include <avr/wdt.h>
+#include <util/delay.h>
+
+#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);
+    }
+}