diff --git a/boards/pico/Cargo.toml b/boards/pico/Cargo.toml index e95165f..8578286 100644 --- a/boards/pico/Cargo.toml +++ b/boards/pico/Cargo.toml @@ -14,8 +14,9 @@ cortex-m = "0.7.2" rp2040-hal = { path = "../../rp2040-hal", version = "0.2.0"} cortex-m-rt = { version = "0.6.14", optional = true } embedded-time = "0.12.0" -usb-device="0.2.8" +usb-device= "0.2.8" usbd-serial = "0.1.1" +usbd-hid = "0.5.0" [dev-dependencies] panic-halt= "0.2.0" diff --git a/boards/pico/examples/pico_usb_twitchy_mouse.rs b/boards/pico/examples/pico_usb_twitchy_mouse.rs new file mode 100644 index 0000000..2ddd025 --- /dev/null +++ b/boards/pico/examples/pico_usb_twitchy_mouse.rs @@ -0,0 +1,152 @@ +//! This is a port of +//! https://github.com/atsamd-rs/atsamd/blob/master/boards/itsybitsy_m0/examples/twitching_usb_mouse.rs +#![no_std] +#![no_main] + +use cortex_m::interrupt::free as disable_interrupts; +use cortex_m_rt::entry; +use embedded_time::fixed_point::FixedPoint; +use panic_halt as _; +use pico::{ + hal::{ + self, + clocks::{init_clocks_and_plls, Clock}, + pac::{self, interrupt}, + usb::UsbBus, + watchdog::Watchdog, + }, + XOSC_CRYSTAL_FREQ, +}; +use usb_device::bus::UsbBusAllocator; +use usb_device::prelude::*; +use usbd_hid::descriptor::generator_prelude::*; +use usbd_hid::descriptor::MouseReport; +use usbd_hid::hid_class::HIDClass; + +#[link_section = ".boot2"] +#[used] +pub static BOOT2: [u8; 256] = rp2040_boot2::BOOT_LOADER; + +#[entry] +fn main() -> ! { + let mut pac = pac::Peripherals::take().unwrap(); + let mut watchdog = Watchdog::new(pac.WATCHDOG); + + let clocks = init_clocks_and_plls( + XOSC_CRYSTAL_FREQ, + pac.XOSC, + pac.CLOCKS, + pac.PLL_SYS, + pac.PLL_USB, + &mut pac.RESETS, + &mut watchdog, + ) + .ok() + .unwrap(); + + let usb_bus = UsbBusAllocator::new(UsbBus::new( + pac.USBCTRL_REGS, + pac.USBCTRL_DPRAM, + clocks.usb_clock, + true, + &mut pac.RESETS, + )); + unsafe { + USB_BUS = Some(usb_bus); + } + + let usb_hid = HIDClass::new( + unsafe { USB_BUS.as_ref().unwrap() }, + MouseReport::desc(), + 60, + ); + unsafe { + USB_HID = Some(usb_hid); + } + + let usb_dev = UsbDeviceBuilder::new( + unsafe { USB_BUS.as_ref().unwrap() }, + UsbVidPid(0x16c0, 0x27dd), + ) + .manufacturer("Fake company") + .product("Twitchy Mousey") + .serial_number("TEST") + .device_class(0xEF) // misc + .build(); + unsafe { + USB_DEVICE = Some(usb_dev); + } + + unsafe { + let p = pac::Peripherals::steal(); + // Enable interrupts for when a buffer is done, when the bus is reset, + // and when a setup packet is received + p.USBCTRL_REGS.inte.modify(|_, w| { + w.buff_status() + .set_bit() + .bus_reset() + .set_bit() + .setup_req() + .set_bit() + }); + } + unsafe { + pac::NVIC::unmask(hal::pac::Interrupt::USBCTRL_IRQ); + }; + let core = pac::CorePeripherals::take().unwrap(); + let mut delay = cortex_m::delay::Delay::new(core.SYST, clocks.system_clock.freq().integer()); + + loop { + delay.delay_ms(100); + push_mouse_movement(MouseReport { + x: 0, + y: 4, + buttons: 0, + wheel: 0, + }) + .ok() + .unwrap_or(0); + delay.delay_ms(100); + push_mouse_movement(MouseReport { + x: 0, + y: -4, + buttons: 0, + wheel: 0, + }) + .ok() + .unwrap_or(0); + } +} + +static mut USB_DEVICE: Option> = None; +static mut USB_BUS: Option> = None; +static mut USB_HID: Option> = None; + +fn push_mouse_movement(report: MouseReport) -> Result { + disable_interrupts(|_| unsafe { USB_HID.as_mut().map(|hid| hid.push_input(&report)) }).unwrap() +} + +#[allow(non_snake_case)] +#[interrupt] +unsafe fn USBCTRL_IRQ() { + // Handle USB request + let usb_dev = USB_DEVICE.as_mut().unwrap(); + let usb_hid = USB_HID.as_mut().unwrap(); + usb_dev.poll(&mut [usb_hid]); + + // Clear pending interrupt flags + let p = pac::Peripherals::steal(); + let status = &p.USBCTRL_REGS.sie_status; + if status.read().ack_rec().bit_is_set() { + status.modify(|_r, w| w.ack_rec().set_bit()); + } + if status.read().setup_rec().bit_is_set() { + status.modify(|_r, w| w.setup_rec().set_bit()); + } + if status.read().trans_complete().bit_is_set() { + status.modify(|_r, w| w.trans_complete().set_bit()); + } + if status.read().bus_reset().bit_is_set() { + status.modify(|_r, w| w.bus_reset().set_bit()); + } +}