reform

MNT Reform: Open Source Portable Computer
Log (Feed) | Files | Refs (Tags) | README

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 }