cmdr-joystick/rp2040/src/hardware.rs

196 lines
6.3 KiB
Rust

//! 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<u32, 1, 1> {
FREQUENCY_HZ.Hz()
}
pub fn system_clock() -> Rate<u32, 1, 1> {
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<DynPinId, FunctionSioInput, PullUp>;
/// Matrix column pins (dynamic push-pull outputs).
pub type MatrixColPin = Pin<DynPinId, FunctionSioOutput, PullNone>;
/// Extra buttons (pull-up inputs).
pub type ExtraButtonPin = Pin<DynPinId, FunctionSioInput, PullUp>;
/// Status LED pin configured for PIO output.
pub type StatusLedPin = Pin<gpio::bank0::Gpio16, FunctionPio0, PullNone>;
/// I2C SDA/SCL pins after reconfiguration.
pub type I2cSdaPin = Pin<gpio::bank0::Gpio14, FunctionI2C, PullUp>;
pub type I2cSclPin = Pin<gpio::bank0::Gpio15, FunctionI2C, PullUp>;
/// Analog axis input pins (remain as SIO inputs until wrapped by `AdcPin`).
pub struct AxisInputs {
pub left_x: Pin<gpio::bank0::Gpio29, FunctionSioInput, PullNone>,
pub left_y: Pin<gpio::bank0::Gpio28, FunctionSioInput, PullNone>,
pub right_x: Pin<gpio::bank0::Gpio27, FunctionSioInput, PullNone>,
pub right_y: Pin<gpio::bank0::Gpio26, FunctionSioInput, PullNone>,
}
/// 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::<PullNone>()
.into_dyn_pin();
let col1 = pins
.gpio10
.into_push_pull_output()
.into_pull_type::<PullNone>()
.into_dyn_pin();
let col2 = pins
.gpio11
.into_push_pull_output()
.into_pull_type::<PullNone>()
.into_dyn_pin();
let col3 = pins
.gpio12
.into_push_pull_output()
.into_pull_type::<PullNone>()
.into_dyn_pin();
let col4 = pins
.gpio13
.into_push_pull_output()
.into_pull_type::<PullNone>()
.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::<FunctionPio0>()
.into_pull_type::<PullNone>();
let i2c_sda = pins
.gpio14
.into_function::<FunctionI2C>()
.into_pull_type::<PullUp>();
let i2c_scl = pins
.gpio15
.into_function::<FunctionI2C>()
.into_pull_type::<PullUp>();
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,
}
}
}