m65 (5×13 Ortho)

5×13 Ortho 65 Keys STM32F401 / RP2040 / XIAO QMK & ZMK
All Keyboards

m65 — 5×13 LEGO Ortholinear Keyboard

Status: Rev 11 Tested 5×13 Ortho (65–66 Keys) XIAO RP2040 / BLE · Pico · STM32 SPI Shift Registers (74HC595 / 589 / 165) OLED & E-Ink + Rotary Encoder QMK & ZMK

m65 rev 11 with WeAct e-ink display m65 rev 11 with OLED display

The m65 is a 5-row, 13-column ortholinear mechanical keyboard designed to accommodate all standard keycaps from an ISO layout.

In its later iterations (Rev 7–Rev 11), it departs from a traditional direct-pin matrix and uses the tiny Seeed Studio XIAO RP2040 or Seeed Studio XIAO nRF52840 BLE (SAMD21 may also work). Because the XIAO only exposes 11 GPIO pins, rows are scanned via a 74HC589 (or 74HC165) input shift register while columns are driven by two chained 74HC595 output shift registers over hardware SPI (sharing clock and latch pins), leaving pins free for an I²C OLED / SPI e-ink screen and a clickable rotary encoder (making Rev 9–11 technically a 14-column matrix, later spun off into the m66 line).

Seeed Studio Sponsorship

For **Rev 7**, Seeed Studio kindly sponsored the 5×13 version with PCBs and XIAO microcontrollers as part of the [Seeed Fusion DIY XIAO Mechanical Keyboard Contest](https://www.seeedstudio.com/seeed-fusion-diy-xiao-mechanical-keyboard-contest.html) and their [Fusion PCB Assembly service](https://www.seeedstudio.com/pcb-assembly.html).

Status: Rev 11 Done and Tested

* [x] Gerbers designed
* [x] Firmware (QMK & ZMK)
* [ ] Breadboard tested
* [x] Gerbers printed
* [x] Board tested

Revision History

* **Rev 11**: XIAO RP2040 with 3 shift registers (2× `74HC595` + 1× `74HC589`), OLED / e-ink (`SSD1680` WeAct), and push rotary encoder with QMK (actively exploring ZMK support for RP2040 and nRF52840)
* **Rev 10**: XIAO RP2040 with 3 shift registers (2× `74HC595` + 1× `74HC589`), OLED, and push rotary encoder with QMK
* **Rev 9**: XIAO RP2040 with 3 shift registers (2× `74HC595` + 1× `74HC165`), OLED, and push rotary encoder with QMK
* **Rev 7/8**: XIAO RP2040 and nRF52840 with 2× `74HC595` shift registers, rotary encoder, no OLED (both QMK and ZMK firmware supported)
* **Rev 5/6**: `RP2040` support (Raspberry Pi Pico, WeAct RP2040, and compatible clones), non-push encoder, OLED
* **Rev 4**: `STM32F401`/`F411` BlackPill, non-push encoder, OLED
* **Rev 3**: `STM32F401`/`F411` BlackPill, non-push encoder
* **Rev 2**: `GD32F303` aka [BluePill Plus](https://github.com/WeActStudio/BluePill-Plus) (USB-C `F103` equivalent)
* **Rev 1**: `STM32F103` / `APM32F103` or `STM32F303`

BOM

Microcontrollers

MCU Selection Guide

Using off-the-shelf development boards makes DIY keyboard design much simpler because all high-frequency crystal and USB protection circuitry lives on the MCU daughterboard. While I have built handwired boards with AVR (`ATmega32U4`), I strongly advise against using AVR for new designs.

* **Rev 1**: [`STM32F103C8T6` BluePill](https://stm32-base.org/boards/STM32F103C8T6-Blue-Pill.html) or [RobotDyn `STM32F303CCT6` BlackPill](https://stm32-base.org/boards/STM32F303CCT6-RobotDyn-Black-Pill.html) *(do not confuse with the WeAct F401/F411 BlackPill)*. Micro-USB; requires flashing an `stm32duino` bootloader.
* **Rev 2**: `GD32F303` [BluePill Plus](https://github.com/WeActStudio/BluePill-Plus) with USB Type-C.
* **Rev 3 / Rev 4**: WeAct [`STM32F401`/`F411` MiniSTM32F4x BlackPill](https://github.com/WeActStudio/WeActStudio.MiniSTM32F4x) with USB Type-C.
* **Rev 5 / Rev 6**: `RP2040` ([Raspberry Pi Pico](https://www.raspberrypi.com/documentation/microcontrollers/raspberry-pi-pico.html), [Waveshare RP2040-Plus](https://www.waveshare.com/wiki/RP2040-Plus), or USB-C compatible clones — verify pinout compatibility).
* **Rev 7 / Rev 9 / Rev 10 / Rev 11**: [Seeed Studio XIAO RP2040](https://wiki.seeedstudio.com/XIAO-RP2040/) and [Seeed Studio XIAO BLE nRF52840](https://wiki.seeedstudio.com/XIAO_BLE/). Ultra-compact footprint using SPI shift registers for matrix scanning.
* **Need more pins?**: Consider the [WeAct `STM32G431` CoreBoard](https://github.com/WeActStudio/WeActStudio.STM32G431CoreBoard), already supported in QMK.

Component Lists by Revision

Rev 11

Revision 11 BOM

* **LEGO Case**: ~46× 1×2 or 1×4 plates (use full bricks if housing a battery), 1× 16×32 double-sided baseplate or 2× 16×16 plates (gray compatible plates minimize PCB flexing), optional 4× 1×1 tiles/eyes and 4× 2×2 corner plates
* **Diodes**: 66× `1N4148` signal diodes (DO-35 through-hole or SOD-123 SMD)
* **Resistors**: 5× 10 kΩ (THT or SMD 1206) and 1× 5.1 kΩ (THT or SMD 1206)
* **MCU**: Seeed Studio XIAO RP2040 (or nRF52840) + recommended female socket headers
* **Shift Registers**: 2× `74HC595` (SOIC-16 SMD) and 1× `74HC589` (SOIC-16 SMD)
* **MOSFET**: 1× `2N7002` FET (SOT-23 SMD)
* **Capacitors (optional)**: 3× 100 nF decoupling capacitors (THT, SMD, or integrated socket)
* **Battery / Wireless (optional)**: Pogo pins, LiPo battery, 2-pin PH 2.0 battery socket, and 1P2T 4 mm micro slide switch
* **Encoder**: Clickable EC11 rotary encoder (Bourns 24-pulse incremental encoder with 6 mm flat shaft)
* **Displays (optional)**:
    * 128×32 I²C OLED (4-pin) + 1×4 DIL socket
    * WeAct SSD1680 e-ink module + 2×4 header and socket

Rev 10

Revision 10 BOM (Superseded by Rev 11)

* Identical to **Rev 11** above, with 128×32 I²C OLED (4-pin) + 1×4 DIL socket (prior to the dedicated 2×4 e-ink header added in Rev 11).

Rev 9

Revision 9 BOM

* **LEGO Case**: 16×32 double-sided plate (or 2× 16×16) + ~46× 1×2 or 1×4 plates
* **Diodes**: 66× `1N4148` signal diodes (DO-35 or SOD-123)
* **Resistors**: 5× 10 kΩ THT resistors
* **MCU**: Seeed Studio XIAO RP2040 (or nRF52840) + female headers
* **Shift Registers**: 2× `74HC595` (DIP-16 THT + sockets) and 1× `74HC165` (DIP-16 THT + socket)
* **Capacitors (optional)**: 3× 100 nF decoupling capacitors
* **Battery / Encoder**: Pogo pins, LiPo battery, 2-pin PH 2.0 socket, 1P2T 4 mm micro switch, EC11 clickable rotary encoder

Rev 7/8

Revision 7/8 BOM

* **LEGO Case**: 16×32 double-sided plate (or 2× 16×16) + ~46× 1×2 or 1×4 plates
* **Diodes**: 65× `1N4148` signal diodes (DO-35 or SOD-123)
* **MCU**: Seeed Studio XIAO RP2040 or XIAO nRF52840 BLE + female headers
* **Shift Registers**: 2× `74HC595` (DIP-16 THT + sockets)
* **Capacitors (optional)**: 2× 100 nF decoupling capacitors
* **Encoder & Battery**: EC11 rotary encoder, optional pogo pins and LiPo battery

Rev 5/6

Revision 5/6 BOM

* **LEGO Case**: 16×32 double-sided plate + ~46× 1×2 or 1×4 plates
* **Diodes**: 65× `1N4148` signal diodes (DO-35 or SOD-123)
* **MCU**: Raspberry Pi Pico (`RP2040`), WeAct RP2040, Teenstar, or compatible clone + 2×20 round pin headers & DIL sockets
* **LEDs & Resistors**: 2× LEDs + 2× resistors (`R1`, `R2` — 510 Ω works well for green/yellow LEDs; 1 kΩ for white LEDs)
* **Switches & Peripherals**: 65× 5-pin PCB-mount MX switches, EC11 rotary encoder, optional 128×32 I²C OLED (with 1×4 DIL socket), optional 3-pin horizontal JST header for LED strip

Rev 4

Revision 4 BOM (and Rev 1–3)

* **LEGO Case**: 16×32 double-sided plate + ~46× 1×2 or 1×4 plates
* **Diodes**: 65× `1N4148` signal diodes (DO-35 or SOD-123)
* **MCU**: WeAct `STM32F401` or `STM32F411` BlackPill + 2×20 round pin headers and 40-way DIL socket (15.24 mm)
* **Resistors & LEDs**:
    * `R1`, `R2`: 2× 510 Ω (green/yellow) or 1 kΩ (white) + 2× indicator LEDs
    * `R3`: 1× 100 kΩ pull-up resistor (ensures reliable `STM32F4x1` bootloader entry)
* **Switches & Peripherals**: 65× 5-pin PCB-mount switches, EC11 rotary encoder, 128×32 I²C OLED (with 1×4 socket), optional 3-pin horizontal JST header for LED strip
* *(For Rev 1–3, use the same BOM as Rev 4 with the corresponding MCU module.)*

Case

Case 3D render LEGO parts for case Case front Case back Case side

Assembly

Follow the illustrated Assembly Guide for soldering diodes, resistors, sockets, and switches.

Thickness Profiles

Depending on how many intermediate LEGO layers you stack, the enclosure can be built ultra-slim or deep enough for JST headers and acoustic foam:

  • 1 double base plate, 1 intermediate plate and pcb. 8 mm

    8.0 mm — Slim Profile

    1× baseplate (single or double-sided, flat or angled) + 1× intermediate plate (3.2 mm) + PCB (1.6 mm). Leaves room for a thin sheet of sound-dampening foam.

  • 1 double base plate, 2 intermediate plate and pcb. 11.2 mm

    11.2 mm — Dual-Plate Profile

    1× double-sided baseplate + 2× intermediate plates + PCB. Fits thicker foam or a hand-soldered LED strip (without a JST connector).

  • 1 double base plate, 1 intermediate brick and pcb. 14.4 mm

    14.4 mm — Full Brick Profile

    1× double-sided baseplate + 1× full LEGO brick + PCB. Provides clearance for a 3-pin JST header or LiPo battery.

3D Renders

Rev 11

m65 rev11 xiao rp2040 front m65 rev11 xiao rp2040 back

Rev 10

m65 rev10 xiao rp2040 front m65 rev10 xiao rp2040 back

Rev 9

m65 rev9 xiao rp2040 front m65 rev9 xiao rp2040 back

Rev 7/8

3d render rev7

Rev 5/6

3d render rev5 front 3d render rev5 back

Rev 4

3d render rev4

Rev 2

3d render rev2

Rev 1

3d render rev1

Schematics

Rev 11

m65 rev11 schematic

Rev 10

m65 rev10 schematic

Rev 9

m65 rev9 schematic

Rev 7

m65 rev7 schematic

Rev 5

m65 rev5 schematic

Rev 4

m65 rev4 schematic

PCBs

Rev 11

m65 rev11 pcb layout m65 rev11 pcb front m65 rev11 pcb back

Rev 10

m65 rev10 pcb layout m65 rev10 pcb front m65 rev10 pcb back

Rev 9

m65 rev9 pcb layout m65 rev9 pcb front m65 rev9 pcb back

Rev 7/8

m65 rev7 xiao pcb pcb rev7 front pcb rev7 back

Rev 5/6

pcb rev5 layout pcb rev5 front pcb rev5 back

Rev 4

pcb rev4 layout pcb rev4 front pcb rev4 back

Rev 3

pcb rev3 front pcb rev3 back

Rev 2

pcb rev2

Rev 1

pcb rev1

Gerbers & KiCad Source

Production-ready Gerbers for JLCPCB:

Full KiCad project files are available in m-lego/m65 on GitLab. Note that the default branch HEAD is always work-in-progress:

KiCad Checkout

Rev 11
git clone https://gitlab.com/m-lego/m65.git
cd m65
git checkout revision11
Rev 10
git clone https://gitlab.com/m-lego/m65.git
cd m65
git checkout revision10
Rev 9
git clone https://gitlab.com/m-lego/m65.git
cd m65
git checkout revision9
Rev 7
git clone https://gitlab.com/m-lego/m65.git
cd m65
git checkout revision7
Rev 5/6
git clone https://gitlab.com/m-lego/m65.git
cd m65
git checkout revision5
Rev 4
git clone https://gitlab.com/m-lego/m65.git
cd m65
git checkout revision4

Firmware

Both QMK and ZMK are supported. You can download prebuilt .uf2 binaries below or build from source.

ZMK

Revisions 7, 5, and 4 support ZMK:

Latest automated ZMK builds are available in GitHub Actions (mlego-zmk) under the Build mlego workflow artifacts.

ZMK Shield Sources:

QMK

Latest automated QMK builds can be downloaded from GitHub Actions.

QMK Build Instructions

Rev 11
# OLED version
git clone --recurse-submodules -b mlego_dev https://github.com/alinelena/qmk_firmware.git qmk-alin
cd qmk-alin
qmk compile -kb mlego/m65/rev11 -km uk_knob
qmk compile -kb mlego/m65/rev11 -km default_knob

# E-Ink version
git clone --recurse-submodules -b mlego_eink https://github.com/alinelena/qmk_firmware.git qmk-alin
cd qmk-alin
qmk compile -kb mlego/m65/rev11 -km uk_knob
Rev 10
git clone --recurse-submodules -b mlego_dev https://github.com/alinelena/qmk_firmware.git qmk-alin
cd qmk-alin
qmk compile -kb mlego/m65/rev10 -km uk_knob
qmk compile -kb mlego/m65/rev10 -km default_knob
Rev 9
git clone --recurse-submodules -b mlego_dev https://github.com/alinelena/qmk_firmware.git qmk-alin
cd qmk-alin
qmk compile -kb mlego/m65/rev9 -km uk_knob
qmk compile -kb mlego/m65/rev9 -km default_knob
Rev 7
git clone --recurse-submodules -b mlego_dev https://github.com/alinelena/qmk_firmware.git qmk-alin
cd qmk-alin
qmk compile -kb mlego/m65/rev7 -km uk
qmk compile -kb mlego/m65/rev7 -km default
Rev 5/6
git clone --recurse-submodules -b mlego_dev https://github.com/alinelena/qmk_firmware.git qmk-alin
cd qmk-alin
qmk compile -kb mlego/m65/rev5 -km uk
qmk compile -kb mlego/m65/rev6 -km uk
Rev 4
git clone --recurse-submodules -b mlego https://github.com/alinelena/qmk_firmware.git qmk-alin
cd qmk-alin
qmk compile -kb mlego/m65/rev4 -km uk
Rev 3
git clone --recurse-submodules https://github.com/qmk/qmk_firmware.git
cd qmk_firmware
make mlego/m65/rev3:uk
make mlego/m65/rev3:uk:flash
Rev 2
# GD32F303 WeAct BluePill Plus
git clone --recurse-submodules https://github.com/qmk/qmk_firmware.git
cd qmk_firmware
make mlego/m65/rev2:uk
make mlego/m65/rev2:uk:flash
Rev 1
git clone --recurse-submodules https://github.com/qmk/qmk_firmware.git
cd qmk_firmware
make mlego/m65/rev1:uk
make mlego/m65/rev1:uk:flash

Build Gallery

By Hardware Revision

Rev 11

m65 rev 11 e-ink m65 rev 11 OLED

Rev 10

m65 rev 10 LOTR A m65 rev 10 dev/tty A m65 rev 10 dev/tty B m65 rev 10 LOTR B

Rev 9

m65 rev 9

Rev 7 & 8

m65 rev8 xiao ble sense m65 rev7 xiao rp2040 m65 rev7 xiao pcb

Rev 5 & 6

rev5/6 with RP2040 USB-C

Rev 1–4

Rev 4 — MT3 Elvish Rev 3 — MT3 Dwarvish Rev 2 — Keyboard #14 (Commodore C64 harvested keycaps) Rev 1 — Keyboard #2 (MT3 Susuwatari, Kailh Crystal Jade)

Switch & Keycap Showcase (#1–#15)

  • Bare switches

    Bare Frame & Switches

    Assembled PCB and switches mounted on the LEGO baseplate prior to keycaps.

  • Keyboard #1 MT3 dev/tty

    Keyboard #1 — MT3 /dev/tty Bleached

    Kailh Crystal Royal tactile switches with MT3 /dev/tty Bleached keycaps (alt view).

  • Keyboard #2 MT3 Susuwatari

    Keyboard #2 — MT3 Susuwatari

    Kailh Crystal Jade clicky switches paired with double-shot ABS MT3 Susuwatari.

  • Keyboard #3 MT3 dev/tty

    Keyboard #3 — MT3 /dev/tty

    Kailh Crystal Royal switches (home view 1, home view 2).

  • Keyboard #4 NK Blueberry

    Keyboard #4 — NovelKeys Blueberry

    Re-sprung NovelKeys Blueberry tactile switches with MT3 /dev/tty.

  • Keyboard #5 Gateron Ink v2

    Keyboard #5 — Gateron Ink v2

    Gateron Ink v2 Yellow and Red linear switches prepared for a DK ISO layout.

  • Keyboard #6 Linearized Jades

    Keyboard #6 — "Linear" Crystal Jades

    Kailh Crystal Jades with clickbars removed, paired with MT3 /dev/tty + Godspeed & Japanese alphas (switches 1, switches 2, build 1).

  • Keyboard #7 Gateron Whites

    Keyboard #7 — Gateron Whites & Godspeed

    Gateron White switches with MT3 /dev/tty and Godspeed modifiers (bare, without mods).

  • Keyboard #8 MT3 Camillo

    Keyboard #8 — Frankenswitch Linears

    Custom frankenswitch linears with MT3 Camillo, Godspeed, and Susuwatari accents.

  • Keyboard #9 MT3 Extended 2048

    Keyboard #9 — MT3 Extended 2048

    MT3 Extended 2048 icon legends with colorful accent modifiers.

  • Keyboard #10 MT3 Dasher

    Keyboard #10 — MT3 Dasher

    Two-tone blue MT3 Dasher keycap set.

  • Keyboard #11 MT3 Cyber

    Keyboard #11 — MT3 MiTo Cyber

    High-contrast purple/magenta/yellow MT3 Cyber set.

  • Keyboard #12 MT3 3277

    Keyboard #12 — MT3 Matt3o 3277

    IBM 3277 terminal-inspired dark gray and turquoise keycaps.

  • Keyboard #13 MT3 Fairlane

    Keyboard #13 — MT3 Fairlane

    Pastel mint and cream MT3 Fairlane keycaps.

  • Keyboard #14 Commodore C64

    Keyboard #14 — Commodore C64 Harvest

    Original harvested Commodore 64 keycaps mounted via 3D-printed MX stem adapters.

  • Keyboard #15 MT3 Camillo Eva-02

    Keyboard #15 — MT3 Camillo & Eva-02

    MT3 Camillo alphas with Evangelion Unit-02 novelty accents.

Keymaps & Layout

Default QMK layers for the 5×13 ortholinear layout:

Layout summary Layout lower Layout raise

Pinouts by Revision

Rev 9–11 (XIAO RP2040)

(Note: Rev 9–11 correspond to m66 Rev 1–3 where the clickable encoder forms Column 13.)

ShiftRegister SN74HC595N (#1 & #2)        ShiftRegister 74HC165N / 74HC589 (#3)
QB |1    16| VCC                          nPL |1    16| VCC
QC |2    15| QA                            CP |2    15| nCE
QD |3    14| SER data                      D4 |3    14| D3
QE |4    13| OE                            D5 |4    13| D2
QF |5    12| RCLK latch                    D6 |5    12| D1
QG |6    11| SRCLK clock                   D7 |6    11| D0
QH |7    10| SRCLR                        nQ7 |7    10| DS
 G |8     9| QH*                            G |8     9| Q7
Rows C0 C1 C2 C3 C4 C5 C6 C7 C8 C9 C10 C11 C12 C13 Row Pin
R0 Esc 1 2 3 4 5 6 7 8 9 0 - Bksp MUTE 3D0
R1 Tab q w e r t y u i o p [ ] X* 3D1
R2 # a s d f g h j k l ; ' Enter X* 3D2
R3 Shift \ z x c v b n m , . Up / X* 3D3
R4 Ctrl Menu Lower Alt Raise Space Space Space AltGr Shift Left Down Right X* 3D4
Col 2QB 2QC 2QD 2QE 2QF 2QG 2QH 2QA 1QH 1QG 1QF 1QE 1QA 1QD (X NC)*
  • Encoders: Pad_A = GP27, Pad_B = GP26
  • OLED (I²C): SCL = GP7, SDA = GP6
  • 74HC595N: MOSI = GP3 (pin 11), Latch = GP1 (pin 8), Clock = GP2 (pin 9)
  • 74HC165N: Latch = GP1 (pin 8), Clock = GP2 (pin 9), MISO = GP4 (pin 10), SPI_DIVISOR = 8
  • LEDs (RP2040): CAPS_LOCK = GP25, Lower = GP16, Raise = GP17, RGB DI = GP12, RGB On = GP11
Rev 7/8 (XIAO RP2040 / BLE)
Rows C0 C1 C2 C3 C4 C5 C6 C7 C8 C9 C10 C11 C12 Row Pin
R0 Esc 1 2 3 4 5 6 7 8 9 0 - Bksp D0 (GP26)
R1 Tab q w e r t y u i o p [ ] D1 (GP27)
R2 # a s d f g h j k l ; ' Enter D2 (GP28)
R3 Shift \ z x c v b n m , . Up / D3 (GP29)
R4 Ctrl Menu Lower Alt Raise Space Space Space AltGr Shift Left Down Right D4 (GP6)
Col 2QH 2QG 2QF 2QE 2QD 2QC 2QB 2QA 1QH 1QG 1QF 1QE 1QA
  • Encoders: Pad_A = D5 (GP7), Pad_B = D6 (GP0)
  • 74HC595N: Data = D10 (GP3), Latch = D7 (GP1), Clock = D8 (GP2), SPI_DIVISOR = 32
  • LEDs (RP2040): CAPS_LOCK = GP25, Lower = GP16, Raise = GP17, RGB DI = GP12, RGB On = GP11
Rev 5 & 6 (RP2040 Pico / WeAct)
Rows C0 C1 C2 C3 C4 C5 C6 C7 C8 C9 C10 C11 C12 Row Pin
R0 Esc 1 2 3 4 5 6 7 8 9 0 - Bksp GP22
R1 Tab q w e r t y u i o p [ ] GP16
R2 # a s d f g h j k l ; ' Enter GP18
R3 Shift \ z x c v b n m , . Up / GP19
R4 Ctrl Menu Lower Alt Raise Space Space Space AltGr Shift Left Down Right GP20
Col GP1 GP6 GP7 GP8 GP9 GP15 GP14 GP13 GP12 GP11 GP10 GP17 GP21
  • Encoders: Pad_A = GP4, Pad_B = GP5
  • OLED (I²C): SDA = GP2, SCL = GP3
  • LEDs (Rev 5 Pico): NUM_LOCK = GP28, CAPS_LOCK = GP25, SCROLL_LOCK = GP27, RGB_DI = GP0
  • LEDs (Rev 6 WeAct): NUM_LOCK = GP29, CAPS_LOCK = GP25, SCROLL_LOCK = GP28, RGB_DI = GP0 (same physical positions on PCB)
Rev 4 (`STM32F401`/`F411`)
Rows C0 C1 C2 C3 C4 C5 C6 C7 C8 C9 C10 C11 C12 Row Pin
R0 Esc 1 2 3 4 5 6 7 8 9 0 - Bksp B10
R1 Tab q w e r t y u i o p [ ] A5
R2 # a s d f g h j k l ; ' Enter A6
R3 Shift \ z x c v b n m , . Up / A7
R4 Ctrl Menu Lower Alt Raise Space Space Space AltGr Shift Left Down Right B0
Col B14 A8 A10 A15 B3 B4 B5 B7 A1 A2 A3 A4 B1
  • Encoders: Pad_A = A0, Pad_B = B6
  • OLED (I²C): SDA = B9, SCL = B8
  • LEDs: NUM_LOCK = B12, CAPS_LOCK = C13, SCROLL_LOCK = B13, RGB_DI = B15
Rev 3 (`STM32F401`/`F411`)
Rows C0 C1 C2 C3 C4 C5 C6 C7 C8 C9 C10 C11 C12 Row Pin
R0 Esc 1 2 3 4 5 6 7 8 9 0 - Bksp B10
R1 Tab q w e r t y u i o p [ ] A5
R2 # a s d f g h j k l ; ' Enter A6
R3 Shift \ z x c v b n m , . Up / A7
R4 Ctrl Menu Lower Alt Raise Space Space Space AltGr Shift Left Down Right B0
Col A10 A15 B3 B4 B5 B9 B8 B7 A1 A2 A3 A4 B1
  • Encoders: Pad_A = A0, Pad_B = B6
  • LEDs: NUM_LOCK = B12, CAPS_LOCK = C13, SCROLL_LOCK = B13, RGB_DI = B15
Rev 1 & 2 (`STM32F103` / `GD32F303`)
Rows C0 C1 C2 C3 C4 C5 C6 C7 C8 C9 C10 C11 C12 Row Pin
R0 Esc 1 2 3 4 5 6 7 8 9 0 - Bksp B11
R1 Tab q w e r t y u i o p [ ] B0
R2 # a s d f g h j k l ; ' Enter B1
R3 Shift \ z x c v b n m , . Up / A2
R4 Ctrl Menu Lower Alt Raise Space Space Space AltGr Shift Left Down Right A3
Col A10 A15 B3 B4 B5 B9 B8 B7 B6 C15 A0 A7 B10
  • Encoders: Pad_A = A8, Pad_B = A9
  • LEDs: NUM_LOCK = B12, CAPS_LOCK = B2 (Rev 2) / C13 (Rev 1), SCROLL_LOCK = B13, RGB_DI = B15

Matrix Scan Frequencies & OLED Benchmarks

Matrix scan frequencies (in Hz) reported by QMK with DEBUG_MATRIX_SCAN_RATE enabled using the default keymap:

MCU Platform Revision Freq 1 (Minimal)¹ [Hz] Freq 2 (Full Features)² [Hz]
Teensy 2.0 (ATmega32U4) Handwire 1860 —
APM32F103C8T6 (STM32F103) Rev 1 3427 —
GD32F303CCT6 (BluePill Plus) Rev 2 3931 —
STM32F401 BlackPill Rev 4 4681 1976
RP2040 Raspberry Pi Pico Rev 5 5277 2214
RP2040 WeAct Rev 6 5278 2202
RP2040 Seeed XIAO (2× 74HC595) Rev 7 6162 —
RP2040 Seeed XIAO (2× 595 + 1× 165) Rev 9³ 4366 2125
RP2040 Seeed XIAO (Unicode enabled) Rev 9 3986 1924
  1. Freq 1: OLED off, WPM off, RGBLight off, Mousekey and Extrakey off.
  2. Freq 2: OLED on with all standard features enabled.
  3. Rev 9: Includes an extra 14th column compared to earlier revisions; SPI_DIVISOR is 8 on Rev 9 vs 32 on Rev 7.

Impact of OLED_UPDATE_INTERVAL

Updating an I²C OLED display on every matrix scan causes a noticeable drop in scan rate. QMK provides OLED_UPDATE_INTERVAL (in milliseconds) to throttle display refreshes. Tested on Rev 9 (128×32 and 128×64 screens yield identical numbers; baselines with OLED off: 3760 Hz with WPM off, 2631 Hz with WPM on):

OLED_UPDATE_INTERVAL [ms] Matrix Scan Frequency [Hz]
0 (QMK default) 2125
1 3154
2 3629
3 3788
4 3868
5 3915
10 (100 FPS — recommended) 4011
20 4058
30 4075
50 4087
100 4097

Takeaways

1. Setting `OLED_UPDATE_INTERVAL` to **`10` ms** (100 FPS) nearly doubles the effective matrix scan rate (`4011 Hz` vs `2125 Hz`) while remaining more than fast enough for any keyboard status OLED.
2. Curiously, throttling the OLED refresh achieves a higher peak scan rate (`4097 Hz`) than compiling with the OLED disabled (`3760 Hz`).
m65 (5×13 Ortho)

Quick Specs

  • Layout: 5×13 Ortho
  • Keys: 65
  • MCU: STM32F401 / RP2040 / XIAO
  • Firmware: QMK & ZMK

Original 65-key ISO-friendly LEGO ortholinear keyboard evolved across 11 revisions — from BluePill to Raspberry Pi Pico and Seeed XIAO SPI shift registers.