mirror of
https://github.com/MCKero6423/uv-k5-v3-emulator.git
synced 2026-10-02 03:15:36 +00:00
Fix the keypad: row_out must be volatile
The previous commit removed three TRACE fprintfs from py32f071.c as
cleanup. That silently broke the keypad completely -- no press reached the
UI, and nothing warned about it.
Root cause is dead-code elimination, not the printing.
qdev_init_gpio_out_named() is inlinable and only records the row_out array;
the lines are filled in later by qdev_connect_gpio_out_named() from board
code, which GCC cannot see. At -O2 GCC therefore proves every element is
still NULL, sees that qemu_set_irq() returns immediately on a NULL irq, and
deletes the body of keypad_update_rows() along with all five calls to it. No
row line is ever driven and the firmware's scan reads all-high.
From the object code:
callers reaching keypad_update_rows
plain none -- the calls are gone
volatile keypad_key_changed, keypad_col_changed, keypad_set_press,
keypad_reset, uvk5_machine_init
keypad_col_changed compiles to a store and a ret with no call at all; with
volatile it ends in jmp keypad_update_rows. Declaring row_out volatile fixes
it at the cause. 10/10 on the press test, 3/3 on keypad_test.py, no build
warnings.
Scoped rather than assumed: PY32GpioState::out is not affected. Marking it
volatile too gives a byte-identical object file, because py32_gpio_write()
is only reachable through a MemoryRegionOps function-pointer table so GCC
cannot enumerate its callers. It stays plain.
Adds tools/keypad_test.py: boots its own instance on private ports and
checks that a short MENU press opens the menu, DOWN moves the cursor, and a
held key is visible to the scan. This is what should have caught the
breakage before it was pushed.
Docs corrected. The breakage had been written up as "power save stops the
keypad scan" and called a gap in the model; it was neither. AGENTS.md now
records the mechanism, the measurements, the objdump check, and the two
measurement traps that made this hard: reading gKeyReading0 after releasing
the key (always KEY_INVALID), and trusting a gdb breakpoint on
KEYBOARD_Poll (with the guest stopped the scan's delays cost no guest time,
so Poll returns KEY_MENU on a build where it fails when running free).
README screenshots regenerated from the current build.
This commit is contained in:
1 parent
1c9a2afe55
commit
2154f80414
6 files changed
+357
-49
No files matched your search
@@ -9,12 +9,12 @@ is not modelled.
|
||||
|
||||
| Main screen | Menu | Navigated with keys |
|
||||
| --- | --- | --- |
|
||||
|  |  |  |
|
||||
|  |  |  |
|
||||
|
||||
Real captures, not mock-ups: `tools/screenshot.py` reads the firmware's
|
||||
`gFrameBuffer` out of guest memory and renders it, so these are the pixels the
|
||||
LCD driver actually wrote. Left to right: the dual-watch main screen, the menu
|
||||
opened with `key.py MENU`, and entry 30/79 reached with keypresses.
|
||||
opened with `key.py MENU` (entry 01/79, Step), and 03/79 after `key.py DOWN DOWN`.
|
||||
|
||||
## What it is for
|
||||
|
||||
@@ -37,7 +37,7 @@ has no public datasheet, so its driver is the only specification available.
|
||||
| Boot to main loop | works, ~5 s |
|
||||
| LCD contents | readable via `tools/screenshot.py` |
|
||||
| SPI flash, settings, calibration | works |
|
||||
| Keypad and menu navigation | works while the radio is awake; power save stops the scan, see below |
|
||||
| Keypad and menu navigation | works, including waking from power save |
|
||||
| Timing accuracy | deliberately wrong, see [Timing](#timing) |
|
||||
| Radio/RF behaviour | not modelled |
|
||||
|
||||
@@ -46,15 +46,19 @@ a submenu, and typing a menu number jumps straight to that entry. Press duration
|
||||
decides short versus held, which the firmware treats as different events -- see
|
||||
[Timing](#timing).
|
||||
|
||||
The limitation is power save. Around six seconds after boot the firmware enters
|
||||
it (`gCurrentFunction` becomes `FUNCTION_POWER_SAVE`) and stops scanning the
|
||||
keypad, so keys are ignored from then on. A real radio wakes on a keypress, so
|
||||
this is a gap in the machine model rather than firmware behaviour.
|
||||
Press duration is the thing to get right. A hold of 400 ms or more is a *long*
|
||||
press, and handlers act on it differently: `MAIN_Key_MENU` opens the menu on a
|
||||
short release and does nothing on the hold path. If a key seems ignored, shorten
|
||||
the press rather than lengthening it. Waking from power save needs nothing
|
||||
special -- one 200 ms press both wakes the radio and opens the menu, verified
|
||||
after 45 s of idle.
|
||||
|
||||
In practice it is not much of an obstacle: keypad activity keeps the radio awake,
|
||||
and being in the menu blocks power save entirely. Open the menu within the first
|
||||
few seconds of boot and the session stays usable. `AGENTS.md` has the details,
|
||||
including two approaches that look like fixes and are not.
|
||||
`tools/keypad_test.py` checks all of this against a throwaway QEMU instance. It
|
||||
exists because the keypad has one non-obvious trap: the keypad model's `row_out`
|
||||
array must stay `volatile`, or GCC at -O2 proves the lines are still NULL and
|
||||
deletes every call to `keypad_update_rows()`, so no row is ever driven and
|
||||
keypresses silently stop working. Run the test after touching that code;
|
||||
`AGENTS.md` has the object-code evidence.
|
||||
|
||||
## Layout
|
||||
|
||||
|
||||
Reference in new issue
Block a user