Files
qunexus-firmware/EFM8UB20_PINOUT.md
T
nils 514983c158 Add tooling + EFM8UB20 QFP48 pinout doc
Make the repo self-contained (no dependency on the parent firmware-tools/
checkout): copy in the patch/disasm/syx tools and the c2probe RP2040 C2
flash-reader/patcher firmware.

- patch_usb1_to_cv.py : byte-level USB-1->CV patch (imported by roundtrip.py)
- d8051.py             : standalone 8051 disassembler
- syx_extract.py       : SysEx extractor
- c2probe/             : RP2040 C2 flash reader + host scripts
  (c2probe.c, cdc/dump_flash/patch_c2/reflash_page/verify.py, CMake build)
- RECOVERY.md          : C2 flash recovery procedure
- EFM8UB20_PINOUT.md   : reverse-engineered QFP48 pinout + firmware pin usage
- roundtrip.py         : import local patch_usb1_to_cv (parent as fallback)
- .gitignore           : exclude c2probe/.venv, c2probe/build

Verified: stock + patched round-trips still re-assemble byte-identical.
2026-08-17 23:35:09 +02:00

156 lines
7.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# EFM8UB20F64G (QFP48) — reverse-engineered pinout & firmware pin usage
QuNexus main MCU = **EFM8UB20F64G-B-QFP48** (48-pin TQFP). App firmware v2.2.1.
> There is **no QFN48** variant of the EFM8UB20F64G — the datasheet lists only
> QFP48, QFP32, and QFN32. The 48-pin part is therefore QFP48, and the QFP48
> ADC channel map is the one that matches the firmware.
All findings below are derived from the disassembly (`qunexus_v2.2.1.asm`) and
verified against the SiLabs header (SFR addresses) and the EFM8UB2 reference
manual (ADC channel→pin map). The pin *configuration* and *which pins are
actively toggled* are directly proven from the code; the identity of the
external device on the P2/P3/P4 bus is inferred from the bus structure.
## Port configuration
The entire crossbar/port setup is one block at `0xde6f`, written once
(firmware-wide — XBR / MDOUT / MDIN / SKIP are never written elsewhere):
| Register | Value | Meaning |
|----------|-------|---------|
| XBR0 | 0x01 | **UART0 only** — no SPI, no SMBus, no comparators routed |
| XBR1 | 0x43 | crossbar enabled (XBARE = 1) |
| XBR2 | 0x00 | default, never written — UART1/SMB1 off |
| P0SKIP | 0xCF | skip P0.0-3,6,7 → leave P0.4/P0.5 for UART0 |
| P1SKIP | 0x03 | skip P1.0/P1.1 |
| P2SKIP | 0x00 | default, never written |
| P3SKIP | 0x00 | default (P3SKIP = 0xDF, never written) |
| P0MDIN | 0x30 | P0.4/P0.5 digital; P0.0-3,6,7 **analog** |
| P1MDIN | 0x3F | P1.0-5 digital; P1.6/P1.7 **analog** |
| P2/P3/P4MDIN | 0xFF | all digital |
| P0MDOUT | 0x10 | P0.4 push-pull (TX); rest open-drain |
| P1MDOUT | 0x3F | P1.0-5 push-pull; P1.6/7 open-drain |
| P2MDOUT | 0xFF | all push-pull |
| P3MDOUT | 0xF0 | P3.4-7 push-pull; **P3.0-3 open-drain** |
| P4MDOUT | 0xFF | all push-pull |
> The P4 port latch is SFR **0xC7** — not 0xC0 (0xC0 is SMB0CN0, the SMBus
> control register). Easy to misidentify.
## Peripherals actually used
- **UART0** → P0.4 (TX) / P0.5 (RX) = the 5-pin DIN MIDI port. (USB-MIDI is a
separate class engine over D+/D−, not this UART.)
- **ADC0**: `AMX0P = 0x11` → **P0.3** (ADC0P.17 on QFP48), `AMX0N = 0x1f`
(GND, single-ended), `ADC0CN0 = 0x02` (enabled). AMX0P is written **exactly
once**, so the firmware reads a single fixed ADC channel. ADC0L/H (0xBD/0xBE)
are read at `0x8a37`, `0xa838`, `0xcadd`. → An **external analog mux**
(steered by the digital scan bus) feeds many sensors into P0.3.
- **No SPI0, no SMB0, no hardware EMIF** — none of their config registers are
ever written. The parallel bus below is **bit-banged**.
## Runtime GPIO — a bit-banged parallel bus to an external engine
The EFM8 is not driving the LEDs/touch directly; it talks to an external chip
(CPLD / expander / LED+touch controller) over a hand-strobed parallel bus, and
advances a select matrix on a timer tick:
- **P2.0–P2.7** (push-pull): written from a Timer ISR — `mov P2,a` at `0xc537`
+ `setb TR2`, ends `reti`. The `rlc a` / `djnz` / `cpl a` loop builds a
walking one-hot pattern → **scan / select bus**.
- **P3.0–P3.3** (open-drain): toggled with `orl/anl P3,#0x0f` (`0xc942`,
`0xc997`, `0xca89`, `0xcad1`, `0xcad8`, `0xd774`) → **control strobes**.
- **P4.0–P4.7** (push-pull, SFR 0xC7): `mov P4,a` (`0xc950`, `0xc999`, `0xd778`),
always paired with a P3 strobe → **8-bit data bus**.
- **P1.5** (push-pull): toggled (`clr P1.5` @ `0x860e`, `mov` @ `0xe38f`) →
GPIO output, function unknown. P1.0/P1.1 are skipped = reserved GPIO.
## Full QFP48 pin table (firmware function)
| Pin | Port | Firmware function | Used? |
|-----|------|--------------------|-------|
| 1 | P0.5 | UART0 RX — MIDI In | ✓ |
| 2 | P0.4 | UART0 TX — MIDI Out | ✓ |
| 3 | P0.3 | **ADC input** (single channel, ext. mux output) | ✓ |
| 4 | P0.2 | analog, no ADC/CMP fn | ○ unused |
| 5 | P0.1 | analog, no ADC/CMP fn | ○ unused |
| 6 | P0.0 | analog, no ADC/CMP fn | ○ unused |
| 7 | GND | ground | — |
| 8 | D+ | USB (USB-MIDI class) | ✓ |
| 9 | D− | USB | ✓ |
| 10 | VDD | supply / reg output | — |
| 11 | VREGIN | 5V reg input | — |
| 12 | VBUS | USB VBUS sense | ✓ |
| 13 | RST/C2CK | reset / C2 flash clock | ✓ |
| 14 | C2D | C2 flash data | ✓ |
| 15 | P4.7 | data bus D7 | ✓ |
| 16 | P4.6 | data bus D6 | ✓ |
| 17 | P4.5 | data bus D5 | ✓ |
| 18 | P4.4 | data bus D4 | ✓ |
| 19 | P4.3 | data bus D3 | ✓ |
| 20 | P4.2 | data bus D2 | ✓ |
| 21 | P4.1 | data bus D1 | ✓ |
| 22 | P4.0 | data bus D0 | ✓ |
| 23 | P3.7 | push-pull out, never written | ○ static |
| 24 | P3.6 | push-pull out, referenced once | ○ ~unused |
| 25 | P3.5 | push-pull out, never written | ○ static |
| 26 | P3.4 | push-pull out, never written | ○ static |
| 27 | P3.3 | open-drain **strobe** | ✓ |
| 28 | P3.2 | open-drain **strobe** | ✓ |
| 29 | P3.1 | open-drain **strobe** | ✓ |
| 30 | P3.0 | open-drain **strobe** | ✓ |
| 31 | P2.7 | **scan/select** (timer ISR) | ✓ |
| 32 | P2.6 | scan/select | ✓ |
| 33 | P2.5 | scan/select | ✓ |
| 34 | P2.4 | scan/select | ✓ |
| 35 | P2.3 | scan/select | ✓ |
| 36 | P2.2 | scan/select | ✓ |
| 37 | P2.1 | scan/select | ✓ |
| 38 | P2.0 | scan/select | ✓ |
| 39 | P1.7 | analog (EMIF /WR not used) | ○ unused |
| 40 | P1.6 | analog (EMIF /RD not used) | ○ unused |
| 41 | P1.5 | GPIO output (toggled) | ✓ |
| 42 | P1.4 | CNVSTR/GPIO (ADC is SW-triggered) | ○ ~unused |
| 43 | P1.3 | push-pull GPIO | ○ ~unused |
| 44 | P1.2 | push-pull GPIO | ○ ~unused |
| 45 | P1.1 | skipped GPIO | ○ ~unused |
| 46 | P1.0 | skipped GPIO | ○ ~unused |
| 47 | P0.7 | XTAL2 — internal oscillator | ○ unused |
| 48 | P0.6 | XTAL1 — internal oscillator | ○ unused |
Legend: ✓ actively driven/read by firmware · ○ configured but no active drive
found (likely unused/static) · — power/USB/debug (hardware).
## Architecture summary
The EFM8UB20 is essentially a **USB-MIDI class engine + DIN-MIDI UART + bus
master**, not the thing doing the LED/touch work:
- **USB** (pins 8/9/12) = USB-MIDI class traffic.
- **UART0** (pins 1/2) = 5-pin DIN MIDI in/out.
- **ADC** (pin 3, P0.3) = one analog channel reading an external analog mux.
- **Bit-banged parallel bus** to an external LED/touch engine: **P4 = 8-bit
data** (pins 15–22), **P3.0–3 = strobes** (pins 27–30), **P2 = scan/select**
(pins 31–38, advanced by a timer ISR).
- **C2** (pins 13/14) = how we flash/read it.
Used pins: **1, 2, 3** (MIDI + ADC), **8, 9, 11, 12** (USB), **13, 14** (C2
debug), **15–22** (P4 data), **27–30** (P3 strobes), **31–38** (P2 scan),
**41** (P1.5 GPIO). Everything else is configured but shows no active drive.
## Caveat
The "external LED/touch engine" on the P2/P3/P4 bus is an inference from the
bus structure and the walking-one scan pattern — the firmware's driving of the
bus is directly visible, but what sits on the other end can only be confirmed
from board photos or a schematic.
## Sources
- EFM8UB2 Reference Manual, Table 12.1 (AMX0P ADC channel→pin map):
https://www.silabs.com/documents/public/reference-manuals/efm8ub2-rm.pdf
- EFM8UB20F64G-B-QFP48 datasheet, Table 6.1 (QFP48 pin definitions):
https://resources.ampheo.com/static/datasheets/silicon-labs/efm8ub20f64g-b-qfp48.pdf
- si_efm8ub2_defs.h (SFR address verification):
https://www.keil.com/dd/docs/c51/silabs/efm8ub2/inc/si_efm8ub2_defs.h