From 8a316768698da3149fa6fc89aae828ff1bbd9fb9 Mon Sep 17 00:00:00 2001 From: mlgarchery Date: Tue, 18 Nov 2025 11:29:43 +0100 Subject: [PATCH 1/2] formatting and typo fixing --- firmware/src/layout.rs | 14 +++++++------- firmware/src/main.rs | 33 ++++++++++++++++----------------- firmware/src/zero.rs | 22 +++++++++++----------- 3 files changed, 34 insertions(+), 35 deletions(-) diff --git a/firmware/src/layout.rs b/firmware/src/layout.rs index 094dfcf..ea23e41 100644 --- a/firmware/src/layout.rs +++ b/firmware/src/layout.rs @@ -3,9 +3,9 @@ const LAYER_COUNT: usize = 6; // adjust to the number of layers defined below pub const COLS: usize = 12; pub const ROWS: usize = 4; -use keyberon::layout; -use keyberon::action::{m, Action}; +use keyberon::action::{Action, m}; use keyberon::key_code::KeyCode; +use keyberon::layout; pub type QuackenLayout = layout::Layout; @@ -13,12 +13,12 @@ pub type QuackenLayout = layout::Layout; // const CLOSE: Action<()> = m(&[KeyCode::LCtrl, KeyCode::T].as_slice()); // const COPY: Action<()> = m(&[KeyCode::LCtrl, KeyCode::W].as_slice()); const CMD: KeyCode = KeyCode::LCtrl; // LGui for macOS -const UNDO: Action<()> = m(&[CMD, KeyCode::Z].as_slice()); -const CUT: Action<()> = m(&[CMD, KeyCode::X].as_slice()); -const COPY: Action<()> = m(&[CMD, KeyCode::C].as_slice()); +const UNDO: Action<()> = m(&[CMD, KeyCode::Z].as_slice()); +const CUT: Action<()> = m(&[CMD, KeyCode::X].as_slice()); +const COPY: Action<()> = m(&[CMD, KeyCode::C].as_slice()); const PASTE: Action<()> = m(&[CMD, KeyCode::V].as_slice()); -const ALL: Action<()> = m(&[CMD, KeyCode::A].as_slice()); -const SAVE: Action<()> = m(&[CMD, KeyCode::S].as_slice()); +const ALL: Action<()> = m(&[CMD, KeyCode::A].as_slice()); +const SAVE: Action<()> = m(&[CMD, KeyCode::S].as_slice()); const CLOSE: Action<()> = m(&[CMD, KeyCode::W].as_slice()); // other shortcuts diff --git a/firmware/src/main.rs b/firmware/src/main.rs index ba4bcc2..9da134d 100644 --- a/firmware/src/main.rs +++ b/firmware/src/main.rs @@ -2,7 +2,7 @@ #![no_main] mod layout; // 3*6 key layout -mod zero; // QuackenZero-specific matrix scanning +mod zero; // QuackenZero-specific matrix scanning // set the panic handler use panic_halt as _; @@ -25,29 +25,24 @@ mod app { use cortex_m::delay::Delay; use rp2040_hal::{ - self, - Clock, + self, Clock, clocks::init_clocks_and_plls, fugit::MicrosDurationU32, - pac::CorePeripherals, gpio::Pins, + pac::CorePeripherals, sio::Sio, - timer::{ Alarm, Alarm0, Timer }, + timer::{Alarm, Alarm0, Timer}, usb::UsbBus, watchdog::Watchdog, }; - use keyberon::{ - debounce::Debouncer, - key_code::KbHidReport, - layout::Layout, - }; + use keyberon::{debounce::Debouncer, key_code::KbHidReport, layout::Layout}; use usb_device::{ - prelude::UsbDeviceState, - class_prelude::UsbBusAllocator, // HACK: import the UsbClass trait, but still allow to use its name for a type later class::UsbClass as _, + class_prelude::UsbBusAllocator, + prelude::UsbDeviceState, }; type UsbClass = keyberon::Class<'static, UsbBus, ()>; @@ -67,8 +62,8 @@ mod app { // Fun fact: the keyboard is invisible to `lsusb` // if the scan time is set to 10_000 us or above. - const SCAN_TIME_US: u32 = 1_000; - const WATCHDOG_TIME_US: u32 = 10_000; + const SCAN_TIME_US: u32 = 1_000; + const WATCHDOG_TIME_US: u32 = 10_000; const EXTERNAL_XTAL_FREQ_HZ: u32 = 12_000_000; #[shared] @@ -156,7 +151,7 @@ mod app { debouncer: Debouncer::new( [[false; kb_layout::COLS]; kb_layout::ROWS], [[false; kb_layout::COLS]; kb_layout::ROWS], - 5 + 5, ), delay, timer, @@ -197,8 +192,12 @@ mod app { c.shared.watchdog.feed(); - let delay_1us = || { c.local.delay.delay_us(1) }; - for event in c.local.debouncer.events(c.local.matrix.get_with_delay(delay_1us).get()) { + let delay_1us = || c.local.delay.delay_us(1); + for event in c + .local + .debouncer + .events(c.local.matrix.get_with_delay(delay_1us).get()) + { c.local.layout.event(event); } diff --git a/firmware/src/zero.rs b/firmware/src/zero.rs index 0e82059..99b3cc1 100644 --- a/firmware/src/zero.rs +++ b/firmware/src/zero.rs @@ -20,11 +20,9 @@ Besides, the ProMicro GPIO pinout used by Ergogen and ZMK rely on the ProMicro A . 8 | | 16 8 | | 23 8 | | 19 . 9 |___________| 10 9 |___________| 21 9 |___________| 10 **/ - // The logical layout is a 12×4 matrix: 3×6 + 3 thumb keys for each hand. // Ergogen has generated an 8*6 matrix rather than a 12×4 one, in order to save two pins: // the two halves are stacked onto one anather. So here’s q quick helper to work around that. - use crate::layout; const MATRIX_COLS: usize = 6; const MATRIX_ROWS: usize = 8; @@ -43,9 +41,9 @@ fn matrix_to_layout(row: usize, col: usize) -> (usize, usize) { // So here's a `Col2RowMatrix` type to implement this. // rp2040 implementations of the embedded_hal::digital::InputPin,OutputPin} traits -use rp2040_hal::gpio; use embedded_hal::digital::{InputPin, OutputPin}; -type KbInputPin = gpio::Pin; +use rp2040_hal::gpio; +type KbInputPin = gpio::Pin; type KbOutputPin = gpio::Pin; pub type QuackenZeroMatrix = Col2RowMatrix; @@ -92,7 +90,7 @@ where Ok(()) } - /// Creates a new Generic FroMicro matrix. + /// Creates a new Generic ProMicro matrix. pub fn new_promicro(pins: gpio::Pins) -> Result { QuackenZeroMatrix::new( [ @@ -132,10 +130,10 @@ where pins.gpio4.into_pull_down_input().into_dyn_pin(), pins.gpio5.into_pull_down_input().into_dyn_pin(), pins.gpio9.into_pull_down_input().into_dyn_pin(), - pins.gpio28.into_pull_down_input().into_dyn_pin(), // promicro 20 - pins.gpio27.into_pull_down_input().into_dyn_pin(), // promicro 19 - pins.gpio26.into_pull_down_input().into_dyn_pin(), // promicro 18 - pins.gpio21.into_pull_down_input().into_dyn_pin(), // promicro 10 + pins.gpio28.into_pull_down_input().into_dyn_pin(), // promicro 20 + pins.gpio27.into_pull_down_input().into_dyn_pin(), // promicro 19 + pins.gpio26.into_pull_down_input().into_dyn_pin(), // promicro 18 + pins.gpio21.into_pull_down_input().into_dyn_pin(), // promicro 10 ], ) } @@ -146,8 +144,10 @@ where /// if it's high, the key is marked as pressed. /// /// Delay function allows pause to let input pins settle. - pub fn get_with_delay(&mut self, mut delay: F) - -> Result<[[bool; layout::COLS]; layout::ROWS], E> + pub fn get_with_delay( + &mut self, + mut delay: F, + ) -> Result<[[bool; layout::COLS]; layout::ROWS], E> where C: OutputPin, R: InputPin, From 6003f0bad1406e485f5d13a3535bf48ff706adff Mon Sep 17 00:00:00 2001 From: mlgarchery Date: Tue, 18 Nov 2025 12:15:53 +0100 Subject: [PATCH 2/2] upside_down matrix --- firmware/README.md | 3 +++ firmware/src/main.rs | 5 ++++- firmware/src/zero.rs | 9 +++++++++ 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/firmware/README.md b/firmware/README.md index c416085..0b0dd89 100644 --- a/firmware/README.md +++ b/firmware/README.md @@ -18,6 +18,9 @@ patched version with: cargo install --git https://github.com/StripedMonkey/elf2uf2-rs.git#c1638b9 ``` +Note: if you soldered your microcontroller face down, uncomment the +`matrix.upside_down();` line in `main.rs`. + ## ZMK [There is a ZMK Configuration on a dedicated repo][2]. diff --git a/firmware/src/main.rs b/firmware/src/main.rs index 9da134d..868200c 100644 --- a/firmware/src/main.rs +++ b/firmware/src/main.rs @@ -136,7 +136,10 @@ mod app { watchdog.start(MicrosDurationU32::micros(WATCHDOG_TIME_US)); - let Ok(matrix) = QuackenZeroMatrix::new_sparkfun_rp2040(pins); + #[allow(unused_mut)] + let Ok(mut matrix) = QuackenZeroMatrix::new_sparkfun_rp2040(pins); + //// Uncomment if you soldered your microcontroller face down: + // matrix.upside_down(); ( Shared { diff --git a/firmware/src/zero.rs b/firmware/src/zero.rs index 99b3cc1..c7233b7 100644 --- a/firmware/src/zero.rs +++ b/firmware/src/zero.rs @@ -138,6 +138,15 @@ where ) } + // To use after creating the QuackenZeroMatrix if the microcontroller was soldered face down. + // Cols and rows order is modified like this: + // [c0, c1, c2, c3, c4, c5] --> [c3, c4, c5, c0, c1, c2] + // [r0, r1, r2, r3, r4, r5, r6, r7] --> [r4, r5, r6, r7, r0, r1, r2, r3] + pub fn upside_down(&mut self) { + self.cols.rotate_left(MATRIX_COLS / 2); + self.rows.rotate_left(MATRIX_ROWS / 2); + } + /// Scans the matrix and checks which keys are pressed. /// /// Every column pin in order is pulled high, and then each row pin is tested: