Select Git revision
Forked from
Per Lindgren / druid-widgets
Source project has a limited visibility.
din_mama.rs 12.75 KiB
// 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;
}
}