From 06fd42430f1bc3a15c00e599185508bab003ce07 Mon Sep 17 00:00:00 2001 From: Serge Bazanski Date: Fri, 23 Jan 2026 23:19:20 +0100 Subject: [PATCH] rewrite esc controller: periodic process func --- spinnyboy_rust/src/bin/dc_driver/dshot.rs | 99 +++++++++++-------- spinnyboy_rust/src/bin/dc_driver/mod.rs | 13 +-- spinnyboy_rust/src/bin/main.rs | 76 ++++---------- spinnyboy_rust/src/bin/peripherals/mod.rs | 1 + spinnyboy_rust/src/bin/peripherals/nextion.rs | 2 +- 5 files changed, 78 insertions(+), 113 deletions(-) diff --git a/spinnyboy_rust/src/bin/dc_driver/dshot.rs b/spinnyboy_rust/src/bin/dc_driver/dshot.rs index 519bb38..ca6dfc8 100644 --- a/spinnyboy_rust/src/bin/dc_driver/dshot.rs +++ b/spinnyboy_rust/src/bin/dc_driver/dshot.rs @@ -2,8 +2,9 @@ use esp_hal::{ delay::Delay, gpio::Level, - rmt::{Channel, PulseCode, Rx, Tx, TxChannelCreator}, + rmt::{Channel, PulseCode, SingleShotTxTransaction}, }; +use core::cell::{Ref, RefCell}; use num_traits::float::FloatCore; use rtt_target::{rprint, rprintln}; @@ -49,7 +50,6 @@ impl BitTicks { pub fn from_clk( clk_speed: u32, clk_divider: u8, - bit_times: BitTimes, speed: DShotSpeed, ) -> Self { let tick_len = (1. / clk_speed as f32) * (clk_divider as f32) * 1_000_000.; @@ -78,6 +78,7 @@ impl BitTimes { } #[derive(Debug, Clone, Copy)] +#[allow(dead_code)] pub enum DShotSpeed { DShot150, DShot300, @@ -108,24 +109,18 @@ impl DShotSpeed { Self::DShot1200 => BitTimes::new(0.625, 0.313), } } - - /// These are for an 80 MHz clock with a clock divider setting of 1 - pub fn default_bit_ticks(&self) -> BitTicks { - match &self { - Self::DShot150 => BitTicks::new(400, 200), - Self::DShot300 => BitTicks::new(200, 100), - Self::DShot600 => BitTicks::new(100, 50), - Self::DShot1200 => BitTicks::new(50, 25), - } - } } #[allow(dead_code)] pub struct DShot<'a> { rx_channel: &'a mut Channel<'static, esp_hal::Blocking, esp_hal::rmt::Rx>, - tx_channel: &'a mut Channel<'static, esp_hal::Blocking, esp_hal::rmt::Tx>, speed: DShotSpeed, bit_ticks: BitTicks, + + pulses: RefCell<[u32; 17]>, + requested_throttle: RefCell, + current_tx: RefCell>>, + } impl<'a> DShot<'a> { @@ -139,19 +134,17 @@ impl<'a> DShot<'a> { ) -> Self { let clk_speed = clk_speed.unwrap_or(80_000_000); let clk_divider = clk_divider.unwrap_or(1); - let bit_ticks = BitTicks::from_clk(clk_speed, clk_divider, speed.bit_times(), speed); - rprint!( - "bit_ticks.t1_h:{},t1_l:{},t0_h:{},t0_l{}", - bit_ticks.t1_h, - bit_ticks.t1_l, - bit_ticks.t0_h, - bit_ticks.t0_l - ); + let bit_ticks = BitTicks::from_clk(clk_speed, clk_divider, speed); + + let txc = tx_channel.reborrow().transmit(&[0u32]).unwrap(); Self { rx_channel: rx_channel, - tx_channel: tx_channel, speed, bit_ticks, + + pulses: RefCell::new([0u32; 17]), + requested_throttle: RefCell::new(0), + current_tx: RefCell::new(Some(txc)), } } @@ -169,16 +162,13 @@ impl<'a> DShot<'a> { } #[allow(clippy::needless_range_loop)] - pub fn create_pulses(&mut self, throttle_value: u16, telemetry: bool) -> [u32; 17] { + pub fn create_pulses(&self, throttle_value: u16, telemetry: bool) -> [u32; 17] { let frame = Self::create_frame(throttle_value, telemetry); let mut pulses = [0; 17]; - rprintln!("CREATING NEW FRAME!"); - rprintln!("--------------------"); for i in 0..16 { let bit = (frame >> (15 - i)) & 1; pulses[i] = if bit == 1 { - rprint!("1"); PulseCode::new( Level::High, self.bit_ticks.t1_h, @@ -187,7 +177,6 @@ impl<'a> DShot<'a> { ) .into() } else { - rprint!("0"); PulseCode::new( Level::High, self.bit_ticks.t0_h, @@ -197,30 +186,56 @@ impl<'a> DShot<'a> { .into() }; } - rprintln!(""); - rprintln!("--------------------"); pulses[16] = 0; pulses } - pub fn write_throttle(&mut self, throttle: u16, telemetry: bool) -> Result<(), &'static str> { - let pulses = self.create_pulses(throttle, telemetry); - let tx_chann = self.tx_channel.reborrow(); - let transaction = tx_chann - .transmit(&pulses) - .unwrap_or(return Err("Failed to send frame")) - .wait() - .unwrap_or(return Err("Waiting failed!")); + pub fn process(&'a self) { + let mut rc = self.current_tx.borrow_mut(); + if let Some(current) = rc.as_mut() { + if current.poll() { + // Schedule new transaction + let tx_chann = rc.take().unwrap().wait().unwrap(); + let mut pulses = self.pulses.borrow_mut(); + let throttle = self.requested_throttle.borrow(); + *pulses = self.create_pulses(*throttle, false); + let pulses = pulses.as_ref(); + // SAFETY: eat shit, rust + let pulses = unsafe { + core::mem::transmute(pulses) + }; + let m = tx_chann + .transmit(pulses) + .unwrap(); - Ok(()) + //self.current_tx = Some(m); + *rc = Some(m); + } + } else { + // Nothing to do, twiddle our thumbs. + } } - pub fn arm(&mut self, delay: &mut Delay) -> Result<(), &'static str> { - for _ in 0..100 { - self.write_throttle(0, false)?; - delay.delay_millis(50); + pub fn set_throttle(&self, v: u16) { + *self.requested_throttle.borrow_mut() = v; + } + + #[allow(dead_code)] + pub fn arm(&'a self) -> Result<(), &'static str> { + let start = esp_hal::time::Instant::now(); + + rprintln!("ARM: Sending pre-throttle..."); + while start.elapsed().as_secs() < 2 { + self.set_throttle(100); + self.process(); } + rprintln!("ARM: Sending 0..."); + while start.elapsed().as_secs() < 4 { + self.set_throttle(0); + self.process(); + } + rprintln!("ARM: Done."); Ok(()) } diff --git a/spinnyboy_rust/src/bin/dc_driver/mod.rs b/spinnyboy_rust/src/bin/dc_driver/mod.rs index e0fbb96..564bce8 100644 --- a/spinnyboy_rust/src/bin/dc_driver/mod.rs +++ b/spinnyboy_rust/src/bin/dc_driver/mod.rs @@ -1,13 +1,2 @@ -pub mod afroesc; +//pub mod afroesc; pub mod dshot; -pub enum EscState { - Starting, - Running, - Stopping, - Stop, -} -//taken from: https://docs.arduino.cc/language-reference/en/functions/math/map/ -//used for the ramp up of the spincoater -pub fn arduino_map(x: i32, in_min: i32, in_max: i32, out_min: i32, out_max: i32) -> i32 { - return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; -} diff --git a/spinnyboy_rust/src/bin/main.rs b/spinnyboy_rust/src/bin/main.rs index 6583f9b..c6b4a9e 100644 --- a/spinnyboy_rust/src/bin/main.rs +++ b/spinnyboy_rust/src/bin/main.rs @@ -5,6 +5,7 @@ reason = "mem::forget is generally not safe to do with esp_hal types, especially those \ holding buffers for the duration of a data transfer." )] +use alloc::boxed::Box; use alloc::string::ToString; use esp_hal::clock::CpuClock; use esp_hal::delay::Delay; @@ -14,26 +15,21 @@ use esp_hal::uart::{Config, Uart}; use esp_hal::gpio::Event; use esp_hal::gpio::{Input, InputConfig}; use esp_hal::handler; -use esp_hal::mcpwm::operator::PwmPinConfig; -use esp_hal::mcpwm::timer::PwmWorkingMode; use esp_hal::rmt::{Rmt, TxChannelConfig, TxChannelCreator}; use esp_hal::time::Rate; use core::cell::RefCell; +use core::mem::forget; use critical_section::Mutex; use esp_backtrace as _; -use esp_hal::time::{Duration, Instant}; -use esp_hal::timer::timg::TimerGroup; use rtt_target::rprintln; mod dc_driver; mod peripherals; -use dc_driver::afroesc::AfroEsc; use peripherals::nextion::Nextion; //DSHOT via rmt! -use dc_driver::dshot::DShot; use dc_driver::dshot::DShotSpeed; use crate::dc_driver::dshot; @@ -57,9 +53,6 @@ const DEFAULT_SPIN_TIME: u32 = 10; // For more information see: esp_bootloader_esp_idf::esp_app_desc!(); use crate::peripherals::Command; -use esp_hal::gpio::Level; -use esp_hal::gpio::Output; -use esp_hal::gpio::OutputConfig; #[main] fn main() -> ! { @@ -69,29 +62,15 @@ fn main() -> ! { let config = esp_hal::Config::default().with_cpu_clock(CpuClock::max()); let peripherals = esp_hal::init(config); - let system = peripherals.SYSTEM; - let mut delay = Delay::new(); + let delay = Delay::new(); esp_alloc::heap_allocator!(#[esp_hal::ram(reclaimed)] size: 65536); - let clock_config = - esp_hal::mcpwm::PeripheralClockConfig::with_frequency(Rate::from_mhz(32)).unwrap(); - let mut mcpwm = esp_hal::mcpwm::McPwm::new(peripherals.MCPWM0, clock_config); - mcpwm.operator0.set_timer(&mcpwm.timer0); - - let mut pwm_pin = mcpwm - .operator0 - .with_pin_a(peripherals.GPIO18, PwmPinConfig::UP_ACTIVE_HIGH); - let timer_clock_cfg = clock_config - .timer_clock_with_frequency(19_999, PwmWorkingMode::Increase, Rate::from_hz(50)) - .unwrap(); - mcpwm.timer0.start(timer_clock_cfg); //setup RMT let freq = Rate::from_mhz(80); let rmt = Rmt::new(peripherals.RMT, freq).expect("CAN NOT SET FREQUENCY"); let rx_config = RxChannelConfig::default().with_clk_divider(1); let tx_config = TxChannelConfig::default().with_clk_divider(1); - let mut toggle_pin = Output::new(peripherals.GPIO2, Level::Low, OutputConfig::default()); let mut tx_channel = rmt .channel0 .configure_tx(peripherals.GPIO23, tx_config) @@ -100,20 +79,24 @@ fn main() -> ! { .channel3 .configure_rx(peripherals.GPIO14, rx_config) .unwrap(); - let mut dshot_esc = dshot::DShot::new( + let dshot_esc = dshot::DShot::new( &mut rx_channel, &mut tx_channel, DShotSpeed::DShot600, Some(80_000_000), Some(1), ); - rprintln!("SENDING RMT"); + let dshot_esc = Box::new(dshot_esc); + let dshot_esc = Box::leak(dshot_esc); + + //dshot_esc.arm(&delay).unwrap(); + + dshot_esc.arm(); loop { - delay.delay_millis(1000); - toggle_pin.set_high(); - dshot_esc.write_throttle(2047, true); - toggle_pin.set_low(); + dshot_esc.process(); + dshot_esc.set_throttle(200); } + // rprintln!("RMT SENT!"); // let mut esc = AfroEsc::new(&mut pwm_pin);; // esc.set_timestamp(1000); @@ -149,8 +132,8 @@ fn main() -> ! { let mut display = Nextion::new(&mut uart0); display.send_command(b"page page0"); - let mut rpm = DEFAULT_TARGET_RPM; - let mut timer = DEFAULT_SPIN_TIME; + let mut _rpm = DEFAULT_TARGET_RPM; + let mut _timer = DEFAULT_SPIN_TIME; let mut started = false; loop { if display.read_ready() { @@ -169,11 +152,11 @@ fn main() -> ! { } Ok(Command::SetRpm(x)) => { rprintln!("SET_RPM with {}", x); - rpm = x; + _rpm = x; } Ok(Command::SetTimer(x)) => { rprintln!("SETTING TIMER {}", x); - timer = x; + _timer = x; } Ok(Command::SendConfig) => { rprintln!("SEND CONFIG"); @@ -193,30 +176,7 @@ fn main() -> ! { // spincoater.armed(); rprintln!("STARTING!") } - //display.send_command(b"page page0"); - //uart0.write(b"page page0\xff\xff\xff"); } - // Example: Ramp from 0% to 50% throttle - //the afro esc starts turning at roughly setup gets a PWM at - //loop{ - // for pulse in 1120..1500 { - // rprintln!("pulse:{}",pulse); - // pwm_pin.set_timestamp(pulse); - // Timer::after_millis(3000).await; - // } - - // Timer::after_millis(1000).await; - - // // Return to idle - // pwm_pin.set_timestamp(1000); - // Timer::after_millis(1000).await; - // } - loop { - rprintln!("Hello world!"); - delay.delay_millis(3000); - } - - // for inspiration have a look at the examples at https://github.com/esp-rs/esp-hal/tree/esp-hal-v1.0.0/examples/src/bin } #[handler] @@ -224,7 +184,7 @@ fn main() -> ! { fn handler() { critical_section::with(|cs| { let mut binding = EMERGENCY_BUTTON.borrow_ref_mut(cs); - let mut button = binding.as_mut().unwrap(); + let button = binding.as_mut().unwrap(); if button.is_interrupt_set() { // do the thing rprintln!("BUTTON1 WAS PRESSED!"); diff --git a/spinnyboy_rust/src/bin/peripherals/mod.rs b/spinnyboy_rust/src/bin/peripherals/mod.rs index 9ff877d..5998a6e 100644 --- a/spinnyboy_rust/src/bin/peripherals/mod.rs +++ b/spinnyboy_rust/src/bin/peripherals/mod.rs @@ -11,5 +11,6 @@ pub enum Command { #[derive(Debug)] pub enum ErrCommand { NoValidCmd, + #[allow(dead_code)] ReadError, } diff --git a/spinnyboy_rust/src/bin/peripherals/nextion.rs b/spinnyboy_rust/src/bin/peripherals/nextion.rs index 69b3f45..b25ae65 100644 --- a/spinnyboy_rust/src/bin/peripherals/nextion.rs +++ b/spinnyboy_rust/src/bin/peripherals/nextion.rs @@ -97,7 +97,7 @@ impl<'a> Nextion<'a> { let mut buf: [u8; 8] = [0; 8]; let _read_bytes = match self.read_frame(&mut buf) { Ok(x) => x, - Err(e) => { + Err(_e) => { rprintln!("ERROR while reading"); return Err(ErrCommand::NoValidCmd); }