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::Timerpac::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.