157 lines
4.9 KiB
Rust
157 lines
4.9 KiB
Rust
//! Board bring-up and peripheral wiring for the CMDR Keyboard firmware.
|
|
//!
|
|
//! This module owns the RP2040 peripheral initialisation lifecycle so that the
|
|
//! rest of the firmware can rely on already-configured clocks, GPIO, timers,
|
|
//! USB, and LED control. Nothing outside this module should directly touch raw
|
|
//! PAC peripherals; callers interact with the strongly typed `BoardParts`
|
|
//! returned after initialisation.
|
|
|
|
use crate::{hardware, ButtonMatrix, MatrixPins, StatusLed};
|
|
use cortex_m::delay::Delay;
|
|
use cortex_m::interrupt;
|
|
use rp2040_hal::{
|
|
Clock,
|
|
clocks::init_clocks_and_plls,
|
|
gpio::Pins,
|
|
pac,
|
|
pio::PIOExt,
|
|
sio::Sio,
|
|
timer::Timer,
|
|
watchdog::Watchdog,
|
|
};
|
|
use usb_device::class_prelude::UsbBusAllocator;
|
|
|
|
pub type KeyboardMatrix = ButtonMatrix<
|
|
MatrixPins<{ hardware::KEY_ROWS }, { hardware::KEY_COLS }>,
|
|
{ hardware::KEY_ROWS },
|
|
{ hardware::KEY_COLS },
|
|
{ hardware::NUMBER_OF_KEYS },
|
|
>;
|
|
|
|
pub type KeyboardStatusLed = StatusLed<pac::PIO0, rp2040_hal::pio::SM0, hardware::StatusLedPin>;
|
|
|
|
/// Aggregates the peripherals required to run the keyboard firmware.
|
|
pub struct Board {
|
|
pub button_matrix: KeyboardMatrix,
|
|
pub status_led: KeyboardStatusLed,
|
|
pub delay: Delay,
|
|
pub timer: Timer,
|
|
usb_bus: &'static UsbBusAllocator<rp2040_hal::usb::UsbBus>,
|
|
}
|
|
|
|
/// Components returned to the application after initialization.
|
|
pub struct BoardParts {
|
|
pub button_matrix: KeyboardMatrix,
|
|
pub status_led: KeyboardStatusLed,
|
|
pub delay: Delay,
|
|
pub timer: Timer,
|
|
pub usb_bus: &'static UsbBusAllocator<rp2040_hal::usb::UsbBus>,
|
|
}
|
|
|
|
impl Board {
|
|
pub fn new() -> Self {
|
|
// Acquire RP2040 peripheral handles before board bring-up.
|
|
let mut pac = pac::Peripherals::take().unwrap();
|
|
let core = pac::CorePeripherals::take().unwrap();
|
|
|
|
let mut watchdog = Watchdog::new(pac.WATCHDOG);
|
|
// Bring up the primary system and USB clocks using the external crystal.
|
|
let clocks = init_clocks_and_plls(
|
|
hardware::XTAL_FREQ_HZ,
|
|
pac.XOSC,
|
|
pac.CLOCKS,
|
|
pac.PLL_SYS,
|
|
pac.PLL_USB,
|
|
&mut pac.RESETS,
|
|
&mut watchdog,
|
|
)
|
|
.ok()
|
|
.unwrap();
|
|
|
|
let sio = Sio::new(pac.SIO);
|
|
// Split the GPIO bank into the strongly typed board pins.
|
|
let pins = Pins::new(
|
|
pac.IO_BANK0,
|
|
pac.PADS_BANK0,
|
|
sio.gpio_bank0,
|
|
&mut pac.RESETS,
|
|
);
|
|
|
|
let (rows, cols, status_pin) = hardware::split_board_pins(pins);
|
|
let matrix_pins = MatrixPins::new(rows, cols);
|
|
|
|
// Create the debounced button matrix scanner with firmware thresholds.
|
|
let mut button_matrix = ButtonMatrix::new(
|
|
matrix_pins,
|
|
hardware::MATRIX_DEBOUNCE_SCANS_PRESS,
|
|
hardware::MATRIX_DEBOUNCE_SCANS_RELEASE,
|
|
hardware::MIN_PRESS_SPACING_SCANS,
|
|
hardware::RELEASE_GRACE_PERIOD_SCANS,
|
|
);
|
|
button_matrix.init_pins();
|
|
|
|
let (mut pio, sm0, _, _, _) = pac.PIO0.split(&mut pac.RESETS);
|
|
let status_led_pin = status_pin;
|
|
// Configure the WS2812 status LED using a dedicated PIO state machine.
|
|
let status_led = StatusLed::new(
|
|
status_led_pin,
|
|
&mut pio,
|
|
sm0,
|
|
clocks.peripheral_clock.freq(),
|
|
);
|
|
|
|
// Prepare shared timer peripherals and a blocking delay helper.
|
|
let timer = Timer::new(pac.TIMER, &mut pac.RESETS, &clocks);
|
|
let delay = Delay::new(core.SYST, clocks.system_clock.freq().to_Hz());
|
|
|
|
// Allocate the global USB bus for the HID device class.
|
|
let usb_bus = usb_allocator(
|
|
pac.USBCTRL_REGS,
|
|
pac.USBCTRL_DPRAM,
|
|
clocks.usb_clock,
|
|
&mut pac.RESETS,
|
|
);
|
|
|
|
Self {
|
|
button_matrix,
|
|
status_led,
|
|
delay,
|
|
timer,
|
|
usb_bus,
|
|
}
|
|
}
|
|
|
|
pub fn into_parts(self) -> BoardParts {
|
|
BoardParts {
|
|
button_matrix: self.button_matrix,
|
|
status_led: self.status_led,
|
|
delay: self.delay,
|
|
timer: self.timer,
|
|
usb_bus: self.usb_bus,
|
|
}
|
|
}
|
|
}
|
|
|
|
fn usb_allocator(
|
|
usbctrl_regs: pac::USBCTRL_REGS,
|
|
usbctrl_dpram: pac::USBCTRL_DPRAM,
|
|
usb_clock: rp2040_hal::clocks::UsbClock,
|
|
resets: &mut pac::RESETS,
|
|
) -> &'static UsbBusAllocator<rp2040_hal::usb::UsbBus> {
|
|
// Lazily create and share the USB bus allocator between HID classes.
|
|
static USB_BUS: static_cell::StaticCell<UsbBusAllocator<rp2040_hal::usb::UsbBus>> =
|
|
static_cell::StaticCell::new();
|
|
|
|
interrupt::free(|_| {
|
|
USB_BUS.init_with(|| {
|
|
UsbBusAllocator::new(rp2040_hal::usb::UsbBus::new(
|
|
usbctrl_regs,
|
|
usbctrl_dpram,
|
|
usb_clock,
|
|
true,
|
|
resets,
|
|
))
|
|
})
|
|
})
|
|
}
|