Deep sleep after shutdown
This commit is contained in:
parent
10b344b2a6
commit
702c7f6d3a
|
@ -68,6 +68,9 @@ static struct hold_key rightshift_hold_key;
|
|||
static struct hold_key alt_hold_key;
|
||||
static struct hold_key sym_hold_key;
|
||||
|
||||
// Re-enter dormant status until Pi booted again
|
||||
static uint8_t reenter_dormancy = false;
|
||||
|
||||
static bool transition_hold_key_state(struct hold_key* hold_key, bool const pressed)
|
||||
{
|
||||
uint key_held_for;
|
||||
|
@ -146,13 +149,29 @@ static void handle_power_key_event(bool pressed)
|
|||
}
|
||||
|
||||
// Normal press / release sends KEY_STOP
|
||||
if ((power_hold_key.state == KEY_STATE_PRESSED)
|
||||
|| (power_hold_key.state == KEY_STATE_RELEASED)) {
|
||||
if (power_hold_key.state == KEY_STATE_PRESSED) {
|
||||
keyboard_inject_event(KEY_STOP, power_hold_key.state);
|
||||
|
||||
} else if (power_hold_key.state == KEY_STATE_RELEASED) {
|
||||
|
||||
// Power button events will wake out of dormancy
|
||||
// Dormancy should be retriggered until power key
|
||||
// is held long enough to rewake Pi
|
||||
if (reenter_dormancy) {
|
||||
dormant_until_power_key();
|
||||
|
||||
// Send power key event
|
||||
} else {
|
||||
keyboard_inject_event(KEY_STOP, power_hold_key.state);
|
||||
}
|
||||
|
||||
// Short press while driver unloaded powers Pi on
|
||||
} if (power_hold_key.state == KEY_STATE_HOLD) {
|
||||
|
||||
// Clear dormancy flag
|
||||
reenter_dormancy = 0;
|
||||
|
||||
// Turn Pi back on
|
||||
if (reg_get_value(REG_ID_DRIVER_STATE) == 0) {
|
||||
pi_cancel_power_alarms();
|
||||
pi_power_on(POWER_ON_BUTTON);
|
||||
|
@ -169,11 +188,11 @@ static void handle_power_key_event(bool pressed)
|
|||
// If driver loaded, send power off
|
||||
if (reg_get_value(REG_ID_DRIVER_STATE) > 0) {
|
||||
|
||||
// Schedule power off
|
||||
// Schedule power off with dormancy
|
||||
uint32_t shutdown_grace_ms = MAX(
|
||||
reg_get_value(REG_ID_SHUTDOWN_GRACE) * 1000,
|
||||
MINIMUM_SHUTDOWN_GRACE_MS);
|
||||
pi_schedule_power_off(shutdown_grace_ms);
|
||||
pi_schedule_power_off_dormant(shutdown_grace_ms, &reenter_dormancy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
17
app/main.c
17
app/main.c
|
@ -4,6 +4,10 @@
|
|||
|
||||
#include <hardware/rtc.h>
|
||||
|
||||
#include "hardware/clocks.h"
|
||||
#include "hardware/rosc.h"
|
||||
#include "hardware/structs/scb.h"
|
||||
|
||||
#include "backlight.h"
|
||||
#include "debug.h"
|
||||
#include "gpioexp.h"
|
||||
|
@ -12,9 +16,12 @@
|
|||
#include "puppet_i2c.h"
|
||||
#include "reg.h"
|
||||
#include "touchpad.h"
|
||||
#include "usb.h"
|
||||
#include "pi.h"
|
||||
|
||||
// https://github.com/micropython/micropython/blob/5114f2c1ea7c05fc7ab920299967595cfc5307de/ports/rp2/modmachine.c#L179
|
||||
// https://github.com/raspberrypi/pico-extras/issues/41
|
||||
#include "pico/sleep.h"
|
||||
|
||||
// since the SDK doesn't support per-GPIO irq, we use this global irq and forward it
|
||||
static void gpio_irq(uint gpio, uint32_t events)
|
||||
{
|
||||
|
@ -26,10 +33,7 @@ static void gpio_irq(uint gpio, uint32_t events)
|
|||
// TODO: Microphone
|
||||
int main(void)
|
||||
{
|
||||
// The here order is important because it determines callback call order
|
||||
#ifdef HAVE_USB
|
||||
usb_init();
|
||||
#endif
|
||||
// This order is important because it determines callback call order
|
||||
|
||||
#ifndef NDEBUG
|
||||
debug_init();
|
||||
|
@ -55,8 +59,11 @@ int main(void)
|
|||
gpio_set_irq_enabled_with_callback(0xFF, 0, true, &gpio_irq);
|
||||
|
||||
led_init();
|
||||
|
||||
pi_power_init();
|
||||
|
||||
dormant_until_power_key();
|
||||
|
||||
pi_power_on(POWER_ON_FW_INIT);
|
||||
|
||||
#ifndef NDEBUG
|
||||
|
|
131
app/pi.c
131
app/pi.c
|
@ -6,7 +6,12 @@
|
|||
#include "hardware/adc.h"
|
||||
#include <hardware/pwm.h>
|
||||
|
||||
#include "hardware/clocks.h"
|
||||
#include "hardware/rosc.h"
|
||||
#include "hardware/structs/scb.h"
|
||||
|
||||
#include <pico/stdlib.h>
|
||||
#include <pico/sleep.h>
|
||||
|
||||
#define LED_FLASH_CYCLE_MS 3000
|
||||
#define LED_FLASH_ON_MS 200
|
||||
|
@ -91,18 +96,24 @@ void pi_schedule_power_on(uint32_t ms)
|
|||
g_power_on_alarm = add_alarm_in_ms(ms, pi_power_on_alarm_callback, NULL, true);
|
||||
}
|
||||
|
||||
static int64_t pi_power_off_alarm_callback(alarm_id_t _, void* __)
|
||||
static int64_t pi_power_off_alarm_callback(alarm_id_t _, void* u8_ptr_dormant)
|
||||
{
|
||||
uint8_t *dormant_flag = (uint8_t*)u8_ptr_dormant;
|
||||
|
||||
if (g_power_off_alarm < 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
pi_power_off();
|
||||
if (dormant_flag != NULL) {
|
||||
*dormant_flag = 1;
|
||||
dormant_until_power_key();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void pi_schedule_power_off(uint32_t ms)
|
||||
static void pi_schedule_power_off(uint32_t ms, uint8_t* dormant_flag)
|
||||
{
|
||||
// Cancel existing alarm if scheduled
|
||||
if (g_power_off_alarm >= 0) {
|
||||
|
@ -111,7 +122,18 @@ void pi_schedule_power_off(uint32_t ms)
|
|||
}
|
||||
|
||||
// Schedule new alarm
|
||||
g_power_off_alarm = add_alarm_in_ms(ms, pi_power_off_alarm_callback, NULL, true);
|
||||
g_power_off_alarm = add_alarm_in_ms(ms, pi_power_off_alarm_callback,
|
||||
(void*)dormant_flag, true);
|
||||
}
|
||||
|
||||
void pi_schedule_power_off_live(uint32_t ms)
|
||||
{
|
||||
pi_schedule_power_off(ms, NULL);
|
||||
}
|
||||
|
||||
void pi_schedule_power_off_dormant(uint32_t ms, uint8_t* dormant_flag)
|
||||
{
|
||||
pi_schedule_power_off(ms, dormant_flag);
|
||||
}
|
||||
|
||||
void pi_cancel_power_alarms()
|
||||
|
@ -170,7 +192,7 @@ void led_init(void)
|
|||
// Default off
|
||||
g_led_state.setting = LED_SET_OFF;
|
||||
g_led_flash_state.setting = LED_SET_OFF;
|
||||
led_sync(false, 0, 0, 0);
|
||||
led_sync(true, 0, 0, 0);
|
||||
}
|
||||
|
||||
static int64_t pi_led_flash_alarm_callback(alarm_id_t _, void* __)
|
||||
|
@ -260,3 +282,104 @@ void led_set(struct led_state const* state)
|
|||
led_sync((state->setting == LED_SET_ON), state->r, state->g, state->b);
|
||||
}
|
||||
}
|
||||
|
||||
struct sleep_state
|
||||
{
|
||||
uint8_t keyboard_backlight;
|
||||
struct led_state led_state;
|
||||
uint scb_orig, clock0_orig, clock1_orig;
|
||||
};
|
||||
|
||||
static void sleep_prepare(struct sleep_state* ss)
|
||||
{
|
||||
// Save backlight and LED state
|
||||
ss->keyboard_backlight = reg_get_value(REG_ID_BKL);
|
||||
ss->led_state = g_led_state;
|
||||
|
||||
// Save existing clock states
|
||||
ss->scb_orig = scb_hw->scr;
|
||||
ss->clock0_orig = clocks_hw->sleep_en0;
|
||||
ss->clock1_orig = clocks_hw->sleep_en1;
|
||||
|
||||
// Clear LED and backlight
|
||||
reg_set_value(REG_ID_BKL, 0);
|
||||
led_sync(true, 0, 0, 0);
|
||||
}
|
||||
|
||||
static void sleep_resume(struct sleep_state const* ss)
|
||||
{
|
||||
// Recover from sleep
|
||||
rosc_write(&rosc_hw->ctrl, ROSC_CTRL_ENABLE_BITS);
|
||||
scb_hw->scr = ss->scb_orig;
|
||||
clocks_hw->sleep_en0 = ss->clock0_orig;
|
||||
clocks_hw->sleep_en1 = ss->clock1_orig;
|
||||
clocks_init();
|
||||
|
||||
// Restore LED and keyboard states
|
||||
led_set(&ss->led_state);
|
||||
reg_set_value(REG_ID_BKL, ss->keyboard_backlight);
|
||||
}
|
||||
|
||||
void dormant_until_power_key()
|
||||
{
|
||||
datetime_t t;
|
||||
struct sleep_state ss;
|
||||
|
||||
// Invalidate RTC
|
||||
t.year = 1970;
|
||||
t.month = 1;
|
||||
t.day = 1;
|
||||
t.dotw = 4;
|
||||
t.hour = 0;
|
||||
t.min = 0;
|
||||
t.sec = 0;
|
||||
|
||||
// Save clocks, LED, backlight
|
||||
sleep_prepare(&ss);
|
||||
|
||||
// Sleep until power key is pressed
|
||||
sleep_run_from_xosc();
|
||||
sleep_goto_dormant_until_pin(4, 0, 0);
|
||||
|
||||
// Restore clocks, LED, backlight
|
||||
sleep_resume(&ss);
|
||||
}
|
||||
|
||||
static void sleep_callback(void)
|
||||
{}
|
||||
|
||||
void dormant_seconds(int seconds)
|
||||
{
|
||||
struct sleep_state ss;
|
||||
datetime_t t;
|
||||
|
||||
// Save clocks, LED, backlight
|
||||
sleep_prepare(&ss);
|
||||
|
||||
// Get datetime offset
|
||||
rtc_get_datetime(&t);
|
||||
t.sec += seconds;
|
||||
while (t.sec >= 60) {
|
||||
t.sec -= 60;
|
||||
t.min += 1;
|
||||
}
|
||||
while (t.min >= 60) {
|
||||
t.min -= 60;
|
||||
t.hour += 1;
|
||||
}
|
||||
// May not work correctly around midnight...
|
||||
while (t.hour >= 24) {
|
||||
t.hour -= 24;
|
||||
t.day += 1;
|
||||
t.dotw = (t.dotw + 1) & 7;
|
||||
}
|
||||
|
||||
sleep_run_from_xosc();
|
||||
sleep_goto_sleep_until(&t, sleep_callback);
|
||||
|
||||
// Advance RTC
|
||||
rtc_set_datetime(&t);
|
||||
|
||||
// Restore clocks, LED, backlight
|
||||
sleep_resume(&ss);
|
||||
}
|
||||
|
|
4
app/pi.h
4
app/pi.h
|
@ -17,7 +17,8 @@ void pi_power_on(enum power_on_reason reason);
|
|||
void pi_power_off(void);
|
||||
|
||||
void pi_schedule_power_on(uint32_t ms);
|
||||
void pi_schedule_power_off(uint32_t ms);
|
||||
void pi_schedule_power_off_live(uint32_t ms);
|
||||
void pi_schedule_power_off_dormant(uint32_t ms, uint8_t* dormant_flag);
|
||||
void pi_cancel_power_alarms();
|
||||
|
||||
enum led_setting
|
||||
|
@ -37,3 +38,4 @@ struct led_state
|
|||
void led_init(void);
|
||||
void led_set(struct led_state const* state);
|
||||
|
||||
void dormant_until_power_key();
|
||||
|
|
|
@ -220,7 +220,7 @@ void reg_process_packet(uint8_t in_reg, uint8_t in_data, uint8_t *out_buffer, ui
|
|||
keyboard_inject_power_key();
|
||||
|
||||
// Power off with grace time to give Pi time to shut down
|
||||
pi_schedule_power_off(shutdown_grace_ms);
|
||||
pi_schedule_power_off_live(shutdown_grace_ms);
|
||||
|
||||
// Schedule power on
|
||||
pi_schedule_power_on(rewake_ms);
|
||||
|
@ -276,7 +276,7 @@ void reg_process_packet(uint8_t in_reg, uint8_t in_data, uint8_t *out_buffer, ui
|
|||
uint32_t shutdown_grace_ms = MAX(
|
||||
reg_get_value(REG_ID_SHUTDOWN_GRACE) * 1000,
|
||||
MINIMUM_SHUTDOWN_GRACE_MS);
|
||||
pi_schedule_power_off(shutdown_grace_ms);
|
||||
pi_schedule_power_off_live(shutdown_grace_ms);
|
||||
add_alarm_in_ms(shutdown_grace_ms + 10,
|
||||
update_commit_alarm_callback, NULL, true);
|
||||
}
|
||||
|
@ -375,7 +375,7 @@ void reg_clear_bit(enum reg_id reg, uint8_t bit)
|
|||
void reg_init(void)
|
||||
{
|
||||
reg_set_value(REG_ID_CFG, CFG_OVERFLOW_INT | CFG_KEY_INT | CFG_USE_MODS);
|
||||
reg_set_value(REG_ID_BKL, 255);
|
||||
reg_set_value(REG_ID_BKL, 128);
|
||||
reg_set_value(REG_ID_DEB, 10);
|
||||
reg_set_value(REG_ID_FRQ, 10); // ms
|
||||
reg_set_value(REG_ID_BK2, 255);
|
||||
|
|
Loading…
Reference in New Issue