/
pico_usb_twitchy_mouse.rs
191 lines (163 loc) · 5.64 KB
/
pico_usb_twitchy_mouse.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
//! # Pico USB 'Twitchy' Mouse Example
//!
//! Creates a USB HID Class Pointing device (i.e. a virtual mouse) on a Pico
//! board, with the USB driver running in the main thread.
//!
//! It generates movement reports which will twitch the cursor up and down by a
//! few pixels, several times a second.
//!
//! See the `Cargo.toml` file for Copyright and license details.
//!
//! 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]
// The macro for our start-up function
use rp_pico::entry;
// The macro for marking our interrupt functions
use rp_pico::hal::pac::interrupt;
// Ensure we halt the program on panic (if we don't mention this crate it won't
// be linked)
use panic_halt as _;
// Pull in any important traits
use rp_pico::hal::prelude::*;
// A shorter alias for the Peripheral Access Crate, which provides low-level
// register access
use rp_pico::hal::pac;
// A shorter alias for the Hardware Abstraction Layer, which provides
// higher-level drivers.
use rp_pico::hal;
// USB Device support
use usb_device::{class_prelude::*, prelude::*};
// USB Human Interface Device (HID) Class support
use usbd_hid::descriptor::generator_prelude::*;
use usbd_hid::descriptor::MouseReport;
use usbd_hid::hid_class::HIDClass;
/// The USB Device Driver (shared with the interrupt).
static mut USB_DEVICE: Option<UsbDevice<hal::usb::UsbBus>> = None;
/// The USB Bus Driver (shared with the interrupt).
static mut USB_BUS: Option<UsbBusAllocator<hal::usb::UsbBus>> = None;
/// The USB Human Interface Device Driver (shared with the interrupt).
static mut USB_HID: Option<HIDClass<hal::usb::UsbBus>> = None;
/// Entry point to our bare-metal application.
///
/// The `#[entry]` macro ensures the Cortex-M start-up code calls this function
/// as soon as all global variables are initialised.
///
/// The function configures the RP2040 peripherals, then submits cursor movement
/// updates periodically.
#[entry]
fn main() -> ! {
// Grab our singleton objects
let mut pac = pac::Peripherals::take().unwrap();
// Set up the watchdog driver - needed by the clock setup code
let mut watchdog = hal::Watchdog::new(pac.WATCHDOG);
// Configure the clocks
//
// The default is to generate a 125 MHz system clock
let clocks = hal::clocks::init_clocks_and_plls(
rp_pico::XOSC_CRYSTAL_FREQ,
pac.XOSC,
pac.CLOCKS,
pac.PLL_SYS,
pac.PLL_USB,
&mut pac.RESETS,
&mut watchdog,
)
.ok()
.unwrap();
#[cfg(feature = "rp2040-e5")]
{
let sio = hal::Sio::new(pac.SIO);
let _pins = rp_pico::Pins::new(
pac.IO_BANK0,
pac.PADS_BANK0,
sio.gpio_bank0,
&mut pac.RESETS,
);
}
// Set up the USB driver
let usb_bus = UsbBusAllocator::new(hal::usb::UsbBus::new(
pac.USBCTRL_REGS,
pac.USBCTRL_DPRAM,
clocks.usb_clock,
true,
&mut pac.RESETS,
));
unsafe {
// Note (safety): This is safe as interrupts haven't been started yet
USB_BUS = Some(usb_bus);
}
// Grab a reference to the USB Bus allocator. We are promising to the
// compiler not to take mutable access to this global variable whilst this
// reference exists!
let bus_ref = unsafe { USB_BUS.as_ref().unwrap() };
// Set up the USB HID Class Device driver, providing Mouse Reports
let usb_hid = HIDClass::new(bus_ref, MouseReport::desc(), 60);
unsafe {
// Note (safety): This is safe as interrupts haven't been started yet.
USB_HID = Some(usb_hid);
}
// Create a USB device with a fake VID and PID
let usb_dev = UsbDeviceBuilder::new(bus_ref, UsbVidPid(0x16c0, 0x27da))
.strings(&[StringDescriptors::default()
.manufacturer("Fake company")
.product("Twitchy Mousey")
.serial_number("TEST")])
.unwrap()
.device_class(0)
.build();
unsafe {
// Note (safety): This is safe as interrupts haven't been started yet
USB_DEVICE = Some(usb_dev);
}
unsafe {
// Enable the USB interrupt
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().to_Hz());
// Move the cursor up and down every 200ms
loop {
delay.delay_ms(100);
let rep_up = MouseReport {
x: 0,
y: 4,
buttons: 0,
wheel: 0,
pan: 0,
};
push_mouse_movement(rep_up).ok().unwrap_or(0);
delay.delay_ms(100);
let rep_down = MouseReport {
x: 0,
y: -4,
buttons: 0,
wheel: 0,
pan: 0,
};
push_mouse_movement(rep_down).ok().unwrap_or(0);
}
}
/// Submit a new mouse movement report to the USB stack.
///
/// We do this with interrupts disabled, to avoid a race hazard with the USB IRQ.
fn push_mouse_movement(report: MouseReport) -> Result<usize, usb_device::UsbError> {
critical_section::with(|_| unsafe {
// Now interrupts are disabled, grab the global variable and, if
// available, send it a HID report
USB_HID.as_mut().map(|hid| hid.push_input(&report))
})
.unwrap()
}
/// This function is called whenever the USB Hardware generates an Interrupt
/// Request.
#[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]);
}
// End of file