UI_ShowSetTimePage() cleared and redrew the entire screen on every
500ms blink toggle and on every up/down value change, causing a
full-screen flash instead of just the intended hour/minute digit
blinking. Split out UI_UpdateSetTimeDigit() which only repaints the
32x32px digit region for the field being edited; the full page draw
is now only used on entry. Field-switch (long-press CONFIRM) refreshes
both digit regions to clear any stale blanked-out state.
Adds KEY_EVENT_SECOND_TICK, generated once per real second from
Clock_IncMS() via a new second_tick_flag and pushed through the normal
event queue, so every foreground App's onEvent() receives it and
decides independently whether to act. Removes the old global
inactivity_timer (60s sleep from anywhere) entirely. HomeApp now
accumulates its own idle-second counter and sleeps after 10s idle
specifically while on the home screen; MenuApp's existing 10s
auto-close is converted from onRun() polling to the same tick+reset
pattern for consistency. Any non-tick key event resets each app's own
counter to 0.
Confirms via code inspection that the firmware isn't uniformly driven
by MAIN_Fosc: Uart1_Init()'s Timer2 baud-rate reload and rf.c/timer.c's
tick-to-microsecond conversion assume an actual 24MHz clock as
hardcoded constants, not values computed from MAIN_Fosc. Flashing at a
different STC-ISP clock setting would silently break UART baud rate
and RF timing without an obvious compile error.
RF_HandleEdgeInterrupt()'s software ping-pong toggle can leave P3IM0
parked in either edge direction while awake; Enter_Low_Power_Sleep()
never reset it, so sleep entry could randomly land on a direction that
misses the wake edge. Per the datasheet truth table (WO-04 2.9), only
edge modes support power-down wake, not level modes, so this forces a
known rising-edge state (matching RF_Init()'s own boot default) instead
of switching to level-triggered as originally requested. wo-05 updated
to record the deviation and rationale.
menu_app now tracks the last key-activity timestamp (via current_sec,
same wraparound-safe pattern already used in pair_app) and switches
back to home_app if no key event arrives for 10 seconds, using the
AppManager's existing per-frame onRun() callback as the check point.
Both the static initializer and Lang_Init()'s erased-flash fallback
now default to LANG_EN, so first boot / never-written flash shows
English instead of French.
SGM_CTRL was turned on at the very start of LCD_Init(), before the
ST7789V register sequence and first frame ran, showing garbage/blank
content briefly on every boot. GPIO_Init() now forces backlight off at
power-on, LCD_Init() no longer touches it, and main() only turns it on
right before entering the main loop (after HomeApp's first frame is
drawn). Wakeup_Restore()'s existing on-then-reinit sequence is
untouched since it sets the level itself before calling LCD_Init().
Splits the ad-hoc menu_level state machine into standalone Apps with a
uniform parent/child exit convention (long-press CONFIRM -> parent):
clock_app renamed to home_app (boot-default), new set_clock_app
extracted from home_app's time-edit mode, new ipc_bind_app extracted
from menu_app's old level-2 screen, and the sensor-type selection step
merged into pair_app as its new initial state. menu_app now only holds
the 4-item top-level list (language toggle inline). Also makes both
menu_app and pair_app's up/down navigation cyclic (wraps at both ends).
Confirms clock_app is the boot-default app (main.c:280) and should be
renamed home_app; time editing becomes its own set_clock_app instead
of time_set_app. Drops all remaining menu_level wording, and
cross-references items 1/2 to the renamed APP_ID_HOME/home_app.
Replace the ad-hoc menu_level state machine plan with a uniform
App-per-screen model (home_app/menu_app/time_set_app/pair_app/
ipc_bind_app), each child App exiting to its own parent via long-press
CONFIRM. Folds the sensor-type selection screen into pair_app as a new
initial state, and updates item 5's cyclic-navigation notes to match.
Lists boot-flicker backlight timing, menu idle auto-close, submenu
long-press-confirm exit, default language, cyclic menu navigation, and
P3.6 sleep-wake trigger reliability, each with a brief implementation
approach based on current code.
Multiple real transmissions (Data=08/04/02/01) decoded correctly and
stably with the ping-pong single-edge trigger fix, no interrupt storm
or crash observed. Closes out the phase-2 RF decode root-cause saga.
Root cause found via datasheet: PxIM0/PxIM1 has no dual-edge mode, only
falling/rising/low-level/high-level. (IM1=1,IM0=1) was actually
high-level interrupt, refiring continuously for the whole high-pulse
duration (confirmed by scope: no real HF noise, and edge_count=0 when
P3.6 grounded). Fix: start in rising-edge mode, software-toggle to the
opposite edge on every trigger (ping-pong) to emulate true dual-edge
triggering, feeding exact edge direction into the incremental decode
state machine (no glitch filter needed). Also switch P3.6 idle bias
from pull-up to pull-down, since floating-high under the old high-level
mode caused the interrupt-storm boot issue; removed the now-conflicting
pull-up restore in Wakeup_Restore().
Diagnostic experiment: restore the commit d8df5d4 polling decode
algorithm/thresholds unchanged, while P3.6 ISR now only increments an
edge counter (no decode state machine). Successful decodes print the
edge-count delta over the decode window to compare against the
EV1527 theoretical 50-edges-per-frame, to validate/refute the
edge-interrupt glitch-noise hypothesis.
TH0/TL0 torn-read protection didn't cover the case where the hardware
counter already wrapped but Timer0_Isr hasn't run yet to bump
timer0_ovf_count, making the timestamp appear to jump backward by one
overflow period (~32.768ms). Read TF0 under a brief EA guard and treat
a pending-but-unserviced overflow as ovf+1 without clearing it.
App/isr_jump.asm was never added to wristband.uvproj, so the Port0/Port2/Port3
hardware vector redirects it defines were never assembled. Port3_Isr (declared
as interrupt 16) never actually ran on real P3 port events, causing the
edge-interrupt-driven RF decode to silently never fire. Adds the missing
CSEG/LJMP redirect for vector 40 and registers the file as FileType=2.