182 lines
4.5 KiB
Rust
182 lines
4.5 KiB
Rust
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)
|
|
}
|
|
}
|