Code refactor

This commit is contained in:
Christoffer Martinsson 2025-09-19 15:22:27 +02:00
parent 6dbec2425e
commit 932d89e9ce
6 changed files with 96 additions and 66 deletions

View File

@ -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()

View File

@ -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;

View File

@ -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])

View File

@ -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;

View File

@ -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<rp2040_hal::usb::UsbBus>,
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,
);
}
}

View File

@ -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 {