midi leds

This commit is contained in:
geens 2025-06-29 20:39:35 +02:00
parent 72115f3783
commit 05d780080f
3 changed files with 430 additions and 131 deletions

80
firmware/src/bus.rs Executable file
View File

@ -0,0 +1,80 @@
use hal::prelude::*;
use panic_halt as _;
use stm32f0xx_hal as hal;
pub struct Bus {
latch_en: hal::gpio::gpioa::PA0<hal::gpio::Output<hal::gpio::PushPull>>,
select_en: hal::gpio::gpiob::PB8<hal::gpio::Output<hal::gpio::OpenDrain>>,
pb0: hal::gpio::gpiob::PB0<hal::gpio::Output<hal::gpio::PushPull>>,
pb1: hal::gpio::gpiob::PB1<hal::gpio::Output<hal::gpio::PushPull>>,
pb2: hal::gpio::gpiob::PB2<hal::gpio::Output<hal::gpio::PushPull>>,
pb3: hal::gpio::gpiob::PB3<hal::gpio::Output<hal::gpio::PushPull>>,
pb4: hal::gpio::gpiob::PB4<hal::gpio::Output<hal::gpio::PushPull>>,
pb5: hal::gpio::gpiob::PB5<hal::gpio::Output<hal::gpio::PushPull>>,
pb6: hal::gpio::gpiob::PB6<hal::gpio::Output<hal::gpio::PushPull>>,
pb7: hal::gpio::gpiob::PB7<hal::gpio::Output<hal::gpio::PushPull>>,
}
impl Bus {
pub fn new(
latch_en: hal::gpio::gpioa::PA0<hal::gpio::Input<hal::gpio::Floating>>,
select_en: hal::gpio::gpiob::PB8<hal::gpio::Input<hal::gpio::Floating>>,
pb0: hal::gpio::gpiob::PB0<hal::gpio::Input<hal::gpio::Floating>>,
pb1: hal::gpio::gpiob::PB1<hal::gpio::Input<hal::gpio::Floating>>,
pb2: hal::gpio::gpiob::PB2<hal::gpio::Input<hal::gpio::Floating>>,
pb3: hal::gpio::gpiob::PB3<hal::gpio::Input<hal::gpio::Floating>>,
pb4: hal::gpio::gpiob::PB4<hal::gpio::Input<hal::gpio::Floating>>,
pb5: hal::gpio::gpiob::PB5<hal::gpio::Input<hal::gpio::Floating>>,
pb6: hal::gpio::gpiob::PB6<hal::gpio::Input<hal::gpio::Floating>>,
pb7: hal::gpio::gpiob::PB7<hal::gpio::Input<hal::gpio::Floating>>,
) -> Self {
cortex_m::interrupt::free(|cs| {
Self {
latch_en: latch_en.into_push_pull_output(cs),
select_en: select_en.into_open_drain_output(cs),
pb0: pb0.into_push_pull_output(cs),
pb1: pb1.into_push_pull_output(cs),
pb2: pb2.into_push_pull_output(cs),
pb3: pb3.into_push_pull_output(cs),
pb4: pb4.into_push_pull_output(cs),
pb5: pb5.into_push_pull_output(cs),
pb6: pb6.into_push_pull_output(cs),
pb7: pb7.into_push_pull_output(cs),
}
})
}
pub fn output(&mut self, ic: u8, data: u8) {
match ic {
0 | 3 => self.select(1),
1 | 10 => self.select(0),
2 | 11 => self.select(5),
_ => self.select(2),
}
self.select_en(true);
self.write(data);
self.select_en(false);
}
fn write(&mut self, data: u8) {
self.pb0.set_state((data & 0b_0000_0001 != 0).into()).ok();
self.pb1.set_state((data & 0b_0000_0010 != 0).into()).ok();
self.pb2.set_state((data & 0b_0000_0100 != 0).into()).ok();
self.pb3.set_state((data & 0b_0000_1000 != 0).into()).ok();
self.pb4.set_state((data & 0b_0001_0000 != 0).into()).ok();
self.pb5.set_state((data & 0b_0010_0000 != 0).into()).ok();
self.pb6.set_state((data & 0b_0100_0000 != 0).into()).ok();
self.pb7.set_state((data & 0b_1000_0000 != 0).into()).ok();
}
fn select_en(&mut self, enable: bool) {
self.select_en.set_state(enable.into()).ok();
}
fn select(&mut self, line: u8) {
self.write(line);
self.latch_en.set_high().ok();
self.latch_en.set_low().ok();
}
}

171
firmware/src/led_state.rs Normal file
View File

@ -0,0 +1,171 @@
#[derive(Default, Clone)]
pub struct LedState {
pub switch_1: bool,
pub switch_2: bool,
pub switches: bool,
pub select: bool,
pub number: bool,
pub value_1: bool,
pub value_2: bool,
pub direct_select: bool,
pub midi_function: bool,
pub midi_chan: bool,
pub config: bool,
pub expression_1: bool,
pub expression_2: bool,
pub buttons: [bool; 10],
pub digit_0: SevenSegmentDisplay,
pub digit_1: SevenSegmentDisplay,
pub digit_2: SevenSegmentDisplay,
}
#[derive(Default, Clone)]
pub struct SevenSegmentDisplay {
pub a: bool,
pub b: bool,
pub c: bool,
pub d: bool,
pub e: bool,
pub f: bool,
pub g: bool,
pub dp: bool,
}
impl LedState {
pub fn boot_sequence(i: usize) -> Option<LedState> {
let mut led_state = LedState::default();
match i {
0 => led_state.switch_1 = true,
1 => led_state.switch_2 = true,
2 => led_state.switches = true,
3 => led_state.select = true,
4 => led_state.number = true,
5 => led_state.value_1 = true,
6 => led_state.value_2 = true,
7 => led_state.direct_select = true,
8 => led_state.midi_function = true,
9 => led_state.midi_chan = true,
10 => led_state.config = true,
11 => led_state.digit_0.a = true,
12 => led_state.digit_0.b = true,
13 => led_state.digit_0.c = true,
14 => led_state.digit_0.g = true,
15 => led_state.digit_0.dp = true,
16 => led_state.digit_1.a = true,
17 => led_state.digit_1.b = true,
18 => led_state.digit_1.c = true,
19 => led_state.digit_1.d = true,
20 => led_state.digit_1.e = true,
21 => led_state.digit_1.f = true,
22 => led_state.digit_1.g = true,
23 => led_state.digit_1.dp = true,
24 => led_state.digit_2.a = true,
25 => led_state.digit_2.b = true,
26 => led_state.digit_2.c = true,
27 => led_state.digit_2.d = true,
28 => led_state.digit_2.e = true,
29 => led_state.digit_2.f = true,
30 => led_state.digit_2.g = true,
31 => led_state.digit_2.dp = true,
32 => led_state.expression_1 = true,
33 => led_state.expression_2 = true,
34 => led_state.buttons[0] = true,
35 => led_state.buttons[1] = true,
36 => led_state.buttons[2] = true,
37 => led_state.buttons[3] = true,
38 => led_state.buttons[4] = true,
39 => led_state.buttons[5] = true,
40 => led_state.buttons[6] = true,
41 => led_state.buttons[7] = true,
42 => led_state.buttons[8] = true,
43 => led_state.buttons[9] = true,
_ => return None,
}
Some(led_state)
}
pub fn ic_3_value(&self, index: usize) -> u8 {
let bits = match index {
0 => {
[
self.digit_2.dp,
self.digit_2.g,
self.digit_2.f,
self.digit_2.e,
self.digit_2.d,
self.digit_2.c,
self.digit_2.b,
self.digit_2.a,
]
}
1 => {
[
self.digit_1.dp,
self.digit_1.g,
self.digit_1.f,
self.digit_1.e,
self.digit_1.d,
self.digit_1.c,
self.digit_1.b,
self.digit_1.a,
]
}
2 => {
[
self.digit_0.dp,
self.digit_0.g,
self.digit_0.f,
self.digit_0.e,
self.digit_0.d,
self.digit_0.c,
self.digit_0.b,
self.digit_0.a,
]
}
3 => {
[
self.midi_function,
self.midi_chan,
self.expression_2,
self.expression_1,
self.switch_1,
self.switch_2,
self.buttons[4],
self.buttons[3],
]
}
4 => {
[
self.buttons[2],
self.buttons[1],
self.buttons[0],
self.buttons[9],
self.buttons[8],
self.buttons[7],
self.buttons[6],
self.buttons[5],
]
}
5 => {
[
self.direct_select,
self.value_2,
self.value_1,
self.number,
self.select,
self.switches,
self.config,
false,
]
}
_ => return 0xFF,
};
let mut value = 0;
for (i, bit) in bits.iter().enumerate() {
if !*bit {
value |= 1 << i;
}
}
value
}
}

View File

@ -1,6 +1,9 @@
#![no_std]
#![no_main]
mod led_state;
mod bus;
use cortex_m_rt::entry;
use defmt::warn;
use hal::prelude::*;
@ -21,19 +24,6 @@ defmt::timestamp!("{=u32}", {
}
});
struct Bus {
latch_en: hal::gpio::gpioa::PA0<hal::gpio::Output<hal::gpio::PushPull>>,
select_en: hal::gpio::gpiob::PB8<hal::gpio::Output<hal::gpio::OpenDrain>>,
pb0: hal::gpio::gpiob::PB0<hal::gpio::Output<hal::gpio::PushPull>>,
pb1: hal::gpio::gpiob::PB1<hal::gpio::Output<hal::gpio::PushPull>>,
pb2: hal::gpio::gpiob::PB2<hal::gpio::Output<hal::gpio::PushPull>>,
pb3: hal::gpio::gpiob::PB3<hal::gpio::Output<hal::gpio::PushPull>>,
pb4: hal::gpio::gpiob::PB4<hal::gpio::Output<hal::gpio::PushPull>>,
pb5: hal::gpio::gpiob::PB5<hal::gpio::Output<hal::gpio::PushPull>>,
pb6: hal::gpio::gpiob::PB6<hal::gpio::Output<hal::gpio::PushPull>>,
pb7: hal::gpio::gpiob::PB7<hal::gpio::Output<hal::gpio::PushPull>>,
}
#[entry]
fn main() -> ! {
// Get device peripherals
@ -60,18 +50,7 @@ fn main() -> ! {
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 = cortex_m::interrupt::free(|cs| Bus {
latch_en: gpioa.pa0.into_push_pull_output(cs),
select_en: gpiob.pb8.into_open_drain_output(cs),
pb0: gpiob.pb0.into_push_pull_output(cs),
pb1: gpiob.pb1.into_push_pull_output(cs),
pb2: gpiob.pb2.into_push_pull_output(cs),
pb3: gpiob.pb3.into_push_pull_output(cs),
pb4: gpiob.pb4.into_push_pull_output(cs),
pb5: gpiob.pb5.into_push_pull_output(cs),
pb6: gpiob.pb6.into_push_pull_output(cs),
pb7: gpiob.pb7.into_push_pull_output(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| {
@ -80,80 +59,190 @@ fn main() -> ! {
Serial::usart1(dp.USART1, (tx, rx), 31_250.bps(), &mut rcc)
});
let (tx, _rx) = serial.split();
let mut midi = embedded_midi::MidiOut::new(tx);
let (tx, rx) = serial.split();
let mut midi_tx = embedded_midi::MidiOut::new(tx);
let mut midi_rx = embedded_midi::MidiIn::new(rx);
bus.output(3, 0xFF, &mut delay);
bus.output(10, 0xFF, &mut delay);
bus.output(11, 0xFF, &mut delay);
// 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 led_state = led_state::LedState::default();
loop {
for i in 0_u8..6_u8 {
bus.output(11, 4_u8 << i, &mut delay);
delay.delay_us(1u16);
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
.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
.write(&embedded_midi::MidiMessage::ControlChange(
embedded_midi::Channel::C1,
control,
midi_types::Value7::new(0),
))
.is_err()
{
warn!("Failed to send MIDI message");
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(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
.write(&embedded_midi::MidiMessage::ControlChange(
embedded_midi::Channel::C1,
control,
midi_types::Value7::new(127),
))
.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");
}
}
} else {
info!("Button {} released", i);
if midi
.write(&embedded_midi::MidiMessage::ControlChange(
embedded_midi::Channel::C1,
control,
midi_types::Value7::new(0),
))
.is_err()
}
match midi_rx.read() {
Ok(embedded_midi::MidiMessage::ControlChange(channel, control, value))
if channel == embedded_midi::Channel::C1 =>
{
warn!("Failed to send MIDI message");
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");
}
}
}
@ -178,44 +267,3 @@ fn control_from_button(button: u8) -> embedded_midi::Control {
_ => embedded_midi::Control::new(0),
}
}
impl Bus {
fn output(&mut self, ic: u8, data: u8, delay: &mut Delay) {
match ic {
0 | 3 => self.select(1, delay),
1 | 10 => self.select(0, delay),
2 | 11 => self.select(5, delay),
_ => self.select(2, delay),
}
delay.delay_us(1u16);
self.select_en(true);
delay.delay_us(1u16);
self.write(data);
delay.delay_us(1u16);
self.select_en(false);
}
fn write(&mut self, data: u8) {
self.pb0.set_state((data & 0b_0000_0001 != 0).into()).ok();
self.pb1.set_state((data & 0b_0000_0010 != 0).into()).ok();
self.pb2.set_state((data & 0b_0000_0100 != 0).into()).ok();
self.pb3.set_state((data & 0b_0000_1000 != 0).into()).ok();
self.pb4.set_state((data & 0b_0001_0000 != 0).into()).ok();
self.pb5.set_state((data & 0b_0010_0000 != 0).into()).ok();
self.pb6.set_state((data & 0b_0100_0000 != 0).into()).ok();
self.pb7.set_state((data & 0b_1000_0000 != 0).into()).ok();
}
fn select_en(&mut self, enable: bool) {
self.select_en.set_state(enable.into()).ok();
}
fn select(&mut self, line: u8, delay: &mut Delay) {
self.write(line);
delay.delay_us(1u16);
self.latch_en.set_high().ok();
delay.delay_us(1u16);
self.latch_en.set_low().ok();
}
}