diff --git a/code/Cargo.lock b/code/Cargo.lock index c3316ff5cbfc94e452634d6bb43ced38de8148ed..2c823d4a690a81dd38aa88f67a5852abd3ede048 100644 --- a/code/Cargo.lock +++ b/code/Cargo.lock @@ -18,7 +18,10 @@ dependencies = [ "cortex-m-rtic", "cortex-m-semihosting", "embedded-hal", + "panic-halt", + "panic-itm", "panic-rtt-target", + "panic-semihosting", "rtt-target 0.3.0", "stm32f4 0.13.0", "stm32f4xx-hal", @@ -251,6 +254,21 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "546c37ac5d9e56f55e73b677106873d9d9f5190605e41a856503623648488cae" +[[package]] +name = "panic-halt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de96540e0ebde571dc55c73d60ef407c653844e6f9a1e2fdbd40c07b9252d812" + +[[package]] +name = "panic-itm" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d577d97d1b31268087b6dddf2470e6794ef5eee87d9dca7fcd0481695391a4c" +dependencies = [ + "cortex-m 0.7.2", +] + [[package]] name = "panic-rtt-target" version = "0.1.1" @@ -261,6 +279,16 @@ dependencies = [ "rtt-target 0.2.2", ] +[[package]] +name = "panic-semihosting" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d55dedd501dfd02514646e0af4d7016ce36bc12ae177ef52056989966a1eec" +dependencies = [ + "cortex-m 0.7.2", + "cortex-m-semihosting", +] + [[package]] name = "proc-macro2" version = "1.0.24" diff --git a/code/Cargo.toml b/code/Cargo.toml index af9967acdf8dd4f70aefcd40a79cdde7e17d8e02..a6326e76914c31ddb9aeb4e7bd3ffb79726ba5fa 100644 --- a/code/Cargo.toml +++ b/code/Cargo.toml @@ -15,16 +15,16 @@ usb-device = "0.2.7" usbd-hid = "0.5.0" # Panic handlers, comment all but one to generate doc! -#panic-halt = "0.2.0" +panic-halt = "0.2.0" # Uncomment for the itm panic examples. -#panic-itm = "0.4.2" +panic-itm = "0.4.2" # Uncomment for the rtt-timing examples. panic-rtt-target = { version = "0.1.1", features = ["cortex-m"] } # Uncomment for the semihosting examples. -#panic-semihosting = "0.5.6" +panic-semihosting = "0.5.6" # Tracing rtt-target = { version = "0.3.0", features = ["cortex-m"] } diff --git a/code/examples/din_mama.rs b/code/examples/din_mama.rs new file mode 100644 index 0000000000000000000000000000000000000000..935d009cb314287ca231b5240a1faa3bdbb3c910 --- /dev/null +++ b/code/examples/din_mama.rs @@ -0,0 +1,413 @@ +// cargo run --example rtt_rtic_usb_mouse --release +// +// Notice, release build required + +#![no_std] +#![no_main] + +use core::{convert::TryInto, i8}; + +use panic_halt as _; + +use cortex_m::{asm::delay, peripheral::DWT}; +use embedded_hal::digital::v2::OutputPin; +use embedded_hal::spi::MODE_3; +use rtic::cyccnt::{Instant, U32Ext as _}; +use stm32f4xx_hal::{ + dwt::Dwt, + gpio::{ + Speed, Alternate, Input, Output, PullUp, PushPull, + gpioa::{PA15}, + gpiob::{self,PB12, PB13, PB14, PB15}, + gpioc::{self,PC6, PC7, PC10, PC11, PC12}, + }, + prelude::*, + rcc::Clocks, + spi::Spi, + stm32, + otg_fs::{UsbBus, UsbBusType, USB}, + prelude::*, +}; +use usb_device::bus; +use usb_device::prelude::*; + +#[allow(unused)] +pub mod hid { + use usb_device::class_prelude::*; + use usb_device::Result; + + pub const USB_CLASS_HID: u8 = 0x03; + + const USB_SUBCLASS_NONE: u8 = 0x00; + const USB_SUBCLASS_BOOT: u8 = 0x01; + + const USB_INTERFACE_NONE: u8 = 0x00; + const USB_INTERFACE_KEYBOARD: u8 = 0x01; + const USB_INTERFACE_MOUSE: u8 = 0x02; + + const REQ_GET_REPORT: u8 = 0x01; + const REQ_GET_IDLE: u8 = 0x02; + const REQ_GET_PROTOCOL: u8 = 0x03; + const REQ_SET_REPORT: u8 = 0x09; + const REQ_SET_IDLE: u8 = 0x0a; + const REQ_SET_PROTOCOL: u8 = 0x0b; + + // https://docs.microsoft.com/en-us/windows-hardware/design/component-guidelines/mouse-collection-report-descriptor + const REPORT_DESCR: &[u8] = &[ + 0x05, 0x01, // USAGE_PAGE (Generic Desktop) + 0x09, 0x02, // USAGE (Mouse) + 0xa1, 0x01, // COLLECTION (Application) + 0x09, 0x01, // USAGE (Pointer) + 0xa1, 0x00, // COLLECTION (Physical) + 0x05, 0x09, // USAGE_PAGE (Button) + 0x19, 0x01, // USAGE_MINIMUM (Button 1) + 0x29, 0x03, // USAGE_MAXIMUM (Button 3) + 0x15, 0x00, // LOGICAL_MINIMUM (0) + 0x25, 0x01, // LOGICAL_MAXIMUM (1) + 0x95, 0x03, // REPORT_COUNT (3) + 0x75, 0x01, // REPORT_SIZE (1) + 0x81, 0x02, // INPUT (Data,Var,Abs) + 0x95, 0x01, // REPORT_COUNT (1) + 0x75, 0x05, // REPORT_SIZE (5) + 0x81, 0x03, // INPUT (Cnst,Var,Abs) + 0x05, 0x01, // USAGE_PAGE (Generic Desktop) + 0x09, 0x30, // USAGE (X) + 0x09, 0x31, // USAGE (Y) + 0x15, 0x81, // LOGICAL_MINIMUM (-127) + 0x25, 0x7f, // LOGICAL_MAXIMUM (127) + 0x75, 0x08, // REPORT_SIZE (8) + 0x95, 0x02, // REPORT_COUNT (2) + 0x81, 0x06, // INPUT (Data,Var,Rel) + 0xc0, // END_COLLECTION + 0xc0, // END_COLLECTION + ]; + + pub fn report(x: i8, y: i8) -> [u8; 3] { + [ + 0x00, // button: none + x as u8, // x-axis + y as u8, // y-axis + ] + } + + pub struct HIDClass<'a, B: UsbBus> { + report_if: InterfaceNumber, + report_ep: EndpointIn<'a, B>, + } + + impl<B: UsbBus> HIDClass<'_, B> { + /// Creates a new HIDClass with the provided UsbBus and max_packet_size in bytes. For + /// full-speed devices, max_packet_size has to be one of 8, 16, 32 or 64. + pub fn new(alloc: &UsbBusAllocator<B>) -> HIDClass<'_, B> { + HIDClass { + report_if: alloc.interface(), + report_ep: alloc.interrupt(8, 10), + } + } + + pub fn write(&mut self, data: &[u8]) { + self.report_ep.write(data).ok(); + } + } + + impl<B: UsbBus> UsbClass<B> for HIDClass<'_, B> { + fn get_configuration_descriptors(&self, writer: &mut DescriptorWriter) -> Result<()> { + writer.interface( + self.report_if, + USB_CLASS_HID, + USB_SUBCLASS_NONE, + USB_INTERFACE_MOUSE, + )?; + + let descr_len: u16 = REPORT_DESCR.len() as u16; + writer.write( + 0x21, + &[ + 0x01, // bcdHID + 0x01, // bcdHID + 0x00, // bCountryCode + 0x01, // bNumDescriptors + 0x22, // bDescriptorType + descr_len as u8, // wDescriptorLength + (descr_len >> 8) as u8, // wDescriptorLength + ], + )?; + + writer.endpoint(&self.report_ep)?; + + Ok(()) + } + + fn control_in(&mut self, xfer: ControlIn<B>) { + let req = xfer.request(); + + if req.request_type == control::RequestType::Standard { + match (req.recipient, req.request) { + (control::Recipient::Interface, control::Request::GET_DESCRIPTOR) => { + let (dtype, _index) = req.descriptor_type_index(); + if dtype == 0x21 { + // HID descriptor + cortex_m::asm::bkpt(); + let descr_len: u16 = REPORT_DESCR.len() as u16; + + // HID descriptor + let descr = &[ + 0x09, // length + 0x21, // descriptor type + 0x01, // bcdHID + 0x01, // bcdHID + 0x00, // bCountryCode + 0x01, // bNumDescriptors + 0x22, // bDescriptorType + descr_len as u8, // wDescriptorLength + (descr_len >> 8) as u8, // wDescriptorLength + ]; + + xfer.accept_with(descr).ok(); + return; + } else if dtype == 0x22 { + // Report descriptor + xfer.accept_with(REPORT_DESCR).ok(); + return; + } + } + _ => { + return; + } + }; + } + + if !(req.request_type == control::RequestType::Class + && req.recipient == control::Recipient::Interface + && req.index == u8::from(self.report_if) as u16) + { + return; + } + + match req.request { + REQ_GET_REPORT => { + // USB host requests for report + // I'm not sure what should we do here, so just send empty report + xfer.accept_with(&report(0, 0)).ok(); + } + _ => { + xfer.reject().ok(); + } + } + } + + fn control_out(&mut self, xfer: ControlOut<B>) { + let req = xfer.request(); + + if !(req.request_type == control::RequestType::Class + && req.recipient == control::Recipient::Interface + && req.index == u8::from(self.report_if) as u16) + { + return; + } + + xfer.reject().ok(); + } + } +} + +use hid::HIDClass; + +// PMW3389 +use app::{ + pmw3389::{self, Register}, + DwtDelay, +}; +use rtt_target::{rprint, rprintln, rtt_init_print}; + +type PMW3389T = pmw3389::Pmw3389< + Spi< + stm32f4xx_hal::stm32::SPI3, + ( + PC10<Alternate<stm32f4xx_hal::gpio::AF6>>, + PC11<Alternate<stm32f4xx_hal::gpio::AF6>>, + PC12<Alternate<stm32f4xx_hal::gpio::AF6>>, + ), + >, + PA15<Output<PushPull>>, +>; + +const PERIOD: u32 = 10000; +const RATIO: u32 = 1; + +#[rtic::app(device = stm32f4xx_hal::stm32, peripherals = true, monotonic = rtic::cyccnt::CYCCNT)] +const APP: () = { + struct Resources { + //counter: u8, + usb_dev: UsbDevice<'static, UsbBusType>, + hid: HIDClass<'static, UsbBusType>, + pmw3389: PMW3389T, + MB2: gpioc::PC6<Input<PullUp>>, + MB1: gpioc::PC7<Input<PullUp>>, + DPIB1: gpiob::PB13<Input<PullUp>>, + DPIB2: gpiob::PB12<Input<PullUp>>, + MB5: gpiob::PB15<Input<PullUp>>, + MB4: gpiob::PB14<Input<PullUp>>, + } + + #[init(schedule = [poll])] + fn init(mut cx: init::Context) -> init::LateResources { + static mut USB_BUS: Option<bus::UsbBusAllocator<UsbBusType>> = None; + static mut EP_MEMORY: [u32; 1024] = [0; 1024]; + cx.core.DCB.enable_trace(); + DWT::unlock(); + cx.core.DWT.enable_cycle_counter(); + + let mut core = cx.core; + + let rcc = cx.device.RCC.constrain(); + + let clocks = rcc + .cfgr + // .use_hse(8.mhz()) + .sysclk(48.mhz()) + .pclk1(24.mhz()) + .freeze(); + + // assert!(clocks.usbclk_valid()); + + let gpioa = cx.device.GPIOA.split(); + let gpioc = cx.device.GPIOC.split(); + let gpiob = cx.device.GPIOB.split(); + + // Buttons + let mb2 = gpioc.pc6.into_pull_up_input(); + let mb1 = gpioc.pc7.into_pull_up_input(); + let mb5 = gpiob.pb15.into_pull_up_input(); + let mb4 = gpiob.pb14.into_pull_up_input(); + let dpib1 = gpiob.pb13.into_pull_up_input(); + let dpib2 = gpiob.pb12.into_pull_up_input(); + + // Sensor communcation + let sck = gpioc.pc10.into_alternate_af6(); + let miso = gpioc.pc11.into_alternate_af6(); + let mosi = gpioc.pc12.into_alternate_af6(); + let cs = gpioa.pa15.into_push_pull_output().set_speed(Speed::High); + + let spi = Spi::spi3( + cx.device.SPI3, + (sck, miso, mosi), + MODE_3, + stm32f4xx_hal::time::KiloHertz(2000).into(), + clocks, + ); + + + let mut delay_dwt = DwtDelay::new(&mut core.DWT, clocks); + let mut pmw3389 = pmw3389::Pmw3389::new(spi, cs, delay_dwt).unwrap(); + + // set in burst mode + pmw3389.write_register(Register::MotionBurst, 0x00); + + // Pull the D+ pin down to send a RESET condition to the USB bus. + let mut usb_dp = gpioa.pa12.into_push_pull_output(); + usb_dp.set_low().ok(); + delay(clocks.sysclk().0 / 100); + let usb_dp = usb_dp.into_floating_input(); + + let usb_dm = gpioa.pa11; + + let usb = USB { + usb_global: cx.device.OTG_FS_GLOBAL, + usb_device: cx.device.OTG_FS_DEVICE, + usb_pwrclk: cx.device.OTG_FS_PWRCLK, + pin_dm: usb_dm.into_alternate_af10(), + pin_dp: usb_dp.into_alternate_af10(), + }; + + *USB_BUS = Some(UsbBus::new(usb, EP_MEMORY)); + + let hid = HIDClass::new(USB_BUS.as_ref().unwrap()); + + let usb_dev = UsbDeviceBuilder::new(USB_BUS.as_ref().unwrap(), UsbVidPid(0xc410, 0x0000)) + .manufacturer("Albatraoz") + .product("VRM") + .serial_number("TEST") + .device_class(0) + .build(); + + cx.schedule.poll(cx.start + 16_000.cycles()).unwrap(); + + init::LateResources { + //counter: 0, + usb_dev, + hid, + MB1:mb1, MB2:mb2, MB4:mb4, MB5:mb5, DPIB1:dpib1, DPIB2:dpib2, + pmw3389, + + } + } + + #[idle] + fn idle(_cx: idle::Context) -> ! { + // rprintln!("idle"); + loop { + continue; + } + } + + #[task(priority = 2, resources = [pmw3389], schedule = [poll], spawn = [trace])] + fn poll(cx: poll::Context) { + static mut COUNTER: u32 = 0; + static mut POS_X: i64 = 0; + static mut POS_Y: i64 = 0; + + *COUNTER += 1; + if *COUNTER == 1000 / RATIO { + cx.spawn.trace(*POS_X, *POS_Y).unwrap(); + *COUNTER = 0; + } + + let (x, y) = cx.resources.pmw3389.read_status().unwrap(); + *POS_X += x as i64; + *POS_Y += y as i64; + + // task should run each second N ms (16_000 cycles at 16MHz) + cx.schedule + .poll(cx.scheduled + (RATIO * 16_000).cycles()) + .unwrap(); + } + + #[task(priority = 1, resources = [hid])] + fn trace(mut cx: trace::Context, x: i64, y:i64) { + static mut OLD_POS_X: i64 = 0; + static mut OLD_POS_Y: i64 = 0; + let hid = &mut cx.resources.hid; + hid.write(&hid::report( + (x - *OLD_POS_X) as i8, + (y - *OLD_POS_Y) as i8 + )); + *OLD_POS_X = x; + *OLD_POS_Y = y; + } + + + #[task(binds=OTG_FS, resources = [usb_dev, hid])] + fn usb_fs(mut cx: usb_fs::Context) { + usb_poll( + //&mut cx.resources.counter, + &mut cx.resources.usb_dev, + &mut cx.resources.hid, + ); + } + + extern "C" { + fn EXTI0(); + fn EXTI1(); + } +}; + +fn usb_poll<B: bus::UsbBus>( + //_counter: &mut u8, + usb_dev: &mut UsbDevice<'static, B>, + hid: &mut HIDClass<'static, B>, +) { + if !usb_dev.poll(&mut [hid]) { + return; + } +} diff --git a/code/examples/rtt_rtic_usb_mouse.rs b/code/examples/rtt_rtic_usb_mouse.rs index 7d425ae907f94310e7276026d1985a753074dca0..2cc82b6af72d473fec8f53d75248f7b68dd93547 100644 --- a/code/examples/rtt_rtic_usb_mouse.rs +++ b/code/examples/rtt_rtic_usb_mouse.rs @@ -57,32 +57,36 @@ pub mod hid { // https://docs.microsoft.com/en-us/windows-hardware/design/component-guidelines/mouse-collection-report-descriptor const REPORT_DESCR: &[u8] = &[ - 0x05, 0x01, // USAGE_PAGE (Generic Desktop) - 0x09, 0x02, // USAGE (Mouse) - 0xa1, 0x01, // COLLECTION (Application) - 0x09, 0x01, // USAGE (Pointer) - 0xa1, 0x00, // COLLECTION (Physical) - 0x05, 0x09, // USAGE_PAGE (Button) - 0x19, 0x01, // USAGE_MINIMUM (Button 1) - 0x29, 0x03, // USAGE_MAXIMUM (Button 3) - 0x15, 0x00, // LOGICAL_MINIMUM (0) - 0x25, 0x01, // LOGICAL_MAXIMUM (1) - 0x95, 0x03, // REPORT_COUNT (3) - 0x75, 0x01, // REPORT_SIZE (1) - 0x81, 0x02, // INPUT (Data,Var,Abs) - 0x95, 0x01, // REPORT_COUNT (1) - 0x75, 0x05, // REPORT_SIZE (5) - 0x81, 0x03, // INPUT (Cnst,Var,Abs) - 0x05, 0x01, // USAGE_PAGE (Generic Desktop) - 0x09, 0x30, // USAGE (X) - 0x09, 0x31, // USAGE (Y) - 0x15, 0x81, // LOGICAL_MINIMUM (-127) - 0x25, 0x7f, // LOGICAL_MAXIMUM (127) - 0x75, 0x08, // REPORT_SIZE (8) - 0x95, 0x02, // REPORT_COUNT (2) - 0x81, 0x06, // INPUT (Data,Var,Rel) - 0xc0, // END_COLLECTION - 0xc0, // END_COLLECTION + 0x05, 0x01, // USAGE_PAGE Generic Desktop + 0x09, 0x02, // USAGE Mouse + 0xa1, 0x01, // COLLECTION Application + 0x09, 0x01, // USAGE Pointer + 0xa1, 0x00, // COLLECTION Physical + + 0x05, 0x09, // USAGE_PAGE (Button) + 0x19, 0x01, // USAGE_MINIMUM (Button 1) + 0x29, 0x05, // USAGE_MAXIMUM (Button 5) + 0x15, 0x00, // LOGICAL_MINIMUM (0) + 0x25, 0x01, // LOGICAL_MAXIMUM (1) + 0x95, 0x05, // REPORT_COUNT (5) + 0x75, 0x01, // REPORT_SIZE (1) + 0x81, 0x02, // INPUT (Data,Var,Abs) + 0x95, 0x01, // REPORT_COUNT (1) + 0x75, 0x03, // REPORT_SIZE (3) + 0x81, 0x03, // INPUT (Cnst,Var,Abs) + + 0x05, 0x01, // USAGE_PAGE Generic Desktop + 0x09, 0x30, // USAGE X + 0x09, 0x31, // USAGE Y + 0x09, 0x38, // USAGE Wheel + 0x15, 0x81, // LOGICAL_MINIMUM -127 + 0x25, 0x7f, // LOGICAL_MAXIMUM 127 + 0x75, 0x08, // REPORT_SIZE 8 + 0x95, 0x03, // REPORT_COUNT 3 + 0x81, 0x06, // INPUT Data,Var,Rel + + 0xc0, // END COLLECTION + 0xc0, // END COLLECTION ]; pub fn report(b: u8,x: i8, y: i8) -> [u8; 3] { @@ -306,9 +310,11 @@ const APP: () = { // set in burst mode - pmw3389.write_register(Register::MotionBurst, 0x00); + pmw3389.write_register(Register::MotionBurst, 0x00).unwrap(); - // Pull the D+ pin down to send a RESET condition to the USB bus. + pmw3389.write_register(Register::ResolutionH, 0 as u8).unwrap(); + + // Pull the D+ pin down to send a RESET chttps://www.usbmadesimple.co.uk/ums_5.htmondition to the USB bus. let mut usb_dp = gpioa.pa12.into_push_pull_output(); usb_dp.set_low().ok(); delay(clocks.sysclk().0 / 100); @@ -358,7 +364,7 @@ const APP: () = { } #[task(resources = [pmw3389, POS_X, POS_Y], schedule = [poll])] - fn poll(mut cx: poll::Context) { + fn poll(cx: poll::Context) { let pos_x = cx.resources.POS_X; let pos_y = cx.resources.POS_Y; @@ -372,14 +378,19 @@ const APP: () = { .unwrap(); } - #[task(resources = [hid, POS_X, POS_Y, OLD_POS_X, OLD_POS_Y, MB1, MB2], schedule = [tick])] + #[task(resources = [hid, POS_X, POS_Y, OLD_POS_X, OLD_POS_Y, MB1, MB2, MB4, MB5], schedule = [tick])] fn tick(mut cx: tick::Context) { cx.schedule.tick(cx.scheduled + (84_000).cycles()).unwrap(); - let res = cx.resources; let pos_x = cx.resources.POS_X; let pos_y = cx.resources.POS_Y; let old_pos_x = cx.resources.OLD_POS_X; let old_pos_y = cx.resources.OLD_POS_Y; + + let mb1 = cx.resources.MB1; + let mb2 = cx.resources.MB2; + + let mb4 = cx.resources.MB4; + let mb5 = cx.resources.MB5; let hid = &mut cx.resources.hid; @@ -390,12 +401,14 @@ const APP: () = { let report = MouseReport { x: diff_x as i8, y: diff_y as i8, - buttons: res.MB1.is_low().unwrap().into(), // (into takes a bool into an integer) + buttons: ( + (mb5.is_low().unwrap() as u8) << 4 + | (mb4.is_low().unwrap() as u8) << 3 + | (mb2.is_low().unwrap() as u8) << 1 + | (mb1.is_low().unwrap() as u8)), // (into takes a bool into an integer) wheel: 0, }; - rprintln!("Report: x:{}, y:{}", *pos_x, *pos_y); - // push the report hid.push_input(&report).ok(); diff --git a/code/src/main.rs b/code/src/main.rs index e75c8637851bb721629ef5f975a0edd29367d5ba..9a9c6237548677b17b2e46c1695ddbcf22dee917 100644 --- a/code/src/main.rs +++ b/code/src/main.rs @@ -1,12 +1,13 @@ -// > cargo run usb-mouse +// > cargo run // or -// > cargo run usb-mouse --release +// > cargo run --release #![no_main] #![no_std] use cortex_m::{asm::delay, peripheral::DWT}; +use cortex_m_semihosting::hprintln; use embedded_hal::digital::v2::OutputPin; use embedded_hal::spi::MODE_3; use rtic::cyccnt::{Instant, U32Ext as _}; @@ -32,10 +33,39 @@ use usbd_hid::{ }; use panic_rtt_target as _; -use rtt_target::{rprintln, rtt_init_print}; +use rtt_target::{rprint, rprintln, rtt_init_print}; // PMW3389 use app::{DwtDelay, pmw3389::{self, Register}, pmw3389e}; +use usbd_hid::descriptor::SerializedDescriptor; +use usbd_hid::descriptor::AsInputReport; +use usbd_hid::descriptor::gen_hid_descriptor; +#[gen_hid_descriptor( + (collection = APPLICATION, usage_page = GENERIC_DESKTOP, usage = MOUSE) = { + (collection = PHYSICAL, usage = POINTER) = { + (usage_page = BUTTON, usage_min = 0x01, usage_max = 0x05) = { + #[packed_bits 5] #[item_settings data,variable,absolute] buttons=input; + }; + (usage_page = GENERIC_DESKTOP,) = { + (usage = X,) = { + #[item_settings data,variable,relative] x=input; + }; + (usage = Y,) = { + #[item_settings data,variable,relative] y=input; + }; + (usage = WHEEL,) = { + #[item_settings data,variable,relative] wheel=input; + }; + }; + }; + } +)] +pub struct NuttaliReport { + pub buttons: u8, + pub x: i8, + pub y: i8, + pub wheel: i8, // Scroll down (negative) or up (positive) this many units +} type PMW3389T = pmw3389::Pmw3389< Spi< @@ -62,6 +92,8 @@ const APP: () = { OLD_POS_X: i64, OLD_POS_Y: i64, + DPI_SCALAR: i8, + MB2: gpioc::PC6<Input<PullUp>>, MB1: gpioc::PC7<Input<PullUp>>, DPIB1: gpiob::PB13<Input<PullUp>>, @@ -70,7 +102,7 @@ const APP: () = { MB4: gpiob::PB14<Input<PullUp>>, } - #[init(schedule = [poll])] + #[init(schedule = [poll, dpi_buttons])] fn init(mut cx: init::Context) -> init::LateResources { static mut EP_MEMORY: [u32; 1024] = [0; 1024]; static mut USB_BUS: Option<UsbBusAllocator<UsbBusType>> = None; @@ -126,9 +158,9 @@ const APP: () = { let mut pmw3389 = pmw3389::Pmw3389::new(spi, cs, delay_dwt).unwrap(); // set in burst mode - pmw3389.write_register(Register::MotionBurst, 0x00); + pmw3389.write_register(Register::MotionBurst, 0x00).unwrap(); //pmw3389.write_register(Register::ResolutionL, 255 as u8); - pmw3389.write_register(Register::ResolutionH, 0 as u8); // sets CPI 0 is lowest, don't know the number tho + pmw3389.write_register(Register::ResolutionH, 0 as u8).unwrap(); // sets CPI 0 is lowest, don't know the number tho let usb = USB { usb_global: device.OTG_FS_GLOBAL, @@ -138,10 +170,11 @@ const APP: () = { pin_dp: gpioa.pa12.into_alternate_af10(), }; + let mut scalar:i8 = 1; USB_BUS.replace(UsbBus::new(usb, EP_MEMORY)); - let hid = HIDClass::new(USB_BUS.as_ref().unwrap(), MouseReport::desc(), 1); + let hid = HIDClass::new(USB_BUS.as_ref().unwrap(), NuttaliReport::desc(), 1); let usb_dev = UsbDeviceBuilder::new(USB_BUS.as_ref().unwrap(), UsbVidPid(0xc410, 0x0000)) .manufacturer("") .product("Vibora Rapido Muerta") @@ -150,19 +183,20 @@ const APP: () = { .build(); cx.schedule.poll(cx.start + (48_000).cycles()).ok(); + cx.schedule.dpi_buttons(cx.start + (50_000).cycles()).ok(); init::LateResources { usb_dev, hid, MB1:mb1, MB2:mb2, MB4:mb4, MB5:mb5, DPIB1:dpib1, DPIB2:dpib2, pmw3389, - POS_X: 0, POS_Y: 0, OLD_POS_X: 0, OLD_POS_Y: 0, + POS_X: 0, POS_Y: 0, OLD_POS_X: 0, OLD_POS_Y: 0, DPI_SCALAR: scalar, } } #[task(resources = [pmw3389, POS_X, POS_Y], schedule = [poll], priority = 2)] - fn poll(mut cx: poll::Context) { + fn poll(cx: poll::Context) { let pos_x = cx.resources.POS_X; let pos_y = cx.resources.POS_Y; @@ -176,7 +210,41 @@ const APP: () = { .unwrap(); } - #[task(binds=OTG_FS, resources = [hid, usb_dev, POS_X, POS_Y, OLD_POS_X, OLD_POS_Y, MB1, MB2], priority = 2)] + #[task(resources = [DPIB1, DPIB2, DPI_SCALAR], schedule = [dpi_buttons], priority = 2)] + fn dpi_buttons(cx: dpi_buttons::Context) { + static mut LEFT_CLICKED: bool = false; + static mut RIGHT_CLICKED: bool = false; + + let dpib1 = cx.resources.DPIB1; + let dpib2 = cx.resources.DPIB2; + let scalar = cx.resources.DPI_SCALAR; + + if dpib1.is_low().unwrap() && !*LEFT_CLICKED { + if *scalar < 5 { + *scalar += 1; + } + *LEFT_CLICKED = true; + } + else if !dpib1.is_low().unwrap() { + *LEFT_CLICKED = false; + } + + if dpib2.is_low().unwrap() && !*RIGHT_CLICKED { + if *scalar > 1 { + *scalar -= 1; + } + *RIGHT_CLICKED = true; + } + else if !dpib2.is_low().unwrap() { + *RIGHT_CLICKED = false; + } + + cx.schedule + .dpi_buttons(cx.scheduled + (100_000_0).cycles()) + .unwrap(); + } + + #[task(binds=OTG_FS, resources = [hid, usb_dev, POS_X, POS_Y, OLD_POS_X, OLD_POS_Y, MB1, MB2, MB4, MB5, DPI_SCALAR], priority = 2)] fn on_usb(cx: on_usb::Context) { // destruct the context let ( @@ -184,28 +252,36 @@ const APP: () = { hid, mb1, mb2, + mb4, + mb5, pos_x, pos_y, + scalar, ) = ( cx.resources.usb_dev, cx.resources.hid, cx.resources.MB1, cx.resources.MB2, + cx.resources.MB4, + cx.resources.MB5, cx.resources.POS_X, cx.resources.POS_Y, + cx.resources.DPI_SCALAR, ); + - let report = MouseReport { - x: *pos_x, - y: *pos_y, - buttons: (( - mb2.is_low().unwrap() as u8) << 1 - | mb1.is_low().unwrap() as u8), // buttons = 1 byte. MB1 = bit 1, MB2 = bit 2, etc etc + let report = NuttaliReport { + x: *pos_x * (*scalar), + y: *pos_y * (*scalar), + buttons: ( + (mb5.is_low().unwrap() as u8) << 3 + | (mb4.is_low().unwrap() as u8) << 4 + | (mb2.is_low().unwrap() as u8) << 1 + | (mb1.is_low().unwrap() as u8)), // buttons = 1 byte. MB1 = bit 1, MB2 = bit 2, etc etc wheel: 0, }; - rprintln!("Report: x:{}, y:{}", *pos_x, *pos_y); // push the report hid.push_input(&report).ok();