aboutsummaryrefslogtreecommitdiffstats
path: root/src/bin/luchie.rs
diff options
context:
space:
mode:
authorBrian Cully <bjc@kublai.com>2022-11-06 16:34:44 -0500
committerBrian Cully <bjc@kublai.com>2022-11-06 16:37:38 -0500
commit20377d4522d513b66406d4ef8231a7cdbfedc157 (patch)
tree3b391f48c056c8a0fadbb3915dac11984a8b814d /src/bin/luchie.rs
parenta484a97111d0897ac6e0e291c4432a91ebdee416 (diff)
downloadluchie-20377d4522d513b66406d4ef8231a7cdbfedc157.tar.gz
luchie-20377d4522d513b66406d4ef8231a7cdbfedc157.zip
cargo: rejigger crate type
convert to hybrid crate, with arch-dependent stuff as a ‘bin’ crate, with the underlying support in ‘lib’. the reason for this change is to allow for automated unit-testing on the device-independent bits. to facilitate this, a ‘t’ alias is provided which will run the unit tests. this target assumes you're on a linux system, but you can't have everything.
Diffstat (limited to 'src/bin/luchie.rs')
-rwxr-xr-xsrc/bin/luchie.rs188
1 files changed, 188 insertions, 0 deletions
diff --git a/src/bin/luchie.rs b/src/bin/luchie.rs
new file mode 100755
index 0000000..7dedc8d
--- /dev/null
+++ b/src/bin/luchie.rs
@@ -0,0 +1,188 @@
+#![no_std]
+#![no_main]
+
+//extern crate panic_semihosting;
+
+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_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 gpiob = dp.GPIOB.split();
+ let mut led = gpiob.pb2.into_push_pull_output(&mut gpiob.crl);
+ led.set_low();
+
+ 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 usb_dev = UsbDeviceBuilder::new(&usb_bus, UsbVidPid(0xdead, 0xbeef))
+ .manufacturer("Fake company")
+ .product("Serial port")
+ .serial_number("TEST")
+ .device_class(USB_CLASS_CDC)
+ .build();
+
+ logln!("🎉 luchie started!");
+ loop {
+ // logln!(".");
+ if let Ok(mut td) = cirque.poll(&mut spi) {
+ td.scale_to(1920, 1080);
+ logln!("td: {:?}", td);
+ }
+
+ if !usb_dev.poll(&mut [&mut serial]) {
+ 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();
+ }
+ _ => {}
+ }
+ }
+}
+
+#[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();
+ }
+}