diff --git a/firmware/src/button.rs b/firmware/src/button.rs new file mode 100644 index 0000000..48e4edc --- /dev/null +++ b/firmware/src/button.rs @@ -0,0 +1,90 @@ +use defmt::{info, warn}; +use embedded_midi::{MidiOut, MidiMessage, Channel, Control}; +use midi_types::Value7; +use stm32f0xx_hal::gpio::{Input, Floating, gpiob}; +use stm32f0xx_hal::prelude::*; + +pub struct ButtonHandler { + pub states: [bool; 12], +} + +impl ButtonHandler { + pub fn new() -> Self { + Self { + states: [false; 12], + } + } + + pub fn check_and_send_midi( + &mut self, + button_index: u8, + is_pressed: bool, + midi_tx: &mut MidiOut>, + ) { + let previous_state = &mut self.states[button_index as usize]; + if is_pressed != *previous_state { + *previous_state = is_pressed; + let control = control_from_button(button_index); + + if is_pressed { + info!("Button {} pressed", button_index); + if midi_tx + .write(&MidiMessage::ControlChange( + Channel::C1, + control, + Value7::new(127), + )) + .is_err() + { + warn!("Failed to send MIDI message"); + } + } else { + info!("Button {} released", button_index); + if midi_tx + .write(&MidiMessage::ControlChange( + Channel::C1, + control, + Value7::new(0), + )) + .is_err() + { + warn!("Failed to send MIDI message"); + } + } + } + } + + pub fn process_buttons( + &mut self, + button_1_5: &gpiob::PB13>, + button_6_10: &gpiob::PB11>, + midi_tx: &mut MidiOut>, + i: u8, + ) + { + let new_state = button_1_5.is_high().unwrap_or(false); + self.check_and_send_midi(i, new_state, midi_tx); + + let i = i + 6; + let new_state = button_6_10.is_high().unwrap_or(false); + self.check_and_send_midi(i, new_state, midi_tx); + } +} + +fn control_from_button(button: u8) -> Control { + match button { + 0 => Control::new(20), + 1 => Control::new(21), + 2 => Control::new(22), + 3 => Control::new(23), + 4 => Control::new(24), + 5 => Control::new(31), + 6 => Control::new(25), + 7 => Control::new(26), + 8 => Control::new(27), + 9 => Control::new(28), + 10 => Control::new(29), + 11 => Control::new(30), + _ => Control::new(0), + } +} \ No newline at end of file diff --git a/firmware/src/display.rs b/firmware/src/display.rs new file mode 100644 index 0000000..1b7c18e --- /dev/null +++ b/firmware/src/display.rs @@ -0,0 +1,37 @@ +use crate::{bus::Bus, led_state::LedState}; +use stm32f0xx_hal::{delay::Delay, prelude::*}; + +pub struct DisplayController; + +impl DisplayController { + pub fn run_boot_sequence(bus: &mut Bus, delay: &mut Delay) { + let mut boot_sequence_index = 0; + while let Some(led_state) = LedState::boot_sequence(boot_sequence_index) { + for _ in 0..20 { + for i in 0_u8..6_u8 { + let ic_3_value = led_state.ic_3_value(i as _); + bus.output(10, 1_u8 << i); + bus.output(3, ic_3_value); + delay.delay_us(1000u16); + bus.output(3, 0xFF); + bus.output(11, 4_u8 << i); + } + } + boot_sequence_index += 1; + } + } + + pub fn display_led_state(bus: &mut Bus, led_state: &LedState, delay: &mut Delay) { + for i in 0_u8..6_u8 { + let ic_3_value = led_state.ic_3_value(i as _); + + bus.output(10, 1_u8 << i); + bus.output(3, ic_3_value); + delay.delay_us(20u16); + bus.output(3, 0xFF); + + bus.output(11, 4_u8 << i); + } + } + +} \ No newline at end of file diff --git a/firmware/src/hardware.rs b/firmware/src/hardware.rs new file mode 100644 index 0000000..4c46785 --- /dev/null +++ b/firmware/src/hardware.rs @@ -0,0 +1,64 @@ +use stm32f0xx_hal as hal; +use hal::prelude::*; +use hal::{delay::Delay, serial::Serial, stm32}; +use hal::gpio::{Input, Floating, gpiob}; +use embedded_midi::{MidiOut, MidiIn}; +use crate::bus::Bus; + +pub struct Hardware { + pub delay: Delay, + pub button_1_5: gpiob::PB13>, + pub button_6_10: gpiob::PB11>, + pub bus: Bus, + pub midi_tx: MidiOut>, + pub midi_rx: MidiIn>, +} + +impl Hardware { + pub fn init() -> Self { + let mut dp = stm32::Peripherals::take().unwrap(); + let cp = cortex_m::Peripherals::take().unwrap(); + + let mut rcc = dp + .RCC + .configure() + .hsi48() + .enable_crs(dp.CRS) + .sysclk(48.mhz()) + .pclk(24.mhz()) + .freeze(&mut dp.FLASH); + + let delay = Delay::new(cp.SYST, &rcc); + + let gpioa = dp.GPIOA.split(&mut rcc); + let gpiob = dp.GPIOB.split(&mut rcc); + + let button_1_5 = cortex_m::interrupt::free(|cs| gpiob.pb13.into_floating_input(cs)); + let button_6_10 = cortex_m::interrupt::free(|cs| gpiob.pb11.into_floating_input(cs)); + + let bus = Bus::new( + gpioa.pa0, gpiob.pb8, gpiob.pb0, gpiob.pb1, + gpiob.pb2, gpiob.pb3, gpiob.pb4, gpiob.pb5, + gpiob.pb6, gpiob.pb7 + ); + + let serial = cortex_m::interrupt::free(|cs| { + let tx = gpioa.pa9.into_alternate_af1(cs); + let rx = gpioa.pa10.into_alternate_af1(cs); + Serial::usart1(dp.USART1, (tx, rx), 31_250.bps(), &mut rcc) + }); + + let (tx, rx) = serial.split(); + let midi_tx = MidiOut::new(tx); + let midi_rx = MidiIn::new(rx); + + Self { + delay, + button_1_5, + button_6_10, + bus, + midi_tx, + midi_rx, + } + } +} \ No newline at end of file diff --git a/firmware/src/main.rs b/firmware/src/main.rs index c3553f5..ba0fb94 100755 --- a/firmware/src/main.rs +++ b/firmware/src/main.rs @@ -3,18 +3,20 @@ mod led_state; mod bus; +mod button; +mod midi; +mod hardware; +mod display; use cortex_m_rt::entry; -use defmt::warn; -use hal::prelude::*; -use hal::stm32; use panic_halt as _; -use stm32f0xx_hal as hal; -use defmt::info; use defmt_rtt as _; -use hal::{delay::Delay, serial::Serial}; +use button::ButtonHandler; +use midi::MidiProcessor; +use hardware::Hardware; +use display::DisplayController; defmt::timestamp!("{=u32}", { static mut COUNTER: u32 = 0; @@ -26,244 +28,28 @@ defmt::timestamp!("{=u32}", { #[entry] fn main() -> ! { - // Get device peripherals - let mut dp = stm32::Peripherals::take().unwrap(); - let cp = cortex_m::Peripherals::take().unwrap(); + let mut hardware = Hardware::init(); - // Configure the clock to 48 MHz - let mut rcc = dp - .RCC - .configure() - .hsi48() - .enable_crs(dp.CRS) - .sysclk(48.mhz()) - .pclk(24.mhz()) - .freeze(&mut dp.FLASH); + DisplayController::run_boot_sequence(&mut hardware.bus, &mut hardware.delay); - // Set up delay provider - let mut delay = Delay::new(cp.SYST, &rcc); - - // Configure GPIO - let gpioa = dp.GPIOA.split(&mut rcc); - let gpiob = dp.GPIOB.split(&mut rcc); - - let button_1_5 = cortex_m::interrupt::free(|cs| gpiob.pb13.into_floating_input(cs)); - let button_6_10 = cortex_m::interrupt::free(|cs| gpiob.pb11.into_floating_input(cs)); - - let mut bus = bus::Bus::new(gpioa.pa0, gpiob.pb8, gpiob.pb0, gpiob.pb1, gpiob.pb2, gpiob.pb3, gpiob.pb4, gpiob.pb5, gpiob.pb6, gpiob.pb7); - - // Configure serial port - let serial = cortex_m::interrupt::free(|cs| { - let tx = gpioa.pa9.into_alternate_af1(cs); - let rx = gpioa.pa10.into_alternate_af1(cs); - Serial::usart1(dp.USART1, (tx, rx), 31_250.bps(), &mut rcc) - }); - - let (tx, rx) = serial.split(); - let mut midi_tx = embedded_midi::MidiOut::new(tx); - let mut midi_rx = embedded_midi::MidiIn::new(rx); - - // Startup sequence - let mut boot_sequence_index = 0; - while let Some(led_state) = led_state::LedState::boot_sequence(boot_sequence_index) { - for _ in 0..20 { - for i in 0_u8..6_u8 { - let ic_3_value = led_state.ic_3_value(i as _); - bus.output(10, 1_u8 << i); - bus.output(3, ic_3_value); - delay.delay_us(1000u16); - bus.output(3, 0xFF); - - bus.output(11, 4_u8 << i); - } - } - boot_sequence_index += 1; - } - - // Main loop - let mut button_states = [false; 12]; + let mut button_handler = ButtonHandler::new(); let mut led_state = led_state::LedState::default(); + loop { for _ in 0..20 { + DisplayController::display_led_state(&mut hardware.bus, &led_state, &mut hardware.delay); + for i in 0_u8..6_u8 { - let ic_3_value = led_state.ic_3_value(i as _); - - bus.output(10, 1_u8 << i); - bus.output(3, ic_3_value); - delay.delay_us(20u16); - bus.output(3, 0xFF); - - bus.output(11, 4_u8 << i); - - let previous_state = &mut button_states[i as usize]; - let new_state = button_1_5.is_high().unwrap_or(false); - if new_state != *previous_state { - *previous_state = new_state; - let control = control_from_button(i); - if new_state { - info!("Button {} pressed", i); - if midi_tx - .write(&embedded_midi::MidiMessage::ControlChange( - embedded_midi::Channel::C1, - control, - midi_types::Value7::new(127), - )) - .is_err() - { - warn!("Failed to send MIDI message"); - } - } else { - info!("Button {} released", i); - if midi_tx - .write(&embedded_midi::MidiMessage::ControlChange( - embedded_midi::Channel::C1, - control, - midi_types::Value7::new(0), - )) - .is_err() - { - warn!("Failed to send MIDI message"); - } - } - } - - let i = i + 6; - let previous_state = &mut button_states[i as usize]; - let new_state = button_6_10.is_high().unwrap_or(false); - if new_state != *previous_state { - *previous_state = new_state; - let control = control_from_button(i); - if new_state { - info!("Button {} pressed", i); - if midi_tx - .write(&embedded_midi::MidiMessage::ControlChange( - embedded_midi::Channel::C1, - control, - midi_types::Value7::new(127), - )) - .is_err() - { - warn!("Failed to send MIDI message"); - } - } else { - info!("Button {} released", i); - if midi_tx - .write(&embedded_midi::MidiMessage::ControlChange( - embedded_midi::Channel::C1, - control, - midi_types::Value7::new(0), - )) - .is_err() - { - warn!("Failed to send MIDI message"); - } - } - } - match midi_rx.read() { - Ok(embedded_midi::MidiMessage::ControlChange(channel, control, value)) - if channel == embedded_midi::Channel::C1 => - { - if control == embedded_midi::Control::new(31) { - let value: u8 = value.into(); - led_state.switch_1 = value & 0b0000_0001 != 0; - led_state.switch_2 = value & 0b0000_0010 != 0; - led_state.expression_1 = value & 0b0000_0100 != 0; - led_state.expression_2 = value & 0b0000_1000 != 0; - } else if control == embedded_midi::Control::new(32) { - let value: u8 = value.into(); - led_state.switches = value & 0b0000_0001 != 0; - led_state.select = value & 0b0000_0010 != 0; - led_state.number = value & 0b0000_0100 != 0; - led_state.value_1 = value & 0b0000_1000 != 0; - led_state.value_2 = value & 0b0001_0000 != 0; - } else if control == embedded_midi::Control::new(33) { - let value: u8 = value.into(); - led_state.direct_select = value & 0b0000_0001 != 0; - led_state.midi_function = value & 0b0000_0010 != 0; - led_state.midi_chan = value & 0b0000_0100 != 0; - led_state.config = value & 0b0000_1000 != 0; - } else if control == embedded_midi::Control::new(34) { - let value: u8 = value.into(); - led_state.digit_0.a = value & 0b0000_0001 != 0; - led_state.digit_0.b = value & 0b0000_0010 != 0; - led_state.digit_0.c = value & 0b0000_0100 != 0; - led_state.digit_0.d = value & 0b0000_1000 != 0; - led_state.digit_0.e = value & 0b0001_0000 != 0; - led_state.digit_0.f = value & 0b0010_0000 != 0; - led_state.digit_0.g = value & 0b0100_0000 != 0; - } else if control == embedded_midi::Control::new(35) { - let value: u8 = value.into(); - led_state.digit_1.a = value & 0b0000_0001 != 0; - led_state.digit_1.b = value & 0b0000_0010 != 0; - led_state.digit_1.c = value & 0b0000_0100 != 0; - led_state.digit_1.d = value & 0b0000_1000 != 0; - led_state.digit_1.e = value & 0b0001_0000 != 0; - led_state.digit_1.f = value & 0b0010_0000 != 0; - led_state.digit_1.g = value & 0b0100_0000 != 0; - } else if control == embedded_midi::Control::new(36) { - let value: u8 = value.into(); - led_state.digit_2.a = value & 0b0000_0001 != 0; - led_state.digit_2.b = value & 0b0000_0010 != 0; - led_state.digit_2.c = value & 0b0000_0100 != 0; - led_state.digit_2.d = value & 0b0000_1000 != 0; - led_state.digit_2.e = value & 0b0001_0000 != 0; - led_state.digit_2.f = value & 0b0010_0000 != 0; - led_state.digit_2.g = value & 0b0100_0000 != 0; - } else if control == embedded_midi::Control::new(37) { - let value: u8 = value.into(); - led_state.buttons[0] = value & 0b0000_0001 != 0; - led_state.buttons[1] = value & 0b0000_0010 != 0; - led_state.buttons[2] = value & 0b0000_0100 != 0; - led_state.buttons[3] = value & 0b0000_1000 != 0; - led_state.buttons[4] = value & 0b0001_0000 != 0; - } else if control == embedded_midi::Control::new(38) { - let value: u8 = value.into(); - led_state.buttons[0] = value & 0b0000_0001 != 0; - led_state.buttons[1] = value & 0b0000_0010 != 0; - led_state.buttons[2] = value & 0b0000_0100 != 0; - led_state.buttons[3] = value & 0b0000_1000 != 0; - led_state.buttons[4] = value & 0b0001_0000 != 0; - } - } - Ok(_) => { - info!("unhandled midi message"); - } - Err(nb::Error::WouldBlock) => {} - Err(nb::Error::Other(hal::serial::Error::Framing)) => { - info!("Error::Framing"); - } - Err(nb::Error::Other(hal::serial::Error::Noise)) => { - info!("Error::Noise"); - } - Err(nb::Error::Other(hal::serial::Error::Overrun)) => { - info!("Error::Overrun"); - } - Err(nb::Error::Other(hal::serial::Error::Parity)) => { - info!("Error::Parity"); - } - Err(nb::Error::Other(_)) => { - info!("Error::Other"); - } - } + button_handler.process_buttons( + &hardware.button_1_5, + &hardware.button_6_10, + &mut hardware.midi_tx, + i, + ); } + + MidiProcessor::process_message(&mut hardware.midi_rx, &mut led_state); } } } -fn control_from_button(button: u8) -> embedded_midi::Control { - match button { - 0 => embedded_midi::Control::new(20), - 1 => embedded_midi::Control::new(21), - 2 => embedded_midi::Control::new(22), - 3 => embedded_midi::Control::new(23), - 4 => embedded_midi::Control::new(24), - 5 => embedded_midi::Control::new(31), - 6 => embedded_midi::Control::new(25), - 7 => embedded_midi::Control::new(26), - 8 => embedded_midi::Control::new(27), - 9 => embedded_midi::Control::new(28), - 10 => embedded_midi::Control::new(29), - 11 => embedded_midi::Control::new(30), - _ => embedded_midi::Control::new(0), - } -} \ No newline at end of file diff --git a/firmware/src/midi.rs b/firmware/src/midi.rs new file mode 100644 index 0000000..6e81754 --- /dev/null +++ b/firmware/src/midi.rs @@ -0,0 +1,108 @@ +use defmt::info; +use embedded_midi::{MidiIn, MidiMessage, Channel, Control}; +use crate::led_state::LedState; +use stm32f0xx_hal as hal; + +pub struct MidiProcessor; + +impl MidiProcessor { + pub fn process_message( + midi_rx: &mut MidiIn>, + led_state: &mut LedState, + ) { + match midi_rx.read() { + Ok(MidiMessage::ControlChange(channel, control, value)) + if channel == Channel::C1 => + { + Self::handle_control_change(control, value, led_state); + } + Ok(_) => { + info!("unhandled midi message"); + } + Err(nb::Error::WouldBlock) => {} + Err(nb::Error::Other(hal::serial::Error::Framing)) => { + info!("Error::Framing"); + } + Err(nb::Error::Other(hal::serial::Error::Noise)) => { + info!("Error::Noise"); + } + Err(nb::Error::Other(hal::serial::Error::Overrun)) => { + info!("Error::Overrun"); + } + Err(nb::Error::Other(hal::serial::Error::Parity)) => { + info!("Error::Parity"); + } + Err(nb::Error::Other(_)) => { + info!("Error::Other"); + } + } + } + + fn handle_control_change(control: Control, value: midi_types::Value7, led_state: &mut LedState) { + let value: u8 = value.into(); + + match control.into() { + 31 => { + led_state.switch_1 = value & 0b0000_0001 != 0; + led_state.switch_2 = value & 0b0000_0010 != 0; + led_state.expression_1 = value & 0b0000_0100 != 0; + led_state.expression_2 = value & 0b0000_1000 != 0; + } + 32 => { + led_state.switches = value & 0b0000_0001 != 0; + led_state.select = value & 0b0000_0010 != 0; + led_state.number = value & 0b0000_0100 != 0; + led_state.value_1 = value & 0b0000_1000 != 0; + led_state.value_2 = value & 0b0001_0000 != 0; + } + 33 => { + led_state.direct_select = value & 0b0000_0001 != 0; + led_state.midi_function = value & 0b0000_0010 != 0; + led_state.midi_chan = value & 0b0000_0100 != 0; + led_state.config = value & 0b0000_1000 != 0; + } + 34 => { + led_state.digit_0.a = value & 0b0000_0001 != 0; + led_state.digit_0.b = value & 0b0000_0010 != 0; + led_state.digit_0.c = value & 0b0000_0100 != 0; + led_state.digit_0.d = value & 0b0000_1000 != 0; + led_state.digit_0.e = value & 0b0001_0000 != 0; + led_state.digit_0.f = value & 0b0010_0000 != 0; + led_state.digit_0.g = value & 0b0100_0000 != 0; + } + 35 => { + led_state.digit_1.a = value & 0b0000_0001 != 0; + led_state.digit_1.b = value & 0b0000_0010 != 0; + led_state.digit_1.c = value & 0b0000_0100 != 0; + led_state.digit_1.d = value & 0b0000_1000 != 0; + led_state.digit_1.e = value & 0b0001_0000 != 0; + led_state.digit_1.f = value & 0b0010_0000 != 0; + led_state.digit_1.g = value & 0b0100_0000 != 0; + } + 36 => { + led_state.digit_2.a = value & 0b0000_0001 != 0; + led_state.digit_2.b = value & 0b0000_0010 != 0; + led_state.digit_2.c = value & 0b0000_0100 != 0; + led_state.digit_2.d = value & 0b0000_1000 != 0; + led_state.digit_2.e = value & 0b0001_0000 != 0; + led_state.digit_2.f = value & 0b0010_0000 != 0; + led_state.digit_2.g = value & 0b0100_0000 != 0; + } + 37 => { + led_state.buttons[0] = value & 0b0000_0001 != 0; + led_state.buttons[1] = value & 0b0000_0010 != 0; + led_state.buttons[2] = value & 0b0000_0100 != 0; + led_state.buttons[3] = value & 0b0000_1000 != 0; + led_state.buttons[4] = value & 0b0001_0000 != 0; + } + 38 => { + led_state.buttons[0] = value & 0b0000_0001 != 0; + led_state.buttons[1] = value & 0b0000_0010 != 0; + led_state.buttons[2] = value & 0b0000_0100 != 0; + led_state.buttons[3] = value & 0b0000_1000 != 0; + led_state.buttons[4] = value & 0b0001_0000 != 0; + } + _ => {} + } + } +} \ No newline at end of file