blackknobs/src/main.rs

211 lines
6.0 KiB
Rust

#![no_std]
#![no_main]
use panic_semihosting as _;
use rtic::app;
use systick_monotonic::*;
use stm32f1xx_hal::{
adc,
dma,
pac::ADC1,
prelude::*,
gpio::{self, Analog},
serial::{Config, Serial},
};
use embedded_midi::{self, MidiMessage};
const CONTROL_CNT: usize = 9;
// Concrete midi out type alias
type MidiOut = embedded_midi::MidiOut<
stm32f1xx_hal::serial::Tx<
stm32f1xx_hal::pac::USART1
>
>;
// Adc channels to scan
pub struct AdcPins(
gpio::gpioa::PA0<Analog>,
gpio::gpioa::PA1<Analog>,
gpio::gpioa::PA2<Analog>,
gpio::gpioa::PA3<Analog>,
gpio::gpioa::PA4<Analog>,
gpio::gpioa::PA5<Analog>,
gpio::gpioa::PA6<Analog>,
gpio::gpioa::PA7<Analog>,
gpio::gpiob::PB0<Analog>,
);
impl adc::SetChannels<AdcPins> for adc::Adc<ADC1>
{
fn set_samples(&mut self) {
for ch in 0..(CONTROL_CNT + 1) as u8 {
self.set_channel_sample_time(ch, adc::SampleTime::T_28);
}
}
fn set_sequence(&mut self) {
self.set_regular_sequence(&[0, 1, 2, 3, 4, 5, 6, 7, 8]);
//self.set_continuous_mode(true);
}
}
// San DMA recources, cunsumed by the DMA controller
pub struct ScanDmaResources(
adc::Adc<ADC1>,
AdcPins,
dma::dma1::C1,
);
#[derive(Copy, Clone)]
pub struct MidiControl{
value: u8, // Actually a 7 bit value
update: bool,
}
#[app(
device = stm32f1xx_hal::pac,
peripherals = true,
dispatchers = [SPI1]
)]
mod app {
use super::*;
#[shared]
struct Shared {
controls: [MidiControl; 9],
}
#[local]
struct Local {
dma_buffer: Option<&'static mut [u16; CONTROL_CNT]>,
scan_dma_rescources: Option<ScanDmaResources>,
midi_out: MidiOut,
}
#[monotonic(priority = 1, binds = SysTick, default = true)]
type MonoTimer = Systick<1000>;
#[init(local = [buffer: [u16; CONTROL_CNT] = [0u16; CONTROL_CNT]])]
fn init(cx: init::Context) -> (Shared, Local, init::Monotonics) {
let mut flash = cx.device.FLASH.constrain();
let rcc = cx.device.RCC.constrain();
let clocks = rcc
.cfgr
.use_hse(8.MHz())
.sysclk(48.MHz())
.adcclk(2.MHz())
.freeze(&mut flash.acr);
let mono = Systick::new(cx.core.SYST, 48_000_000);
let mut afio = cx.device.AFIO.constrain();
let mut gpioa = cx.device.GPIOA.split();
let tx = gpioa.pa9.into_alternate_open_drain(&mut gpioa.crh);
let rx = gpioa.pa10;
let usart = Serial::new(
cx.device.USART1,
(tx, rx),
&mut afio.mapr,
Config::default().baudrate(31250.bps()).parity_none(),
&clocks,
);
let mut gpiob = cx.device.GPIOB.split();
// SPI 2, PB13, PB15
let adc_pins = AdcPins(
gpioa.pa0.into_analog(&mut gpioa.crl),
gpioa.pa1.into_analog(&mut gpioa.crl),
gpioa.pa2.into_analog(&mut gpioa.crl),
gpioa.pa3.into_analog(&mut gpioa.crl),
gpioa.pa4.into_analog(&mut gpioa.crl),
gpioa.pa5.into_analog(&mut gpioa.crl),
gpioa.pa6.into_analog(&mut gpioa.crl),
gpioa.pa7.into_analog(&mut gpioa.crl),
gpiob.pb0.into_analog(&mut gpiob.crl),
);
let adc1 = adc::Adc::adc1(cx.device.ADC1, clocks);
let dma_ch1 = cx.device.DMA1.split().1;
let scan_dma_rescources = ScanDmaResources(adc1, adc_pins, dma_ch1);
let (tx, _rx) = usart.split();
let midi_out = MidiOut::new(tx);
read_adc::spawn().unwrap();
(
Shared {
controls: [
MidiControl { value: 0, update: false };
CONTROL_CNT
],
},
Local {
dma_buffer: Some(cx.local.buffer),
scan_dma_rescources: Some(scan_dma_rescources),
midi_out,
},
init::Monotonics(mono)
)
}
// This can be implemented non-blocking (use the DMA1 interrupt)
// Is it worth the effort?
#[task(local = [scan_dma_rescources, dma_buffer], shared = [controls])]
fn read_adc(mut cx: read_adc::Context) {
let resources = cx.local.scan_dma_rescources;
let ScanDmaResources(adc, pins, dma) = resources.take().unwrap();
let adc_scan = adc.with_scan_dma(pins, dma);
// Get DMA buffer
let buffer = cx.local.dma_buffer.take().unwrap();
// Perform read
let (buffer, adc_scan) = adc_scan.read(buffer).wait();
// Put the recources back in place
let (adc, pins, dma) = adc_scan.split();
resources.replace(ScanDmaResources(adc, pins, dma));
// Convert adc values to midi control values and check if they changed
cx.shared.controls.lock(|controls| {
for i in 0..controls.len() {
let value = adc_to_midi(&buffer[i]);
if controls[i].value != value {
controls[i].value = value;
controls[i].update = true;
}
}
});
// Put the buffer back in place
cx.local.dma_buffer.replace(buffer);
send_control_changes::spawn().unwrap();
}
#[task(local = [midi_out], shared = [controls])]
fn send_control_changes(mut cx: send_control_changes::Context) {
cx.shared.controls.lock(|controls| {
for i in 0..controls.len() {
if controls[i].update {
let event = MidiMessage::ControlChange(
0u8.into(),
(i as u8).into(),
controls[i].value.into()
);
cx.local.midi_out.write(&event).unwrap();
controls[i].update = false;
}
}
});
read_adc::spawn_after(systick_monotonic::ExtU64::millis(100)).unwrap();
}
}
// Converts a 12 bit adc value to a 7 bit midi control value
fn adc_to_midi(adc_val: &u16) -> u8{
((adc_val & 0x0FFF) >> 5) as u8
}