Loading files
This file is pinned to the selected release. Download the original to open it in your design tool.
c305a21f7This file is pinned to the selected release. Download the original to open it in your design tool.
#![no_std]
#![no_main]
extern crate panic_semihosting;
mod envelope;
mod reverse_bits;
mod bu2505fv;
use rtfm::app;
use stm32f1xx_hal::{prelude::*, stm32, gpio::*, serial, timer, spi};
use embedded_hal::digital::v2::OutputPin;
use embedded_hal::digital::v1_compat::OldOutputPin;
#[macro_use(singleton)]
extern crate cortex_m;
use cortex_m::asm::delay;
use cortex_m_rt::entry;
use stm32::interrupt;
use usb_device::prelude::*;
use stm32_usbd::UsbBus;
use usbd_midi;
mod midi;
mod vco;
use core::cell::RefCell;
use core::fmt::Write;
use core::mem::MaybeUninit;
mod coop_threadsafe_container; use coop_threadsafe_container as ctc;
use ctc::ThreadToken;
use mcp49xx;
use shared_bus;
use noop_bus_mutex;
static mut GLOB_EXTI_PIN : MaybeUninit<gpioa::PA7<Input<Floating>>> = MaybeUninit::uninit();
static mut GLOB_TX : MaybeUninit<serial::Tx<stm32::USART1>> = MaybeUninit::uninit();
static mut GLOB_TIMER : MaybeUninit<timer::CountDownTimer<stm32::TIM2>> = MaybeUninit::uninit();
static PINGPONG : ctc::CoopContainer<vco::MeasurementState> = ctc::CoopContainer::new( vco::MeasurementState::const_default(), ctc::Token::<ctc::Main>::ID);
type MyUsbPins = (stm32f1xx_hal::gpio::gpioa::PA11<stm32f1xx_hal::gpio::Input<stm32f1xx_hal::gpio::Floating>>, stm32f1xx_hal::gpio::gpioa::PA12<stm32f1xx_hal::gpio::Input<stm32f1xx_hal::gpio::Floating>>);
type MyUsbBus = stm32_usbd::bus::UsbBus<MyUsbPins>;
type MySpiPins = (
gpiob::PB13<Alternate<PushPull>>,
gpiob::PB14<Input<Floating>>,
gpiob::PB15<Alternate<PushPull>>
);
type MySpi = stm32f1xx_hal::spi::Spi<
stm32::SPI2,
MySpiPins
>;
type MySharedSpiManager = shared_bus::proxy::BusManager<
noop_bus_mutex::NoopBusMutex<
core::cell::RefCell<
MySpi
>
>,
MySpi
>;
type MySharedSpiProxy = shared_bus::proxy::BusProxy<'static,
noop_bus_mutex::NoopBusMutex<
core::cell::RefCell<
MySpi
>
>,
MySpi
>;
type Mcp = mcp49xx::Mcp49xx<
mcp49xx::interface::SpiInterface<
MySharedSpiProxy,
OldOutputPin<Pxx<Output<PushPull>>>
>,
mcp49xx::marker::Resolution12Bit,
mcp49xx::marker::DualChannel,
mcp49xx::marker::Unbuffered
>;
type VcoType = vco::VoltageControlledOscillator<'static, Mcp>;
pub struct SpiDevices {
vcos : [VcoType; 2],
envelope : envelope::Envelope,
bu2505 : bu2505fv::Bu2505fv<MySharedSpiProxy, Pxx<Output<PushPull>>, bu2505fv::Reversed>,
spi_accessor : MySharedSpiProxy
}
static mut SPI_DEVICES : MaybeUninit<SpiDevices> = MaybeUninit::uninit();
mod vco_token { // TODO FIXME rename this
use crate::{SpiDevices, SPI_DEVICES};
pub struct VcoToken { dummy : () }
impl VcoToken {
pub const unsafe fn new() -> VcoToken { VcoToken{dummy:()} }
}
pub fn vcos<'a>(token : &'a mut VcoToken) -> &'a mut SpiDevices {
unsafe { &mut *SPI_DEVICES.as_mut_ptr() }
}
}
use crate::vco_token::{VcoToken,vcos};
#[derive(Debug)]
enum Command {
Calibrate,
Midi(midi::MidiChannelMessage)
}
use stm32f1xx_hal::time::Hertz;
const SYSCLK : Hertz = Hertz(72_000_000);
#[app(device = stm32)]
const APP: () = {
struct Resources {
// TODO
#[init(())]
dummy : (),
exti : stm32::EXTI,
#[init(core::ptr::null_mut())]
exti_pin_ptr : *mut Pxx<Input<Floating>>,
tx : serial::Tx<stm32::USART1>,
led : gpioc::PC13<Output<PushPull>>,
usb_dev : usb_device::device::UsbDevice<'static, MyUsbBus>,
midi : usbd_midi::MidiClass<'static, MyUsbBus>,
mytimer : timer::CountDownTimer<stm32::TIM2>,
env_timer : timer::CountDownTimer<stm32::TIM1>,
#[init(false)]
calibration_in_progress : bool,
#[init(envelope::Envelope::new())]
test_env1 : envelope::Envelope,
test_env2 : envelope::Envelope,
test_lfo1 : envelope::Envelope,
test_lfo2 : envelope::Envelope,
#[init( unsafe{VcoToken::new()} )]
vco_token : VcoToken
}
//#[init(resources=[vco_token])]
#[init(spawn=[xmain],resources=[vco_token])]
fn init(mut cx : init::Context) -> init::LateResources {
static mut USB_BUS: Option<usb_device::bus::UsbBusAllocator<MyUsbBus>> = None;
let dp = stm32::Peripherals::take().unwrap();
let p = &mut cx.core; //cortex_m::peripheral::Peripherals::take().unwrap();
// Clock configuration
let mut flash = dp.FLASH.constrain();
let mut rcc = dp.RCC.constrain();
let clocks = rcc.cfgr
.use_hse(8.mhz())
.sysclk(SYSCLK)
.pclk1(36.mhz())
.freeze(&mut flash.acr);
assert!(clocks.usbclk_valid());
// GPIO and peripheral configuration
let mut afio = dp.AFIO.constrain(&mut rcc.apb2);
let mut gpioa = dp.GPIOA.split(&mut rcc.apb2);
let mut gpiob = dp.GPIOB.split(&mut rcc.apb2);
let mut gpioc = dp.GPIOC.split(&mut rcc.apb2);
// Configure the on-board LED (PC13, green)
let mut led = gpioc.pc13.into_push_pull_output(&mut gpioc.crh);
led.set_high().ok(); // Turn off
// Configure the USART
let gpio_tx = gpioa.pa9.into_alternate_push_pull(&mut gpioa.crh);
let gpio_rx = gpioa.pa10;
let serial = serial::Serial::usart1(
dp.USART1,
(gpio_tx, gpio_rx),
&mut afio.mapr,
serial::Config::default().baudrate(115200.bps()),
clocks,
&mut rcc.apb2
);
let (mut tx, _rx) = serial.split();
writeln!(tx, "usart initialized").ok();
writeln!(tx, "========================================================").ok();
writeln!(tx, "analog-synth @ {}", env!("VERGEN_SHA")).ok();
writeln!(tx, " built on {}", env!("VERGEN_BUILD_TIMESTAMP")).ok();
writeln!(tx, "========================================================\n").ok();
// Configure USB
// BluePill board has a pull-up resistor on the D+ line.
// Pull the D+ pin down to send a RESET condition to the USB bus.
let mut usb_dp = gpioa.pa12.into_push_pull_output(&mut gpioa.crh);
usb_dp.set_low().ok();
delay(clocks.sysclk().0 / 100);
let usb_dm = gpioa.pa11;
let usb_dp = usb_dp.into_floating_input(&mut gpioa.crh);
*USB_BUS = Some( UsbBus::new(dp.USB, (usb_dm, usb_dp)) );
let usb_bus = USB_BUS.as_ref().unwrap();
let mut midi = usbd_midi::MidiClass::new(usb_bus);
let mut usb_dev = UsbDeviceBuilder::new(usb_bus, UsbVidPid(0x16c0, 0x27dd))
.manufacturer("Fake company")
.product("Serial port")
.serial_number("TEST")
.device_class(usbd_midi::USB_CLASS_NONE)
.build();
writeln!(tx, "spi...");
// SPI
let mcp_ss : OldOutputPin<_> = gpiob.pb12.into_push_pull_output(&mut gpiob.crh).downgrade().into();
let mcp2_ss : OldOutputPin<_> = gpiob.pb11.into_push_pull_output(&mut gpiob.crh).downgrade().into();
let sck = gpiob.pb13.into_alternate_push_pull(&mut gpiob.crh);
let mosi = gpiob.pb15.into_alternate_push_pull(&mut gpiob.crh);
let miso = gpiob.pb14;
let spi = spi::Spi::spi2(dp.SPI2, (sck, miso, mosi), spi::Mode { phase : spi::Phase::CaptureOnFirstTransition, polarity : spi::Polarity::IdleLow }, 5.mhz(), clocks, &mut rcc.apb1);
let shared_spi : &'static MySharedSpiManager = singleton!(:MySharedSpiManager =
shared_bus::BusManager::<noop_bus_mutex::NoopBusMutex<_>, _>::new(spi)
).unwrap();
let mcp4822 = singleton!(:RefCell<Mcp> = RefCell::new(mcp49xx::Mcp49xx::new_mcp4822(shared_spi.acquire(), mcp_ss)) ).unwrap();
let bu2505_ld_pin = gpiob.pb10.into_push_pull_output(&mut gpiob.crh).downgrade();
let mut spi_bla = shared_spi.acquire();
let mut bu2505 = bu2505fv::create_reversed(bu2505_ld_pin, &spi_bla);
writeln!(tx, "exti...");
// EXTI
p.NVIC.enable(stm32::Interrupt::EXTI9_5);
let mut exti_pin = gpiob.pb9.into_floating_input(&mut gpiob.crh);
exti_pin.make_interrupt_source(&mut afio);
exti_pin.trigger_on_edge(&dp.EXTI, Edge::RISING_FALLING);
exti_pin.disable_interrupt(&dp.EXTI);
let mut exti_pin2 = gpiob.pb8.into_floating_input(&mut gpiob.crh);
exti_pin2.make_interrupt_source(&mut afio);
exti_pin2.trigger_on_edge(&dp.EXTI, Edge::RISING_FALLING);
exti_pin2.disable_interrupt(&dp.EXTI);
let mut vco1 = vco::VoltageControlledOscillator::new(mcp4822, mcp49xx::Channel::Ch0, exti_pin.downgrade());
let mut vco2 = vco::VoltageControlledOscillator::new(mcp4822, mcp49xx::Channel::Ch1, exti_pin2.downgrade());
*vcos(cx.resources.vco_token) = SpiDevices{ vcos : [vco1, vco2], envelope : envelope::Envelope::new(), bu2505, spi_accessor : spi_bla };
// Timer
let mytimer = timer::Timer::tim2(dp.TIM2, &clocks, &mut rcc.apb1).start_raw(4800, 65535);
// Periodic timer
let mut env_timer = timer::Timer::tim1(dp.TIM1, &clocks, &mut rcc.apb2).start_count_down(1000.hz());
env_timer.listen(timer::Event::Update);
// USB interrupt
p.NVIC.enable(stm32::Interrupt::USB_LP_CAN_RX0);
// Envelope/LFO
let mut test_lfo1 = envelope::Envelope::new();
test_lfo1.repeat = true;
test_lfo1.attack = 600;
test_lfo1.decay = 1;
test_lfo1.sustain = 65535;
test_lfo1.release = 600;
test_lfo1.hold = false;
test_lfo1.trigger();
let mut test_lfo2 = test_lfo1;
test_lfo2.attack = 300;
test_lfo2.release = 300;
let mut test_env2 = envelope::Envelope::new();
test_env2.attack = 200;
test_env2.release /= 2;
cx.spawn.xmain(Command::Calibrate);
return init::LateResources { exti : dp.EXTI, tx, led, usb_dev, midi, mytimer, env_timer, test_env2, test_lfo1, test_lfo2};
}
#[task(resources = [mytimer, exti, exti_pin_ptr, vco_token, tx, test_env1, test_env2, test_lfo1, test_lfo2, calibration_in_progress], capacity=10, priority=1)]
fn xmain(mut c : xmain::Context, cmd : Command)
{
writeln!(c.resources.tx, "in main, cmd = {:?}", cmd);
let token = unsafe{ coop_threadsafe_container::Token::<coop_threadsafe_container::Main>::new() };
let spi_devices = vcos(c.resources.vco_token);
writeln!(c.resources.tx, "got vcos");
match cmd {
Command::Calibrate => {
for vco in &mut spi_devices.vcos {
writeln!(c.resources.tx, "Calibrating VCO...");
writeln!(c.resources.tx, "timer clock is {}", c.resources.mytimer.lock(|t| {t.clk()}));
writeln!(c.resources.tx, "lo");
vco.send(1024);
delay(12000000);
writeln!(c.resources.tx, "hi");
vco.send(1024*2);
delay(12000000);
let freq = vco.measure_freq_at(2048, 10, &mut c.resources.exti, &mut c.resources.exti_pin_ptr, &mut c.resources.mytimer, &PINGPONG, &token);
writeln!(c.resources.tx, "frequency is {}", freq as u32);
let freq = vco.measure_freq_at(512, 10, &mut c.resources.exti, &mut c.resources.exti_pin_ptr, &mut c.resources.mytimer, &PINGPONG, &token);
writeln!(c.resources.tx, "frequency is {}", freq as u32);
vco.calibrate(&mut c.resources.exti, &mut c.resources.exti_pin_ptr, &mut c.resources.mytimer, &PINGPONG, &token, &mut c.resources.tx);
c.resources.calibration_in_progress.lock(|c| {*c = false;});
}
}
Command::Midi(msg) => {
match msg {
midi::MidiChannelMessage::NoteOn{note, velocity: _} => {
let mc = note as i32 * 100_000 + 5_000_000;
writeln!(c.resources.tx, "playing millicents={}", mc);
let result = spi_devices.vcos[0].output_millicents( mc );
writeln!(c.resources.tx, "\t-> {:?}", result);
let result2= spi_devices.vcos[1].output_millicents( mc + 700_000 );
writeln!(c.resources.tx, "\t-> {:?}", result2);
c.resources.test_env1.trigger();
c.resources.test_env2.trigger();
}
midi::MidiChannelMessage::NoteOff{note:_, velocity:_} => {
c.resources.test_env1.release();
c.resources.test_env2.release();
}
_ => {}
}
}
}
}
#[task(binds = TIM1_UP, resources=[test_env1, test_env2, test_lfo1, test_lfo2, tx, env_timer, vco_token], priority=1)]
fn envelope_timer(mut c : envelope_timer::Context)
{
static mut i : u32 = 0;
let spi_devices = vcos(c.resources.vco_token);
*i = (*i+1)%10000;
if *i == 0 {
writeln!(c.resources.tx, "envelope update: {}", c.resources.test_env.value10bit());
}
spi_devices.bu2505.set(1, c.resources.test_env1.value10bit(), &mut spi_devices.spi_accessor, SYSCLK.0);
c.resources.test_env1.tick();
spi_devices.bu2505.set(2, c.resources.test_env2.value10bit(), &mut spi_devices.spi_accessor, SYSCLK.0);
c.resources.test_env2.tick();
spi_devices.bu2505.set(9, c.resources.test_lfo1.value10bit(), &mut spi_devices.spi_accessor, SYSCLK.0);
c.resources.test_lfo1.tick();
spi_devices.bu2505.set(10, c.resources.test_lfo2.value10bit(), &mut spi_devices.spi_accessor, SYSCLK.0);
c.resources.test_lfo2.tick();
c.resources.env_timer.clear_update_interrupt_flag();
}
#[task(binds = USB_LP_CAN_RX0, spawn=[xmain], resources=[midi, usb_dev, led, calibration_in_progress], priority=2)]
fn periodic_usb_poll(mut c : periodic_usb_poll::Context)
{
if c.resources.usb_dev.poll(&mut [c.resources.midi]) {
//midi.note_on(1, usbd_midi::Note::A4, 127).ok();
let mut data : [u8;64] = [0;64];
if let Ok(size) = c.resources.midi.poll(&mut data)
{
for i in (0..size).step_by(4) {
let (cable, msg) = midi::parse_midi(&[data[i],data[i+1],data[i+2],data[i+3]]);//FIXME there must be a better way...
match msg {
midi::MidiMessage::Channel(channel, msg) =>
{
match msg {
midi::MidiChannelMessage::NoteOn{note:_, velocity:_} => {
c.resources.led.set_low().ok(); // Turn off
c.spawn.xmain(Command::Midi(msg));
}
midi::MidiChannelMessage::NoteOff{note:_, velocity:_} => {
c.resources.led.set_high().ok(); // Turn off
c.spawn.xmain(Command::Midi(msg));
}
midi::MidiChannelMessage::ControlChange{ctrl, value} => {
if ctrl == 92 && *c.resources.calibration_in_progress == false {
*c.resources.calibration_in_progress = true;
c.spawn.xmain(Command::Calibrate);
}
c.spawn.xmain(Command::Midi(msg));
}
_ => ()
}
}
_ => ()
}
}
}
}
}
#[task(binds = EXTI9_5, resources = [mytimer, exti, exti_pin_ptr], priority=3)]
fn measurement_isr(c : measurement_isr::Context)
{
let token = unsafe{ coop_threadsafe_container::Token::<coop_threadsafe_container::ISR>::new() };
let exti_pin = unsafe {&mut **c.resources.exti_pin_ptr};
vco::handle_measurement_isr(&token, exti_pin, c.resources.exti, c.resources.mytimer, &PINGPONG);
}
extern "C" {
fn EXTI0();
}
};