|
|
|
@ -1,3 +1,10 @@ |
|
|
|
|
use crate::{LayerSelect, Sequencer}; |
|
|
|
|
use embedded_hal::digital::v2::{InputPin, OutputPin}; |
|
|
|
|
use stm32f1xx_hal::gpio::{ |
|
|
|
|
gpioa::{PA1, PA2, PA3, PA4}, |
|
|
|
|
gpiob::{PB0, PB1, PB5, PB6, PB7, PB8}, |
|
|
|
|
Input, Output, PullDown, PushPull, |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
/// An I/O abstraction for yesman
|
|
|
|
|
///
|
|
|
|
@ -6,6 +13,74 @@ |
|
|
|
|
/// the same time. An "active step" can be fed into the `Io`
|
|
|
|
|
/// abstraction from the `Sequencer` module.
|
|
|
|
|
pub struct Io { |
|
|
|
|
// LED output states
|
|
|
|
|
pub led1: PA1<Output<PushPull>>, |
|
|
|
|
pub led2: PA2<Output<PushPull>>, |
|
|
|
|
pub led3: PA3<Output<PushPull>>, |
|
|
|
|
pub led4: PA4<Output<PushPull>>, |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Button input states
|
|
|
|
|
pub btn1: PB0<Input<PullDown>>, |
|
|
|
|
pub btn1_last: bool, |
|
|
|
|
|
|
|
|
|
pub btn2: PB1<Input<PullDown>>, |
|
|
|
|
pub btn2_last: bool, |
|
|
|
|
|
|
|
|
|
pub btn3: PB5<Input<PullDown>>, |
|
|
|
|
pub btn3_last: bool, |
|
|
|
|
|
|
|
|
|
pub btn4: PB6<Input<PullDown>>, |
|
|
|
|
pub btn4_last: bool, |
|
|
|
|
|
|
|
|
|
// Eurorack signal I/O
|
|
|
|
|
pub clk: PB7<Input<PullDown>>, |
|
|
|
|
pub clk_last: bool, |
|
|
|
|
pub gate: PB8<Output<PushPull>>, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
impl Io { |
|
|
|
|
/// Check the state of each pin and update the sequencer accordingly
|
|
|
|
|
pub fn update_sequence(&mut self, seq: &mut Sequencer) { |
|
|
|
|
// Update sequencer states based on button inputs
|
|
|
|
|
btn_rising_edge(&mut self.btn1_last, &mut self.btn1, || seq.toggle(0)); |
|
|
|
|
btn_rising_edge(&mut self.btn2_last, &mut self.btn2, || seq.toggle(1)); |
|
|
|
|
btn_rising_edge(&mut self.btn3_last, &mut self.btn3, || seq.toggle(2)); |
|
|
|
|
btn_rising_edge(&mut self.btn4_last, &mut self.btn4, || seq.toggle(3)); |
|
|
|
|
|
|
|
|
|
// Update LED states based on sequencer
|
|
|
|
|
let l = seq.layer(LayerSelect::A); |
|
|
|
|
set_output(&mut self.led1, l[0]); |
|
|
|
|
set_output(&mut self.led2, l[1]); |
|
|
|
|
set_output(&mut self.led3, l[2]); |
|
|
|
|
set_output(&mut self.led4, l[3]); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
pub fn update_cv(&mut self, seq: &mut Sequencer) { |
|
|
|
|
// Always output the current steps - if no clock is coming in this will change nothing in our output
|
|
|
|
|
set_output(&mut self.gate, seq.get().0); |
|
|
|
|
|
|
|
|
|
// On a rising clock edge, step the sequencer
|
|
|
|
|
btn_rising_edge(&mut self.clk_last, &mut self.clk, || seq.step()); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
/// If the button is pressed _and_ it was previously not pressed,
|
|
|
|
|
/// run the given closure and update the button state to avoid
|
|
|
|
|
/// running it again
|
|
|
|
|
fn btn_rising_edge<I: InputPin, F: FnMut()>(last: &mut bool, i: &mut I, mut f: F) { |
|
|
|
|
if let Ok(true) = i.is_high() { |
|
|
|
|
if !*last { |
|
|
|
|
f(); |
|
|
|
|
*last = true; |
|
|
|
|
} |
|
|
|
|
} else if let Ok(false) = i.is_high() { |
|
|
|
|
*last = false; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fn set_output<O: OutputPin>(o: &mut O, s: bool) { |
|
|
|
|
if s { |
|
|
|
|
o.set_high().ok(); |
|
|
|
|
} else { |
|
|
|
|
o.set_low().ok(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|