Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
5c2dfe7
suspend-to-off: record the RTC WUT period in the rtc test mock
evgeny-boger Jul 7, 2026
6933439
suspend-to-off: sleep in STM32 Stop 1 during the off-mode window
evgeny-boger Jul 7, 2026
bff1190
suspend-to-off: test the Stop-1 wake classification
evgeny-boger Jul 7, 2026
60b2db4
suspend-to-off: tear down the Stop wake wiring on window exit
evgeny-boger Jul 7, 2026
15901ec
suspend-to-off: dismiss the Stop alarm line via a WPR-unlocked helper
evgeny-boger Jul 7, 2026
893ec1e
suspend-to-off: cover the real mcu-pwr Stop register/ISR behavior
evgeny-boger Jul 7, 2026
881f7e6
suspend-to-off: drive the Stop button wake through the real debounce …
evgeny-boger Jul 7, 2026
df0f545
suspend-to-off: mark the real Stop window start on the console + benc…
evgeny-boger Jul 8, 2026
4421ec8
suspend-to-off: do not re-deliver the wake button press to Linux afte…
evgeny-boger Jul 8, 2026
be7bb1c
suspend-to-off: press PWRON immediately on wake - the PMIC never self…
evgeny-boger Jul 8, 2026
40adfd3
usart: actually wait for TX completion - Stop froze the console mid-f…
evgeny-boger Jul 8, 2026
4060b9b
suspend-to-off: beep to confirm a button wake; alarm/deadline wakes s…
evgeny-boger Jul 9, 2026
93d307e
DIAGNOSTIC: trace button-wake classification, IRQ latch, beep calls o…
evgeny-boger Jul 9, 2026
65f3d72
suspend-to-off: fix the four button-wake failures the diagnostic run …
evgeny-boger Jul 9, 2026
06cf384
suspend-to-off: wake on press CONFIRMATION - beep while the button is…
evgeny-boger Jul 9, 2026
21c9663
suspend-to-off: a resumed system is booted - preserve linux_booted ac…
evgeny-boger Jul 9, 2026
959db0d
suspend-to-off: strip the bench diagnostic console prints
evgeny-boger Jul 9, 2026
70eb9c9
suspend-to-off: delay + retry the wake PWRON so the PMIC can't swallo…
evgeny-boger Jul 9, 2026
30b658c
Revert "suspend-to-off: delay + retry the wake PWRON so the PMIC can'…
evgeny-boger Jul 9, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions include/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,15 @@
#define WBEC_PERIODIC_WAKEUP_FIRST_TIMEOUT_S 5
#define WBEC_PERIODIC_WAKEUP_NEXT_TIMEOUT_S 2

// suspend-to-off: период периодического пробуждения (RTC WUT) во время окна
// сна в режиме STM32 Stop 1. Выбран < 10 с (период аппаратного IWDG): каждое
// пробуждение по WUT кормит IWDG и продвигает дедлайн-бэкстоп (systick в Stop
// не идёт). 4 с даёт запас к IWDG и пренебрежимо мало по току.
#define WBEC_SUSPEND_STOP_FEED_PERIOD_S 4
// Сколько EC остаётся бодрствовать после пробуждения по фронту кнопки, давая
// антидребезгу (500 мс) подтвердить короткое нажатие, прежде чем снова уснуть.
#define WBEC_SUSPEND_STOP_BUTTON_GRACE_MS 700

// Число попыток перезапуска PMIC при пропадании 3.3В
// Если за указанное время 3.3В пропадёт больше, чем указанное число раз,
// то EC выключит питание и уйдёт в спящий режим
Expand All @@ -74,6 +83,11 @@
// Пищалка
#define EC_BUZZER_BEEP_FREQ 1000
#define EC_BUZZER_BEEP_POWERON_MS 100
// suspend-to-off: бип-подтверждение пробуждения по кнопке. Звучит в такте
// классификации (SoC ещё в сбросе, PMIC спит) и обязан закончиться до нажатия
// PWRON (~105 мс после классификации) - задолго до хрупкой ре-инициализации
// DRAM в SPL (~0.6 с), которую бип срывает (провал f3face8).
#define EC_BUZZER_BEEP_SUSPEND_WAKE_MS 100
#define EC_BUZZER_BEEP_SHORT_PRESS_MS 300
#define EC_BUZZER_BEEP_LONG_PRESS_MS 1000

Expand Down
7 changes: 7 additions & 0 deletions include/gpio-subsystem.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
#pragma once

#include <stdbool.h>

void gpio_init(void);
void gpio_reset(void);
void gpio_do_periodic_work(void);

// suspend-to-off: при on = true выключает V_OUT и держит его выключенным на всё
// окно сна (защита по V_IN в Stop не работает); при on = false возвращает
// штатное управление V_OUT.
void gpio_suspend(bool on);
7 changes: 7 additions & 0 deletions include/linux-power-control.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@ void linux_cpu_pwr_seq_init(bool on);
void linux_cpu_pwr_seq_off_and_goto_standby(uint16_t wakeup_after_s);
void linux_cpu_pwr_seq_on(void);
void linux_cpu_pwr_seq_wakeup(void);
// suspend-to-off: запросить бип-подтверждение кнопочного пробуждения. Пищалка
// (SG1) запитана от 3.3В, которое во время окна сна ВЫКЛЮЧЕНО - звук физически
// невозможен до рестарта PMIC. Бип выдаётся последовательностью пробуждения в
// первый момент, когда 3.3В вернулось, а SoC ещё удержан в сбросе (безопасно
// для DRAM: ре-инициализация ещё не началась); импульс PWROK откладывается до
// конца бипа.
void linux_cpu_pwr_seq_wake_beep_request(void);
void linux_cpu_pwr_seq_hard_off(void);
void linux_cpu_pwr_seq_hard_reset(void);
void linux_cpu_pwr_seq_reset_pmic(void);
Expand Down
30 changes: 30 additions & 0 deletions include/mcu-pwr.h
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#pragma once
#include <stdbool.h>
#include "wbmcu_system.h"
#include "config.h"

Expand All @@ -21,3 +22,32 @@ void mcu_goto_standby(uint16_t wakeup_after_s);
enum mcu_vcc_5v_state mcu_get_vcc_5v_last_state(void);
void mcu_save_vcc_5v_last_state(enum mcu_vcc_5v_state state);
void mcu_check_vbat_do_periodic_work(void);

// --- suspend-to-off: окно сна в режиме STM32 Stop 1 ---
// В отличие от Standby, Stop сохраняет SRAM и защёлки GPIO и просыпается на
// месте (WFI возвращается, сброса НЕТ). Источники пробуждения заводятся через
// EXTI/NVIC, а не WKUP. Подробности — в ec-stop-mode-brief.md.

// Однократно вооружает источники пробуждения окна Stop:
// - RTC (Alarm A + WUT, прямая линия EXTI 19) -> IMR19 + NVIC(RTC_TAMP_IRQn);
// - кнопка PWRON (PA0, фронт вниз = нажатие) -> EXTI0 + NVIC(EXTI0_1_IRQn);
// и маскирует EXTI9 (SoC-CS/SPI2), чтобы тёмный SoC не будил EC ложно.
// Идемпотентно; вызывать один раз при входе в окно.
void mcu_stop_window_prepare(void);
// Возвращает прерывания к состоянию до окна сна: снимает EXTI-источники
// пробуждения Stop (кнопка EXTI0, RTC EXTI19) и восстанавливает маску EXTI9,
// наложенную mcu_stop_window_prepare(). Вызывать один раз на выходе окна по
// реальному пробуждению (не на кормящих ре-входах).
void mcu_stop_window_finish(void);
// Кормит IWDG и уходит в STM32 Stop 1 (LPMS=001, SLEEPDEEP, WFI). Перед сном
// сбрасывает только устаревшие WUT/кнопка/CWUF (НИКОГДА не CALRAF — реальный
// будильник обязан разбудить). Блокируется до пробуждения по EXTI (тик WUT,
// будильник или фронт кнопки); на выходе снова кормит IWDG и восстанавливает
// тактирование 64 МГц, systick и АЦП. Возвращается после пробуждения.
void mcu_stop_enter(void);
// Потребляет признак «проснулись по тику WUT» (взводит ISR RTC). true один раз
// на каждый истёкший период WUT — база кормления IWDG и дедлайна.
bool mcu_stop_take_wut_tick(void);
// Потребляет признак «проснулись по фронту кнопки» (взводит ISR EXTI0). true
// один раз, если пробуждение было фронтом PWRON, ещё не прошедшим антидребезг.
bool mcu_stop_take_button_wake(void);
4 changes: 4 additions & 0 deletions include/rtc.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ void rtc_set_alarm(const struct rtc_alarm * alarm);
uint16_t rtc_get_offset(void);
void rtc_set_offset(uint16_t offeset);
void rtc_clear_alarm_flag(void);
// Снимает ALRAIE (разрешение прерывания будильника) под разблокировкой WPR, НЕ
// трогая флаг ALRAF. Снимает прямую линию EXTI19 будильника в режиме Stop,
// сохраняя защёлку ALRAF для опросчика rtc_alarm_do_periodic_work.
void rtc_mask_alarm_irq(void);
void rtc_enable_pc13_1hz_clkout(void);
void rtc_disable_pc13_1hz_clkout(void);
void rtc_enable_pa4_1hz_clkout(void);
Expand Down
5 changes: 5 additions & 0 deletions include/temperature-control.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,8 @@ int16_t temperature_control_get_temperature_c_x100(void);
// При force_enable = false нагреватель управляется по температуре
void temperature_control_heater_force_control(bool force_enable);

// suspend-to-off: при on = true выключает нагреватель и держит его выключенным
// на всё окно сна (термостат и NTC в Stop не работают); при on = false
// возвращает управление по температуре.
void temperature_control_suspend(bool on);

7 changes: 7 additions & 0 deletions include/voltage-monitor.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,10 @@ bool vmon_ready(void);
bool vmon_get_ch_status(enum vmon_channel ch);
bool vmon_check_ch_once(enum vmon_channel ch);
void vmon_do_periodic_work(void);

// Перевзводит окно «устаканивания» после пробуждения из STM32 Stop: делает
// vmon снова «не готов» и перезапускает 100 мс задержку. АЦП в Stop стоит, и
// первый DMA-отсчёт после пробуждения переинициализирует фильтр одним сырым
// измерением; без перевзвода одиночный низкий V50 мог бы увести
// последовательность питания в Standby. Фильтр АЦП при этом НЕ трогаем.
void vmon_suspend_rearm_settle(void);
18 changes: 18 additions & 0 deletions src/gpio-subsytem.c
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,10 @@ struct gpio_ctx {
uint16_t gpio_ctrl;
uint16_t gpio_dir;
uint16_t gpio_af;
// suspend-to-off: во время окна сна V_OUT держим выключенным. V_IN в Stop
// не измеряется, защита по перенапряжению (> 28 В) работать не может, а
// защёлка GPIO сохраняется — безопасное состояние — OFF.
bool suspend_off;
};

static struct gpio_ctx gpio_ctx = {
Expand Down Expand Up @@ -161,11 +165,25 @@ static void set_mod_gpio_af(void)

static void control_v_out(void)
{
if (gpio_ctx.suspend_off) {
// Окно suspend-to-off: держим V_OUT выключенным (защита по V_IN не
// может работать в Stop), не трогая пользовательский бит gpio_ctrl.
set_v_out_state(false);
return;
}
bool v_in_is_in_proper_range = vmon_get_ch_status(VMON_CHANNEL_V_OUT);
bool v_out_ctrl = gpio_ctx.gpio_ctrl & BIT(EC_EXT_GPIO_V_OUT);
set_v_out_state(v_in_is_in_proper_range && v_out_ctrl);
}

void gpio_suspend(bool on)
{
gpio_ctx.suspend_off = on;
if (on) {
set_v_out_state(false);
}
}

static void set_mod_gpio_values(uint16_t new_ctrl)
{
gpio_ctx.gpio_ctrl_req_val = new_ctrl;
Expand Down
57 changes: 56 additions & 1 deletion src/linux-power-control.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include "regmap-int.h"
#include "wbmz-common.h"
#include "wdt-stm32.h"
#include "buzzer.h"

static const gpio_pin_t gpio_linux_power = { EC_GPIO_LINUX_POWER };
static const gpio_pin_t gpio_pmic_pwron = { EC_GPIO_LINUX_PMIC_PWRON };
Expand All @@ -27,6 +28,7 @@ enum pwr_state {
PS_ON_STEP1_WAIT_3V3, // Ждём, пока появится 3.3В
PS_ON_STEP2_PMIC_PWRON, // Если 3.3В не появляется, пробуем включить PMIC "нажатием" на PWRON
PS_ON_STEP3_PMIC_PWRON_OFF_WAIT, // Если и это не помогло, отпускаем PWRON и делаем несколько попыток
PS_WAKE_BEEP_WAIT, // Бип-подтверждение кнопочного пробуждения (3.3В уже есть, SoC ещё в сбросе)
PS_ON_COMPLETE,

PS_RESET_5V_WAIT, // Нужно при перезаргрузке - выключаем 5В и ждём разрядку линий
Expand All @@ -39,6 +41,8 @@ struct pwr_ctx {
// импульс на PWROK - PMIC при выходе из сна восстанавливает
// питание, но не выдаёт сброс, и SoC сам не стартует
bool wake_pending;
// Бип-подтверждение кнопочного пробуждения, отложенный до возврата 3.3В
bool wake_beep;
enum pwr_state state;
systime_t timestamp;
unsigned attempt;
Expand Down Expand Up @@ -126,6 +130,11 @@ void linux_cpu_pwr_seq_off_and_goto_standby(uint16_t wakeup_after_s)
mcu_goto_standby(wakeup_after_s);
}

void linux_cpu_pwr_seq_wake_beep_request(void)
{
pwr_ctx.wake_beep = true;
}

/**
* @brief Включает питание линукс штатным способом:
* Включается 5В, затем контролируется появление 3.3В.
Expand Down Expand Up @@ -293,15 +302,42 @@ void linux_cpu_pwr_seq_do_periodic_work(void)
// Питание восстановлено после сна PMIC: SoC ещё в
// сбросе, толкаем его импульсом на PWROK
pwr_ctx.wake_pending = false;
if (pwr_ctx.wake_beep) {
// Кнопочное пробуждение: первый момент, когда пищалка
// (запитана от 3.3В!) может звучать. SoC ещё в сбросе,
// DRAM в автономном самообновлении - безопасно (f3face8).
pwr_ctx.wake_beep = false;
buzzer_beep(EC_BUZZER_BEEP_FREQ, EC_BUZZER_BEEP_SUSPEND_WAKE_MS);
new_state(PS_WAKE_BEEP_WAIT);
break;
}
pmic_reset_gpio_on();
new_state(PS_WARM_RESET_PULSE);
break;
}
// Если 3.3В появилось, то считаем что питание включено
new_state(PS_ON_COMPLETE);
break;
}
if (pwr_ctx.wake_pending) {
// Пробуждение из suspend-to-off: 5В не пропадало, PMIC спит и сам
// 3.3В никогда не поднимет - секундное ожидание самозапуска здесь
// гарантированно мёртвое время (~1 с на КАЖДОМ пробуждении,
// стенд 2026-07-08). Жмём PWRON сразу; показание V33 к этому
// моменту уже отфильтровано (вход в do_periodic_work закрыт
// vmon_ready() - 100 мс устаканивания после Stop), а ретраи
// STEP2/STEP3 остаются штатным fallback. Случай «3.3В присутствует»
// (поздний отказ SoC от сна) обработан веткой выше: импульс PWROK
// без нажатия.
console_print_w_prefix("Suspend-to-off: PMIC is sleeping, press PWRON now\r\n");
pmic_pwron_gpio_on();
pwr_ctx.attempt = 0;
new_state(PS_ON_STEP2_PMIC_PWRON);
break;
}
if (in_state_time_ms() > 1000) {
// Если 3.3В не появилось, то попробуем включить PMIC через PWRON
// Обычное включение (после подачи 5В PMIC должен стартовать сам).
// Если 3.3В не появилось за 1 с - пробуем включить PMIC через PWRON
console_print_w_prefix("No voltage on 3.3V line, try to switch on PMIC throught PWRON\r\n");
pmic_pwron_gpio_on();
pwr_ctx.attempt = 0;
Expand All @@ -318,6 +354,14 @@ void linux_cpu_pwr_seq_do_periodic_work(void)
pmic_pwron_gpio_off();
if (pwr_ctx.wake_pending) {
pwr_ctx.wake_pending = false;
if (pwr_ctx.wake_beep) {
// См. PS_ON_STEP1_WAIT_3V3: бип при вернувшемся 3.3В,
// SoC ещё в сбросе; PWROK - после бипа.
pwr_ctx.wake_beep = false;
buzzer_beep(EC_BUZZER_BEEP_FREQ, EC_BUZZER_BEEP_SUSPEND_WAKE_MS);
new_state(PS_WAKE_BEEP_WAIT);
break;
}
pmic_reset_gpio_on();
new_state(PS_WARM_RESET_PULSE);
break;
Expand Down Expand Up @@ -349,6 +393,17 @@ void linux_cpu_pwr_seq_do_periodic_work(void)
}
break;

// Бип-подтверждение кнопочного пробуждения: 3.3В вернулось (пищалка
// запитана), SoC удержан в сбросе, DRAM в самообновлении - ре-инициализация
// ещё не начата, звук безопасен (урок f3face8). Импульс PWROK откладывается
// до конца бипа (+~120 мс к resume).
case PS_WAKE_BEEP_WAIT:
if (in_state_time_ms() > (EC_BUZZER_BEEP_SUSPEND_WAKE_MS + 20)) {
pmic_reset_gpio_on();
new_state(PS_WARM_RESET_PULSE);
}
break;

// Сброс питания 5В
case PS_RESET_5V_WAIT:
if (in_state_time_ms() > WBEC_POWER_RESET_TIME_MS) {
Expand Down
Loading