Initial button impl
This commit is contained in:
parent
4b11c8c9d9
commit
119643e36f
|
@ -6,9 +6,11 @@ name = "trinket-streamdeck"
|
|||
version = "0.1.0"
|
||||
|
||||
[dependencies]
|
||||
atsamd-hal = "0.12"
|
||||
cortex-m = "0.6.0"
|
||||
cortex-m-rt = "0.6.10"
|
||||
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"
|
||||
|
|
181
src/buttons.rs
Normal file
181
src/buttons.rs
Normal file
|
@ -0,0 +1,181 @@
|
|||
use atsamd_hal::{
|
||||
common::gpio::{
|
||||
v2::{
|
||||
pin::{PA02, PA06, PA07, PA08, PA09},
|
||||
Floating, Input, PullUp,
|
||||
},
|
||||
Pin, Port,
|
||||
},
|
||||
samd21::usb::UsbBus,
|
||||
};
|
||||
use cortex_m::peripheral::SYST;
|
||||
use embedded_hal::digital::v2::InputPin;
|
||||
use smart_leds::{SmartLedsWrite, RGB8};
|
||||
use usbd_serial::SerialPort;
|
||||
|
||||
const DEBOUNCE_TIMEOUT: u32 = 100;
|
||||
|
||||
pub(crate) struct Buttons {
|
||||
pub(crate) d0: Pin<PA08, Input<Floating>>,
|
||||
pub(crate) d1: Pin<PA02, Input<Floating>>,
|
||||
pub(crate) d2: Pin<PA09, Input<Floating>>,
|
||||
pub(crate) d3: Pin<PA07, Input<Floating>>,
|
||||
pub(crate) d4: Pin<PA06, Input<Floating>>,
|
||||
}
|
||||
|
||||
pub(crate) struct ButtonState {
|
||||
pins: Pins,
|
||||
debounce_times: [u32; 5],
|
||||
last_states: [bool; 5],
|
||||
current_states: [bool; 5],
|
||||
}
|
||||
|
||||
struct Pins {
|
||||
d0: Pin<PA08, Input<PullUp>>,
|
||||
d1: Pin<PA02, Input<PullUp>>,
|
||||
d2: Pin<PA09, Input<PullUp>>,
|
||||
d3: Pin<PA07, Input<PullUp>>,
|
||||
d4: Pin<PA06, Input<PullUp>>,
|
||||
}
|
||||
|
||||
struct Inputs<'a> {
|
||||
buttons: [&'a dyn InputPin<Error = ()>; 5],
|
||||
count: usize,
|
||||
}
|
||||
|
||||
impl Buttons {
|
||||
pub(crate) fn init(self, port: &mut Port) -> ButtonState {
|
||||
ButtonState {
|
||||
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),
|
||||
},
|
||||
debounce_times: [0u32; 5],
|
||||
last_states: [false; 5],
|
||||
current_states: [false; 5],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ButtonState {
|
||||
pub(crate) fn tick(
|
||||
&mut self,
|
||||
rgb: &mut impl SmartLedsWrite<Color = RGB8>,
|
||||
serial: &mut SerialPort<UsbBus>,
|
||||
) -> Option<()> {
|
||||
if self.check_buttons()? {
|
||||
let byte = self.calculate_press_state();
|
||||
self.reset_state();
|
||||
|
||||
serial.write(&[byte]).ok()?;
|
||||
|
||||
let color = color_from_byte(byte);
|
||||
rgb.write([color].iter().cloned()).ok()?;
|
||||
}
|
||||
|
||||
Some(())
|
||||
}
|
||||
|
||||
fn check_buttons(&mut self) -> Option<bool> {
|
||||
let mut needs_update = false;
|
||||
let current_time = SYST::get_current();
|
||||
|
||||
let iter = self
|
||||
.pins
|
||||
.iter()
|
||||
.zip(&mut self.debounce_times)
|
||||
.zip(&mut self.last_states)
|
||||
.zip(&mut self.current_states);
|
||||
|
||||
for (((input, debounce_time), last_state), current_state) in iter {
|
||||
let reading = input.is_high().ok()?;
|
||||
|
||||
if reading != *last_state {
|
||||
*last_state = reading;
|
||||
*debounce_time = current_time;
|
||||
}
|
||||
|
||||
if *debounce_time + DEBOUNCE_TIMEOUT < current_time {
|
||||
if *current_state != *last_state {
|
||||
*current_state = *last_state;
|
||||
|
||||
if *current_state {
|
||||
needs_update |= true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some(needs_update)
|
||||
}
|
||||
|
||||
fn reset_state(&mut self) {
|
||||
let current_time = SYST::get_current();
|
||||
|
||||
for time in &mut self.debounce_times {
|
||||
*time = current_time;
|
||||
}
|
||||
}
|
||||
|
||||
fn calculate_press_state(&self) -> u8 {
|
||||
self.current_states.iter().rev().fold(0u8, |acc, state| {
|
||||
// state keeps track of HIGH, but we | on low
|
||||
if *state {
|
||||
acc << 1
|
||||
} else {
|
||||
(acc << 1) | 1
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Pins {
|
||||
fn iter(&self) -> Inputs<'_> {
|
||||
Inputs {
|
||||
buttons: [&self.d0, &self.d1, &self.d2, &self.d3, &self.d4],
|
||||
count: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Expect byte to be in range 0..32
|
||||
fn color_from_byte(byte: u8) -> RGB8 {
|
||||
let position: u8 = byte * 8; // max value here is 248
|
||||
|
||||
match position {
|
||||
0..=85 => RGB8 {
|
||||
r: (255 - position * 3),
|
||||
g: (position * 3),
|
||||
b: 0,
|
||||
},
|
||||
86..=170 => {
|
||||
let position = position - 85;
|
||||
RGB8 {
|
||||
r: 0,
|
||||
g: (255 - position * 3),
|
||||
b: (position * 3),
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
let position = position - 170;
|
||||
RGB8 {
|
||||
r: (position * 3),
|
||||
g: 0,
|
||||
b: (255 - position * 3),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Iterator for Inputs<'a> {
|
||||
type Item = &'a dyn InputPin<Error = ()>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let prev_count = self.count;
|
||||
self.count += 1;
|
||||
self.buttons.get(prev_count).map(|r| *r)
|
||||
}
|
||||
}
|
|
@ -2,6 +2,8 @@ use crate::hal::usb::UsbBus;
|
|||
use cortex_m::peripheral::SYST;
|
||||
use usbd_serial::SerialPort;
|
||||
|
||||
const HANDSHAKE_TIMEOUT: u32 = 100;
|
||||
|
||||
pub(crate) struct Handshake {
|
||||
step_1_complete: bool,
|
||||
step_2_complete: bool,
|
||||
|
@ -48,7 +50,7 @@ impl Handshake {
|
|||
|
||||
if self.step_1_complete
|
||||
&& !self.step_2_complete
|
||||
&& self.timestamp > SYST::get_current() + 100
|
||||
&& self.timestamp > SYST::get_current() + HANDSHAKE_TIMEOUT
|
||||
{
|
||||
self.step_1_complete = false;
|
||||
}
|
||||
|
|
42
src/main.rs
42
src/main.rs
|
@ -20,9 +20,11 @@ use smart_leds::{SmartLedsWrite, RGB8};
|
|||
use usb_device::{bus::UsbBusAllocator, prelude::*};
|
||||
use usbd_serial::{SerialPort, USB_CLASS_CDC};
|
||||
|
||||
mod buttons;
|
||||
mod handshake;
|
||||
mod unsafe_sync;
|
||||
|
||||
use buttons::Buttons;
|
||||
use handshake::Handshake;
|
||||
use unsafe_sync::UnsafeSync;
|
||||
|
||||
|
@ -33,6 +35,7 @@ static REPO: &'static [u8] = b"https://git.asonix.dog/asonix/trinket-streamdeck"
|
|||
static USB_ALLOCATOR: UnsafeSync<OnceCell<UsbBusAllocator<UsbBus>>> =
|
||||
UnsafeSync::new(OnceCell::new());
|
||||
|
||||
static OPERATION_LOCK: Mutex<RefCell<bool>> = Mutex::new(RefCell::new(false));
|
||||
static USB_BUS: Mutex<RefCell<Option<UsbDevice<UsbBus>>>> = Mutex::new(RefCell::new(None));
|
||||
static USB_SERIAL: Mutex<RefCell<Option<SerialPort<UsbBus>>>> = Mutex::new(RefCell::new(None));
|
||||
static HANDSHAKE: Mutex<RefCell<Handshake>> = Mutex::new(RefCell::new(Handshake::new()));
|
||||
|
@ -110,6 +113,15 @@ fn main() -> ! {
|
|||
NVIC::unmask(interrupt::USB);
|
||||
}
|
||||
|
||||
let mut buttons = Buttons {
|
||||
d0: pins.d0,
|
||||
d1: pins.d1,
|
||||
d2: pins.d2,
|
||||
d3: pins.d3,
|
||||
d4: pins.d4,
|
||||
}
|
||||
.init(&mut pins.port);
|
||||
|
||||
let mut rgb = Dotstar {
|
||||
ci: pins.dotstar_ci,
|
||||
di: pins.dotstar_di,
|
||||
|
@ -121,7 +133,14 @@ fn main() -> ! {
|
|||
red_led.set_low().unwrap();
|
||||
|
||||
loop {
|
||||
cycle_delay(15 * 1024 * 1024);
|
||||
cycle_delay(1024 * 15);
|
||||
with_op_lock(|| {
|
||||
cortex_m::interrupt::free(|cs| {
|
||||
let mut serial = USB_SERIAL.borrow(cs).borrow_mut();
|
||||
let serial = serial.as_mut().unwrap();
|
||||
buttons.tick(&mut rgb, serial);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -204,7 +223,26 @@ fn write_length_delim(bytes: &[u8]) -> Option<()> {
|
|||
})
|
||||
}
|
||||
|
||||
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
|
||||
}) {
|
||||
cycle_delay(1024);
|
||||
}
|
||||
|
||||
(f)();
|
||||
|
||||
cortex_m::interrupt::free(|cs| {
|
||||
*OPERATION_LOCK.borrow(cs).borrow_mut() = false;
|
||||
});
|
||||
}
|
||||
|
||||
#[interrupt]
|
||||
fn USB() {
|
||||
poll_usb();
|
||||
with_op_lock(|| {
|
||||
poll_usb();
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue