powersave.c (2781B)
1 /* 2 MNT Reform 2.0 Keyboard Firmware 3 See keyboard.c for Copyright 4 SPDX-License-Identifier: MIT 5 */ 6 7 #include <avr/io.h> 8 #include <avr/sleep.h> 9 #include "powersave.h" 10 #include "keyboard.h" 11 #include "oled.h" 12 13 /* Setup the AVR to enter the Power-Down state to greatly save power. 14 * Configures all outputs to be in the low state if possible, and disables 15 * services like USB and Serial. 16 * 17 * Will leave the ports setup so that the Circle key row is being scanned 18 * so when the watchdog wakes up it can quickly check and go back to sleep if not 19 * Added by Chartreuse - 2021/08/14 20 * 21 */ 22 void keyboard_power_off(void) 23 { 24 USB_Disable(); // Stop USB stack so it doesn't wake us up 25 26 // turn off backlight, but don't overwrite setting 27 OCR0A = 0; 28 29 // Turn off OLED to save power 30 gfx_clear_screen(); 31 gfx_off(); 32 // Disable ADC to save even more power 33 ADCSRA=0; 34 35 cli(); // No interrupts 36 37 // Set all ports not floating if possible, leaving pullups alone 38 PORTB=0x3F; // Leave pull-up on all the columns on PB0-3, drive rows 2-3 high, 1-low 39 PORTC=0xC0; 40 PORTD=0xF0; // Keep pullup on PD5 like setup did, drive rows 4,5,6 high 41 PORTE=0x40; // Pullup on PE6 42 PORTF=0xFF; // Pullups on PF (columns) 43 // ROW1 is the only row driven low and left low, thus is always ready to be read out 44 // We just need to check COL14 (PC6) if it is low (pressed) or high 45 46 // Unfortunately the circle key is on COL14(PC6) which doesn't have pin change interrupt 47 // capabilities, so we need to wake up every so often to check if it is pressed, and 48 // if so bring us out of power-off 49 // We can use the Watchdog timer to do this. 50 51 do { 52 // Setting WDT parameters must be done within 4 cycles of setting WDCE, so interrupts 53 // must be disabled. 54 cli(); 55 56 wdt_reset(); 57 58 // Enable writes to watchdog, then set interrupt-only mode, 1s timeout. 59 // (Interrupt-only mode is preferred to interrupt-and-reset mode, because 60 // the latter has the risk of WDT-induced bootloops if the bootloader doesn't 61 // correctly handle WDT resets. Whereas we have a physical reset button, if 62 // a hard reset is actually needed.) 63 WDTCSR = (1<<WDCE) | (1<<WDE); 64 WDTCSR = (1<<WDIE) | (0<<WDE) | (0<<WDP3) | (1<<WDP2) | (1<<WDP1) | (0<<WDP0); 65 66 // Enter Power-save mode 67 set_sleep_mode(SLEEP_MODE_PWR_DOWN); 68 sleep_enable(); 69 sei(); // Enable interrupts so we can actually wake 70 sleep_cpu(); // Actually go to sleep 71 // Zzzzzz 72 sleep_disable(); // We've woken up 73 wdt_disable(); // Disable watchdog for now 74 sei(); 75 // Check if circle key has been pressed (active-low) 76 // If not reset the watchdog and try again 77 } while(PINC&(1<<6)); 78 79 // Resume and reinitialize hardware 80 setup_hardware(); 81 }