diff --git a/layout.drawio b/layout.drawio index 162f7e8..fc14a26 100644 --- a/layout.drawio +++ b/layout.drawio @@ -1,4 +1,4 @@ - + @@ -193,195 +193,389 @@ - - + + - - - + + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - + + - - + + - + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/rp2040/Cargo.toml b/rp2040/Cargo.toml index 7225b9c..1fbc3c5 100644 --- a/rp2040/Cargo.toml +++ b/rp2040/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "rp2040" +name = "cmdr-joystick-25" version = "0.2.0" edition = "2024" @@ -9,7 +9,7 @@ cortex-m = "0.7.2" cortex-m-rt = "0.7" cortex-m-rtic = "1.1.4" critical-section = {version = "1.2.0"} -defmt = {version = "0.3", optional=true} +defmt = "0.3" defmt-rtt = "0.4.0" dht-sensor = "0.2.1" embedded-alloc = "0.5.1" @@ -32,31 +32,28 @@ rp2040-hal = {version = "0.11.0", features = ["binary-info", "critical-section-i static_cell = "2.1.0" # USB hid dependencies -#usbd-human-interface-device = {version = "0.5.1", features = ["defmt"]} usbd-human-interface-device = {version = "0.5.1"} usb-device = "0.3" packed_struct = { version = "0.10", default-features = false } heapless = "0.8" -# Other dependencies +# EEPROM dependencies eeprom24x = "0.7.2" + +# ws2812-pio dependencies ws2812-pio = "0.9.0" -dyn-smooth = "0.2.0" -libm = "0.2.7" smart-leds = "0.4.0" -[features] -defmt = ["dep:defmt", "usb-device/defmt"] - -# [target.'cfg( target_arch = "arm" )'.dependencies] -# embassy-executor = {version = "0.5", features = ["arch-cortex-m", "executor-thread"]} +# Analog filter dependencies +dyn-smooth = "0.2.0" +libm = "0.2.7" [lints.clippy] too_long_first_doc_paragraph = "allow" [profile.release] codegen-units = 1 -debug = 2 +debug = 0 debug-assertions = false incremental = false lto = 'fat' @@ -64,7 +61,7 @@ opt-level = 3 overflow-checks = false [[bin]] -name = "rp2040" +name = "cmdr-joystick-25" test = false bench = false diff --git a/rp2040/src/button_matrix.rs b/rp2040/src/button_matrix.rs index 0ee1340..3f4b223 100644 --- a/rp2040/src/button_matrix.rs +++ b/rp2040/src/button_matrix.rs @@ -1,5 +1,5 @@ //! Project: CMtec CMDR joystick 24 -//! Date: 2023-08-01 +//! Date: 2025-03-09 //! Author: Christoffer Martinsson //! Email: cm@cmtec.se //! License: Please refer to LICENSE in root directory diff --git a/rp2040/src/main.rs b/rp2040/src/main.rs index b89b1d4..8aff440 100644 --- a/rp2040/src/main.rs +++ b/rp2040/src/main.rs @@ -3,63 +3,6 @@ //! Author: Christoffer Martinsson //! Email: cm@cmtec.se //! License: Please refer to LICENSE in root directory -// -// 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 | | -// --------------------------------------------------------------- -// -// USB HID joystick map : -// --------------------------------------------------------------- -// | Fn L| B2 U| B26 U| | B3 | | B28 U| B5 U| B4 L| -// --------------------------------------------------------------- -// | | B1 | B6 | B8 | | B13 | B12 | B11 | | -// | | -// | | B9 | | B14 | | -// | | B10 | | B15 | | -// | X1/Y1 X2/Y2 | -// | | H1U | | H2U | | -// | | H1L | B18 | H1R | | H2L | B19 | H2R | | -// | | H1D | | H2D | | -// --------------------------------------------------------------- -// -// USB HID joystick map (Fn): -// --------------------------------------------------------------- -// | Fn L| B21 U| B27 U| | B3 | | B28 U| B32 U| B22 L| -// --------------------------------------------------------------- -// | | TH | B7 | B16 | | B17 | B24 | B23 | | -// | | -// | | B29 | | B31 | | -// | | B10 | | B15 | | -// | X1/Y1 X2/Y2 | -// | | H3U | | H4U | | -// | | H3L | B20 | H3R | | H4L | B25 | H4R | | -// | | H3D | | H4D | | -// --------------------------------------------------------------- -// -// Config Layer (holding CONFIG button) -// --------------------------------------------------------------- -// |BOOT L| CAL U| | CONFIG | | - | - | -// --------------------------------------------------------------- -// | | - | - | - | | - | - | - | | -// | | -// | | - | | - | | -// | | - | | - | | -// | -/- -/- | -// | | - | | - | | -// | | - | - | - | | - | - | - | | -// | | - | | - | | -// --------------------------------------------------------------- - #![no_std] #![no_main] @@ -316,73 +259,146 @@ fn main() -> ! { let mut buttons: [Button; NUMBER_OF_BUTTONS + 2] = [Button::default(); NUMBER_OF_BUTTONS + 2]; 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 | - | | + // | | - | | - | | + // --------------------------------------------------------------- + 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; + // 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 | | + // --------------------------------------------------------------- + // + // USB HID joystick map : + // --------------------------------------------------------------- + // | Fn L| B19 U| B7 U| | B32 | | B8 U| B1 U| B2 L| + // --------------------------------------------------------------- + // | | B5 | B14 | B9 | | B10 | B15 | B6 | | + // | | + // | | B13 | | B17 | | + // | | B16 | | B18 | | + // | X1/Y1 X2/Y2 | + // | | H1U | | H2U | | + // | | H1L | B11 | H1R | | H2L | B12 | H2R | | + // | | H1D | | H2D | | + // --------------------------------------------------------------- + // + // USB HID joystick map (Fn): + // --------------------------------------------------------------- + // | Fn L| B21 U| B27 U| | B32 | | B8 U| B3 U| B4 L| + // --------------------------------------------------------------- + // | | TH | B22 | B26 | | B28 | B24 | B23 | | + // | | + // | | B29 | | B31 | | + // | | B16 | | B18 | | + // | X1/Y1 X2/Y2 | + // | | H3U | | H4U | | + // | | H3L | B20 | H3R | | H4L | B25 | H4R | | + // | | H3D | | H4D | | + // --------------------------------------------------------------- // Special button functions // Throttle hold: - pub const TH_BUTTON: u8 = 5; - pub const TH_BUTTON_TRIGGER: u8 = 0; + pub const TH_BUTTON: usize = 5; + pub const TH_BUTTON_TRIGGER: usize = 0; // Set up usb button layout (button 0 to 15, 20, 25 and 26) buttons[0].usb_button = 0; // Fn. Setting to 0 to disable USB button generation - buttons[1].usb_button = 2; + buttons[1].usb_button = 19; buttons[1].usb_button_sec_enable = true; buttons[1].usb_button_sec = 21; buttons[1].usb_button_sec_trigger_index = 0; - buttons[2].usb_button = 3; // Button used as global config. Do not define secondary USB button! - buttons[3].usb_button = 4; + buttons[2].usb_button = 32; // Button used as global config. Do not define secondary USB button! + buttons[3].usb_button = 2; buttons[3].usb_button_sec_enable = true; - buttons[3].usb_button_sec = 22; + buttons[3].usb_button_sec = 4; buttons[3].usb_button_sec_trigger_index = 0; - buttons[4].usb_button = 5; + buttons[4].usb_button = 1; buttons[4].usb_button_sec_enable = true; - buttons[4].usb_button_sec = 32; + buttons[4].usb_button_sec = 3; buttons[4].usb_button_sec_trigger_index = 0; - buttons[5].usb_button = 1; + buttons[5].usb_button = 5; buttons[5].usb_button_sec_enable = true; // Set TH_BUTTON to inhibit button press on throttle hold buttons[5].usb_button_sec = 0; // Setting to 0 to disable USB buttton generation buttons[5].usb_button_sec_trigger_index = 0; // Set same as TH_BUTTON_TRIGGER - buttons[6].usb_button = 6; + buttons[6].usb_button = 14; buttons[6].usb_button_sec_enable = true; - buttons[6].usb_button_sec = 7; + buttons[6].usb_button_sec = 22; buttons[6].usb_button_sec_trigger_index = 0; - buttons[7].usb_button = 8; + buttons[7].usb_button = 9; buttons[7].usb_button_sec_enable = true; - buttons[7].usb_button_sec = 16; + buttons[7].usb_button_sec = 26; buttons[7].usb_button_sec_trigger_index = 0; - buttons[8].usb_button = 9; + buttons[8].usb_button = 13; buttons[8].usb_button_sec_enable = true; buttons[8].usb_button_sec = 29; buttons[8].usb_button_sec_trigger_index = 0; - buttons[9].usb_button = 10; - buttons[10].usb_button = 11; + buttons[9].usb_button = 16; + buttons[10].usb_button = 6; buttons[10].usb_button_sec_enable = true; buttons[10].usb_button_sec = 23; buttons[10].usb_button_sec_trigger_index = 0; - buttons[11].usb_button = 12; + buttons[11].usb_button = 15; buttons[11].usb_button_sec_enable = true; buttons[11].usb_button_sec = 24; buttons[11].usb_button_sec_trigger_index = 0; - buttons[12].usb_button = 13; + buttons[12].usb_button = 10; buttons[12].usb_button_sec_enable = true; - buttons[12].usb_button_sec = 17; + buttons[12].usb_button_sec = 28; buttons[12].usb_button_sec_trigger_index = 0; - buttons[13].usb_button = 14; + buttons[13].usb_button = 17; buttons[13].usb_button_sec_enable = true; buttons[13].usb_button_sec = 31; buttons[13].usb_button_sec_trigger_index = 0; - buttons[14].usb_button = 15; - buttons[15].usb_button = 18; + buttons[14].usb_button = 18; + buttons[15].usb_button = 11; buttons[15].usb_button_sec_enable = true; buttons[15].usb_button_sec = 20; buttons[15].usb_button_sec_trigger_index = 0; - buttons[20].usb_button = 19; + buttons[20].usb_button = 12; buttons[20].usb_button_sec_enable = true; buttons[20].usb_button_sec = 25; buttons[20].usb_button_sec_trigger_index = 0; - buttons[25].usb_button = 26; + buttons[25].usb_button = 7; buttons[25].usb_button_sec_enable = true; buttons[25].usb_button_sec = 27; buttons[25].usb_button_sec_trigger_index = 0; - buttons[26].usb_button = 28; + buttons[26].usb_button = 8; // Table for gimbal expo curve lookup insded of doing floating point math for every analog read let expo_lut: [u16; AXIS_MAX as usize + 1] = generate_expo_lut(0.3); @@ -437,8 +453,9 @@ fn main() -> ! { } if scan_count_down.wait().is_ok() { + // Scan button matrix button_matrix.scan_matrix(&mut delay); - + // Read ADC values let mut left_x: u16 = adc.read(&mut adc_pin_left_x).unwrap(); let mut left_y: u16 = adc.read(&mut adc_pin_left_y).unwrap(); let mut right_x: u16 = adc.read(&mut adc_pin_right_x).unwrap(); @@ -453,7 +470,7 @@ fn main() -> ! { left_y = AXIS_MAX - left_y; right_x = AXIS_MAX - right_x; } - + // Process anlog filter smoother[GIMBAL_AXIS_LEFT_X].tick(left_x as i32); smoother[GIMBAL_AXIS_LEFT_Y].tick(left_y as i32); smoother[GIMBAL_AXIS_RIGHT_X].tick(right_x as i32); @@ -480,7 +497,7 @@ fn main() -> ! { buttons[26].pressed = right_extra_button.is_low().unwrap(); // Secondary way to enter bootloader (pressing all left hands buttons except the hat - if buttons[0].pressed && buttons[2].pressed { + if buttons[BOOT_BUTTON].pressed && buttons[CONFIG_BUTTON].pressed { status_led.update(StatusMode::Bootloader); let gpio_activity_pin_mask: u32 = 0; let disable_interface_mask: u32 = 0; @@ -490,12 +507,9 @@ fn main() -> ! { ); } - // // ON/OFF switch for Throttle hold mode - throttle_hold_enable = axis[GIMBAL_AXIS_LEFT_Y].hold != AXIS_CENTER; - // Calibration of center position (pressing all right hands buttons except // the hat switch) - if buttons[1].pressed && buttons[2].pressed { + if buttons[CAL_BUTTON].pressed && buttons[CONFIG_BUTTON].pressed { for (index, item) in axis.iter_mut().enumerate() { item.center = smoother[index].value() as u16; item.min = item.center; @@ -515,14 +529,14 @@ fn main() -> ! { } } - if calibration_active && buttons[8].pressed { + if calibration_active && buttons[CAL_M10_GIMBLE_BUTTON].pressed { gimbal_mode = GIMBAL_MODE_M10; for (index, item) in axis.iter_mut().enumerate() { item.center = smoother[index].value() as u16; item.min = item.center; item.max = item.center; } - } else if calibration_active && buttons[9].pressed { + } else if calibration_active && buttons[CAL_M7_GIMBLE_BUTTON].pressed { gimbal_mode = GIMBAL_MODE_M7; for (index, item) in axis.iter_mut().enumerate() { item.center = smoother[index].value() as u16; @@ -531,7 +545,7 @@ fn main() -> ! { } } // Save calibration data to eeprom (pressing right hat switch) - else if calibration_active && buttons[20].pressed { + else if calibration_active && buttons[CAL_DONE_BUTTON].pressed { let mut eeprom_data: [u8; 25] = [0; 25]; for (index, item) in axis.iter_mut().enumerate() { eeprom_data[index * 6] = item.min as u8; @@ -546,6 +560,9 @@ fn main() -> ! { calibration_active = false; } + // // ON/OFF switch for Throttle hold mode + throttle_hold_enable = axis[GIMBAL_AXIS_LEFT_Y].hold != AXIS_CENTER; + // Process axis values for (index, item) in axis.iter_mut().enumerate() { item.value = calculate_axis_value( @@ -608,7 +625,7 @@ fn main() -> ! { // Generate led activity when a button is pressed let mut th_trigger_pressed: bool = false; for (index, key) in buttons.iter_mut().enumerate() { - if key.pressed && index == TH_BUTTON_TRIGGER.into() { + if key.pressed && index == TH_BUTTON_TRIGGER { th_trigger_pressed = true; } } @@ -621,7 +638,7 @@ fn main() -> ! { // Set throttle_hold_value if key.pressed != key.previous_pressed && key.pressed - && index == TH_BUTTON.into() + && index == TH_BUTTON && th_trigger_pressed && unprocessed_value != AXIS_CENTER { @@ -629,7 +646,7 @@ fn main() -> ! { axis[GIMBAL_AXIS_LEFT_Y].hold_pending = true; } else if key.pressed != key.previous_pressed && key.pressed - && index == TH_BUTTON.into() + && index == TH_BUTTON && th_trigger_pressed && unprocessed_value == AXIS_CENTER { @@ -709,12 +726,10 @@ fn get_joystick_report( matrix_keys: &mut [Button; NUMBER_OF_BUTTONS + 2], axis: &mut [GimbalAxis; 4], ) -> JoystickReport { - let x: u16 = axis[GIMBAL_AXIS_LEFT_X].value; - let y: u16 = AXIS_MAX - axis[GIMBAL_AXIS_LEFT_Y].value; - let z: u16 = axis[GIMBAL_AXIS_RIGHT_X].value; - let rx: u16 = AXIS_MAX - axis[GIMBAL_AXIS_RIGHT_Y].value; - let ry: u16 = AXIS_CENTER; - let rz: u16 = AXIS_CENTER; + let x: i16 = axis_12bit_to_i16(axis[GIMBAL_AXIS_LEFT_X].value); + let y: i16 = axis_12bit_to_i16(AXIS_MAX - axis[GIMBAL_AXIS_LEFT_Y].value); + let z: i16 = axis_12bit_to_i16(axis[GIMBAL_AXIS_RIGHT_X].value); + let rx: i16 = axis_12bit_to_i16(AXIS_MAX - axis[GIMBAL_AXIS_RIGHT_Y].value); let mut hat1: u8 = 0xf; let mut hat2: u8 = 0xf; let mut hat3: u8 = 0xf; @@ -849,8 +864,6 @@ fn get_joystick_report( y, z, rx, - ry, - rz, hat1, hat2, hat3, @@ -868,7 +881,7 @@ fn get_joystick_report( /// 0 = not pressed /// 1 = pressed fn format_hat_value(input: u8) -> (u8, u8) { - const HAT_CENTER: u8 = 0xf; + const HAT_CENTER: u8 = 8; //8 or 15 (OS-dependent; usually 8) const HAT_UP: u8 = 0; const HAT_UP_RIGHT: u8 = 1; const HAT_RIGHT: u8 = 2; @@ -1001,3 +1014,17 @@ fn generate_expo_lut(expo: f32) -> [u16; AXIS_MAX as usize + 1] { } lut } + +/// Convert 12bit unsigned values to 16bit signed +/// +/// # Arguments +/// * `val` - 12bit unsigned +fn axis_12bit_to_i16(val: u16) -> i16 { + assert!(val <= 0x0FFF); // Ensure it's 12-bit + + // Map 0..4095 → -32768..32767 using integer math + // Formula: ((val * 65535) / 4095) - 32768 + let scaled = ((val as u32 * 65535) / 4095) as i32 - 32768; + + scaled as i16 +} diff --git a/rp2040/src/status_led.rs b/rp2040/src/status_led.rs index 1656e02..a607690 100644 --- a/rp2040/src/status_led.rs +++ b/rp2040/src/status_led.rs @@ -1,5 +1,5 @@ //! Project: CMtec CMDR joystick 24 -//! Date: 2023-08-01 +//! Date: 2025-03-09 //! Author: Christoffer Martinsson //! Email: cm@cmtec.se //! License: Please refer to LICENSE in root directory diff --git a/rp2040/src/usb_joystick_device.rs b/rp2040/src/usb_joystick_device.rs index 553b51c..da44dc4 100644 --- a/rp2040/src/usb_joystick_device.rs +++ b/rp2040/src/usb_joystick_device.rs @@ -11,14 +11,6 @@ use usb_device::bus::UsbBus; use usb_device::class_prelude::UsbBusAllocator; use usbd_human_interface_device::usb_class::prelude::*; -#[cfg(feature = "defmt")] -macro_rules! unwrap { - ($($x:tt)*) => { - ::defmt::unwrap!($($x)*) - }; -} - -#[cfg(not(feature = "defmt"))] macro_rules! unwrap { ($arg:expr) => { match $crate::usb_joystick_device::Try::into_result($arg) { @@ -71,60 +63,55 @@ impl Try for Result { // Updated to 6x 12bit axis, 32x buttons and 4x hat switches #[rustfmt::skip] pub const JOYSTICK_DESCRIPTOR: &[u8] = &[ - 0x05, 0x01, // Usage Page (Generic Desktop) - 0x09, 0x04, // Usage (Joystick) - 0xa1, 0x01, // Collection (Application) - 0x09, 0x01, // Usage Page (Pointer) - 0xa1, 0x00, // Collection (Physical) - 0x09, 0x30, // Usage (X) - 0x09, 0x31, // Usage (Y) - 0x09, 0x32, // Usage (Z) - 0x09, 0x33, // Usage (RX) - 0x09, 0x34, // Usage (RY) - 0x09, 0x35, // Usage (RZ) - 0x15, 0x00, // Logical Minimum (0) - 0x26, 0xFF, 0x0F, // Logical Maximum (4095) - 0x75, 0x0C, // Report Size (12) - 0x95, 0x06, // Report count (6) - 0x81, 0x02, // Input (Data, Variable, Absolute) - 0xc0, // End Collection - 0x05, 0x09, // Usage Page (Button) - 0x19, 0x01, // Usage Minimum (1) - 0x29, 0x20, // Usage Maximum (32) - 0x15, 0x00, // Logical Minimum (0) - 0x25, 0x01, // Logical Maximum (1) - 0x75, 0x01, // Report Size (1) - 0x95, 0x20, // Report Count (32) - 0x81, 0x02, // Input (Data, Variable, Absolute) - 0x15, 0x00, // Logical Minimum (0) - 0x25, 0x07, // Logical Maximum (7) - 0x35, 0x00, // Physical Minimum (0) - 0x46, 0x3B, 0x01, // Physical Maximum (315) - 0x75, 0x04, // Report Size (4) - 0x95, 0x04, // Report Count (4) - 0x65, 0x14, // Unit (20) - 0x05, 0x01, // Usage Page (Generic Desktop) - 0x09, 0x39, // Usage (Hat switch) - 0x09, 0x39, // Usage (Hat switch) - 0x09, 0x39, // Usage (Hat switch) - 0x09, 0x39, // Usage (Hat switch) - 0x81, 0x42, // Input (variable,absolute,null_state) - 0xc0, // End Collection +0x05, 0x01, // Usage Page (Generic Desktop) +0x09, 0x04, // Usage (Joystick) +0xA1, 0x01, // Collection (Application) + // 4 signed 16-bit axes: X, Y, Z, Rx + 0x09, 0x30, // Usage (X) + 0x09, 0x31, // Usage (Y) + 0x09, 0x32, // Usage (Z) + 0x09, 0x33, // Usage (Rx) + 0x16, 0x00, 0x80,// Logical Minimum (-32768) + 0x26, 0xFF, 0x7F,// Logical Maximum (32767) + 0x75, 0x10, // Report Size (16) + 0x95, 0x04, // Report Count (4) + 0x81, 0x02, // Input (Data,Var,Abs) + // 4 Hat Switches (4-bit) + 0x09, 0x39, // Usage (Hat switch) + 0x09, 0x39, + 0x09, 0x39, + 0x09, 0x39, + 0x15, 0x00, // Logical Minimum (0) + 0x25, 0x07, // Logical Maximum (7) + 0x35, 0x00, // Physical Minimum (0) + 0x46, 0x3B, 0x01,// Physical Maximum (315) + 0x65, 0x14, // Unit (Degrees) + 0x75, 0x04, // Report Size (4) + 0x95, 0x04, // Report Count (4) + 0x81, 0x02, // Input (Data,Var,Abs) + // 32 Buttons (1-bit each) + 0x05, 0x09, // Usage Page (Button) + 0x19, 0x01, // Usage Minimum (1) + 0x29, 0x20, // Usage Maximum (32) + 0x15, 0x00, // Logical Minimum (0) + 0x25, 0x01, // Logical Maximum (1) + 0x75, 0x01, // Report Size (1) + 0x95, 0x20, // Report Count (32) + 0x81, 0x02, // Input (Data,Var,Abs) +0xC0 // End Collection ]; #[derive(Clone, Copy, Debug, Eq, PartialEq, Default)] pub struct JoystickReport { - pub x: u16, // 12bit - pub y: u16, // 12bit - pub z: u16, // 12bit - pub rx: u16, // 12bit - pub ry: u16, // 12bit - pub rz: u16, // 12bit - pub buttons: u32, // 32bit + pub x: i16, // 16bit + pub y: i16, // 16bit + pub z: i16, // 16bit + pub rx: i16, // 16bit pub hat1: u8, // 4bit pub hat2: u8, // 4bit pub hat3: u8, // 4bit pub hat4: u8, // 4bit + pub buttons: u32, // 32bit } pub struct Joystick<'a, B: UsbBus> { @@ -133,24 +120,23 @@ pub struct Joystick<'a, B: UsbBus> { impl Joystick<'_, B> { pub fn write_report(&mut self, report: &JoystickReport) -> Result<(), UsbHidError> { - let mut data: [u8; 15] = [0; 15]; + let mut data: [u8; 14] = [0; 14]; // Did not make the packed struct work, so doing it manually data[0] = report.x as u8; - data[1] = ((report.x >> 8) as u8) | ((report.y << 4) as u8); - data[2] = (report.y >> 4) as u8; - data[3] = report.z as u8; - data[4] = ((report.z >> 8) as u8) | ((report.rx << 4) as u8); - data[5] = (report.rx >> 4) as u8; - data[6] = report.ry as u8; - data[7] = ((report.ry >> 8) as u8) | ((report.rz << 4) as u8); - data[8] = (report.rz >> 4) as u8; - data[9] = report.buttons as u8; - data[10] = (report.buttons >> 8) as u8; - data[11] = (report.buttons >> 16) as u8; - data[12] = (report.buttons >> 24) as u8; - data[13] = (report.hat1) | (report.hat2 << 4); - data[14] = (report.hat3) | (report.hat4 << 4); + data[1] = (report.x >> 8) as u8; + data[2] = report.y as u8; + data[3] = (report.y >> 8) as u8; + data[4] = report.z as u8; + data[5] = (report.z >> 8) as u8; + data[6] = report.rx as u8; + data[7] = (report.rx >> 8) as u8; + data[8] = (report.hat3) | (report.hat4 << 4); + data[9] = (report.hat1) | (report.hat2 << 4); + data[10] = report.buttons as u8; + data[11] = (report.buttons >> 8) as u8; + data[12] = (report.buttons >> 16) as u8; + data[13] = (report.buttons >> 24) as u8; self.interface .write_report(&data)