diff --git a/armsrc/appmain.c b/armsrc/appmain.c index 1678a10cb..b67974745 100644 --- a/armsrc/appmain.c +++ b/armsrc/appmain.c @@ -4859,7 +4859,18 @@ void __attribute__((noreturn)) AppMain(void) { } #ifdef PM5 - pm5_power_idle(); + // WFI sleep adds ~40-50ms of latency before the main loop notices a + // fresh byte on CEP's SPI1 - fine at idle, but it's what made the + // original attach handshake miss bytes (see cep_attach_poll()'s own + // comment on the attach-edge bug this caused, GH #3667). Skip the + // sleep entirely while a Flipper is attached so the loop polls as + // fast as the hardware allows; cep_is_attached() drops back to false + // within CEP_ACTIVITY_TIMEOUT_MS of the FAP going away, so this + // isn't a permanent always-on cost. +#ifdef WITH_CEP + if (!cep_is_attached()) +#endif + pm5_power_idle(); #endif } } diff --git a/armsrc/pm5_cep.c b/armsrc/pm5_cep.c index ac9f425dc..cd5648fec 100644 --- a/armsrc/pm5_cep.c +++ b/armsrc/pm5_cep.c @@ -55,12 +55,23 @@ #define CEP_SYNC_BYTE 0x55 #define CEP_SYNC_MAX_DISCARD 600 +// How long cep_attach_poll() will go without seeing a real SPI poll from the +// Flipper before deciding the FAP has quit (see the comment at its one use +// below) and letting AppMain() go back to sleeping between iterations. +#define CEP_ACTIVITY_TIMEOUT_MS 3000 + static bool s_cep_active = false; // handshake completed, SPI transport live +static bool s_cep_attached = false; // CC controller reports a physical attach +static uint32_t s_last_spi_activity_tick = 0; // last time a real SPI poll was seen bool cep_is_active(void) { return s_cep_active; } +bool cep_is_attached(void) { + return s_cep_attached; +} + // --------------------------------------------------------------------------- // One-time peripheral bring-up // --------------------------------------------------------------------------- @@ -279,6 +290,7 @@ void cep_attach_poll(void) { } bool attached = ((cc_ctrl_data >> 6 & 0x03) != 0); + s_cep_attached = attached; if (!attached) { s_cep_active = false; @@ -294,6 +306,18 @@ void cep_attach_poll(void) { // can't hang the main loop. if (usart_flag_get(USART1, USART_RDBF_FLAG) != RESET) { s_cep_active = cep_do_handshake(); + if (s_cep_active) { + s_last_spi_activity_tick = GetTickCount(); + } + } + + // The FAP quitting doesn't touch the CC controller's attach bit - the + // cable stays connected, so `attached` alone can't tell us it's gone. + // Losing its SPI poll cadence (normally every 20-120ms) is what + // actually means it has - fall back to the slower/idle main loop + // once that cadence has been missing this long. + if (s_cep_active && (GetTickCountDelta(s_last_spi_activity_tick) > CEP_ACTIVITY_TIMEOUT_MS)) { + s_cep_active = false; } } @@ -336,6 +360,7 @@ bool cep_spi_data_available(void) { if (!cep_spi_read_byte(&len_header[1])) { return false; } + s_last_spi_activity_tick = GetTickCount(); // FAP is still polling, valid frame or not uint16_t data_len = (len_header[1] << 8) | len_header[0]; if (data_len > PM3_CMD_DATA_SIZE * 2) { return false; diff --git a/armsrc/pm5_cep.h b/armsrc/pm5_cep.h index d040bd1ef..28d7bb0d9 100644 --- a/armsrc/pm5_cep.h +++ b/armsrc/pm5_cep.h @@ -39,6 +39,13 @@ void cep_init(void); // Flipper is attached. bool cep_is_active(void); +// True once the CC controller reports a Flipper physically attached, +// independent of whether the handshake/SPI transport is up yet. Gate for +// AppMain()'s WFI-skip (see appmain.c) - a Flipper merely being plugged in +// is reason enough to stop sleeping between main-loop iterations, before +// any handshake has even started. +bool cep_is_attached(void); + // Rate-limited (see PM5_CEP_ATTACH_POLL_MS), non-blocking. Call once per // AppMain() main-loop iteration, alongside bwm_autooff_check(). Detects the // Flipper attach/detach transition via the CC controller and, on a fresh