My personal project and infrastructure archive
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
nomicon/hardware/devices/euro-yesman-1u/src/io.rs

102 lines
3.4 KiB

use crate::{LayerSelect, Sequencer};
use embedded_hal::{
digital::v2::{InputPin, OutputPin},
prelude::*,
};
use stm32f1xx_hal::{
delay::Delay,
gpio::{
gpioa::{PA1, PA2, PA3, PA4},
gpiob::{PB0, PB1, PB5, PB6, PB7, PB8},
Input, Output, PullDown, PushPull,
},
};
/// An I/O abstraction for yesman
///
/// The main I/O of the yesman module is buttons as inputs that need
/// to be polled, and LEDs as outputs. All LED states are written at
/// 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 {
pub fn startup(&mut self, _: &mut Delay) {
set_output(&mut self.led1, true);
set_output(&mut self.led2, true);
set_output(&mut self.led3, true);
set_output(&mut self.led4, true);
// TODO: silly start-up animation here
}
/// 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, _: &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;
}
}
/// Set the output of a PIN. For LEDs this means `true` is off,
/// `false` is on.
///
/// This is because LEDs are wired from 3V3 to the output, which turns
/// a low pin into a drain, and thus power can flow. This is done to
/// reduce load on the stm32 when a lot of LEDs are active at once.
fn set_output<O: OutputPin>(o: &mut O, s: bool) {
if s {
o.set_high().ok();
} else {
o.set_low().ok();
}
}