euro-yesman-1u: implement 4-step sequencer logic

wip/yesman
Katharina Fey 3 years ago
parent 652e52ebd4
commit 475433d4fe
Signed by: kookie
GPG Key ID: 90734A9E619C8A6C
  1. 77
      hardware/devices/euro-yesman-1u/src/io.rs
  2. 51
      hardware/devices/euro-yesman-1u/src/main.rs
  3. 45
      hardware/devices/euro-yesman-1u/src/seq.rs

@ -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();
}
}

@ -9,6 +9,8 @@ use stm32f1xx_hal::{delay::Delay, pac, prelude::*};
#[allow(unused_imports)]
use panic_halt;
mod io;
mod seq;
pub use seq::{LayerSelect, SeqLayer, Sequencer};
@ -32,8 +34,33 @@ fn main() -> ! {
// Create a button input
let btn1 = gpiob.pb0.into_pull_down_input(&mut gpiob.crl);
let btn2 = gpiob.pb1.into_pull_down_input(&mut gpiob.crl);
let btn3 = gpiob.pb2.into_pull_down_input(&mut gpiob.crl);
let btn4 = gpiob.pb3.into_pull_down_input(&mut gpiob.crl);
let btn3 = gpiob.pb5.into_pull_down_input(&mut gpiob.crl);
let btn4 = gpiob.pb6.into_pull_down_input(&mut gpiob.crl);
// Eurorack clock input/ gate output
let clk = gpiob.pb7.into_pull_down_input(&mut gpiob.crl);
let gate = gpiob.pb8.into_push_pull_output(&mut gpiob.crh);
// Setup the I/O states
let mut io = io::Io {
led1,
led2,
led3,
led4,
btn1,
btn2,
btn3,
btn4,
btn1_last: false,
btn2_last: false,
btn3_last: false,
btn4_last: false,
clk,
clk_last: false,
gate,
};
let mut seq = Sequencer::default();
// Initialize the delay object with the MCU frequency
let mut flash = dp.FLASH.constrain();
@ -45,22 +72,14 @@ fn main() -> ! {
.freeze(&mut flash.acr);
let mut delay = Delay::new(cp.SYST, clocks);
// Now go blinkenlights
loop {
set_led(&btn1, &mut led1);
set_led(&btn2, &mut led2);
set_led(&btn3, &mut led3);
set_led(&btn4, &mut led4);
// Update sequencer state based on inputs
io.update_sequence(&mut seq);
delay.delay_ms(5_u16);
}
}
// Write out gate CV
io.update_cv(&mut seq);
fn set_led<'k, I: InputPin, O: OutputPin>(i: &'k I, o: &'k mut O) {
if let Ok(true) = i.is_high() {
o.set_low().ok();
} else {
o.set_high().ok();
// Wait 5ms until we do it again
delay.delay_ms(5_u16);
}
}

@ -1,11 +1,22 @@
const SEQ_LEN: usize = 4;
/// Represent a single layer on the sequencer
pub struct SeqLayer {
/// List of steps that are either on or off
steps: [bool; 16],
steps: [bool; SEQ_LEN],
/// The length of the sequence to loop
length: u8,
}
impl Default for SeqLayer {
fn default() -> Self {
Self {
steps: [true; SEQ_LEN],
length: SEQ_LEN as u8,
}
}
}
impl SeqLayer {
/// Get the step by index, wrapping around if the index is beyond the length
fn step(&self, idx: u8) -> bool {
@ -16,6 +27,10 @@ impl SeqLayer {
fn set(&mut self, idx: usize, val: bool) {
self.steps[idx] = val;
}
fn toggle(&mut self, idx: usize) {
self.steps[idx] = !self.steps[idx];
}
}
/// Indicate which layer is selected
@ -26,7 +41,14 @@ pub enum LayerSelect {
D,
}
impl Default for LayerSelect {
fn default() -> Self {
Self::A
}
}
/// Main sequencer state structure
#[derive(Default)]
pub struct Sequencer {
a: SeqLayer,
b: SeqLayer,
@ -37,7 +59,7 @@ pub struct Sequencer {
}
impl Sequencer {
/// Step to the next step, wrapping around at 16
/// Step to the next step, wrapping around at SEQ_LEN
pub fn step(&mut self) {
self.active_step %= self.active_step;
}
@ -63,4 +85,23 @@ impl Sequencer {
LayerSelect::D => self.d.set(idx, val),
}
}
pub fn toggle(&mut self, idx: usize) {
assert!(idx <= 16);
match self.active_layer {
LayerSelect::A => self.a.toggle(idx),
LayerSelect::B => self.b.toggle(idx),
LayerSelect::C => self.c.toggle(idx),
LayerSelect::D => self.d.toggle(idx),
}
}
pub fn layer(&self, sel: LayerSelect) -> &[bool; SEQ_LEN] {
match sel {
LayerSelect::A => &self.a.steps,
LayerSelect::B => &self.b.steps,
LayerSelect::C => &self.c.steps,
LayerSelect::D => &self.d.steps,
}
}
}

Loading…
Cancel
Save