diff --git a/code/Cargo.lock b/code/Cargo.lock index 8499c420d19c9f991ea53b5c9d8dc44f1b143422..c3316ff5cbfc94e452634d6bb43ced38de8148ed 100644 --- a/code/Cargo.lock +++ b/code/Cargo.lock @@ -23,6 +23,7 @@ dependencies = [ "stm32f4 0.13.0", "stm32f4xx-hal", "usb-device", + "usbd-hid", ] [[package]] @@ -60,9 +61,9 @@ checksum = "46afbd2983a5d5a7bd740ccb198caf5b82f45c40c09c0eed36052d91cb92e719" [[package]] name = "byteorder" -version = "1.4.3" +version = "1.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" [[package]] name = "cast" @@ -164,6 +165,12 @@ dependencies = [ "void", ] +[[package]] +name = "encode_unicode" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" + [[package]] name = "generic-array" version = "0.12.4" @@ -344,6 +351,22 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" +[[package]] +name = "serde" +version = "1.0.124" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd761ff957cb2a45fbb9ab3da6512de9de55872866160b23c25f1a841e99d29f" + +[[package]] +name = "ssmarshal" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3e6ad23b128192ed337dfa4f1b8099ced0c2bf30d61e551b65fda5916dbb850" +dependencies = [ + "encode_unicode", + "serde", +] + [[package]] name = "stable_deref_trait" version = "1.2.0" @@ -449,6 +472,41 @@ version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6be90410d4772074ea49525e2e753b65920b94b57eee21a6ef7b6a6fe6296245" +[[package]] +name = "usbd-hid" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bab0c5efbec0d66d9fd77b64be7a186f7cfff99d9baaae3b7c312538c20ede02" +dependencies = [ + "serde", + "ssmarshal", + "usb-device", + "usbd-hid-macros", +] + +[[package]] +name = "usbd-hid-descriptors" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e00430c0b39caf3957aba6c2b2824f8e70e1ac0278941418bf1fe35f9566d64" +dependencies = [ + "bitfield", +] + +[[package]] +name = "usbd-hid-macros" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b854699aa737a7176d3a0019032350deab561da90a237b69efe3d5ded3732efe" +dependencies = [ + "byteorder", + "proc-macro2", + "quote", + "serde", + "syn", + "usbd-hid-descriptors", +] + [[package]] name = "vcell" version = "0.1.3" diff --git a/code/Cargo.toml b/code/Cargo.toml index fbede03661387d654bfdb89def8e34f34fc71617..af9967acdf8dd4f70aefcd40a79cdde7e17d8e02 100644 --- a/code/Cargo.toml +++ b/code/Cargo.toml @@ -12,6 +12,7 @@ cortex-m-semihosting = "0.3.7" cortex-m-rtic = "0.5.5" embedded-hal = "0.2.4" usb-device = "0.2.7" +usbd-hid = "0.5.0" # Panic handlers, comment all but one to generate doc! #panic-halt = "0.2.0" diff --git a/code/examples/rtt_rtic_usb_mouse.rs b/code/examples/rtt_rtic_usb_mouse.rs index ddd04db8b70e19938463f8bdb7a595f41df3ddf1..7d425ae907f94310e7276026d1985a753074dca0 100644 --- a/code/examples/rtt_rtic_usb_mouse.rs +++ b/code/examples/rtt_rtic_usb_mouse.rs @@ -28,8 +28,11 @@ use stm32f4xx_hal::{ otg_fs::{UsbBus, UsbBusType, USB}, prelude::*, }; -use usb_device::bus; -use usb_device::prelude::*; +use usb_device::{bus::{self, UsbBusAllocator}, prelude::*}; +use usbd_hid::{ + descriptor::{generator_prelude::*, MouseReport}, + hid_class::HIDClass, +}; #[allow(unused)] pub mod hid { @@ -211,8 +214,6 @@ pub mod hid { } } -use hid::HIDClass; - // PMW3389 use app::{DwtDelay, pmw3389::{self, Register}, pmw3389e}; use rtt_target::{rprintln, rtt_init_print}; @@ -253,7 +254,7 @@ const APP: () = { #[init(schedule = [poll, tick])] fn init(mut cx: init::Context) -> init::LateResources { - static mut USB_BUS: Option<bus::UsbBusAllocator<UsbBusType>> = None; + static mut USB_BUS: Option<UsbBusAllocator<UsbBusType>> = None; static mut EP_MEMORY: [u32; 1024] = [0; 1024]; cx.core.DCB.enable_trace(); DWT::unlock(); @@ -277,16 +278,6 @@ const APP: () = { let gpioc = device.GPIOC.split(); let gpiob = device.GPIOB.split(); -<<<<<<< HEAD - 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(); -======= // Buttons let mb2 = gpioc.pc6.into_pull_up_input(); let mb1 = gpioc.pc7.into_pull_up_input(); @@ -316,7 +307,6 @@ const APP: () = { // set in burst mode pmw3389.write_register(Register::MotionBurst, 0x00); ->>>>>>> 3043b359af07960fb4472e373824f552897ee4aa // Pull the D+ pin down to send a RESET condition to the USB bus. let mut usb_dp = gpioa.pa12.into_push_pull_output(); @@ -336,7 +326,7 @@ const APP: () = { *USB_BUS = Some(UsbBus::new(usb, EP_MEMORY)); - let hid = HIDClass::new(USB_BUS.as_ref().unwrap()); + let hid = HIDClass::new(USB_BUS.as_ref().unwrap(), MouseReport::desc(), 1); let usb_dev = UsbDeviceBuilder::new(USB_BUS.as_ref().unwrap(), UsbVidPid(0xc410, 0x0000)) .manufacturer("Albatraoz") @@ -361,7 +351,7 @@ const APP: () = { #[idle] fn idle(_cx: idle::Context) -> ! { - // rprintln!("idle"); + rprintln!("idle"); loop { continue; } @@ -385,6 +375,7 @@ const APP: () = { #[task(resources = [hid, POS_X, POS_Y, OLD_POS_X, OLD_POS_Y, MB1, MB2], 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; @@ -396,8 +387,18 @@ const APP: () = { let diff_x = *pos_x-*old_pos_x; let diff_y = *pos_y-*old_pos_y; - // Send to computer - hid.write(&hid::report(0x01 as u8,(diff_x) as i8, (diff_y) as i8)); + 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) + wheel: 0, + }; + + rprintln!("Report: x:{}, y:{}", *pos_x, *pos_y); + + // push the report + hid.push_input(&report).ok(); + *old_pos_x = *pos_x; *old_pos_y = *pos_y; diff --git a/code/src/main.rs b/code/src/main.rs index 792259654a49249056b0368a6cc45d2f3959069e..e75c8637851bb721629ef5f975a0edd29367d5ba 100644 --- a/code/src/main.rs +++ b/code/src/main.rs @@ -1,20 +1,231 @@ -#![no_std] +// > cargo run usb-mouse +// or +// > cargo run usb-mouse --release + #![no_main] +#![no_std] + + +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::UsbBusAllocator, prelude::*}; +use usbd_hid::{ + descriptor::{generator_prelude::*, MouseReport}, + hid_class::HIDClass, +}; + +use panic_rtt_target as _; +use rtt_target::{rprintln, rtt_init_print}; + +// PMW3389 +use app::{DwtDelay, pmw3389::{self, Register}, pmw3389e}; + +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>>, +>; + +#[rtic::app(device = stm32f4xx_hal::stm32, peripherals = true, monotonic = rtic::cyccnt::CYCCNT)] +const APP: () = { + struct Resources { + usb_dev: UsbDevice<'static, UsbBusType>, + hid: HIDClass<'static, UsbBusType>, + + pmw3389: PMW3389T, + + POS_X: i8, + POS_Y: i8, + OLD_POS_X: i64, + OLD_POS_Y: i64, + + 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 EP_MEMORY: [u32; 1024] = [0; 1024]; + static mut USB_BUS: Option<UsbBusAllocator<UsbBusType>> = None; + + rtt_init_print!(); + rprintln!("init"); + + cx.core.DCB.enable_trace(); + DWT::unlock(); + cx.core.DWT.enable_cycle_counter(); + + let mut core = cx.core; + let device = cx.device; + + let rcc = device.RCC.constrain(); + + let clocks = rcc + .cfgr + .sysclk(84.mhz()) + .pclk1(42.mhz()) + .pclk2(64.mhz()) + .freeze(); + + // assert!(clocks.usbclk_valid()); + + let gpioa = device.GPIOA.split(); + let gpioc = device.GPIOC.split(); + let gpiob = 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(); -// pick a panicking behavior -use panic_halt as _; // you can put a breakpoint on `rust_begin_unwind` to catch panics -// use panic_abort as _; // requires nightly -// use panic_itm as _; // logs messages over ITM; requires ITM support -// use panic_semihosting as _; // logs messages to the host stderr; requires a debugger + // 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); -use cortex_m::asm; -use cortex_m_rt::entry; + let spi = Spi::spi3( + device.SPI3, + (sck, miso, mosi), + MODE_3, + stm32f4xx_hal::time::KiloHertz(2000).into(), + clocks, + ); -#[entry] -fn main() -> ! { - asm::nop(); // To not have main optimize to abort in release mode, remove when you add code + 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); + //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 + + let usb = USB { + usb_global: device.OTG_FS_GLOBAL, + usb_device: device.OTG_FS_DEVICE, + usb_pwrclk: device.OTG_FS_PWRCLK, + pin_dm: gpioa.pa11.into_alternate_af10(), + pin_dp: gpioa.pa12.into_alternate_af10(), + }; + + + USB_BUS.replace(UsbBus::new(usb, EP_MEMORY)); + + let hid = HIDClass::new(USB_BUS.as_ref().unwrap(), MouseReport::desc(), 1); + let usb_dev = UsbDeviceBuilder::new(USB_BUS.as_ref().unwrap(), UsbVidPid(0xc410, 0x0000)) + .manufacturer("") + .product("Vibora Rapido Muerta") + .serial_number("33-32-33") + .device_class(0) + .build(); + + cx.schedule.poll(cx.start + (48_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, + + } + } + + #[task(resources = [pmw3389, POS_X, POS_Y], schedule = [poll], priority = 2)] + fn poll(mut cx: poll::Context) { + let pos_x = cx.resources.POS_X; + let pos_y = cx.resources.POS_Y; + + let (x, y) = cx.resources.pmw3389.read_status().unwrap(); + *pos_x = x as i8; + *pos_y = y as i8; + + // task should run each Sclck/N times each second. 84M/8400 = 10_000 + cx.schedule + .poll(cx.scheduled + (84_00).cycles()) + .unwrap(); + } + + #[task(binds=OTG_FS, resources = [hid, usb_dev, POS_X, POS_Y, OLD_POS_X, OLD_POS_Y, MB1, MB2], priority = 2)] + fn on_usb(cx: on_usb::Context) { + // destruct the context + let ( + usb_dev, + hid, + mb1, + mb2, + pos_x, + pos_y, + ) = ( + cx.resources.usb_dev, + cx.resources.hid, + cx.resources.MB1, + cx.resources.MB2, + cx.resources.POS_X, + cx.resources.POS_Y, + ); + + + 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 + wheel: 0, + }; + + rprintln!("Report: x:{}, y:{}", *pos_x, *pos_y); + + // push the report + hid.push_input(&report).ok(); + + // update the usb device state + if usb_dev.poll(&mut [hid]) { + return; + } + } + + extern "C" { + fn EXTI0(); + fn EXTI1(); + } - loop { - // your code goes here + #[idle] + fn idle(_cx: idle::Context) -> ! { + rprintln!("idle"); + loop { + continue; + } } -} +}; diff --git a/code/src/pmw3389.rs b/code/src/pmw3389.rs index a978c32dfeece778559228503d94d6cf1242b5cf..77c08e916c51eaccb4da1dbb0397394dec085a54 100644 --- a/code/src/pmw3389.rs +++ b/code/src/pmw3389.rs @@ -340,7 +340,7 @@ where // // set initial CPI resolution // // adns_write_reg(Config1, 0x15); - let cpi: u16 = 16000; + let cpi: u16 = 800; self.write_register(Register::ResolutionL, cpi as u8)?; self.write_register(Register::ResolutionH, (cpi >> 8) as u8)?;