From ccdb1fbb6e46af6dde97c36fcbb627271af4deb3 Mon Sep 17 00:00:00 2001 From: Mike Naberezny Date: Mon, 3 Jun 2024 20:29:55 -0700 Subject: [PATCH] Sleep between keypresses to save power --- README.md | 3 +- firmware/fuses.asm | 6 +- firmware/main.asm | 135 ++++++++++++++++++++++++++++++--------------- 3 files changed, 97 insertions(+), 47 deletions(-) diff --git a/README.md b/README.md index 67db9b6..e2fdffc 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,8 @@ Features: - Simulates up to 4 locking keys using an [ATtiny214/414/814](https://web.archive.org/web/20231029180615if_/https://ww1.microchip.com/downloads/en/DeviceDoc/40001912A.pdf) and a [4066](https://web.archive.org/web/20231029180910if_/https://www.ti.com/lit/ds/symlink/cd4066b-mil.pdf) - Shows the on/off state of each key with LEDs - Remembers the lock states between power cycles (useful for mode keys like `40/80`) - - Resets the computer by pulling `/RESET` low if a key is held down + - Resets the computer by pulling `/RESET` low if the `LOCK0` key is held down + - Puts the ATtiny to sleep between keypresses to save power ## Hardware diff --git a/firmware/fuses.asm b/firmware/fuses.asm index eaaa37d..4219fc3 100644 --- a/firmware/fuses.asm +++ b/firmware/fuses.asm @@ -5,11 +5,11 @@ ;Watchdog Configuration .org 0+FUSE_WDTCFG_offset -.byte FUSE_WINDOW_OFF_gc | FUSE_PERIOD_4KCLK_gc +.byte 0 ;BOD Configuration .org 0+FUSE_BODCFG_offset -.byte BOD_LVL_BODLEVEL7_gc | BOD_SAMPFREQ_1KHZ_gc | BOD_ACTIVE_ENABLED_gc | BOD_SLEEP_DIS_gc +.byte 0 ;Oscillator Configuration .org 0+FUSE_OSCCFG_offset @@ -23,7 +23,7 @@ ;System Configuration 0 .org 0+FUSE_SYSCFG0_offset -.byte FUSE_CRCSRC_NOCRC_gc | FUSE_RSTPINCFG_UPDI_gc | (0<INTERNAL_SRAM_END out CPU_SPH, r16 - rcall wdog_init + ;Initialize GPIO and restore 4066 contacts to last saved state rcall gpio_init - - rcall eeprom_read_contacts ;Read 4066 contacts saved in EEPROM - rcall gpio_write_contacts ; and restore the 4066 to that state + rcall eeprom_read_contacts + rcall gpio_write_contacts ;Now that the 4066 is set up, drop down to 1 MHz. The clock ;will run at 1 MHz from now on to save a little power. @@ -81,9 +125,13 @@ reset: sts previous_keys, r16 sts lock0_down_ticks, r16 -main_loop: - wdr ;Keep watchdog happy + ;Initialize remaining peripherals and enable interrupts + rcall sleep_init + sei + + ;Fall through +main_loop: rcall read_debounced_keys ;Read keys (delays 1 tick to debounce) sts current_keys, r16 @@ -91,7 +139,8 @@ main_loop: rcall task_reset ;Reset computer if LOCK0 is held down rcall task_eeprom ;Store 4066 contacts in EEPROM rcall task_leds ;Update LEDs from 4066 contacts - + rcall task_sleep ;Go to sleep until a key changes + lds r16, current_keys sts previous_keys, r16 ;Save keys for next time around @@ -200,13 +249,31 @@ task_eeprom: 1$: ret ;Update the LEDs from the 4066 contacts -;This task should be called last in the main loop because other -;tasks may temporarily change the state of the LEDs. +; +;This task should be called before the sleep task to ensure +;the LEDs reflect the 4066 contacts because other tasks may +;temporarily change the LEDs. ; task_leds: rcall gpio_read_contacts rjmp gpio_write_leds +;Go to sleep until a key changes +; +;This task should always be the last one called. It puts +;the MCU to sleep until a key is pressed in order to save +;power. If this task is removed from the main loop, all +;other tasks will continue to work the same. +; +task_sleep: + lds r16, current_keys + or r16, r16 + brne 1$ ;Do nothing if any key is down + + sleep ;Returns when MCU wakes back up + +1$: ret + ;UTILITIES ================================================================== ;Read the keys with gpio_read_keys and debounce for one tick @@ -255,11 +322,6 @@ wait_1_ms: pop r16 ret -;Work around RJMP range limitation -; -jmp_fatal: - jmp fatal - ;GPIO ======================================================================= ; @@ -418,7 +480,8 @@ gpio_init: ldi r16, 1<<2 | 1<<1 | 1<<0 ;PB2, PB1, PB0 sts PORTB_DIRCLR, r16 ;Set pins as input - ldi r16, PORT_PULLUPEN_bm ;Pull-up enabled, other features disabled + ;Enable pull-ups and pin-change interrupts + ldi r16, PORT_PULLUPEN_bm | PORT_ISC_BOTHEDGES_gc sts PORTB_PIN2CTRL, r16 ; on PB2 sts PORTB_PIN1CTRL, r16 ; on PB1 sts PORTB_PIN0CTRL, r16 ; on PB0 @@ -426,8 +489,8 @@ gpio_init: ;UPDI/PA0 Key Input ldi r16, 1<<0 ;UPDI/PA0 sts PORTA_DIRCLR, r16 ;Set UPDI pin also as a GPIO input - clr r16 - sts PORTA_PIN0CTRL, r16 ;No pull-up or other features on PA0 + ldi r16, PORT_ISC_BOTHEDGES_gc + sts PORTA_PIN0CTRL, r16 ;Enable pin change interrupt on PA0 ;Note: The UPDI/PA0 pin is configured for UPDI in the fuses. In UPDI ;mode, the pin can still be used as an input. The MCU can still be @@ -453,6 +516,13 @@ gpio_init: sts PORTA_DIRSET, r16 ;Set PA4 as output ret +;SLEEP ====================================================================== + +sleep_init: + ldi r16, SLPCTRL_SEN_bm | SLPCTRL_SMODE_PDOWN_gc + sts SLPCTRL_CTRLA, r16 + ret + ;EEPROM ===================================================================== ; @@ -549,27 +619,6 @@ eeprom_wait_ready: rjmp eeprom_wait_ready ret -;WATCHDOG =================================================================== - -;Ensure the watchdog was started by the fuses and reset the timer. -;The WDR instruction must be executed at least once every 4 seconds -;or the watchdog will reset the system. -wdog_init: - ;Ensure watchdog period has been configured - lds r16, WDT_CTRLA - andi r16, WDT_PERIOD_gm - cpi r16, WDT_PERIOD_4KCLK_gc ;Watchdog period set by the fuses? - breq 1$ ;Yes: continue - jmp fatal ;No: bad fuses, jump to fatal - - ;Ensure watchdog is locked so it can't be stopped -1$: lds r16, WDT_STATUS - sbrs r16, WDT_LOCK_bp ;Skip fatal if locked - jmp fatal - - wdr ;Reset watchdog timer - ret - ;END OF CODE ================================================================ ;Fill all unused program words with a nop sled that ends with