wip adc
This commit is contained in:
241
firmware/src/adc.rs
Normal file
241
firmware/src/adc.rs
Normal file
@@ -0,0 +1,241 @@
|
||||
use stm32f0xx_hal as hal;
|
||||
use hal::prelude::*;
|
||||
use hal::spi::{Spi, Mode, Phase, Polarity, EightBit};
|
||||
use hal::gpio::{Output, PushPull, Alternate, AF0};
|
||||
use embedded_hal::blocking::spi::Transfer;
|
||||
use defmt::*;
|
||||
|
||||
pub const PEDAL_A_MIN: u8 = 10;
|
||||
pub const PEDAL_A_MAX: u8 = 245;
|
||||
pub const PEDAL_B_MIN: u8 = 10;
|
||||
pub const PEDAL_B_MAX: u8 = 245;
|
||||
|
||||
const IIR_ALPHA: u16 = 51;
|
||||
|
||||
pub struct Tlc0832 {
|
||||
spi: Spi<hal::stm32::SPI2,
|
||||
hal::gpio::gpiob::PB10<Alternate<AF0>>,
|
||||
hal::gpio::gpiob::PB14<Alternate<AF0>>,
|
||||
hal::gpio::gpiob::PB15<Alternate<AF0>>,
|
||||
EightBit>,
|
||||
cs: hal::gpio::gpiob::PB12<Output<PushPull>>,
|
||||
pedal_a_filtered: u16,
|
||||
pedal_b_filtered: u16,
|
||||
last_midi_a: u8,
|
||||
last_midi_b: u8,
|
||||
}
|
||||
|
||||
impl Tlc0832 {
|
||||
pub fn new(
|
||||
spi2: hal::stm32::SPI2,
|
||||
sck: hal::gpio::gpiob::PB10<hal::gpio::Input<hal::gpio::Floating>>,
|
||||
miso: hal::gpio::gpiob::PB14<hal::gpio::Input<hal::gpio::Floating>>,
|
||||
mosi: hal::gpio::gpiob::PB15<hal::gpio::Input<hal::gpio::Floating>>,
|
||||
cs: hal::gpio::gpiob::PB12<hal::gpio::Input<hal::gpio::Floating>>,
|
||||
rcc: &mut hal::rcc::Rcc,
|
||||
) -> Self {
|
||||
cortex_m::interrupt::free(|critical_section| {
|
||||
let sck = sck.into_alternate_af0(critical_section);
|
||||
let miso = miso.into_alternate_af0(critical_section);
|
||||
let mosi = mosi.into_alternate_af0(critical_section);
|
||||
let cs = cs.into_push_pull_output(critical_section);
|
||||
|
||||
let spi_mode = Mode {
|
||||
polarity: Polarity::IdleLow,
|
||||
phase: Phase::CaptureOnFirstTransition,
|
||||
};
|
||||
|
||||
let spi = Spi::spi2(
|
||||
spi2,
|
||||
(sck, miso, mosi),
|
||||
spi_mode,
|
||||
100.khz(),
|
||||
rcc,
|
||||
);
|
||||
|
||||
let adc = Self {
|
||||
spi,
|
||||
cs,
|
||||
pedal_a_filtered: 128 << 8,
|
||||
pedal_b_filtered: 128 << 8,
|
||||
last_midi_a: 64,
|
||||
last_midi_b: 64,
|
||||
};
|
||||
|
||||
// Small delay to ensure SPI peripheral is ready
|
||||
for _ in 0..1000 {
|
||||
cortex_m::asm::nop();
|
||||
}
|
||||
|
||||
adc
|
||||
})
|
||||
}
|
||||
|
||||
fn test_spi_loopback(&mut self, channel: u8) -> Result<u8, &'static str> {
|
||||
info!("SPI Loopback Test - Channel {}", channel);
|
||||
|
||||
// Test pattern: send different values and check if we get them back
|
||||
let test_values = [0x55, 0xAA, 0x12, 0x34];
|
||||
let test_byte = test_values[channel as usize % test_values.len()];
|
||||
|
||||
unsafe {
|
||||
let spi2_base = 0x4000_3800 as *mut u32;
|
||||
let dr_offset = 0x0C / 4; // Data register offset
|
||||
let sr_offset = 0x08 / 4; // Status register offset
|
||||
|
||||
let dr_ptr = spi2_base.add(dr_offset);
|
||||
let sr_ptr = spi2_base.add(sr_offset);
|
||||
|
||||
info!("Sending test byte: 0x{:02x}", test_byte);
|
||||
|
||||
// Send test byte
|
||||
dr_ptr.write_volatile(test_byte as u32);
|
||||
|
||||
// Wait for TXE (transmit buffer empty) with timeout
|
||||
let mut timeout = 1000;
|
||||
while (sr_ptr.read_volatile() & (1 << 1)) == 0 {
|
||||
timeout -= 1;
|
||||
if timeout == 0 {
|
||||
error!("Loopback timeout waiting for TXE");
|
||||
return Err("SPI loopback timeout");
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for RXNE (receive buffer not empty)
|
||||
timeout = 1000;
|
||||
while (sr_ptr.read_volatile() & (1 << 0)) == 0 {
|
||||
timeout -= 1;
|
||||
if timeout == 0 {
|
||||
error!("Loopback timeout waiting for RXNE");
|
||||
return Err("SPI loopback timeout");
|
||||
}
|
||||
}
|
||||
|
||||
let received = dr_ptr.read_volatile() as u8;
|
||||
info!("Received: 0x{:02x}, Expected: 0x{:02x}", received, test_byte);
|
||||
|
||||
if received == test_byte {
|
||||
info!("✓ SPI Loopback SUCCESS!");
|
||||
Ok(received)
|
||||
} else {
|
||||
error!("✗ SPI Loopback FAILED - mismatch");
|
||||
Err("SPI loopback mismatch")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_channel(&mut self, channel: u8) -> Result<u8, &'static str> {
|
||||
if channel > 1 {
|
||||
return Err("Invalid channel");
|
||||
}
|
||||
|
||||
self.cs.set_high().ok();
|
||||
// Small delay before starting transaction
|
||||
for _ in 0..100 {
|
||||
cortex_m::asm::nop();
|
||||
}
|
||||
|
||||
self.cs.set_low().ok();
|
||||
// Small delay after CS low
|
||||
for _ in 0..100 {
|
||||
cortex_m::asm::nop();
|
||||
}
|
||||
|
||||
let start_bit = 1u8 << 7;
|
||||
let channel_bit = if channel == 0 { 0x00 } else { 1u8 << 6 };
|
||||
let command = start_bit | channel_bit;
|
||||
|
||||
info!("Reading channel {} with command 0x{:02x}", channel, command);
|
||||
|
||||
// Use HAL SPI transfer - much simpler and more reliable
|
||||
let mut data = [command, 0x00, 0x00]; // TLC0832 needs 3 bytes
|
||||
let result = match self.spi.transfer(&mut data) {
|
||||
Ok(response) => {
|
||||
info!("SPI transfer successful: [{:02x}, {:02x}, {:02x}]",
|
||||
response[0], response[1], response[2]);
|
||||
// TLC0832 returns data in the 3rd byte for 8-bit mode
|
||||
response[2]
|
||||
},
|
||||
Err(_) => {
|
||||
self.cs.set_high().ok();
|
||||
error!("SPI transfer error");
|
||||
return Err("SPI transfer error");
|
||||
}
|
||||
};
|
||||
|
||||
// Small delay before CS high
|
||||
for _ in 0..100 {
|
||||
cortex_m::asm::nop();
|
||||
}
|
||||
self.cs.set_high().ok();
|
||||
info!("Read channel {} = {}", channel, result);
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
fn apply_iir_filter(&mut self, channel: u8, new_value: u8) {
|
||||
let new_value_scaled = (new_value as u16) << 8;
|
||||
|
||||
match channel {
|
||||
0 => {
|
||||
self.pedal_a_filtered = self.pedal_a_filtered - (self.pedal_a_filtered >> 8) * IIR_ALPHA / 256 + new_value_scaled * IIR_ALPHA / 256;
|
||||
},
|
||||
1 => {
|
||||
self.pedal_b_filtered = self.pedal_b_filtered - (self.pedal_b_filtered >> 8) * IIR_ALPHA / 256 + new_value_scaled * IIR_ALPHA / 256;
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn adc_to_midi(&self, adc_value: u8, min_adc: u8, max_adc: u8) -> u8 {
|
||||
if adc_value <= min_adc {
|
||||
return 0;
|
||||
}
|
||||
if adc_value >= max_adc {
|
||||
return 127;
|
||||
}
|
||||
|
||||
let range = max_adc - min_adc;
|
||||
let scaled = ((adc_value - min_adc) as u16 * 127) / range as u16;
|
||||
scaled.min(127) as u8
|
||||
}
|
||||
|
||||
pub fn update_and_get_midi_changes(&mut self) -> (Option<u8>, Option<u8>) {
|
||||
info!("Updating and getting MIDI changes");
|
||||
let mut midi_a_change = None;
|
||||
let mut midi_b_change = None;
|
||||
|
||||
match self.read_channel(0) {
|
||||
Ok(raw_a) => {
|
||||
self.apply_iir_filter(0, raw_a);
|
||||
let filtered_a = (self.pedal_a_filtered >> 8) as u8;
|
||||
let midi_a = self.adc_to_midi(filtered_a, PEDAL_A_MIN, PEDAL_A_MAX);
|
||||
|
||||
if midi_a != self.last_midi_a {
|
||||
self.last_midi_a = midi_a;
|
||||
midi_a_change = Some(midi_a);
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
error!("Error reading channel 0: {}", e);
|
||||
}
|
||||
}
|
||||
|
||||
match self.read_channel(1) {
|
||||
Ok(raw_b) => {
|
||||
self.apply_iir_filter(1, raw_b);
|
||||
let filtered_b = (self.pedal_b_filtered >> 8) as u8;
|
||||
let midi_b = self.adc_to_midi(filtered_b, PEDAL_B_MIN, PEDAL_B_MAX);
|
||||
|
||||
if midi_b != self.last_midi_b {
|
||||
self.last_midi_b = midi_b;
|
||||
midi_b_change = Some(midi_b);
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
error!("Error reading channel 1: {}", e);
|
||||
}
|
||||
}
|
||||
|
||||
(midi_a_change, midi_b_change)
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,8 @@ use stm32f0xx_hal::{delay::Delay, prelude::*};
|
||||
pub struct DisplayController;
|
||||
|
||||
impl DisplayController {
|
||||
|
||||
#[cfg(not(debug_assertions))]
|
||||
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) {
|
||||
|
||||
@@ -4,6 +4,7 @@ use hal::{delay::Delay, serial::Serial, stm32};
|
||||
use hal::gpio::{Input, Floating, gpiob};
|
||||
use embedded_midi::{MidiOut, MidiIn};
|
||||
use crate::bus::Bus;
|
||||
use crate::adc::Tlc0832;
|
||||
|
||||
pub struct Hardware {
|
||||
pub delay: Delay,
|
||||
@@ -12,6 +13,7 @@ pub struct Hardware {
|
||||
pub bus: Bus,
|
||||
pub midi_tx: MidiOut<hal::serial::Tx<stm32::USART1>>,
|
||||
pub midi_rx: MidiIn<hal::serial::Rx<stm32::USART1>>,
|
||||
pub adc: Tlc0832,
|
||||
}
|
||||
|
||||
impl Hardware {
|
||||
@@ -52,6 +54,13 @@ impl Hardware {
|
||||
let midi_tx = MidiOut::new(tx);
|
||||
let midi_rx = MidiIn::new(rx);
|
||||
|
||||
let adc_sck = cortex_m::interrupt::free(|cs| gpiob.pb10.into_floating_input(cs));
|
||||
let adc_miso = cortex_m::interrupt::free(|cs| gpiob.pb14.into_floating_input(cs));
|
||||
let adc_mosi = cortex_m::interrupt::free(|cs| gpiob.pb15.into_floating_input(cs));
|
||||
let adc_cs = cortex_m::interrupt::free(|cs| gpiob.pb12.into_floating_input(cs));
|
||||
|
||||
let adc = Tlc0832::new(dp.SPI2, adc_sck, adc_miso, adc_mosi, adc_cs, &mut rcc);
|
||||
|
||||
Self {
|
||||
delay,
|
||||
button_1_5,
|
||||
@@ -59,6 +68,7 @@ impl Hardware {
|
||||
bus,
|
||||
midi_tx,
|
||||
midi_rx,
|
||||
adc,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -32,6 +32,8 @@ pub struct SevenSegmentDisplay {
|
||||
}
|
||||
|
||||
impl LedState {
|
||||
|
||||
#[cfg(not(debug_assertions))]
|
||||
pub fn boot_sequence(i: usize) -> Option<LedState> {
|
||||
let mut led_state = LedState::default();
|
||||
match i {
|
||||
|
||||
@@ -7,6 +7,7 @@ mod button;
|
||||
mod midi;
|
||||
mod hardware;
|
||||
mod display;
|
||||
mod adc;
|
||||
|
||||
use cortex_m_rt::entry;
|
||||
use panic_halt as _;
|
||||
@@ -17,6 +18,8 @@ use button::ButtonHandler;
|
||||
use midi::MidiProcessor;
|
||||
use hardware::Hardware;
|
||||
use display::DisplayController;
|
||||
use embedded_midi::MidiMessage;
|
||||
use midi_types::{Control, Channel, Value7};
|
||||
|
||||
defmt::timestamp!("{=u32}", {
|
||||
static mut COUNTER: u32 = 0;
|
||||
@@ -30,6 +33,7 @@ defmt::timestamp!("{=u32}", {
|
||||
fn main() -> ! {
|
||||
let mut hardware = Hardware::init();
|
||||
|
||||
#[cfg(not(debug_assertions))]
|
||||
DisplayController::run_boot_sequence(&mut hardware.bus, &mut hardware.delay);
|
||||
|
||||
let mut button_handler = ButtonHandler::new();
|
||||
@@ -49,6 +53,18 @@ fn main() -> ! {
|
||||
}
|
||||
|
||||
MidiProcessor::process_message(&mut hardware.midi_rx, &mut led_state);
|
||||
|
||||
let (pedal_a_change, pedal_b_change) = hardware.adc.update_and_get_midi_changes();
|
||||
|
||||
if let Some(value) = pedal_a_change {
|
||||
let msg = MidiMessage::ControlChange(Channel::C1, Control::from(1), Value7::new(value));
|
||||
hardware.midi_tx.write(&msg).ok();
|
||||
}
|
||||
|
||||
if let Some(value) = pedal_b_change {
|
||||
let msg = MidiMessage::ControlChange(Channel::C1, Control::from(7), Value7::new(value));
|
||||
hardware.midi_tx.write(&msg).ok();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user