Refactored firmware

This commit is contained in:
geens 2025-06-29 21:03:09 +02:00
parent 05d780080f
commit 5876e286c6
5 changed files with 321 additions and 236 deletions

90
firmware/src/button.rs Normal file
View File

@ -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<stm32f0xx_hal::serial::Tx<stm32f0xx_hal::stm32::USART1>>,
) {
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<Input<Floating>>,
button_6_10: &gpiob::PB11<Input<Floating>>,
midi_tx: &mut MidiOut<stm32f0xx_hal::serial::Tx<stm32f0xx_hal::stm32::USART1>>,
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),
}
}

37
firmware/src/display.rs Normal file
View File

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

64
firmware/src/hardware.rs Normal file
View File

@ -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<Input<Floating>>,
pub button_6_10: gpiob::PB11<Input<Floating>>,
pub bus: Bus,
pub midi_tx: MidiOut<hal::serial::Tx<stm32::USART1>>,
pub midi_rx: MidiIn<hal::serial::Rx<stm32::USART1>>,
}
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,
}
}
}

View File

@ -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");
}
}
button_handler.process_buttons(
&hardware.button_1_5,
&hardware.button_6_10,
&mut hardware.midi_tx,
i,
);
}
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");
}
}
}
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),
}
}

108
firmware/src/midi.rs Normal file
View File

@ -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<hal::serial::Rx<hal::stm32::USART1>>,
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;
}
_ => {}
}
}
}