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

7.2 KiB
Raw Blame History

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