Skip to content
Snippets Groups Projects
Select Git revision
  • 07a353962a8a4ae96bf6426e1d0c38df8dd71ca3
  • master default protected
2 results

din_mama.rs

Blame
  • 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;
        }
    }