With the goal of spinning a motor, I wrote some rust based on my reverse engineering of the boards. Do not use rust-embedded. It’s not nice.
Here's a callout title
#![no_std] #![no_main] use cortex_m::asm; use defmt_rtt as _; use panic_halt as _; use core::cell::RefCell; use cortex_m::asm::delay; use cortex_m_rt::entry; use critical_section::Mutex; #[allow(deprecated)] use stm32f3xx_hal::adc::OneShot; use stm32f3xx_hal::gpio::GpioExt; use stm32f3xx_hal::{ adc, pac::{self, interrupt}, prelude::*, timer, }; use stm32f3xx_hal::pac::usb::*; use stm32f3xx_hal::usb::{Peripheral, UsbBus}; use usb_device::prelude::*; use usbd_serial::{SerialPort, USB_CLASS_CDC}; static TIMER: Mutex<RefCell<Option<timer::Timer<pac::TIM2>>>> = Mutex::new(RefCell::new(None)); #[entry] fn main() -> ! { let dp = pac::Peripherals::take().unwrap(); let mut rcc = dp.RCC.constrain(); let mut flash = dp.FLASH.constrain(); // let clocks: stm32f3xx_hal::rcc::Clocks = rcc.cfgr.freeze(&mut dp.FLASH.constrain().acr); let clocks = rcc .cfgr .use_hse(8.MHz()) .sysclk(48.MHz()) .pclk1(24.MHz()) .pclk2(24.MHz()) .freeze(&mut flash.acr); assert!(clocks.usbclk_valid()); let mut gpioc = dp.GPIOC.split(&mut rcc.ahb); let mut gpiob = dp.GPIOB.split(&mut rcc.ahb); let mut gpioa = dp.GPIOA.split(&mut rcc.ahb); let mut usb_dp = gpioa .pa12 .into_push_pull_output(&mut gpioa.moder, &mut gpioa.otyper); usb_dp.set_low().ok(); delay(clocks.sysclk().0 / 100); let usb_dm = gpioa .pa11 .into_af_push_pull(&mut gpioa.moder, &mut gpioa.otyper, &mut gpioa.afrh); let usb_dp = usb_dp.into_af_push_pull(&mut gpioa.moder, &mut gpioa.otyper, &mut gpioa.afrh); let usb = Peripheral { usb: dp.USB, pin_dm: usb_dm, pin_dp: usb_dp, }; let usb_bus = UsbBus::new(usb); let mut serial = SerialPort::new(&usb_bus); let mut usb_dev = UsbDeviceBuilder::new(&usb_bus, UsbVidPid(0x16c0, 0x27dd)) .manufacturer("YonesWire") .product("SporkMax") .serial_number("bruh we aint doin all that") .device_class(USB_CLASS_CDC) .build(); let mut adc_common = adc::CommonAdc::new(dp.ADC1_2, &clocks, &mut rcc.ahb); let mut tuple = (dp.ADC1, dp.ADC2); let mut ts = adc::TemperatureSensor::new(&mut adc_common, &mut tuple); // Set up ADC1 let mut adc = adc::Adc::new( tuple.0, // The ADC we are going to control adc::config::Config::default(), // The following is only needed to make sure the clock signal for the ADC is set up // correctly. &clocks, &adc_common, ) .into_oneshot(); let mut adc2 = adc::Adc::new_disabled(tuple.1); adc2.calibrate(&clocks, &adc_common); adc2.set_config(adc::config::Config::default()); let _ = adc2.into_enabled(); // Set up pin PA0 as analog pin. // This pin is connected to the user button on the stm32f3discovery board. let mut analog_pin = gpioa.pa2.into_analog(&mut gpioa.moder, &mut gpioa.pupdr); let mut timer = timer::Timer::new(dp.TIM2, clocks, &mut rcc.apb1); unsafe { cortex_m::peripheral::NVIC::unmask(timer.interrupt()); } timer.enable_interrupt(timer::Event::Update); // Start a timer which fires regularly to wake up from `asm::wfi` timer.start(500.milliseconds()); // Put the timer in the global context. critical_section::with(|cs| { TIMER.borrow(cs).replace(Some(timer)); }); let mut led_b = gpioc .pc15 .into_push_pull_output(&mut gpioc.moder, &mut gpioc.otyper); let mut led_g = gpioc .pc14 .into_push_pull_output(&mut gpioc.moder, &mut gpioc.otyper); let mut led_r = gpioc .pc13 .into_push_pull_output(&mut gpioc.moder, &mut gpioc.otyper); led_b.set_high().unwrap(); led_g.set_high().unwrap(); led_r.set_high().unwrap(); let mut enable = gpioa .pa0 .into_push_pull_output(&mut gpioa.moder, &mut gpioa.otyper); let mut inha = gpiob .pb4 .into_push_pull_output(&mut gpiob.moder, &mut gpiob.otyper); let mut inla = gpioa .pa1 .into_push_pull_output(&mut gpioa.moder, &mut gpioa.otyper); let mut inhb = gpiob .pb5 .into_push_pull_output(&mut gpiob.moder, &mut gpiob.otyper); let mut inlb = gpiob .pb10 .into_push_pull_output(&mut gpiob.moder, &mut gpiob.otyper); let mut inhc = gpiob .pb0 .into_push_pull_output(&mut gpiob.moder, &mut gpiob.otyper); let mut inlc = gpiob .pb7 .into_push_pull_output(&mut gpiob.moder, &mut gpiob.otyper); enable.set_low().unwrap(); inha.set_low().unwrap(); inla.set_low().unwrap(); inhb.set_low().unwrap(); inlb.set_low().unwrap(); inhc.set_low().unwrap(); inlc.set_low().unwrap(); let mut delay_time: u32 = 10000; loop { let adc_data: u16 = adc.read(&mut analog_pin).unwrap(); led_r.toggle(); asm::delay(delay_time); } }
I flashed it using picoprobe
It does work: (video from March 1, 2024)
This does not have back-emf sensing, so it really can’t be used reliably. The phase connections on a spark max are not tapped to the ADC of the micro controller, so you can only use spark-max based hardware with motors that have encoders.