diff --git a/Cargo.toml b/Cargo.toml index 879181d..465b23b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,15 +7,17 @@ version = "0.1.0" build = "src/build.rs" [dependencies] -atsamd-hal = "0.12" -cortex-m = "0.6.0" -cortex-m-rt = "0.6.10" +apa102-spi = "0.3.2" +atsamd-hal = "0.14" +bitbang-hal = "0.3.2" +cortex-m = "0.7.4" +cortex-m-rt = "0.7.1" cortex-m-semihosting = "0.3.3" embedded-hal = "0.2" once_cell = { version = "1.7.2", default-features = false } panic-halt = "0.2.0" smart-leds = "0.3" -trinket_m0 = { version = "0.9.0", features = ["unproven", "usb"] } +trinket_m0 = { version = "0.11.0", features = ["unproven", "usb"] } usb-device = "0.2" usbd-serial = "0.1" streamdeck-common = { version = "0.1.0", path = "./streamdeck-common" } @@ -32,10 +34,8 @@ bench = false [profile.release] codegen-units = 1 # better optimizations -debug = true # symbols are nice and they don't increase the size on Flash -lto = true # better optimizations +debug = true # symbols are nice and they don't increase the size on Flash +lto = true # better optimizations [workspace] -members = [ - "./streamdeck-common", -] +members = ["./streamdeck-common"] diff --git a/src/bsp.rs b/src/bsp.rs new file mode 100644 index 0000000..0a99b89 --- /dev/null +++ b/src/bsp.rs @@ -0,0 +1,110 @@ +use atsamd_hal::gpio::v2::{ + Disabled, Floating, Input, Output, Pin, PullUp, PushPull, PA00, PA01, PA14, +}; +use atsamd_hal::prelude::_embedded_hal_timer_CountDown as CountDown; +use embedded_hal::timer::Periodic; + +pub struct Dotstar { + pub ci: Pin>, + pub di: Pin>, + pub nc: Pin>, +} + +pub type Spi = bitbang_hal::spi::SPI< + Pin>, + Pin>, + Pin>, + C, +>; + +impl Dotstar { + pub fn init(self, timer: C) -> apa102_spi::Apa102> { + let spi = bitbang_hal::spi::SPI::new( + apa102_spi::MODE, + self.nc.into(), + self.di.into(), + self.ci.into(), + timer, + ); + + apa102_spi::Apa102::new(spi) + } +} + +atsamd_hal::bsp_pins! ( + PA08 { + name: d0 + aliases: { + AlternateC: Sda + } + } + PA02 { + name: d1 + aliases: { + AlternateC: Scl + } + } + PA09 { + name: d2 + } + PA07 { + name: d3 + aliases: { + AlternateC: UartTx + } + } + PA06 { + name: d4 + aliases: { + AlternateC: UartRx + } + } + PA10 { + name: d13 + aliases: { + PushPullOutput: RedLed + } + } + PA01 { + name: dotstar_ci + aliases: { + AlternateD: Sclk + } + } + PA00 { + name: dotstar_di + aliases: { + AlternateD: Mosi + } + } + PA14 { + name: dotstar_nc + aliases: { + AlternateD: Miso + } + } + PA31 { + name: swdio + } + PA30 { + name: swdclk + } + PA28 { + name: usb_host_enable + } + PA23 { + name: usb_sof + } + PA24 { + name: usb_dm + aliases: { + AlternateG: UsbDm + } + } + PA25 { + name: usb_dp + aliases: { + AlternateG: UsbDp + } + } +); diff --git a/src/main.rs b/src/main.rs index a2cdcd4..89c102c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,7 @@ #![no_std] #![no_main] +use atsamd_hal::clock::{ClockGenId, ClockSource}; use panic_halt as _; // you can put a breakpoint on `rust_begin_unwind` to catch panics use trinket_m0 as hal; @@ -15,9 +16,7 @@ use hal::{ entry, pac::{interrupt, CorePeripherals, Peripherals}, prelude::_atsamd_hal_embedded_hal_digital_v2_OutputPin, - timer::SpinTimer, usb::UsbBus, - Dotstar, }; use once_cell::unsync::OnceCell; use smart_leds::{SmartLedsWrite, RGB8}; @@ -25,8 +24,10 @@ use streamdeck_common::{handle_input, Handshake, UnsafeSync}; use usb_device::{bus::UsbBusAllocator, prelude::*}; use usbd_serial::{SerialPort, USB_CLASS_CDC}; +mod bsp; mod trinket_buttons; +use bsp::{Dotstar, Pins}; use trinket_buttons::Buttons; static USB_ALLOCATOR: UnsafeSync>> = @@ -46,9 +47,9 @@ fn main() -> ! { &mut peripherals.SYSCTRL, &mut peripherals.NVMCTRL, ); - let mut pins = hal::Pins::new(peripherals.PORT); + let pins = Pins::new(peripherals.PORT); - let mut red_led = pins.d13.into_open_drain_output(&mut pins.port); + let mut red_led = pins.d13.into_push_pull_output(); red_led.set_low().unwrap(); let usb_allocator = hal::usb_allocator( @@ -57,9 +58,15 @@ fn main() -> ! { &mut peripherals.PM, pins.usb_dm, pins.usb_dp, - &mut pins.port, ); + let timer_clock = clocks + .configure_gclk_divider_and_source(ClockGenId::GCLK1, 1, ClockSource::OSC32K, false) + .unwrap(); + clocks.configure_standby(ClockGenId::GCLK1, true); + let tc45 = &clocks.tc4_tc5(&timer_clock).unwrap(); + let timer = atsamd_hal::timer::TimerCounter::tc4_(tc45, peripherals.TC4, &mut peripherals.PM); + // SAFETY: No interrupt will access USB_ALLOCATOR before this point, since it is only // referenced from inside the USB_SERIAL mutex and the USB_BUS mutex, which have not been set // yet. @@ -101,14 +108,14 @@ fn main() -> ! { d3: pins.d3, d4: pins.d4, } - .init(&mut pins.port); + .init(); let mut rgb = Dotstar { ci: pins.dotstar_ci, di: pins.dotstar_di, nc: pins.dotstar_nc, } - .init(SpinTimer::new(12), &mut pins.port); + .init(timer); let off = RGB8 { r: 0, g: 0, b: 0 }; rgb.write([off].iter().cloned()).unwrap(); @@ -168,10 +175,8 @@ fn poll_usb(handshake: &mut Handshake) -> Option<()> { let current_time = SYST::get_current(); - if count == 32 { - if handshake.perform(&input, current_time, &mut Device)? { - return Some(()); - } + if count == 32 && handshake.perform(&input, current_time, &mut Device)? { + return Some(()); } let is_complete = handshake.is_complete(); @@ -182,13 +187,15 @@ fn poll_usb(handshake: &mut Handshake) -> Option<()> { handle_input(&mut Device, &input, count) } +fn with_op_lock_spin(cs: &cortex_m::interrupt::CriticalSection) -> bool { + let mut operation_lock = OPERATION_LOCK.borrow(cs).borrow_mut(); + let prev = *operation_lock; + *operation_lock = true; + prev +} + fn with_op_lock(mut f: impl FnMut()) { - while cortex_m::interrupt::free(|cs| { - let mut operation_lock = OPERATION_LOCK.borrow(cs).borrow_mut(); - let prev = *operation_lock; - *operation_lock = true; - prev - }) { + while cortex_m::interrupt::free(with_op_lock_spin) { cycle_delay(1024); } @@ -214,7 +221,7 @@ impl streamdeck_common::Device<16, 1> for Device { let mut id = [0u8; 16]; for i in 0..4 { - id[i * 4 + 0] = unsafe { (*generator[i])[3] }; + id[i * 4] = unsafe { (*generator[i])[3] }; id[i * 4 + 1] = unsafe { (*generator[i])[2] }; id[i * 4 + 2] = unsafe { (*generator[i])[1] }; id[i * 4 + 3] = unsafe { (*generator[i])[0] }; diff --git a/src/trinket_buttons.rs b/src/trinket_buttons.rs index 18c882d..5ee452b 100644 --- a/src/trinket_buttons.rs +++ b/src/trinket_buttons.rs @@ -1,19 +1,16 @@ -use atsamd_hal::common::gpio::{ - v2::{ - pin::{PA02, PA06, PA07, PA08, PA09}, - Floating, Input, PullUp, - }, - Pin, Port, +use atsamd_hal::common::gpio::v2::{ + Disabled, Floating, Input, Pin, PullUp, PA02, PA06, PA07, PA08, PA09, }; +use core::convert::Infallible; use embedded_hal::digital::v2::InputPin; use streamdeck_common::{ButtonPins, ButtonState}; pub(crate) struct Buttons { - pub(crate) d0: Pin>, - pub(crate) d1: Pin>, - pub(crate) d2: Pin>, - pub(crate) d3: Pin>, - pub(crate) d4: Pin>, + pub(crate) d0: Pin>, + pub(crate) d1: Pin>, + pub(crate) d2: Pin>, + pub(crate) d3: Pin>, + pub(crate) d4: Pin>, } pub(crate) struct Pins { @@ -25,19 +22,19 @@ pub(crate) struct Pins { } impl<'a> ButtonPins<'a, 5> for Pins { - fn to_array(&'a self) -> [&'a dyn InputPin; 5] { + fn to_array(&'a self) -> [&'a dyn InputPin; 5] { [&self.d0, &self.d1, &self.d2, &self.d3, &self.d4] } } impl Buttons { - pub(crate) fn init(self, port: &mut Port) -> ButtonState { + pub(crate) fn init(self) -> ButtonState { ButtonState::from_pins(Pins { - d0: self.d0.into_pull_up_input(port), - d1: self.d1.into_pull_up_input(port), - d2: self.d2.into_pull_up_input(port), - d3: self.d3.into_pull_up_input(port), - d4: self.d4.into_pull_up_input(port), + d0: self.d0.into(), + d1: self.d1.into(), + d2: self.d2.into(), + d3: self.d3.into(), + d4: self.d4.into(), }) } } diff --git a/streamdeck-common/src/buttons.rs b/streamdeck-common/src/buttons.rs index a56661c..05be848 100644 --- a/streamdeck-common/src/buttons.rs +++ b/streamdeck-common/src/buttons.rs @@ -1,9 +1,10 @@ +use core::convert::Infallible; use embedded_hal::digital::v2::InputPin; const DEBOUNCE_TIMEOUT: u32 = 100; pub trait ButtonPins<'a, const N: usize> { - fn to_array(&'a self) -> [&'a dyn InputPin; N]; + fn to_array(&'a self) -> [&'a dyn InputPin; N]; } pub struct ButtonState { @@ -56,13 +57,11 @@ where *debounce_time = current_time; } - if *debounce_time + DEBOUNCE_TIMEOUT < current_time { - if *current_state != *last_state { - *current_state = *last_state; + if *debounce_time + DEBOUNCE_TIMEOUT < current_time && *current_state != *last_state { + *current_state = *last_state; - if *current_state { - needs_update |= true; - } + if *current_state { + needs_update |= true; } } } diff --git a/streamdeck-common/src/unsafe_sync.rs b/streamdeck-common/src/unsafe_sync.rs index f82aebe..b146275 100644 --- a/streamdeck-common/src/unsafe_sync.rs +++ b/streamdeck-common/src/unsafe_sync.rs @@ -5,6 +5,9 @@ impl UnsafeSync { UnsafeSync(item) } + /// # Safety + /// + /// uhh... pub unsafe fn get(&self) -> &T { &self.0 } diff --git a/streamdeck-common/src/usb.rs b/streamdeck-common/src/usb.rs index 1a770d1..f092d0c 100644 --- a/streamdeck-common/src/usb.rs +++ b/streamdeck-common/src/usb.rs @@ -1,8 +1,8 @@ use crate::device::Device; -static NAME: &'static [u8] = b"Streamdeck"; -static AUTHOR: &'static [u8] = b"asonix"; -static REPO: &'static [u8] = b"https://git.asonix.dog/asonix/trinket-streamdeck"; +static NAME: &[u8] = b"Streamdeck"; +static AUTHOR: &[u8] = b"asonix"; +static REPO: &[u8] = b"https://git.asonix.dog/asonix/trinket-streamdeck"; pub fn handle_input( device: &mut D,