diff --git a/hardware/connector.FCStd b/hardware/connector.FCStd index c6cc3c9..309ca54 100644 Binary files a/hardware/connector.FCStd and b/hardware/connector.FCStd differ diff --git a/spinnyboy_rust/Cargo.lock b/spinnyboy_rust/Cargo.lock index 733c75c..742f3de 100644 --- a/spinnyboy_rust/Cargo.lock +++ b/spinnyboy_rust/Cargo.lock @@ -40,6 +40,12 @@ dependencies = [ "syn 2.0.111", ] +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + [[package]] name = "bitflags" version = "2.10.0" @@ -155,6 +161,38 @@ dependencies = [ "syn 2.0.111", ] +[[package]] +name = "defmt" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "548d977b6da32fa1d1fda2876453da1e7df63ad0304c8b3dae4dbe7b96f39b78" +dependencies = [ + "bitflags 1.3.2", + "defmt-macros", +] + +[[package]] +name = "defmt-macros" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d4fc12a85bcf441cfe44344c4b72d58493178ce635338a3f3b78943aceb258e" +dependencies = [ + "defmt-parser", + "proc-macro-error2", + "proc-macro2", + "quote", + "syn 2.0.111", +] + +[[package]] +name = "defmt-parser" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10d60334b3b2e7c9d91ef8150abfb6fa4c1c39ebbcf4a81c2e346aad939fee3e" +dependencies = [ + "thiserror", +] + [[package]] name = "delegate" version = "0.13.5" @@ -227,7 +265,7 @@ dependencies = [ "embedded-io-async 0.6.1", "futures-sink", "futures-util", - "heapless", + "heapless 0.8.0", ] [[package]] @@ -241,7 +279,7 @@ dependencies = [ "embedded-io-async 0.6.1", "futures-core", "futures-sink", - "heapless", + "heapless 0.8.0", ] [[package]] @@ -366,6 +404,23 @@ dependencies = [ "rlsf", ] +[[package]] +name = "esp-backtrace" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3318413fb566c7227387f67736cf70cd74d80a11f2bb31c7b95a9eb48d079669" +dependencies = [ + "cfg-if", + "defmt", + "document-features", + "esp-config", + "esp-metadata-generated", + "esp-println", + "heapless 0.9.2", + "riscv", + "xtensa-lx", +] + [[package]] name = "esp-bootloader-esp-idf" version = "0.4.0" @@ -403,7 +458,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "54786287c0a61ca0f78cb0c338a39427551d1be229103b4444591796c579e093" dependencies = [ "bitfield", - "bitflags", + "bitflags 2.10.0", "bytemuck", "cfg-if", "critical-section", @@ -469,6 +524,18 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a93e39c8ad8d390d248dc7b9f4b59a873f313bf535218b8e2351356972399e3" +[[package]] +name = "esp-println" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a30e6c9fbcc01c348d46706fef8131c7775ab84c254a3cd65d0cd3f6414d592" +dependencies = [ + "document-features", + "esp-metadata-generated", + "esp-sync", + "log", +] + [[package]] name = "esp-riscv-rt" version = "0.13.0" @@ -662,6 +729,16 @@ dependencies = [ "stable_deref_trait", ] +[[package]] +name = "heapless" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2af2455f757db2b292a9b1768c4b70186d443bcb3b316252d6b540aec1cd89ed" +dependencies = [ + "hash32", + "stable_deref_trait", +] + [[package]] name = "heck" version = "0.5.0" @@ -742,6 +819,12 @@ version = "0.2.178" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091" +[[package]] +name = "libm" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" + [[package]] name = "linked_list_allocator" version = "0.10.5" @@ -788,6 +871,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", + "libm", ] [[package]] @@ -841,6 +925,28 @@ dependencies = [ "toml_edit", ] +[[package]] +name = "proc-macro-error-attr2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "proc-macro-error2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" +dependencies = [ + "proc-macro-error-attr2", + "proc-macro2", + "quote", + "syn 2.0.111", +] + [[package]] name = "proc-macro2" version = "1.0.103" @@ -1028,9 +1134,12 @@ name = "spinnyboy_rust" version = "0.1.0" dependencies = [ "critical-section", + "embedded-hal 1.0.0", "esp-alloc", + "esp-backtrace", "esp-bootloader-esp-idf", "esp-hal", + "num-traits", "rtt-target", ] @@ -1111,6 +1220,26 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "thiserror" +version = "2.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.111", +] + [[package]] name = "toml_datetime" version = "0.7.5+spec-1.1.0" diff --git a/spinnyboy_rust/Cargo.toml b/spinnyboy_rust/Cargo.toml index 653e6f9..38277ee 100644 --- a/spinnyboy_rust/Cargo.toml +++ b/spinnyboy_rust/Cargo.toml @@ -14,12 +14,13 @@ esp-hal = { version = "1.0.0", features = ["esp32c6", "unstable"] } esp-bootloader-esp-idf = { version = "0.4.0", features = ["esp32c6"] } -critical-section = "1.2.0" +critical-section = {version = "1.2.0"} esp-alloc = "0.9.0" rtt-target = "0.6.2" embedded-hal = "1.0.0" esp-backtrace = {version = "0.18.1",features = ["esp32c6","defmt"]} - +#esp-hal-dshot = {version = "0.3.1",default-features = false ,features = ["esp32c6"]} +num-traits = { version = "0.2.19", default-features = false, features = ["libm"] } [profile.dev] # Rust debug is too slow. diff --git a/spinnyboy_rust/src/bin/dc_driver/dshot.rs b/spinnyboy_rust/src/bin/dc_driver/dshot.rs new file mode 100644 index 0000000..1ac3866 --- /dev/null +++ b/spinnyboy_rust/src/bin/dc_driver/dshot.rs @@ -0,0 +1,207 @@ +// use embedded_hal_async::delay::DelayNs; +use esp_hal::{ + delay::Delay, + gpio::Level, + rmt::{Channel, PulseCode, Rx, Tx, TxChannelCreator}, +}; +use num_traits::float::FloatCore; + +#[allow(dead_code)] +#[allow(non_camel_case_types)] +#[derive(Debug)] +pub enum DSHOT_TELEMETRY_CMD { + MOTOR_STOP = 0x0, + BEEP1 = 0x01, + BEEP2 = 0x02, + BEEP3 = 0x03, + BEEP4 = 0x04, + BEEP5 = 0x05, + ESC_INFO = 0x06, + SPIN_DIRECTION_1 = 0x07, + SPIN_DIRECTION_2 = 0x08, + MODE_3D_OFF = 0x09, + MODE_3D_ON = 0x0A, + SETTINGS_REQUEST = 0x0B, + SAVE_SETTINGS = 0x0C, + EXTENDED_TELEMETRY_ENABLE = 0x0D, + EXTENDED_TELEMETRY_DISABLE = 0x0E, +} + +#[derive(Debug, Clone, Copy)] +pub struct BitTicks { + t0_h: u16, + t0_l: u16, + t1_h: u16, + t1_l: u16, +} + +impl BitTicks { + pub fn new(t1_h: u16, t0_h: u16) -> Self { + Self { + t0_h, + t1_h, + t0_l: t1_h, + t1_l: t0_h, + } + } + + pub fn from_clk(clk_speed: u32, clk_divider: u8, bit_times: BitTimes) -> Self { + let tick_len = (1. / clk_speed as f32) * (clk_divider as f32) * 1_000_000.; + + let t1_h = (bit_times.t1_h / tick_len).round() as u16; + let t0_h = (bit_times.t0_h / tick_len).round() as u16; + + Self::new(t1_h, t0_h) + } +} + +/// High and Low Times in µs +#[derive(Debug, Clone, Copy)] +pub struct BitTimes { + t0_h: f32, + t1_h: f32, +} + +impl BitTimes { + pub fn new(t1_h: f32, t0_h: f32) -> Self { + Self { t0_h, t1_h } + } +} + +#[derive(Debug, Clone, Copy)] +pub enum DShotSpeed { + DShot150, + DShot300, + DShot600, + DShot1200, +} + +impl DShotSpeed { + pub fn bit_period_ns(&self) -> u32 { + match self { + // 6.67µs per bit + Self::DShot150 => 6667, + // 3.33µs per bit + Self::DShot300 => 3333, + // 1.67µs per bit + Self::DShot600 => 1667, + // 0.83µs per bit + Self::DShot1200 => 833, + } + } + + /// High and Low Times in µs + pub fn bit_times(&self) -> BitTimes { + match &self { + Self::DShot150 => BitTimes::new(5.00, 2.50), + Self::DShot300 => BitTimes::new(2.50, 1.25), + Self::DShot600 => BitTimes::new(1.25, 0.625), + 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)] +#[derive(Debug)] +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, +} + +impl<'a> DShot<'a> { + pub fn new( + 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, + clk_speed: Option, + clk_divider: Option, + ) -> 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()); + Self { + rx_channel: rx_channel, + tx_channel: tx_channel, + speed, + bit_ticks, + } + } + + pub fn calculate_crc(frame: u16) -> u16 { + (frame ^ (frame >> 4) ^ (frame >> 8)) & 0xF + } + + pub fn create_frame(value: u16, telemetry: bool) -> u16 { + // Mask to 11 bits (0-2047 range) and set telemetry bit + let frame = ((value & 0x07FF) << 1) | telemetry as u16; + + let crc = Self::calculate_crc(frame); + + (frame << 4) | crc + } + + #[allow(clippy::needless_range_loop)] + pub fn create_pulses(&mut self, throttle_value: u16, telemetry: bool) -> [u32; 17] { + let frame = Self::create_frame(throttle_value, telemetry); + let mut pulses = [0; 17]; + for i in 0..16 { + let bit = (frame >> (15 - i)) & 1; + + pulses[i] = if bit == 1 { + PulseCode::new( + Level::High, + self.bit_ticks.t1_h, + Level::Low, + self.bit_ticks.t1_l, + ) + .into() + } else { + PulseCode::new( + Level::High, + self.bit_ticks.t0_h, + Level::Low, + self.bit_ticks.t0_l, + ) + .into() + }; + } + + // Add empty pulse to end of pulses frame + 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!")); + + Ok(()) + } + + pub fn arm(&mut self, delay: &mut Delay) -> Result<(), &'static str> { + for _ in 0..100 { + self.write_throttle(0, false)?; + delay.delay_millis(20); + } + + Ok(()) + } +} diff --git a/spinnyboy_rust/src/bin/dc_driver/mod.rs b/spinnyboy_rust/src/bin/dc_driver/mod.rs index 5ebbfd5..40e125f 100644 --- a/spinnyboy_rust/src/bin/dc_driver/mod.rs +++ b/spinnyboy_rust/src/bin/dc_driver/mod.rs @@ -1,5 +1,5 @@ pub mod afroesc; - +pub mod dshot; pub enum EscState { Starting, Running, diff --git a/spinnyboy_rust/src/bin/main.rs b/spinnyboy_rust/src/bin/main.rs index 09b3f51..5efd3fc 100644 --- a/spinnyboy_rust/src/bin/main.rs +++ b/spinnyboy_rust/src/bin/main.rs @@ -7,7 +7,7 @@ )] use core::fmt::Write; use alloc::string::ToString; -use esp_hal::uart::{self, Config, Uart}; +use esp_hal::uart::{Config, Uart}; use esp_hal::clock::CpuClock; use esp_hal::delay::Delay; @@ -171,7 +171,6 @@ fn main() -> ! { // pwm_pin.set_timestamp(1000); // Timer::after_millis(1000).await; // } - loop { rprintln!("Hello world!"); delay.delay_millis(3000); diff --git a/src/main.py b/src/main.py index 2a43e84..c96a337 100644 --- a/src/main.py +++ b/src/main.py @@ -87,6 +87,7 @@ def fafo(): bgr = f.read(3) tft._pushcolor(TFTColor(bgr[2],bgr[1],bgr[0])) time.sleep(5) + tft.fill(TFT.BLACK) def tftprinttest(): tft.fill(TFT.BLACK) v = 30 @@ -132,6 +133,7 @@ def start_view(state, rotary): #print(rotary.value()) def draw_edit_menu(state, rotary): + tft.fill(TFT.BLACK) display.print("Deposit speed:") display.print("{: >{w}} RPM".format(config["deposit_rpm"], w=5)) display.print("Coating speed:") @@ -160,7 +162,6 @@ def draw_rpm(rpm): def deposit_view(state, rotary): - # display.fill_rect(0, 0, 127, 14, 1) display.print("Deposit") draw_rpm(state["rpm"]) display.print("Press to") @@ -176,6 +177,7 @@ def coating_view(state, rotary): #def decode_ESC_telemetry(data, motor_poles=14): # if len(data) > 10: + # display.fill_rect(0, 0, 127, 14, 1) # # use latest telemetry # data = data[-10:] # @@ -190,13 +192,13 @@ def coating_view(state, rotary): # erpm = int((data[7] << 8) | data[8]) * 100 # rpm = erpm / (motor_poles / 2) # crc = data[9] -# # print(" Temp (C):", temperature) # print(" Voltage (V):", voltage) # print(" Current (A):", current) # print("Consumption (mAh):", consumption) # print(" Erpm:", erpm) # print(" RPM:", rpm) +# # print(" CRC:", crc) # print() # @@ -207,7 +209,6 @@ async def update_display(): global state global rotary while True: - display.print("LOLOLOL") state["view"](state, rotary) await uasyncio.sleep_ms(200) @@ -263,7 +264,7 @@ def timer_callback(f): def kill_me(f): state["target_rpm"] = 0 -def update_motor_pwm(): +async def update_motor_pwm(): global timer3 global current_rpm pwm = PWM(Pin(26)) @@ -279,30 +280,14 @@ def update_motor_pwm(): pwm.freq(50) timer3.init(mode=Timer.PERIODIC, period=100, callback=timer_callback) - pwm.duty_ns(1055*10**3) - time.sleep(1) - - print((current_rpm*60)/COILS) - pwm.duty_ns(1055*10**3) - - #pwm.duty_ns(1200*10**3) - #global rpm_counter - #rpm_counter= 0 - #time.sleep(10) - - # time.sleep(10) # keep low signal to arm - # # for i in range(1060,1800): - # # pwm.duty_u16(i*3) - # # time.sleep(1) - # pwm.duty_ns(1070*10**3) - # pwm.duty_u16(1700) - # time.sleep(200) pwm.duty_ns(1060*10**3) - state["target_rpm"] = 5000 - timer2.init(mode=Timer.PERIODIC, period=10000, callback=kill_me) - + time.sleep(3) + print((current_rpm*60)/COILS) + state["target_rpm"] = 4000 + coating_time = config["coating_time"]*1000 + timer2.init(mode=Timer.PERIODIC, period=coating_time, callback=kill_me) + while(True): - print(f'target RPM:{state["target_rpm"]}') rpm_pid.setpoint = state["target_rpm"] @@ -323,15 +308,14 @@ def update_motor_pwm(): if state["target_rpm"] == 0: print("Killing Coater!") throttle = 0 - rpm_pid.reset() - #pwm.duty_ns(1800*10**3) - #time.sleep(3) - #return - print(throttle) + if throttle !=0: - new_duty = 1060+(int(660*throttle)) + # print(throttle) + new_duty = 1055+(int(665*throttle)) + print(new_duty) pwm.duty_ns(new_duty*10**3) else: + print("KILLED!") new_duty = 1055 pwm.duty_ns(new_duty*10**3) timer2.deinit() @@ -392,7 +376,6 @@ def on_button_press(p): if state["view"] == edit_coating_rpm_view: state["view"] = edit_coating_time_view rotary.set( - max_val=1000, min_val=0, max_val=9999, range_mode=RotaryIRQ.RANGE_BOUNDED, @@ -521,14 +504,15 @@ tft.fill(TFT.BLACK) # splash() fafo() +tft.fill(TFT.BLACK) # for ff in fonts: # display.set_font(ff) # display.print(text) -update_motor_pwm() +#update_motor_pwm() event_loop = uasyncio.get_event_loop() -# event_loop.create_task(update_display()) - -# #event_loop.create_task(update_motor_pwm()) -# event_loop.run_forever() +event_loop.create_task(update_display()) +update_display() +#event_loop.create_task(update_motor_pwm()) +#event_loop.run_forever()