#![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, gpio::gpioa::PA1, gpio::gpioa::PA2, gpio::gpioa::PA3, gpio::gpioa::PA4, gpio::gpioa::PA5, gpio::gpioa::PA6, gpio::gpioa::PA7, gpio::gpiob::PB0, ); impl adc::SetChannels for adc::Adc { 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, 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, 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 }