From 932d89e9ce644a28da955187a1d3160d6476abd8 Mon Sep 17 00:00:00 2001 From: Christoffer Martinsson Date: Fri, 19 Sep 2025 15:22:27 +0200 Subject: [PATCH] Code refactor --- rp2040/src/bootloader.rs | 8 ++++-- rp2040/src/button_matrix.rs | 28 ++++++++++--------- rp2040/src/keyboard.rs | 55 ++++++++++++++++++++---------------- rp2040/src/lib.rs | 4 +++ rp2040/src/main.rs | 56 ++++++++++++++++++++++++------------- rp2040/src/status.rs | 11 ++++---- 6 files changed, 96 insertions(+), 66 deletions(-) diff --git a/rp2040/src/bootloader.rs b/rp2040/src/bootloader.rs index 79ca94b..3ca5f27 100644 --- a/rp2040/src/bootloader.rs +++ b/rp2040/src/bootloader.rs @@ -1,6 +1,8 @@ //! Bootloader entry helpers shared between power-on checks and runtime combos. -use crate::{NUMBER_OF_KEYS, StatusLed, layout, status::StatusMode}; +use crate::{KeyMatrix, StatusLed, layout, status::StatusMode}; +#[cfg(all(test, feature = "std"))] +use crate::NUMBER_OF_KEYS; use cortex_m::asm; use rp2040_hal::{ gpio::AnyPin, @@ -11,7 +13,7 @@ use usbd_human_interface_device::page::Keyboard; /// Returns true when the runtime bootloader chord is held. /// /// The chord requires two Fn buttons, both Shift buttons and the primary Ctrl. -pub fn chord_requested(pressed_keys: &[bool; NUMBER_OF_KEYS]) -> bool { +pub fn chord_requested(pressed_keys: &KeyMatrix) -> bool { if !modifier_pressed(pressed_keys, Keyboard::LeftShift) || !modifier_pressed(pressed_keys, Keyboard::RightShift) { @@ -30,7 +32,7 @@ pub fn chord_requested(pressed_keys: &[bool; NUMBER_OF_KEYS]) -> bool { active_fn >= 2 } -fn modifier_pressed(pressed_keys: &[bool; NUMBER_OF_KEYS], key: Keyboard) -> bool { +fn modifier_pressed(pressed_keys: &KeyMatrix, key: Keyboard) -> bool { layout::MAP[0] .iter() .enumerate() diff --git a/rp2040/src/button_matrix.rs b/rp2040/src/button_matrix.rs index de49d5b..abd295c 100644 --- a/rp2040/src/button_matrix.rs +++ b/rp2040/src/button_matrix.rs @@ -126,19 +126,10 @@ where }; if self.debounce_counter[button_index] >= threshold { - if current_state { - let elapsed = self - .scan_counter - .wrapping_sub(self.last_press_scan[button_index]); - if self.last_press_scan[button_index] == 0 - || elapsed >= self.min_press_gap_scans - { - self.pressed[button_index] = true; - self.last_press_scan[button_index] = self.scan_counter; - } - } else { - self.pressed[button_index] = false; - } + self.pressed[button_index] = match current_state { + true => self.should_register_press(button_index), + false => false, + }; self.debounce_counter[button_index] = 0; } } @@ -148,6 +139,17 @@ where self.pressed } + fn should_register_press(&mut self, button_index: usize) -> bool { + let elapsed = self.scan_counter.wrapping_sub(self.last_press_scan[button_index]); + let can_register = self.last_press_scan[button_index] == 0 + || elapsed >= self.min_press_gap_scans; + + if can_register { + self.last_press_scan[button_index] = self.scan_counter; + } + can_register + } + #[cfg(all(test, feature = "std"))] pub(crate) fn set_scan_counter(&mut self, value: u32) { self.scan_counter = value; diff --git a/rp2040/src/keyboard.rs b/rp2040/src/keyboard.rs index 50ff539..36db64a 100644 --- a/rp2040/src/keyboard.rs +++ b/rp2040/src/keyboard.rs @@ -1,6 +1,6 @@ //! Keyboard state management and HID report generation. -use crate::NUMBER_OF_KEYS; +use crate::{NUMBER_OF_KEYS, KeyMatrix, KeyReport}; use crate::layout; use crate::status::StatusSummary; use usbd_human_interface_device::page::Keyboard; @@ -50,8 +50,8 @@ impl KeyboardState { pub fn process_scan( &mut self, - pressed_keys: [bool; NUMBER_OF_KEYS], - ) -> [Keyboard; NUMBER_OF_KEYS] { + pressed_keys: KeyMatrix, + ) -> KeyReport { let fn_mode = Self::fn_mode(&pressed_keys); for (index, pressed) in pressed_keys.iter().enumerate() { @@ -85,6 +85,16 @@ impl KeyboardState { self.sticky_state } + fn toggle_sticky_state(&mut self) { + self.sticky_state = match self.sticky_state { + StickyState::Inactive => StickyState::Armed, + StickyState::Armed | StickyState::Latched => { + self.sticky_key = Keyboard::NoEventIndicated; + StickyState::Inactive + } + }; + } + pub fn status_summary( &self, usb_initialized: bool, @@ -103,34 +113,27 @@ impl KeyboardState { ) } - fn build_report(&mut self, fn_mode: u8) -> [Keyboard; NUMBER_OF_KEYS] { + fn build_report(&mut self, fn_mode: u8) -> KeyReport { let mut report = [Keyboard::NoEventIndicated; NUMBER_OF_KEYS]; + let mut sticky_toggle_requested = false; for (index, button) in self.buttons.iter_mut().enumerate() { let changed = button.pressed != button.previous_pressed; let just_pressed = changed && button.pressed; - if just_pressed - && index as u8 == layout::STICKY_BUTTON[0] - && fn_mode == layout::STICKY_BUTTON[1] - { - self.sticky_state = match self.sticky_state { - StickyState::Inactive => StickyState::Armed, - StickyState::Armed | StickyState::Latched => { - self.sticky_key = Keyboard::NoEventIndicated; - StickyState::Inactive - } - }; - } else if just_pressed - && index as u8 == layout::OS_LOCK_BUTTON[0] - && fn_mode == layout::OS_LOCK_BUTTON[1] - { - report[36] = layout::OS_LOCK_BUTTON_KEYS[0]; - report[37] = layout::OS_LOCK_BUTTON_KEYS[1]; - } - if just_pressed { button.fn_mode = fn_mode; + + match (index as u8, fn_mode) { + (idx, layer) if idx == layout::STICKY_BUTTON[0] && layer == layout::STICKY_BUTTON[1] => { + sticky_toggle_requested = true; + } + (idx, layer) if idx == layout::OS_LOCK_BUTTON[0] && layer == layout::OS_LOCK_BUTTON[1] => { + report[36] = layout::OS_LOCK_BUTTON_KEYS[0]; + report[37] = layout::OS_LOCK_BUTTON_KEYS[1]; + } + _ => {} + } } let layer_key = layout::MAP[button.fn_mode as usize][index]; @@ -151,13 +154,17 @@ impl KeyboardState { button.previous_pressed = button.pressed; } + if sticky_toggle_requested { + self.toggle_sticky_state(); + } + const STICKY_REPORT_INDEX: usize = 46; report[STICKY_REPORT_INDEX] = self.sticky_key; report } - fn fn_mode(pressed_keys: &[bool; NUMBER_OF_KEYS]) -> u8 { + fn fn_mode(pressed_keys: &KeyMatrix) -> u8 { let active_fn_keys = layout::FN_BUTTONS .iter() .filter(|key_index| pressed_keys[**key_index as usize]) diff --git a/rp2040/src/lib.rs b/rp2040/src/lib.rs index 86116c8..dcf6a90 100644 --- a/rp2040/src/lib.rs +++ b/rp2040/src/lib.rs @@ -24,6 +24,10 @@ pub use hardware::{ pub use keyboard::{KeyboardState, StickyState}; pub use status::{StatusLed, StatusMode, StatusSummary}; +// Type aliases for better code readability +pub type KeyMatrix = [bool; NUMBER_OF_KEYS]; +pub type KeyReport = [usbd_human_interface_device::page::Keyboard; NUMBER_OF_KEYS]; + #[cfg(feature = "std")] #[unsafe(no_mangle)] static mut __bi_entries_start: u8 = 0; diff --git a/rp2040/src/main.rs b/rp2040/src/main.rs index 50627f6..f2c6fbe 100644 --- a/rp2040/src/main.rs +++ b/rp2040/src/main.rs @@ -8,7 +8,7 @@ #![no_main] use cmdr_keyboard_42::hardware::{self, timers}; -use cmdr_keyboard_42::{Board, BoardParts, KeyboardState, StatusMode, bootloader}; +use cmdr_keyboard_42::{Board, BoardParts, KeyboardState, StatusMode, bootloader, KeyReport}; use embedded_hal_0_2::timer::CountDown; use fugit::ExtU32; use panic_halt as _; @@ -25,6 +25,31 @@ use usbd_human_interface_device::prelude::*; #[used] pub static BOOT2_FIRMWARE: [u8; 256] = rp2040_boot2::BOOT_LOADER_W25Q080; + +fn handle_usb_state_changes( + usb_dev: &UsbDevice, + usb_suspended: &mut bool, + wake_on_input: &mut bool, + last_activity_ms: &mut u32, + status_time_ms: u32, +) { + let current_suspended = usb_dev.state() == UsbDeviceState::Suspend; + let was_suspended = *usb_suspended; + + match (was_suspended, current_suspended) { + (true, false) => { + *last_activity_ms = status_time_ms; + *wake_on_input = false; + } + (false, true) => { + *wake_on_input = true; + } + _ => {} + } + + *usb_suspended = current_suspended; +} + #[rp2040_hal::entry] fn main() -> ! { let BoardParts { @@ -86,14 +111,12 @@ fn main() -> ! { ); } - let should_scan = if usb_suspended { + let should_scan = !usb_suspended || { static mut SUSPENDED_SCAN_COUNTER: u8 = 0; unsafe { SUSPENDED_SCAN_COUNTER = (SUSPENDED_SCAN_COUNTER + 1) % 20; SUSPENDED_SCAN_COUNTER == 0 } - } else { - true }; if usb_tick.wait().is_ok() && should_scan { @@ -102,9 +125,8 @@ fn main() -> ! { if bootloader::chord_requested(&pressed_keys) { if !usb_suspended { - let mut attempts: u8 = 0; - while attempts < 3 { - let clear_report = [Keyboard::NoEventIndicated; hardware::NUMBER_OF_KEYS]; + for _ in 0..3 { + let clear_report: KeyReport = [Keyboard::NoEventIndicated; hardware::NUMBER_OF_KEYS]; match keyboard.device().write_report(clear_report) { Ok(_) => break, Err(UsbHidError::WouldBlock) | Err(UsbHidError::Duplicate) => { @@ -112,7 +134,6 @@ fn main() -> ! { } Err(_) => break, } - attempts = attempts.saturating_add(1); } } @@ -167,17 +188,12 @@ fn main() -> ! { } } - let usb_state = usb_dev.state(); - let was_suspended = usb_suspended; - usb_suspended = usb_state == UsbDeviceState::Suspend; - - if was_suspended && !usb_suspended { - last_activity_ms = status_time_ms; - wake_on_input = false; - } - - if !was_suspended && usb_suspended { - wake_on_input = true; - } + handle_usb_state_changes( + &usb_dev, + &mut usb_suspended, + &mut wake_on_input, + &mut last_activity_ms, + status_time_ms, + ); } } diff --git a/rp2040/src/status.rs b/rp2040/src/status.rs index 339312c..279dba6 100644 --- a/rp2040/src/status.rs +++ b/rp2040/src/status.rs @@ -166,18 +166,17 @@ fn breathe(color: RGB8, elapsed_ms: u32, period_ms: u32) -> RGB8 { if period_ms == 0 { return color; } + let period = period_ms.max(1); - let time_in_period = (elapsed_ms % period) as f32; - let period_f = period as f32; - let phase = time_in_period / period_f; + let phase = (elapsed_ms % period) as f32 / period as f32; let brightness = if phase < 0.5 { 1.0 - (phase * 2.0) } else { (phase - 0.5) * 2.0 }; - let clamped = brightness.max(0.0).min(1.0); - let ramp = (clamped * 255.0 + 0.5) as u8; - scale_color(color, ramp) + let brightness_factor = ((brightness.clamp(0.0, 1.0) * 255.0) + 0.5) as u8; + + scale_color(color, brightness_factor) } fn scale_color(color: RGB8, factor: u8) -> RGB8 {