#![no_std] #![no_main] //extern crate panic_semihosting; use luchie::{ cirque::Cirque, event_filter::{AbsToRel, TrackBall}, log, logger, logln, }; use cortex_m::{ asm::{bkpt, wfi}, interrupt, }; use cortex_m_rt::entry; use embedded_hal::spi; use stm32f1xx_hal::{ pac, prelude::*, serial::{Config, Serial, StopBits, WordLength}, spi::Spi, usb::{self, UsbBus}, }; use usb_device::prelude::*; use usbd_human_interface_device::{ device::mouse::{WheelMouseInterface, WheelMouseReport}, prelude::*, }; use usbd_serial::{SerialPort, USB_CLASS_CDC}; // Relies on cortex-m struct SysTimer { syst: pac::SYST, last: u32, err: u32, reload: u32, ticks_per_microsec: u32, } impl SysTimer { pub fn new(mut syst: pac::SYST, ticks_per_microsec: u32) -> Self { syst.set_clock_source(cortex_m::peripheral::syst::SystClkSource::Core); let reload = 0xffffff; syst.set_reload(reload); syst.clear_current(); syst.enable_counter(); Self { syst, last: 0, err: 0, reload, ticks_per_microsec } } pub fn update(&mut self) -> u32 { let mut time = self.reload - cortex_m::peripheral::SYST::get_current(); if self.syst.has_wrapped() { time += self.reload + 1; } let elapsed = time.wrapping_sub(self.last) + self.err; self.err = elapsed % self.ticks_per_microsec; let elapsed = elapsed / self.ticks_per_microsec; self.last = time & self.reload; // FIXME: might not be 2^x - 1 elapsed } } #[entry] fn main() -> ! { let cp = cortex_m::Peripherals::take().unwrap(); let dp = pac::Peripherals::take().unwrap(); let mut flash = dp.FLASH.constrain(); let rcc = dp.RCC.constrain(); let clocks = rcc .cfgr .use_hse(8.MHz()) .sysclk(72.MHz()) // TODO: gd32 can get up to 120MHz .pclk1(24.MHz()) .freeze(&mut flash.acr); assert!(clocks.usbclk_valid()); let mut afio = dp.AFIO.constrain(); let mut gpioa = dp.GPIOA.split(); let tx_pin = gpioa.pa9.into_alternate_push_pull(&mut gpioa.crh); let rx_pin = gpioa.pa10; let serial = Serial::usart1( dp.USART1, (tx_pin, rx_pin), &mut afio.mapr, Config::default() .baudrate(115200.bps()) .wordlength(WordLength::Bits8) .parity_none() .stopbits(StopBits::STOP1), clocks, ); let (tx, _) = serial.split(); logger::init(tx); logln!("๐Ÿ luchie startingโ€ฆ"); logln!("โฒ๏ธ init timer"); let mut sys_timer = SysTimer::new(cp.SYST, clocks.sysclk().to_MHz()); logln!("๐ŸŽน init buttons"); // fusion mouse button labels let mut gpiob = dp.GPIOB.split(); let left_button = gpiob.pb9.into_pull_up_input(&mut gpiob.crh); let middle_button = gpiob.pb8.into_pull_up_input(&mut gpiob.crh); let forward_button = gpiob.pb7.into_pull_up_input(&mut gpiob.crl); let back_button = gpiob.pb6.into_pull_up_input(&mut gpiob.crl); let dpi_button = gpiob.pb5.into_pull_up_input(&mut gpiob.crl); // cirque spi connections to spi1: // // pb0 - dr // pa4 - ss1 // pa5 - clk1 // pa6 - miso1 // pa7 - mosi1 logln!("๐Ÿ‘† init trackpad"); // TODO: hook an interrupt up to the dr pin to trigger a poll. //let dr_pin = gpiob.pb1.into_pull_down_input(&mut gpiob.crl); let sck_pin = gpioa.pa5.into_alternate_push_pull(&mut gpioa.crl); let miso_pin = gpioa.pa6; let mosi_pin = gpioa.pa7.into_alternate_push_pull(&mut gpioa.crl); let cs_pin = gpioa.pa4.into_push_pull_output(&mut gpioa.crl); let mut spi = Spi::spi1( dp.SPI1, (sck_pin, miso_pin, mosi_pin), &mut afio.mapr, spi::MODE_1, 1_000_000.Hz(), // pinnacle supports up to 13mhz clocks, ); let mut cirque = match Cirque::new(cs_pin, &mut spi, clocks.sysclk().raw()) { Ok(c) => c, Err(e) => { logln!("err: {:?}", e); panic!(); } }; logln!("๐Ÿ–ฅ๏ธ init usb"); // BluePill board has a pull-up resistor on the D+ line. // Pull the D+ pin down to send a RESET condition to the USB bus. // This forced reset is needed only for development, without it host // will not reset your device when you upload new firmware. let mut usb_dp = gpioa.pa12.into_push_pull_output(&mut gpioa.crh); usb_dp.set_low(); cortex_m::asm::delay(clocks.sysclk().raw() / 100); let usb = usb::Peripheral { usb: dp.USB, pin_dm: gpioa.pa11, pin_dp: usb_dp.into_floating_input(&mut gpioa.crh), }; let usb_bus = UsbBus::new(usb); let mut serial = SerialPort::new(&usb_bus); let mut mouse_report = WheelMouseReport::default(); let mut mouse = UsbHidClassBuilder::new() .add_interface(WheelMouseInterface::default_config()) .build(&usb_bus); let mut usb_dev = UsbDeviceBuilder::new(&usb_bus, UsbVidPid(0xdead, 0xbeef)) .manufacturer("Fake company") .product("Serial port") .serial_number("TEST") .device_class(USB_CLASS_CDC) .build(); while usb_dev.state() != UsbDeviceState::Configured { usb_dev.poll(&mut [&mut serial, &mut mouse]); } logln!("๐Ÿ’ก init led"); let mut led = gpiob.pb2.into_push_pull_output(&mut gpiob.crl); led.set_low(); logln!("๐ŸŽ‰ luchie started!"); let mut abs_to_rel = AbsToRel::new(); let mut trackball = TrackBall::new(1); let mut last_buttons = 0; loop { let elapsed = sys_timer.update(); usb_dev.poll(&mut [&mut serial, &mut mouse]); if usb_dev.state() != UsbDeviceState::Configured { continue; } let mut buf = [0u8; 64]; match serial.read(&mut buf) { Ok(count) if count > 0 => { led.set_high(); // Echo back in upper case for c in buf[0..count].iter_mut() { if 0x61 <= *c && *c <= 0x7a { *c &= !0x20; } } let mut write_offset = 0; while write_offset < count { match serial.write(&buf[write_offset..count]) { Ok(len) if len > 0 => { write_offset += len; } _ => {} } } led.set_low(); } _ => {} } if left_button.is_low() { logln!("lb"); mouse_report.buttons |= 1; } if middle_button.is_low() { logln!("mb"); mouse_report.buttons |= 2; } if forward_button.is_low() { logln!("fb"); mouse_report.buttons |= 4; } if back_button.is_low() { logln!("bb"); mouse_report.buttons |= 8; } if dpi_button.is_low() { logln!("db"); mouse_report.buttons |= 16; } let rel_p = cirque .poll(&mut spi) .ok() .map(|td| { //logln!("td: {:?}", td); abs_to_rel.update(&td) }) .flatten(); if let Some(p) = rel_p { logln!("rel_p: {:?}", p); } let p = trackball.update(rel_p, elapsed); if p.0 != 0 || p.1 != 0 || mouse_report.buttons ^ last_buttons != 0 { mouse_report.x = p.0; mouse_report.y = p.1; logln!("p: {:?}", p); match mouse.interface().write_report(&mouse_report) { Err(UsbHidError::WouldBlock) => {} Err(e) => { panic!("couldn't write mouse report: {:?}", e) } _ => {} } } last_buttons = mouse_report.buttons; } } #[panic_handler] fn panic(info: &core::panic::PanicInfo) -> ! { interrupt::free(|_cs| { log!("!!! panic "); if let Some(loc) = info.location() { log!("in {}:{} ", loc.file(), loc.line()); } if let Some(msg) = info.payload().downcast_ref::<&str>() { log!("โ‡’ {} ", msg); } logln!("!!!"); }); spin(); } fn spin() -> ! { bkpt(); loop { wfi(); } }