Added bootloader module to handle how to enter bootloader during runtime

This commit is contained in:
Christoffer Martinsson 2025-09-18 13:42:01 +02:00
parent 33531719d6
commit 7be20ad841
3 changed files with 103 additions and 6 deletions

77
rp2040/src/bootloader.rs Normal file
View File

@ -0,0 +1,77 @@
//! Bootloader entry helpers shared between power-on checks and runtime combos.
use crate::{layout, status::StatusMode, StatusLed, NUMBER_OF_KEYS};
use cortex_m::asm;
use rp2040_hal::{
gpio::AnyPin,
pio::{PIOExt, StateMachineIndex},
};
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 {
if !modifier_pressed(pressed_keys, Keyboard::LeftShift)
|| !modifier_pressed(pressed_keys, Keyboard::RightShift)
{
return false;
}
if !modifier_pressed(pressed_keys, Keyboard::LeftControl) {
return false;
}
let active_fn = layout::FN_BUTTONS
.iter()
.filter(|index| pressed_keys[**index as usize])
.count();
active_fn >= 2
}
fn modifier_pressed(pressed_keys: &[bool; NUMBER_OF_KEYS], key: Keyboard) -> bool {
layout::MAP[0]
.iter()
.enumerate()
.any(|(index, mapped)| *mapped == key && pressed_keys[index])
}
/// Puts the RP2040 into the ROM bootloader.
pub fn enter<P, SM, I>(status_led: &mut StatusLed<P, SM, I>) -> !
where
P: PIOExt,
SM: StateMachineIndex,
I: AnyPin<Function = P::PinFunction>,
{
status_led.update(StatusMode::Bootloader);
let gpio_activity_pin_mask: u32 = 0;
let disable_interface_mask: u32 = 0;
rp2040_hal::rom_data::reset_to_usb_boot(gpio_activity_pin_mask, disable_interface_mask);
loop {
asm::nop();
}
}
#[cfg(all(test, feature = "std"))]
mod tests {
use super::*;
#[test]
fn chord_requires_all_modifier_keys_and_two_fn() {
let mut pressed = [false; NUMBER_OF_KEYS];
for (index, key) in layout::MAP[0].iter().enumerate() {
match key {
Keyboard::LeftShift | Keyboard::RightShift | Keyboard::LeftControl => {
pressed[index] = true;
}
_ => {}
}
}
pressed[layout::FN_BUTTONS[0] as usize] = true;
assert!(!chord_requested(&pressed));
pressed[layout::FN_BUTTONS[1] as usize] = true;
assert!(chord_requested(&pressed));
}
}

View File

@ -6,6 +6,7 @@
//! details, status handling, and keyboard processing modular.
pub mod button_matrix;
pub mod bootloader;
pub mod hardware;
pub mod keyboard;
pub mod layout;

View File

@ -8,7 +8,7 @@
#![no_main]
use cmdr_keyboard_42::hardware::{self, timers};
use cmdr_keyboard_42::{ButtonMatrix, KeyboardState, StatusLed, StatusMode};
use cmdr_keyboard_42::{bootloader, ButtonMatrix, KeyboardState, StatusLed, StatusMode};
use cortex_m::delay::Delay;
use embedded_hal::digital::{InputPin, OutputPin};
use embedded_hal_0_2::timer::CountDown;
@ -26,6 +26,7 @@ use rp2040_hal::{
use usb_device::class_prelude::*;
use usb_device::prelude::*;
use usb_device::device::UsbDeviceState;
use usbd_human_interface_device::page::Keyboard;
use usbd_human_interface_device::prelude::*;
#[unsafe(link_section = ".boot2")]
@ -127,11 +128,9 @@ fn main() -> ! {
button_matrix.scan_matrix(&mut delay);
}
if button_matrix.buttons_pressed()[0] {
status_led.update(StatusMode::Bootloader);
let gpio_activity_pin_mask: u32 = 0;
let disable_interface_mask: u32 = 0;
rp2040_hal::rom_data::reset_to_usb_boot(gpio_activity_pin_mask, disable_interface_mask);
let initial_pressed = button_matrix.buttons_pressed();
if initial_pressed[0] {
bootloader::enter(&mut status_led);
}
let usb_bus = UsbBusAllocator::new(rp2040_hal::usb::UsbBus::new(
@ -186,6 +185,26 @@ fn main() -> ! {
button_matrix.scan_matrix(&mut delay);
let pressed_keys = button_matrix.buttons_pressed();
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];
match keyboard.device().write_report(clear_report) {
Ok(_) => break,
Err(UsbHidError::WouldBlock) | Err(UsbHidError::Duplicate) => {
let _ = keyboard.tick();
}
Err(_) => break,
}
attempts = attempts.saturating_add(1);
}
}
delay.delay_ms(5);
bootloader::enter(&mut status_led);
}
// Check for input activity
if pressed_keys.iter().any(|pressed| *pressed) {
last_activity_ms = status_time_ms;