//! Hardware configuration for CMDR Joystick (RP2040) //! //! Mirrors the structure introduced for the CMDR Keyboard firmware so that //! bring-up, pin management, and timing constants follow the same layout. use rp2040_hal::gpio::Pins; use rp2040_hal::gpio::{ self, DynPinId, FunctionI2C, FunctionPio0, FunctionSioInput, FunctionSioOutput, Pin, PullNone, PullUp, }; /// External crystal frequency (Hz). pub const XTAL_FREQ_HZ: u32 = 12_000_000; /// USB Vendor ID/Product ID. pub const USB_VID: u16 = 0x1209; pub const USB_PID: u16 = 0x0002; /// Button matrix geometry (rows/cols) and count. pub const BUTTON_ROWS: usize = 5; pub const BUTTON_COLS: usize = 5; pub const NUMBER_OF_BUTTONS: usize = BUTTON_ROWS * BUTTON_COLS; /// ADC characteristics. pub const ADC_MIN: u16 = 0; pub const ADC_MAX: u16 = 4095; pub const AXIS_CENTER: u16 = (ADC_MIN + ADC_MAX) / 2; pub const NBR_OF_GIMBAL_AXIS: usize = 4; /// Debounce thresholds. pub const MATRIX_DEBOUNCE_SCANS: u8 = 15; pub const MIN_PRESS_SPACING_SCANS: u32 = 25; // ~5ms @ 200µs cadence /// EEPROM storage length (calibration data + gimbal mode). pub const EEPROM_DATA_LENGTH: usize = 25; /// USB string descriptors. pub mod usb { pub const MANUFACTURER: &str = "CMtec"; pub const PRODUCT: &str = "CMDR Joystick"; pub const SERIAL_NUMBER: &str = "0001"; } /// Timing cadences. pub mod timers { pub const STATUS_LED_INTERVAL_MS: u32 = 40; pub const SCAN_INTERVAL_US: u32 = 200; pub const USB_UPDATE_INTERVAL_MS: u32 = 1; pub const USB_ACTIVITY_TIMEOUT_MS: u32 = 5_000; } /// I2C helpers. pub mod i2c { use eeprom24x::SlaveAddr; use fugit::{Rate, RateExtU32}; pub const FREQUENCY_HZ: u32 = 400_000; pub const SYSTEM_CLOCK_HZ: u32 = 125_000_000; pub fn frequency() -> Rate { FREQUENCY_HZ.Hz() } pub fn system_clock() -> Rate { SYSTEM_CLOCK_HZ.Hz() } pub const EEPROM_ADDRESS: SlaveAddr = SlaveAddr::Alternative(false, false, false); } /// Raw GPIO constants retained for documentation/reference. pub mod pins { pub const LEFT_EXTRA_BUTTON: u8 = 1; pub const RIGHT_EXTRA_BUTTON: u8 = 0; pub const BUTTON_ROW_0: u8 = 6; pub const BUTTON_ROW_1: u8 = 8; pub const BUTTON_ROW_2: u8 = 4; pub const BUTTON_ROW_3: u8 = 7; pub const BUTTON_ROW_4: u8 = 5; pub const BUTTON_COL_0: u8 = 9; pub const BUTTON_COL_1: u8 = 10; pub const BUTTON_COL_2: u8 = 11; pub const BUTTON_COL_3: u8 = 12; pub const BUTTON_COL_4: u8 = 13; pub const ADC_LEFT_X: u8 = 29; pub const ADC_LEFT_Y: u8 = 28; pub const ADC_RIGHT_X: u8 = 27; pub const ADC_RIGHT_Y: u8 = 26; pub const STATUS_LED: u8 = 16; pub const I2C_SDA: u8 = 14; pub const I2C_SCL: u8 = 15; } /// Matrix row pins (dynamic to simplify scanning code). pub type MatrixRowPin = Pin; /// Matrix column pins (dynamic push-pull outputs). pub type MatrixColPin = Pin; /// Extra buttons (pull-up inputs). pub type ExtraButtonPin = Pin; /// Status LED pin configured for PIO output. pub type StatusLedPin = Pin; /// I2C SDA/SCL pins after reconfiguration. pub type I2cSdaPin = Pin; pub type I2cSclPin = Pin; /// Analog axis input pins (remain as SIO inputs until wrapped by `AdcPin`). pub struct AxisInputs { pub left_x: Pin, pub left_y: Pin, pub right_x: Pin, pub right_y: Pin, } /// Bundle returned by `split_board_pins`. pub struct BoardPins { pub matrix_rows: [MatrixRowPin; BUTTON_ROWS], pub matrix_cols: [MatrixColPin; BUTTON_COLS], pub left_extra_button: ExtraButtonPin, pub right_extra_button: ExtraButtonPin, pub axis_inputs: AxisInputs, pub status_led: StatusLedPin, pub i2c_sda: I2cSdaPin, pub i2c_scl: I2cSclPin, } impl BoardPins { pub fn new(pins: Pins) -> Self { let row0 = pins.gpio6.into_pull_up_input().into_dyn_pin(); let row1 = pins.gpio8.into_pull_up_input().into_dyn_pin(); let row2 = pins.gpio4.into_pull_up_input().into_dyn_pin(); let row3 = pins.gpio7.into_pull_up_input().into_dyn_pin(); let row4 = pins.gpio5.into_pull_up_input().into_dyn_pin(); let col0 = pins .gpio9 .into_push_pull_output() .into_pull_type::() .into_dyn_pin(); let col1 = pins .gpio10 .into_push_pull_output() .into_pull_type::() .into_dyn_pin(); let col2 = pins .gpio11 .into_push_pull_output() .into_pull_type::() .into_dyn_pin(); let col3 = pins .gpio12 .into_push_pull_output() .into_pull_type::() .into_dyn_pin(); let col4 = pins .gpio13 .into_push_pull_output() .into_pull_type::() .into_dyn_pin(); let left_extra = pins.gpio1.into_pull_up_input().into_dyn_pin(); let right_extra = pins.gpio0.into_pull_up_input().into_dyn_pin(); let axis_inputs = AxisInputs { left_x: pins.gpio29.into_floating_input(), left_y: pins.gpio28.into_floating_input(), right_x: pins.gpio27.into_floating_input(), right_y: pins.gpio26.into_floating_input(), }; let status_led = pins .gpio16 .into_function::() .into_pull_type::(); let i2c_sda = pins .gpio14 .into_function::() .into_pull_type::(); let i2c_scl = pins .gpio15 .into_function::() .into_pull_type::(); Self { matrix_rows: [row0, row1, row2, row3, row4], matrix_cols: [col0, col1, col2, col3, col4], left_extra_button: left_extra, right_extra_button: right_extra, axis_inputs, status_led, i2c_sda, i2c_scl, } } }