Initial design record for the three-piece keyboard
Two mirrored halves plus a numpad that carries the USB hub. Captures the architecture decisions and the reasoning behind them: CH32L103 with an STM32F103 pin-compatible escape hatch, split protocol tunnelled over USB-C SBU through the numpad, hotswap sockets bottom-side, and why crystal-less USB was ruled out across the whole WCH catalogue. No boards ordered, no firmware written. Layout still undesigned. Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
commit
4467aa9433
4 files changed
+404
No files matched your search
@@ -0,0 +1,221 @@
|
||||
# tertiary
|
||||
|
||||
A three-piece keyboard: two mirrored halves plus a numpad/nav/media board that
|
||||
also carries the USB hub. One cable reaches the computer.
|
||||
|
||||
Status: **design**. No boards ordered, no firmware written.
|
||||
|
||||
---
|
||||
|
||||
## The three boards
|
||||
|
||||
| Board | Qty | Keys | Carries |
|
||||
|---|---|---|---|
|
||||
| `hardware/half` | 2 (mirrored) | TBD — layout not designed | MCU, matrix, USB-C, SBU split link |
|
||||
| `hardware/numpad` | 1 | 26 (17 numpad + 9 nav) | MCU, matrix, 2 encoders, **CH334R hub**, 3 downstream ports |
|
||||
|
||||
Cabling:
|
||||
|
||||
```
|
||||
PC ──USB-C── numpad ──USB-C── left half
|
||||
└─USB-C── right half
|
||||
```
|
||||
|
||||
Both halves plug into the numpad. The numpad's hub gives each half a USB port,
|
||||
and a **passive trace across the numpad shorts SBU between the two half ports**,
|
||||
which is how QMK's split protocol gets from one half to the other. See below.
|
||||
|
||||
---
|
||||
|
||||
## Decisions, and why
|
||||
|
||||
### MCU: CH32L103C8T6, with an STM32F103C8T6 escape hatch
|
||||
|
||||
`C22398291`, LQFP-48, 62–64 K flash / 20 K SRAM, QingKe RISC-V4C.
|
||||
|
||||
**QMK does not support CH32.** Verified: zero issues or PRs mentioning CH32 in
|
||||
`qmk/qmk_firmware` ever, and no `os/hal/ports/CH32` in ChibiOS-Contrib. Adding it
|
||||
means a ChibiOS HAL port — PAL, ST, and critically a `usb_lld` for WCH's own USBFS
|
||||
peripheral. Estimated 3–6 weeks of evenings; the USB driver is ~75% of it.
|
||||
|
||||
So the board is laid out to the **STM32F103C8T6 (`C8734`) footprint**, which the
|
||||
L103 is pin-compatible with (same LQFP-48, `PC13`/`PC14`/`PC15`, `PD0`/`PD1` as
|
||||
`OSC_IN`/`OSC_OUT`, `PA11`/`PA12` as USB DM/DP, `PA13`/`PA14` as SWDIO/SWCLK).
|
||||
Populate the F103 and it works with stock QMK today; populate the L103 when the
|
||||
port lands. Same board either way.
|
||||
|
||||
> ⚠️ **Unverified assumption the whole board rests on:** pin *names and functions*
|
||||
> were confirmed identical from both datasheets, but the pin *numbering* was not —
|
||||
> the PDFs interleave four packages and extract badly. **Overlay the two pinout
|
||||
> drawings before routing.**
|
||||
|
||||
**Only one component differs between the two MCUs:** `R5`, a 1.5 kΩ pull-up on
|
||||
D+. The F103 has no internal one. The L103 does (~1.5 kΩ, enabled via
|
||||
`RB_UC_DEV_PU_EN` in `R8_USB_CTRL`) — so populate `R5` for F103, DNF for L103.
|
||||
|
||||
Buy three loose F103s and keep them in a drawer. When the first board doesn't
|
||||
enumerate, hot-air one over and flash stock QMK: that tells you instantly whether
|
||||
the fault is the PCB or your USB driver. Debugging both at once is where this
|
||||
project would die.
|
||||
|
||||
### Why not RP2040
|
||||
|
||||
No internal flash, so it needs an external QSPI chip, a crystal, and fussy
|
||||
decoupling — ~24 line items versus ~16. Modules (RP2040-Zero, $3.99) were the
|
||||
first plan and are still viable, but bare silicon gives USB-C placement freedom
|
||||
and no single-supplier risk.
|
||||
|
||||
### Why not crystal-less
|
||||
|
||||
Nothing in WCH's catalogue does spec-compliant crystal-less USB. Checked V203,
|
||||
X035 and L103: all use factory-calibrated RC with manual `HSITRIM` and **no
|
||||
SOF-based clock recovery anywhere in the family**. Accuracies run −1.0/+1.6 %
|
||||
(V203), −1.7/+1.6 % (X035), ±1.6 % rising to ±2.2 % over full range (L103).
|
||||
USB full-speed wants ±0.25 %.
|
||||
|
||||
WCH's own datasheet admits it: crystal-free "may deviate from the USB
|
||||
specification, and are only applicable to non-precision occasions", and it's a
|
||||
factory ordering option not enabled by default on stock parts.
|
||||
|
||||
The only genuinely crystal-less option found was **STM32F0x2** (HSI48 + CRS,
|
||||
actively trimmed against host SOF packets) — rejected on stock: 773 units.
|
||||
|
||||
An 8 MHz crystal plus two caps is $0.12 and satisfies both MCUs' USB PLL.
|
||||
|
||||
### Hub in the numpad, not the right half
|
||||
|
||||
Keeps both halves the same design. The numpad is a one-off anyway and it's the
|
||||
roomiest board. Cost: the numpad must be plugged in for the halves to reach the
|
||||
computer — but each half has a complete USB stack, so any half can be plugged
|
||||
directly into a machine when travelling.
|
||||
|
||||
**CH334R** (`C4154405`, QSOP-16, $0.50), 4 ports mapping exactly:
|
||||
|
||||
| Port | Destination |
|
||||
|---|---|
|
||||
| Upstream | PC |
|
||||
| 1 | numpad's own MCU — internal trace, no connector |
|
||||
| 2 | left half |
|
||||
| 3 | right half |
|
||||
| 4 | spare external |
|
||||
|
||||
CH334R has no port power switching (dropped in the 16-pin package), so each
|
||||
downstream port gets a **CH213K** ideal diode for overcurrent — WCH's own
|
||||
recommendation in the CH334 reference design.
|
||||
|
||||
Downstream ports are host-side: **56 kΩ Rp pull-*ups* on CC, not 5.1 kΩ Rd
|
||||
pull-downs.** Getting this backwards means nothing enumerates.
|
||||
|
||||
### Split link over USB-C SBU
|
||||
|
||||
The 16-pin USB-C connector breaks out **SBU1 (A8) and SBU2 (B8)** and nothing
|
||||
uses them. Each half ties SBU1+SBU2 together — **SBU is crossed inside the
|
||||
cable**, so tying them makes the link orientation-proof and doubles the
|
||||
conductor — and runs it through 100 Ω + ESD to a USART pin. The numpad shorts
|
||||
the two half-ports' SBU nets with a passive trace.
|
||||
|
||||
Result: QMK `split_common` runs between the halves with no extra cable and no
|
||||
TRRS.
|
||||
|
||||
> ⚠️ **Requires a full-featured USB-C cable.** Cheap USB 2.0 charge cables omit
|
||||
> SBU entirely. Failure mode is nasty: power and USB work, only the split dies.
|
||||
> **Put "USE A FULL-FEATURED CABLE" on the silkscreen.**
|
||||
|
||||
A 3-pin JST footprint is carried on both halves as **DNF insurance** if SBU
|
||||
proves unreliable.
|
||||
|
||||
### Hotswap sockets, everything bottom-side
|
||||
|
||||
Kailh MX `CPG151101S11` (`C41430893`, $0.037, 93 k in stock — they *are* in JLC's
|
||||
library). Sockets mount on the back, so put **all** SMD on the bottom: MCU,
|
||||
passives, diodes, sockets. Single-sided assembly, one JLC setup instead of two.
|
||||
|
||||
**A plate is mandatory.** Without one, switch insertion flexes the PCB and tears
|
||||
socket pads off. FR4 plate is just another JLC board.
|
||||
|
||||
Use [perigoso/keyswitch-kicad-library](https://github.com/perigoso/keyswitch-kicad-library)
|
||||
for footprints — don't draw them.
|
||||
|
||||
### Mirrored board files, not a reversible PCB
|
||||
|
||||
The socket footprint isn't mirror-symmetric, so a reversible board needs two
|
||||
socket footprints per key with one populated. Two mirrored layouts sharing one
|
||||
schematic and one BOM is simpler and keeps everything that mattered.
|
||||
|
||||
### RGB: numpad underglow only
|
||||
|
||||
Per-key RGB is arithmetically impossible on a bus-powered hub. Each half sits on
|
||||
a downstream port capped at 100 mA; 42 SK6812MINI-E at full white is 1.5 A, and
|
||||
even 10 % brightness overruns the port. Real per-key RGB needs a self-powered hub
|
||||
with an external supply — and probably a bigger CH334 variant, since the small
|
||||
packages don't expose `PSELF`.
|
||||
|
||||
Numpad has ~355 mA to itself: about 9 LEDs at full white or ~30 at 30 %.
|
||||
|
||||
If any SK6812 is fitted: **3.3 V data into a 5 V part is out of spec** (Vih ≈
|
||||
3.5 V). SK6812MINI-E is rated 3.7–5.5 V, so run the LED rail at ~4.3 V through a
|
||||
series diode — Vih drops to ~3.0 V and you stay in spec.
|
||||
|
||||
---
|
||||
|
||||
## Firmware notes
|
||||
|
||||
Nothing written yet. Things already known:
|
||||
|
||||
**Do not use `SPLIT_USB_DETECT`.** Both halves are plugged into the hub, so both
|
||||
would see VBUS and both would decide they're master. Set `MASTER_LEFT` (or
|
||||
`MASTER_RIGHT`) at compile time. The peripheral still enumerates as an idle HID
|
||||
device — expected, not a fault.
|
||||
|
||||
**Split pin:** `PA9` (USART1_TX) exists on both MCUs. QMK's half-duplex
|
||||
`serial_usart` uses TX only, open-drain with pull-up.
|
||||
|
||||
**Numpad Num Lock handling.** Num Lock is global host state on Windows and Linux;
|
||||
with it off, `KP_7` sends Home and the pad silently becomes a duplicate of the
|
||||
nav cluster three inches away. macOS has no functional Num Lock (Apple keyboards
|
||||
use Clear) and the pad always types digits there.
|
||||
|
||||
Plan is belt-and-braces:
|
||||
|
||||
1. One attempt per power-up to turn Num Lock on, fired from
|
||||
`housekeeping_task_user` (**never** from inside `led_update_user` — an
|
||||
unbounded loop there has been reported to brick a Moonlander), after a 500 ms
|
||||
settle so a transient enumeration report doesn't trigger a false correction.
|
||||
2. Regardless of whether that took, resolve each keypad key at press time against
|
||||
`host_keyboard_led_state().num_lock` and emit the number-row keycode when Num
|
||||
Lock is off. Latch what was registered so the release matches if state flips
|
||||
mid-hold.
|
||||
|
||||
Only eleven keys need this (1–9, 0, `.`). `KP_SLASH`/`ASTERISK`/`MINUS`/`PLUS`/
|
||||
`ENTER` mean the same thing either way.
|
||||
|
||||
**Encoders:** volume wants a **detented** EC11; scroll wants a **detentless** one,
|
||||
because hi-res scrolling (`POINTING_DEVICE_HIRES_SCROLL_ENABLE`, resolution
|
||||
multiplier, 1/120 tick) fights physical detents. Confirm the detentless SKU from
|
||||
the datasheet — vendor descriptions are unreliable.
|
||||
|
||||
---
|
||||
|
||||
## Open questions
|
||||
|
||||
- **The layout.** Undesigned, and it's the only input the PCB can't defer. If a
|
||||
thumb layer on one half reaches keys on the other, the halves must be a QMK
|
||||
split. If each half is self-sufficient, they can be three independent
|
||||
keyboards. The hardware supports both — see `docs/104-key-split.svg` for how a
|
||||
104-key layout maps onto three boards, and note a straight chop gives 36 vs 39
|
||||
keys, so a symmetric core plus layers is needed for mirrored boards.
|
||||
- Pin-number overlay of L103 vs F103 (above).
|
||||
- CH334 internal crystal load-cap value vs the 20 pF-CL crystal — fit DNF cap pads.
|
||||
- Whether AT32F415 is a better fallback than F103 (128 K/32 K vs 64 K/20 K, same price).
|
||||
|
||||
---
|
||||
|
||||
## TODO before this repo is made public
|
||||
|
||||
Per house convention. This one is unusually clean:
|
||||
|
||||
- [ ] Nothing here touches LAN topology, credentials, or personal data — no
|
||||
scrubbing needed, unlike most repos here.
|
||||
- [ ] Confirm no JLC/LCSC account identifiers end up in fab exports.
|
||||
- [ ] Decide on a licence before publishing hardware files (CERN-OHL-S or
|
||||
TAPR OHL for hardware; GPL2 for anything derived from QMK).
|
||||
Reference in new issue
Block a user