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 alt_hold_key;
|
||||||
static struct hold_key sym_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)
|
static bool transition_hold_key_state(struct hold_key* hold_key, bool const pressed)
|
||||||
{
|
{
|
||||||
uint key_held_for;
|
uint key_held_for;
|
||||||
|
@ -146,13 +149,29 @@ static void handle_power_key_event(bool pressed)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Normal press / release sends KEY_STOP
|
// Normal press / release sends KEY_STOP
|
||||||
if ((power_hold_key.state == KEY_STATE_PRESSED)
|
if (power_hold_key.state == KEY_STATE_PRESSED) {
|
||||||
|| (power_hold_key.state == KEY_STATE_RELEASED)) {
|
|
||||||
keyboard_inject_event(KEY_STOP, power_hold_key.state);
|
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
|
// Short press while driver unloaded powers Pi on
|
||||||
} if (power_hold_key.state == KEY_STATE_HOLD) {
|
} 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) {
|
if (reg_get_value(REG_ID_DRIVER_STATE) == 0) {
|
||||||
pi_cancel_power_alarms();
|
pi_cancel_power_alarms();
|
||||||
pi_power_on(POWER_ON_BUTTON);
|
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 driver loaded, send power off
|
||||||
if (reg_get_value(REG_ID_DRIVER_STATE) > 0) {
|
if (reg_get_value(REG_ID_DRIVER_STATE) > 0) {
|
||||||
|
|
||||||
// Schedule power off
|
// Schedule power off with dormancy
|
||||||
uint32_t shutdown_grace_ms = MAX(
|
uint32_t shutdown_grace_ms = MAX(
|
||||||
reg_get_value(REG_ID_SHUTDOWN_GRACE) * 1000,
|
reg_get_value(REG_ID_SHUTDOWN_GRACE) * 1000,
|
||||||
MINIMUM_SHUTDOWN_GRACE_MS);
|
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/rtc.h>
|
||||||
|
|
||||||
|
#include "hardware/clocks.h"
|
||||||
|
#include "hardware/rosc.h"
|
||||||
|
#include "hardware/structs/scb.h"
|
||||||
|
|
||||||
#include "backlight.h"
|
#include "backlight.h"
|
||||||
#include "debug.h"
|
#include "debug.h"
|
||||||
#include "gpioexp.h"
|
#include "gpioexp.h"
|
||||||
|
@ -12,9 +16,12 @@
|
||||||
#include "puppet_i2c.h"
|
#include "puppet_i2c.h"
|
||||||
#include "reg.h"
|
#include "reg.h"
|
||||||
#include "touchpad.h"
|
#include "touchpad.h"
|
||||||
#include "usb.h"
|
|
||||||
#include "pi.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
|
// 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)
|
static void gpio_irq(uint gpio, uint32_t events)
|
||||||
{
|
{
|
||||||
|
@ -26,10 +33,7 @@ static void gpio_irq(uint gpio, uint32_t events)
|
||||||
// TODO: Microphone
|
// TODO: Microphone
|
||||||
int main(void)
|
int main(void)
|
||||||
{
|
{
|
||||||
// The here order is important because it determines callback call order
|
// This order is important because it determines callback call order
|
||||||
#ifdef HAVE_USB
|
|
||||||
usb_init();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
debug_init();
|
debug_init();
|
||||||
|
@ -55,8 +59,11 @@ int main(void)
|
||||||
gpio_set_irq_enabled_with_callback(0xFF, 0, true, &gpio_irq);
|
gpio_set_irq_enabled_with_callback(0xFF, 0, true, &gpio_irq);
|
||||||
|
|
||||||
led_init();
|
led_init();
|
||||||
|
|
||||||
pi_power_init();
|
pi_power_init();
|
||||||
|
|
||||||
|
dormant_until_power_key();
|
||||||
|
|
||||||
pi_power_on(POWER_ON_FW_INIT);
|
pi_power_on(POWER_ON_FW_INIT);
|
||||||
|
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
|
|
131
app/pi.c
131
app/pi.c
|
@ -6,7 +6,12 @@
|
||||||
#include "hardware/adc.h"
|
#include "hardware/adc.h"
|
||||||
#include <hardware/pwm.h>
|
#include <hardware/pwm.h>
|
||||||
|
|
||||||
|
#include "hardware/clocks.h"
|
||||||
|
#include "hardware/rosc.h"
|
||||||
|
#include "hardware/structs/scb.h"
|
||||||
|
|
||||||
#include <pico/stdlib.h>
|
#include <pico/stdlib.h>
|
||||||
|
#include <pico/sleep.h>
|
||||||
|
|
||||||
#define LED_FLASH_CYCLE_MS 3000
|
#define LED_FLASH_CYCLE_MS 3000
|
||||||
#define LED_FLASH_ON_MS 200
|
#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);
|
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) {
|
if (g_power_off_alarm < 0) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
pi_power_off();
|
pi_power_off();
|
||||||
|
if (dormant_flag != NULL) {
|
||||||
|
*dormant_flag = 1;
|
||||||
|
dormant_until_power_key();
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
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
|
// Cancel existing alarm if scheduled
|
||||||
if (g_power_off_alarm >= 0) {
|
if (g_power_off_alarm >= 0) {
|
||||||
|
@ -111,7 +122,18 @@ void pi_schedule_power_off(uint32_t ms)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Schedule new alarm
|
// 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()
|
void pi_cancel_power_alarms()
|
||||||
|
@ -170,7 +192,7 @@ void led_init(void)
|
||||||
// Default off
|
// Default off
|
||||||
g_led_state.setting = LED_SET_OFF;
|
g_led_state.setting = LED_SET_OFF;
|
||||||
g_led_flash_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* __)
|
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);
|
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_power_off(void);
|
||||||
|
|
||||||
void pi_schedule_power_on(uint32_t ms);
|
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();
|
void pi_cancel_power_alarms();
|
||||||
|
|
||||||
enum led_setting
|
enum led_setting
|
||||||
|
@ -37,3 +38,4 @@ struct led_state
|
||||||
void led_init(void);
|
void led_init(void);
|
||||||
void led_set(struct led_state const* state);
|
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();
|
keyboard_inject_power_key();
|
||||||
|
|
||||||
// Power off with grace time to give Pi time to shut down
|
// 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
|
// Schedule power on
|
||||||
pi_schedule_power_on(rewake_ms);
|
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(
|
uint32_t shutdown_grace_ms = MAX(
|
||||||
reg_get_value(REG_ID_SHUTDOWN_GRACE) * 1000,
|
reg_get_value(REG_ID_SHUTDOWN_GRACE) * 1000,
|
||||||
MINIMUM_SHUTDOWN_GRACE_MS);
|
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,
|
add_alarm_in_ms(shutdown_grace_ms + 10,
|
||||||
update_commit_alarm_callback, NULL, true);
|
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)
|
void reg_init(void)
|
||||||
{
|
{
|
||||||
reg_set_value(REG_ID_CFG, CFG_OVERFLOW_INT | CFG_KEY_INT | CFG_USE_MODS);
|
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_DEB, 10);
|
||||||
reg_set_value(REG_ID_FRQ, 10); // ms
|
reg_set_value(REG_ID_FRQ, 10); // ms
|
||||||
reg_set_value(REG_ID_BK2, 255);
|
reg_set_value(REG_ID_BK2, 255);
|
||||||
|
|
Loading…
Reference in New Issue