From 9572df9e3b9406e5b26380a6fb1fefce4862b18e Mon Sep 17 00:00:00 2001 From: Christoffer Martinsson Date: Thu, 17 Jul 2025 19:03:56 +0200 Subject: [PATCH] Changed to 7 axis and added back one HAT swith --- layout.drawio | 1101 +++-------------------------- rp2040/src/main.rs | 431 ++++++++--- rp2040/src/usb_joystick_device.rs | 47 +- 3 files changed, 434 insertions(+), 1145 deletions(-) diff --git a/layout.drawio b/layout.drawio index 4c6c3a0..4492d13 100644 --- a/layout.drawio +++ b/layout.drawio @@ -1,200 +1,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + @@ -204,43 +10,19 @@ - + - + - - - - - - - - - - - - - - - - - - - + - + - - - - - - - + @@ -291,7 +73,7 @@ - + @@ -321,844 +103,143 @@ - - + + - - - - + - + - + - + - + - + - + - + - + - - + + - - + + - + - + - - + + - + - + - + - - + + - - + + - - - - - - - - - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - + + + + + + + + + + + + + + + + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + diff --git a/rp2040/src/main.rs b/rp2040/src/main.rs index 1b5a7fb..1e348c8 100644 --- a/rp2040/src/main.rs +++ b/rp2040/src/main.rs @@ -49,6 +49,73 @@ pub static BOOT2_FIRMWARE: [u8; 256] = rp2040_boot2::BOOT_LOADER_W25Q080; const XTAL_FREQ_HZ: u32 = 12_000_000u32; // Public constants +// HW Button index map: +// --------------------------------------------------------------- +// | 0 L| 1 U| 25 U | | 2 | | 26 U | 4 U| 3 L| +// --------------------------------------------------------------- +// | | 5 | 6 | 7 | | 12 | 11 | 10 | | +// | | +// | | 8 | | 13 | | +// | | 9 | | 14 | | +// | X1/Y1 X2/Y2 | +// | | 16 | | 21 | | +// | | 19 | 15 | 17 | | 24 | 20 | 22 | | +// | | 18 | | 23 | | +// --------------------------------------------------------------- +// Config Layer (holding CONFIG button) +// --------------------------------------------------------------- +// |BOOT L| CAL U| | CONFIG | | - | - | +// --------------------------------------------------------------- +// | | - | - | - | | - | - | - | | +// | | +// | |C M10| | - | | +// | |C M7 | | - | | +// | -/- -/- | +// | | - | | - | | +// | | - | - | - | | - |C OK | - | | +// | | - | | - | | +// --------------------------------------------------------------- +// +pub const CONFIG_BUTTON: usize = 2; +pub const BOOT_BUTTON: usize = 0; +pub const CAL_BUTTON: usize = 1; +pub const CAL_DONE_BUTTON: usize = 20; +pub const CAL_M10_GIMBLE_BUTTON: usize = 8; +pub const CAL_M7_GIMBLE_BUTTON: usize = 9; + +pub const BUTTON_FRONT_LEFT_LOWER: usize = 0; +pub const BUTTON_FRONT_LEFT_UPPER: usize = 1; +pub const BUTTON_FRONT_LEFT_EXTRA: usize = 25; +pub const BUTTON_FRONT_CONFIG: usize = 2; +pub const BUTTON_FRONT_RIGHT_LOWER: usize = 3; +pub const BUTTON_FRONT_RIGHT_UPPER: usize = 4; +pub const BUTTON_FRONT_RIGHT_EXTRA: usize = 26; +pub const BUTTON_TOP_LEFT_LOW: usize = 5; +pub const BUTTON_TOP_LEFT_HIGH: usize = 6; +pub const BUTTON_TOP_LEFT_MODE: usize = 7; +pub const BUTTON_TOP_LEFT_UP: usize = 8; +pub const BUTTON_TOP_LEFT_DOWN: usize = 9; +pub const BUTTON_TOP_LEFT_HAT: usize = 15; +pub const BUTTON_TOP_LEFT_HAT_UP: usize = 16; +pub const BUTTON_TOP_LEFT_HAT_RIGHT: usize = 17; +pub const BUTTON_TOP_LEFT_HAT_DOWN: usize = 18; +pub const BUTTON_TOP_LEFT_HAT_LEFT: usize = 19; +pub const BUTTON_TOP_RIGHT_LOW: usize = 10; +pub const BUTTON_TOP_RIGHT_HIGH: usize = 11; +pub const BUTTON_TOP_RIGHT_MODE: usize = 12; +pub const BUTTON_TOP_RIGHT_UP: usize = 13; +pub const BUTTON_TOP_RIGHT_DOWN: usize = 14; +pub const BUTTON_TOP_RIGHT_HAT: usize = 20; +pub const BUTTON_TOP_RIGHT_HAT_UP: usize = 21; +pub const BUTTON_TOP_RIGHT_HAT_RIGHT: usize = 22; +pub const BUTTON_TOP_RIGHT_HAT_DOWN: usize = 23; +pub const BUTTON_TOP_RIGHT_HAT_LEFT: usize = 24; + +// Special button functions +// Throttle hold: +pub const TH_BUTTON: usize = 7; +pub const VT_BUTTON: usize = 12; + pub const BUTTON_ROWS: usize = 5; pub const BUTTON_COLS: usize = 5; pub const NUMBER_OF_BUTTONS: usize = BUTTON_ROWS * BUTTON_COLS; @@ -81,7 +148,17 @@ pub struct Button { pub previous_pressed: bool, pub usb_changed: bool, pub usb_changed_to_pressed: bool, - pub usb_button: usize, + pub usb_button: usize, // For short press + pub usb_button_long: usize, // For long press + pub enable_long_press: bool, // Flag to enable special behavior + pub enable_long_hold: bool, // Flag to enable special behavior + + // Internals + pub press_start_time: u32, // When physical press started + pub long_press_handled: bool, // True if long press activated + pub active_usb_button: usize, // Currently active USB button + pub usb_press_active: bool, // Is USB press currently "down" + pub usb_press_start_time: u32, // When USB press was sent } #[derive(Copy, Clone)] @@ -235,6 +312,9 @@ fn main() -> ! { let mut status_led_count_down = timer.count_down(); status_led_count_down.start(250.millis()); + let mut ms_count_down = timer.count_down(); + ms_count_down.start(1.millis()); + let mut scan_count_down = timer.count_down(); scan_count_down.start(200u32.micros()); @@ -248,6 +328,7 @@ fn main() -> ! { let mut usb_active: bool = false; let mut calibration_active: bool = false; let mut throttle_hold_enable: bool = false; + let mut vt_enable: bool = false; let mut axis: [GimbalAxis; NBR_OF_GIMBAL_AXIS] = [Default::default(); NBR_OF_GIMBAL_AXIS]; let mut buttons: [Button; NUMBER_OF_BUTTONS + 2] = [Button::default(); NUMBER_OF_BUTTONS + 2]; @@ -255,84 +336,58 @@ fn main() -> ! { let mut virtual_rz: u16 = AXIS_CENTER; let mut gimbal_mode: u8; - // HW Button index map: - // --------------------------------------------------------------- - // | 0 L| 1 U| 25 U | | 2 | | 26 U | 4 U| 3 L| - // --------------------------------------------------------------- - // | | 5 | 6 | 7 | | 12 | 11 | 10 | | - // | | - // | | 8 | | 13 | | - // | | 9 | | 14 | | - // | X1/Y1 X2/Y2 | - // | | 16 | | 21 | | - // | | 19 | 15 | 17 | | 24 | 20 | 22 | | - // | | 18 | | 23 | | - // --------------------------------------------------------------- - // Config Layer (holding CONFIG button) - // --------------------------------------------------------------- - // |BOOT L| CAL U| | CONFIG | | - | - | - // --------------------------------------------------------------- - // | | - | - | - | | - | - | - | | - // | | - // | |C M10| | - | | - // | |C M7 | | - | | - // | -/- -/- | - // | | - | | - | | - // | | - | - | - | | - |C OK | - | | - // | | - | | - | | - // --------------------------------------------------------------- - // USB HID joystick map : - // --------------------------------------------------------------- - // | Ry- L| Ry+ U| Rz- U| | B26 | | Rz+ U| B1 U| B2 L| - // --------------------------------------------------------------- - // | | B5 | B14 | B9 | | B10 | B15 | B6 | | - // | | - // | | B13 | | B17 | | - // | | B16 | | B12 | | - // | X1/Y1 X2/Y2 | - // | | B18 | | B22 | | - // | | B21 | B11 | B19 | | B25 | TH | B23 | | - // | | B20 | | B24 | | - // --------------------------------------------------------------- - // - pub const CONFIG_BUTTON: usize = 2; - pub const BOOT_BUTTON: usize = 0; - pub const CAL_BUTTON: usize = 1; - pub const CAL_DONE_BUTTON: usize = 20; - pub const CAL_M10_GIMBLE_BUTTON: usize = 8; - pub const CAL_M7_GIMBLE_BUTTON: usize = 9; - // Special button functions - // Throttle hold: - pub const TH_BUTTON: usize = 20; - // Set up usb button layout - buttons[0].usb_button = 4; - buttons[1].usb_button = 3; - buttons[2].usb_button = 26; // Button used as global config. Do not define secondary USB button! - buttons[3].usb_button = 2; - buttons[4].usb_button = 1; - buttons[5].usb_button = 5; - buttons[6].usb_button = 14; - buttons[7].usb_button = 9; - buttons[8].usb_button = 13; - buttons[9].usb_button = 16; - buttons[10].usb_button = 6; - buttons[11].usb_button = 15; - buttons[12].usb_button = 10; - buttons[13].usb_button = 17; - buttons[14].usb_button = 12; - buttons[15].usb_button = 11; - buttons[16].usb_button = 18; - buttons[17].usb_button = 19; - buttons[18].usb_button = 20; - buttons[19].usb_button = 21; - buttons[20].usb_button = 0; - buttons[21].usb_button = 22; - buttons[22].usb_button = 23; - buttons[23].usb_button = 24; - buttons[24].usb_button = 25; - buttons[25].usb_button = 7; - buttons[26].usb_button = 8; + buttons[BUTTON_FRONT_LEFT_LOWER].usb_button = 29; + buttons[BUTTON_FRONT_LEFT_UPPER].usb_button = 28; + buttons[BUTTON_FRONT_CONFIG].usb_button = 32; // Button used as global config. + buttons[BUTTON_FRONT_CONFIG].usb_button_long = 3; + buttons[BUTTON_FRONT_CONFIG].enable_long_press = true; + buttons[BUTTON_FRONT_RIGHT_LOWER].usb_button = 2; + buttons[BUTTON_FRONT_RIGHT_UPPER].usb_button = 1; + buttons[BUTTON_TOP_LEFT_LOW].usb_button = 4; + buttons[BUTTON_TOP_LEFT_LOW].usb_button_long = 5; + buttons[BUTTON_TOP_LEFT_LOW].enable_long_press = true; + buttons[BUTTON_TOP_LEFT_LOW].enable_long_hold = true; + buttons[BUTTON_TOP_LEFT_HIGH].usb_button = 6; + buttons[BUTTON_TOP_LEFT_HIGH].usb_button_long = 7; + buttons[BUTTON_TOP_LEFT_HIGH].enable_long_press = true; + buttons[BUTTON_TOP_LEFT_MODE].usb_button = 0; + buttons[BUTTON_TOP_LEFT_UP].usb_button = 12; + buttons[BUTTON_TOP_LEFT_UP].usb_button_long = 13; + buttons[BUTTON_TOP_LEFT_UP].enable_long_press = true; + buttons[BUTTON_TOP_LEFT_DOWN].usb_button = 16; + buttons[BUTTON_TOP_LEFT_DOWN].usb_button_long = 17; + buttons[BUTTON_TOP_LEFT_DOWN].enable_long_press = true; + buttons[BUTTON_TOP_LEFT_DOWN].enable_long_hold = true; + buttons[BUTTON_TOP_RIGHT_LOW].usb_button = 10; + buttons[BUTTON_TOP_RIGHT_LOW].usb_button_long = 11; + buttons[BUTTON_TOP_RIGHT_LOW].enable_long_press = true; + buttons[BUTTON_TOP_RIGHT_HIGH].usb_button = 8; + buttons[BUTTON_TOP_RIGHT_HIGH].usb_button_long = 9; + buttons[BUTTON_TOP_RIGHT_HIGH].enable_long_press = true; + buttons[BUTTON_TOP_RIGHT_MODE].usb_button = 0; + buttons[BUTTON_TOP_RIGHT_UP].usb_button = 14; + buttons[BUTTON_TOP_RIGHT_UP].usb_button_long = 15; + buttons[BUTTON_TOP_RIGHT_UP].enable_long_press = true; + buttons[BUTTON_TOP_RIGHT_DOWN].usb_button = 18; + buttons[BUTTON_TOP_RIGHT_DOWN].usb_button_long = 19; + buttons[BUTTON_TOP_RIGHT_DOWN].enable_long_press = true; + buttons[BUTTON_TOP_LEFT_HAT].usb_button = 20; + buttons[BUTTON_TOP_LEFT_HAT].usb_button_long = 21; + buttons[BUTTON_TOP_LEFT_HAT].enable_long_press = true; + buttons[BUTTON_TOP_LEFT_HAT_UP].usb_button = 22; + buttons[BUTTON_TOP_LEFT_HAT_RIGHT].usb_button = 23; + buttons[BUTTON_TOP_LEFT_HAT_DOWN].usb_button = 24; + buttons[BUTTON_TOP_LEFT_HAT_LEFT].usb_button = 25; + buttons[BUTTON_TOP_RIGHT_HAT].usb_button = 26; + buttons[BUTTON_TOP_RIGHT_HAT].usb_button_long = 27; + buttons[BUTTON_TOP_RIGHT_HAT].enable_long_press = true; + buttons[BUTTON_TOP_RIGHT_HAT_UP].usb_button = 33; + buttons[BUTTON_TOP_RIGHT_HAT_RIGHT].usb_button = 34; + buttons[BUTTON_TOP_RIGHT_HAT_DOWN].usb_button = 35; + buttons[BUTTON_TOP_RIGHT_HAT_LEFT].usb_button = 36; + buttons[BUTTON_FRONT_LEFT_EXTRA].usb_button = 30; + buttons[BUTTON_FRONT_RIGHT_EXTRA].usb_button = 31; // Table for gimbal expo curve lookup insded of doing floating point math for every analog read let expo_lut: [u16; ADC_MAX as usize + 1] = generate_expo_lut(0.3); @@ -418,6 +473,7 @@ fn main() -> ! { &usb_active, &calibration_active, &throttle_hold_enable, + &vt_enable, ); } @@ -428,28 +484,40 @@ fn main() -> ! { } // Updated extra buttons - buttons[25].pressed = left_extra_button.is_low().unwrap(); - buttons[26].pressed = right_extra_button.is_low().unwrap(); + buttons[BUTTON_FRONT_LEFT_EXTRA].pressed = left_extra_button.is_low().unwrap(); + buttons[BUTTON_FRONT_RIGHT_EXTRA].pressed = right_extra_button.is_low().unwrap(); // Filter left hat swith buttons - for i in 16..=19 { - if (16..=19).filter(|&j| j != i).any(|j| buttons[j].pressed) { + for i in BUTTON_TOP_LEFT_HAT_UP..=BUTTON_TOP_LEFT_HAT_LEFT { + if (BUTTON_TOP_LEFT_HAT_UP..=BUTTON_TOP_LEFT_HAT_LEFT) + .filter(|&j| j != i) + .any(|j| buttons[j].pressed) + { buttons[i].pressed = false; } } // Fix button state for center hat press on hat - if buttons[16..=19].iter().any(|b| b.pressed) { - buttons[15].pressed = false; + if buttons[BUTTON_TOP_LEFT_HAT_UP..=BUTTON_TOP_LEFT_HAT_LEFT] + .iter() + .any(|b| b.pressed) + { + buttons[BUTTON_TOP_LEFT_HAT].pressed = false; } // Filter right hat swith buttons - for i in 21..=24 { - if (21..=24).filter(|&j| j != i).any(|j| buttons[j].pressed) { + for i in BUTTON_TOP_RIGHT_HAT_UP..=BUTTON_TOP_RIGHT_HAT_LEFT { + if (BUTTON_TOP_RIGHT_HAT_UP..=BUTTON_TOP_RIGHT_HAT_LEFT) + .filter(|&j| j != i) + .any(|j| buttons[j].pressed) + { buttons[i].pressed = false; } } // Fix button state for center hat press on hat - if buttons[21..=24].iter().any(|b| b.pressed) { - buttons[20].pressed = false; + if buttons[BUTTON_TOP_RIGHT_HAT_UP..=BUTTON_TOP_RIGHT_HAT_LEFT] + .iter() + .any(|b| b.pressed) + { + buttons[BUTTON_TOP_RIGHT_HAT].pressed = false; } // Secondary way to enter bootloader (pressing all left hands buttons except the hat @@ -564,25 +632,37 @@ fn main() -> ! { } // Update Virtual RY - let virtual_step: u16 = 5; + let virtual_step: u16 = 2; // Compensate value when changing direction - if buttons[1].pressed && !buttons[0].pressed && virtual_ry < AXIS_CENTER { - virtual_ry = AXIS_CENTER + (AXIS_CENTER - virtual_ry); - } else if buttons[0].pressed && !buttons[1].pressed && virtual_ry > AXIS_CENTER { - virtual_ry = AXIS_CENTER - (virtual_ry - AXIS_CENTER); + if buttons[BUTTON_FRONT_LEFT_UPPER].pressed + && !buttons[BUTTON_FRONT_LEFT_LOWER].pressed + && virtual_ry < AXIS_CENTER + { + virtual_ry = AXIS_CENTER + (AXIS_CENTER - virtual_ry) / 2; + } else if buttons[BUTTON_FRONT_LEFT_LOWER].pressed + && !buttons[BUTTON_FRONT_LEFT_UPPER].pressed + && virtual_ry > AXIS_CENTER + { + virtual_ry = AXIS_CENTER - (virtual_ry - AXIS_CENTER) / 2; } // Move virtual axis - if buttons[1].pressed && !buttons[0].pressed && virtual_ry < ADC_MAX - virtual_step { + if buttons[BUTTON_FRONT_LEFT_UPPER].pressed + && !buttons[BUTTON_FRONT_LEFT_LOWER].pressed + && virtual_ry < ADC_MAX - virtual_step + { virtual_ry = virtual_ry + virtual_step; usb_activity = true; - } else if buttons[0].pressed - && !buttons[1].pressed + } else if buttons[BUTTON_FRONT_LEFT_LOWER].pressed + && !buttons[BUTTON_FRONT_LEFT_UPPER].pressed && virtual_ry > ADC_MIN + virtual_step { virtual_ry = virtual_ry - virtual_step; usb_activity = true; - } else if (virtual_ry != AXIS_CENTER && !buttons[1].pressed && !buttons[0].pressed) - || (buttons[1].pressed && buttons[0].pressed) + } else if (virtual_ry != AXIS_CENTER + && !buttons[BUTTON_FRONT_LEFT_UPPER].pressed + && !buttons[BUTTON_FRONT_LEFT_LOWER].pressed) + || (buttons[BUTTON_FRONT_LEFT_UPPER].pressed + && buttons[BUTTON_FRONT_LEFT_LOWER].pressed) { if virtual_ry < AXIS_CENTER + virtual_step { virtual_ry = virtual_ry + virtual_step; @@ -594,23 +674,35 @@ fn main() -> ! { // Update Virtual RZ // Compensate value when changing direction - if buttons[25].pressed && !buttons[26].pressed && virtual_rz < AXIS_CENTER { - virtual_rz = AXIS_CENTER + (AXIS_CENTER - virtual_rz); - } else if buttons[26].pressed && !buttons[25].pressed && virtual_rz > AXIS_CENTER { - virtual_rz = AXIS_CENTER - (virtual_rz - AXIS_CENTER); + if buttons[BUTTON_FRONT_RIGHT_EXTRA].pressed + && !buttons[BUTTON_FRONT_LEFT_EXTRA].pressed + && virtual_rz < AXIS_CENTER + { + virtual_rz = AXIS_CENTER + (AXIS_CENTER - virtual_rz) / 2; + } else if buttons[BUTTON_FRONT_LEFT_EXTRA].pressed + && !buttons[BUTTON_FRONT_RIGHT_EXTRA].pressed + && virtual_rz > AXIS_CENTER + { + virtual_rz = AXIS_CENTER - (virtual_rz - AXIS_CENTER) / 2; } // Move virtual axis - if buttons[25].pressed && !buttons[26].pressed && virtual_rz < ADC_MAX - virtual_step { + if buttons[BUTTON_FRONT_RIGHT_EXTRA].pressed + && !buttons[BUTTON_FRONT_LEFT_EXTRA].pressed + && virtual_rz < ADC_MAX - virtual_step + { virtual_rz = virtual_rz + virtual_step; usb_activity = true; - } else if buttons[26].pressed - && !buttons[25].pressed + } else if buttons[BUTTON_FRONT_LEFT_EXTRA].pressed + && !buttons[BUTTON_FRONT_RIGHT_EXTRA].pressed && virtual_rz > ADC_MIN + virtual_step { virtual_rz = virtual_rz - virtual_step; usb_activity = true; - } else if (virtual_rz != AXIS_CENTER && !buttons[25].pressed && !buttons[26].pressed) - || (buttons[25].pressed && buttons[26].pressed) + } else if (virtual_rz != AXIS_CENTER + && !buttons[BUTTON_FRONT_RIGHT_EXTRA].pressed + && !buttons[BUTTON_FRONT_LEFT_EXTRA].pressed) + || (buttons[BUTTON_FRONT_RIGHT_EXTRA].pressed + && buttons[BUTTON_FRONT_LEFT_EXTRA].pressed) { if virtual_rz < AXIS_CENTER + virtual_step { virtual_rz = virtual_rz + virtual_step; @@ -630,10 +722,10 @@ fn main() -> ! { // Generate led activity when a button is pressed for (index, key) in buttons.iter_mut().enumerate() { + update_button_press_type(key, (timer.get_counter().ticks() / 1000) as u32); + if key.pressed != key.previous_pressed { key.usb_changed = true; - key.usb_changed_to_pressed = key.pressed; - usb_activity = true; } // Set throttle_hold_value if key.pressed != key.previous_pressed @@ -650,6 +742,12 @@ fn main() -> ! { { axis[GIMBAL_AXIS_LEFT_Y].hold = AXIS_CENTER; axis[GIMBAL_AXIS_LEFT_Y].hold_pending = true; + } else if key.pressed != key.previous_pressed && key.pressed && index == VT_BUTTON { + vt_enable = !vt_enable; + } + + if key.usb_changed { + usb_activity = true; } key.previous_pressed = key.pressed; } @@ -686,6 +784,7 @@ fn main() -> ! { true, &expo_lut_virtual, ), + &vt_enable, )) { Err(UsbHidError::WouldBlock) => {} Ok(_) => {} @@ -699,6 +798,60 @@ fn main() -> ! { } } +fn update_button_press_type(button: &mut Button, current_time: u32) { + const LONG_PRESS_THRESHOLD: u32 = 200; + const USB_MIN_HOLD_MS: u32 = 50; + + // Pressing button + if button.pressed && !button.previous_pressed { + button.press_start_time = current_time; + button.long_press_handled = false; + } + + // While held: trigger long press if applicable + if button.pressed && button.enable_long_press && !button.long_press_handled { + if current_time - button.press_start_time >= LONG_PRESS_THRESHOLD { + button.active_usb_button = button.usb_button_long; + button.usb_press_start_time = current_time; + button.usb_press_active = true; + button.usb_changed = true; + button.long_press_handled = true; + } + } + + // Releasing button + if !button.pressed && button.previous_pressed { + // If long press wasn't triggered, it's a short press + if (!button.enable_long_press || !button.long_press_handled) && button.usb_button != 0 { + button.active_usb_button = button.usb_button; + button.usb_press_start_time = current_time; + button.usb_press_active = true; + button.usb_changed = true; + } + + // If long press was active, release now + if button.long_press_handled && button.usb_press_active { + button.usb_changed = true; + button.usb_press_active = false; + button.active_usb_button = 0; + } + } + + // Auto-release for short press after minimum hold time + if button.usb_press_active + && (!button.pressed + && !button.long_press_handled + && current_time - button.usb_press_start_time >= USB_MIN_HOLD_MS) + || (!button.enable_long_hold + && button.long_press_handled + && current_time - button.usb_press_start_time >= USB_MIN_HOLD_MS) + { + button.usb_changed = true; + button.usb_press_active = false; + button.active_usb_button = 0; + } +} + /// Update status LED colour /// /// Waiting for USB enumerate = flashing green @@ -715,6 +868,7 @@ fn update_status_led( usb_active: &bool, calibration_active: &bool, throttle_hold_enable: &bool, + vt_enable: &bool, ) where I: AnyPin, P: PIOExt, @@ -724,6 +878,8 @@ fn update_status_led( status_led.update(StatusMode::ActivityFlash); } else if !*usb_active { status_led.update(StatusMode::NormalFlash); + } else if *usb_active && *vt_enable { + status_led.update(StatusMode::Activity); } else if *usb_active && *throttle_hold_enable { status_led.update(StatusMode::Other); } else if *usb_active && !*throttle_hold_enable { @@ -743,21 +899,64 @@ fn get_joystick_report( axis: &mut [GimbalAxis; 4], virtual_ry: u16, virtual_rz: u16, + vt_enable: &bool, ) -> JoystickReport { let x: i16 = axis_12bit_to_i16(axis[GIMBAL_AXIS_LEFT_X].value); - let y: i16 = axis_12bit_to_i16(ADC_MAX - axis[GIMBAL_AXIS_LEFT_Y].value); - let z: i16 = axis_12bit_to_i16(axis[GIMBAL_AXIS_RIGHT_X].value); + let y: i16 = axis_12bit_to_i16(axis[GIMBAL_AXIS_LEFT_Y].value); + let mut z: i16 = axis_12bit_to_i16(axis[GIMBAL_AXIS_RIGHT_X].value); let rx: i16 = axis_12bit_to_i16(ADC_MAX - axis[GIMBAL_AXIS_RIGHT_Y].value); let ry: i16 = axis_12bit_to_i16(virtual_ry); let rz: i16 = axis_12bit_to_i16(virtual_rz); - let slider: i16 = 0; - let dial: i16 = 0; + let mut slider: i16 = axis_12bit_to_i16(ADC_MIN); + let mut hat: u8 = 8; // Hat center position + + // Virtual axix control. Disables z and rx axis and using girght gimbal Y axis to control + // slider axis. Values from center stick to max or min will be recalculated to min to max. + if *vt_enable { + if axis[GIMBAL_AXIS_RIGHT_X].value >= AXIS_CENTER { + slider = axis_12bit_to_i16(remap( + axis[GIMBAL_AXIS_RIGHT_X].value, + AXIS_CENTER, + ADC_MAX, + ADC_MIN, + ADC_MAX, + )); + } else { + slider = axis_12bit_to_i16( + ADC_MAX + - remap( + axis[GIMBAL_AXIS_RIGHT_X].value, + ADC_MIN, + AXIS_CENTER, + ADC_MIN, + ADC_MAX, + ), + ); + } + z = 0; + } // Update button state for joystick buttons let mut buttons: u32 = 0; for key in matrix_keys.iter_mut() { - if key.pressed && key.usb_button != 0 { - buttons |= 1 << (key.usb_button - 1); + if key.enable_long_press { + if key.active_usb_button != 0 { + // Check if key is assigned as hat switch + if key.active_usb_button >= 33 && key.active_usb_button <= 36 { + hat = (key.active_usb_button as u8 - 33) * 2; + } else { + buttons |= 1 << (key.active_usb_button - 1); + } + } + } else { + if key.pressed && key.usb_button != 0 { + // Check if key is assigned as hat switch + if key.usb_button >= 33 && key.usb_button <= 36 { + hat = (key.usb_button as u8 - 33) * 2; + } else { + buttons |= 1 << (key.usb_button - 1); + } + } } } @@ -774,7 +973,7 @@ fn get_joystick_report( ry, rz, slider, - dial, + hat, buttons, } } diff --git a/rp2040/src/usb_joystick_device.rs b/rp2040/src/usb_joystick_device.rs index 1d4c292..be6118c 100644 --- a/rp2040/src/usb_joystick_device.rs +++ b/rp2040/src/usb_joystick_device.rs @@ -67,7 +67,7 @@ pub const JOYSTICK_DESCRIPTOR: &[u8] = &[ 0x09, 0x04, // Usage (Joystick) 0xA1, 0x01, // Collection (Application) - // 8 signed 16-bit axes: X, Y, Z, Rx, Ry, Rz, Slider, Dial + // 7 signed 16-bit axes: X, Y, Z, Rx, Ry, Rz, Slider 0x09, 0x30, // Usage (X) 0x09, 0x31, // Usage (Y) 0x09, 0x32, // Usage (Z) @@ -75,28 +75,38 @@ pub const JOYSTICK_DESCRIPTOR: &[u8] = &[ 0x09, 0x34, // Usage (Ry) 0x09, 0x35, // Usage (Rz) 0x09, 0x36, // Usage (Slider) - 0x09, 0x37, // Usage (Dial) 0x16, 0x00, 0x80, // Logical Minimum (-32768) 0x26, 0xFF, 0x7F, // Logical Maximum (32767) 0x75, 0x10, // Report Size (16) - 0x95, 0x08, // Report Count (8) + 0x95, 0x07, // Report Count (7) 0x81, 0x02, // Input (Data,Var,Abs) - // 26 Buttons (1-bit each) + // 1 Hat Switch + 0x09, 0x39, // Usage (Hat switch) + 0x15, 0x00, // Logical Minimum (0) + 0x25, 0x07, // Logical Maximum (7) + 0x35, 0x00, // Physical Minimum (0) + 0x46, 0x3B, 0x01, // Physical Maximum (315) + 0x65, 0x14, // Unit (Eng Rot: Degrees) + 0x75, 0x04, // Report Size (4) + 0x95, 0x01, // Report Count (1) + 0x81, 0x42, // Input (Data,Var,Abs,Null) + + // Padding for 4 bits to align to byte + 0x75, 0x04, // Report Size (4) + 0x95, 0x01, // Report Count (1) + 0x81, 0x03, // Input (Const,Var,Abs) – padding + + // 32 Buttons (1-bit each) 0x05, 0x09, // Usage Page (Button) 0x19, 0x01, // Usage Minimum (1) - 0x29, 0x1A, // Usage Maximum (26) + 0x29, 0x20, // Usage Maximum (32) 0x15, 0x00, // Logical Minimum (0) 0x25, 0x01, // Logical Maximum (1) 0x75, 0x01, // Report Size (1) - 0x95, 0x1A, // Report Count (26) + 0x95, 0x20, // Report Count (32) 0x81, 0x02, // Input (Data,Var,Abs) - // Padding to align buttons to byte boundary (26 bits → +6 bits padding) - 0x75, 0x01, // Report Size (1) - 0x95, 0x06, // Report Count (6) - 0x81, 0x03, // Input (Const,Var,Abs) - 0xC0 // End Collection ]; @@ -109,7 +119,7 @@ pub struct JoystickReport { pub ry: i16, // 16bit pub rz: i16, // 16bit pub slider: i16, // 16bit - pub dial: i16, // 16bit + pub hat: u8, // 8bit pub buttons: u32, // 32bit } @@ -119,7 +129,7 @@ pub struct Joystick<'a, B: UsbBus> { impl Joystick<'_, B> { pub fn write_report(&mut self, report: &JoystickReport) -> Result<(), UsbHidError> { - let mut data: [u8; 20] = [0; 20]; + let mut data: [u8; 19] = [0; 19]; // Did not make the packed struct work, so doing it manually data[0] = report.x as u8; @@ -136,12 +146,11 @@ impl Joystick<'_, B> { data[11] = (report.rz >> 8) as u8; data[12] = report.slider as u8; data[13] = (report.slider >> 8) as u8; - data[14] = report.dial as u8; - data[15] = (report.dial >> 8) as u8; - data[16] = report.buttons as u8; - data[17] = (report.buttons >> 8) as u8; - data[18] = (report.buttons >> 16) as u8; - data[19] = (report.buttons >> 24) as u8; + data[14] = report.hat; + data[15] = report.buttons as u8; + data[16] = (report.buttons >> 8) as u8; + data[17] = (report.buttons >> 16) as u8; + data[18] = (report.buttons >> 24) as u8; self.interface .write_report(&data)