#![no_std] #![no_main] //extern crate panic_semihosting; use core::cmp; use luchie::{ cirque::Cirque, 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::{ prelude::*, device::mouse::{WheelMouseInterface, WheelMouseReport}, }; use usbd_serial::{SerialPort, USB_CLASS_CDC}; #[entry] fn main() -> ! { 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()) // TODO: causes issues with gd32 usb (dropped packets) and garbled usart1 output .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โ€ฆ"); // 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(); // let mut delay = dp.TIM2.delay_us(&clocks); // delay.delay_ms(10u8); 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 gpiob = dp.GPIOB.split(); let mut led = gpiob.pb2.into_push_pull_output(&mut gpiob.crl); led.set_low(); logln!("๐ŸŽ‰ luchie started!"); let mut is_pressed = false; let mut last_x = 0u16; let mut last_y = 0u16; loop { // logln!("."); usb_dev.poll(&mut [&mut serial, &mut mouse]); 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 let Ok(td) = cirque.poll(&mut spi) { logln!("td: {:?}", td); if td.is_pressed { if is_pressed { /* * The trackpad's actual valid return values are * only about 2^11, so overflow isn't possible * when converting to signed 16 bit integers. */ let s_x: i16 = td.x as i16 - last_x as i16; let s_y: i16 = td.y as i16 - last_y as i16; // Clamp to i8 range. let raw_x = cmp::max(i8::MIN as i16, cmp::min(i8::MAX as i16, s_x)); let raw_y = cmp::max(i8::MIN as i16, cmp::min(i8::MAX as i16, s_y)); mouse_report.x = raw_x as i8; mouse_report.y = raw_y as i8; } is_pressed = true; last_x = td.x; last_y = td.y; } else { mouse_report.x = 0; mouse_report.y = 0; is_pressed = false; } match mouse.interface().write_report(&mouse_report) { Err(UsbHidError::WouldBlock) => {}, Err(e) => { panic!("couldn't write mouse report: {:?}", e) }, _ => {}, } } } } #[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(); } }