From b4a76db6f3139a73b64f786262f3f0b7565355bf Mon Sep 17 00:00:00 2001 From: rootiest Date: Mon, 13 Apr 2026 12:57:14 -0400 Subject: [PATCH] docs: expand CLAUDE.md with K17 Max, branch names, and EEPROM notes - Rename dev branch references from q5_dev to dev/q5; add dev/k17 for K17 Max work - Add K17 Max build/flash commands using the correct rgb subvariant path (keychron/k17_max/ansi_encoder/rgb) to distinguish from the white LED variant - Add EEPROM Layout Notes section documenting the Q5 Max address map, VIA_EEPROM_MAGIC_ADDR pinning rules, and EECONFIG_KB_DATA_SIZE #undef requirement - Add Keychron RGB section capturing lessons from the persistence work: when to call eeconfig_init_custom_rgb(), version stamp placement, kc_rgb_save() mode persistence, retail_demo_enable 0xFF bug, and extern array requirements - Add DIP Switch section explaining the frame overlay approach and why direct rgb_matrix_mode() calls must be avoided --- CLAUDE.md | 46 ++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 40 insertions(+), 6 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 8dd34b7077..510441ca3b 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,13 +1,16 @@ -# CLAUDE.md - QMK Development (Keychron Q5 Max) +# CLAUDE.md - QMK Development (Keychron Q5 Max / K17 Max) -Guidelines and commands for the customized Keychron Q5 Max firmware project based on the `wireless_playground` fork. +Guidelines and commands for the customized Keychron firmware project based on the `wireless_playground` fork. ## Project Scope * **Origin:** [git.rootiest.dev/rootiest/qmk_firmware](https://git.rootiest.dev/rootiest/qmk_firmware) * **Upstream:** [github.com/Keychron/qmk_firmware](https://github.com/Keychron/qmk_firmware) (branch: `wireless_playground`) * **Primary Keyboard:** Keychron Q5 Max (ANSI Encoder) -* **Development Workflow:** Work is performed on the `q5_dev` branch before merging to `main`. +* **Secondary Keyboard:** Keychron K17 Max (occasionally) +* **Development Branches:** + * `dev/q5` — Q5 Max work-in-progress, merges to `main` + * `dev/k17` — K17 Max work-in-progress, merges to `main` * **Feature Goals:** Tap-Dance, expanded layers, advanced Chording, Unicode support, and Auto-correct. ## Build and Flash Commands @@ -28,6 +31,9 @@ Every `qmk` command below assumes the venv is active (or prefix each with ```bash # Build the Q5 Max ANSI Encoder firmware qmk compile -kb keychron/q5_max/ansi_encoder -km via + +# Build the K17 Max firmware (rgb variant; separate 'white' LED variant exists) +qmk compile -kb keychron/k17_max/ansi_encoder/rgb -km via ``` ### Flashing @@ -35,6 +41,9 @@ qmk compile -kb keychron/q5_max/ansi_encoder -km via ```bash # Flash the Q5 Max (requires the board to be in bootloader mode) qmk flash -kb keychron/q5_max/ansi_encoder -km via + +# Flash the K17 Max (requires the board to be in bootloader mode) +qmk flash -kb keychron/k17_max/ansi_encoder/rgb -km via ``` ### Environment Setup @@ -58,16 +67,41 @@ qmk config user.keymap=via ## Development Workflow -1. Verify the current branch is `q5_dev`. -2. Implement features in `keyboards/keychron/q5_max/ansi_encoder/keymaps/via/`. +1. Verify the current branch is `dev/q5` (Q5 Max) or `dev/k17` (K17 Max). +2. Implement features in the relevant keymap directory: + * Q5 Max: `keyboards/keychron/q5_max/ansi_encoder/keymaps/via/` + * K17 Max: `keyboards/keychron/k17_max/ansi_encoder/rgb/keymaps/via/` 3. Test compilation locally before committing. 4. Ensure `rules.mk` has the necessary flags enabled (e.g., `TAP_DANCE_ENABLE = yes`, `UNICODE_ENABLE = yes`). ## Git Conventions -* Use conventional commits (`feat:`, `fix:`, `docs:`, `chore:`, etc.) scoped to the keyboard where relevant (e.g. `feat(q5_max):`). +* Use conventional commits (`feat:`, `fix:`, `docs:`, `chore:`, etc.) scoped to the keyboard where relevant (e.g. `feat(q5_max):`, `fix(k17_max):`). * Do **not** include `Co-Authored-By: Claude` trailers in commit messages. ### Chained / Stacked PRs When merging a chain of PRs (e.g. `A → main`, `B → A`, `C → B`), always **delete the branch after each merge**. Gitea (and GitHub) will automatically retarget any open PRs pointing at the deleted branch to the branch it was merged into. This keeps the chain collapsing cleanly into `main` without manual retargeting or cleanup PRs. + +## EEPROM Layout Notes + +The Q5 Max uses wear-leveling EEPROM (STM32F401). Key layout facts: + +* `EECONFIG_RGB_MATRIX` is at bytes 24–31; byte 0 packs `mode[7:2] | enable[1:0]`. +* Keychron custom RGB data (effect list, regions, per-key colours, retail demo flag) lives in `EECONFIG_KB_DATABLOCK` immediately after `EECONFIG_BASE_SIZE` (37 bytes). +* `VIA_EEPROM_MAGIC_ADDR` is pinned to **544** in `ansi_encoder/config.h`. Do not lower this value — it must stay above `EECONFIG_BASE_SIZE + EECONFIG_KB_DATA_SIZE`. If Keychron EEPROM grows, raise 544 accordingly and clear EEPROM on the board. +* `EECONFIG_KB_DATA_SIZE` is computed in `eeconfig_kb.h` and requires an `#undef` before the `#define` to suppress QMK's default-zero value. + +## Keychron RGB (`KEYCHRON_RGB_ENABLE`) + +Enabled via `KEYCHRON_RGB_ENABLE = yes` in `rules.mk`. Key behavioural notes: + +* `eeconfig_init_custom_rgb()` **loads** Keychron RGB state from EEPROM into RAM. It must be called in `keyboard_post_init_kb()` and in `wireless_enter_connected_kb()`; without it the arrays are zero-initialised and Launcher settings are lost on every boot or transport change. +* `eeconfig_reset_custom_rgb()` **writes** defaults to EEPROM and stamps the version. The version stamp (`eeprom_update_dword(EECONFIG_KEYBOARD, ...)`) belongs here only — not in the load path. +* `kc_rgb_save()` must call `eeconfig_update_rgb_matrix()` to persist the QMK RGB mode alongside the Keychron custom data; otherwise `rgb_matrix_init()` (triggered on every transport change by `REINIT_LED_DRIVER = 1`) reloads the compile-time default `RGB_MATRIX_TYPING_HEATMAP`. +* `retail_demo_enable` is a single byte. A bug in the original Keychron code used `eeprom_read_block` instead of `eeprom_update_block` in `eeconfig_reset_custom_rgb()`, leaving `0xFF` on freshly-flashed boards. `retail_demo_task()` treats any non-zero value as "demo active" and forces `CUSTOM_MIXED_RGB` every scan. The load path now clamps `> 1` to `0` and re-writes the byte as a one-time recovery. +* `default_per_key_led[]` and `default_region[]` must be defined in board-specific code (e.g. `ansi_encoder.c`) — `keychron_rgb.c` declares them `extern`. + +## DIP Switch (Win/Mac) + +The Win-side dip switch uses a **frame overlay** rather than calling `rgb_matrix_mode()`. A `dip_win_active` flag is set on switch change; `rgb_matrix_indicators_advanced_user()` paints all LEDs white each frame when the flag is set. This avoids writing to EEPROM and preserves the Launcher-configured effect, which would otherwise be overwritten by the direct mode call.