From e47f8af4a2b74bd7d5ed54ca3a8fb359141c4f13 Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Sun, 26 Jun 2022 23:02:05 +0200 Subject: [PATCH 01/62] The beginning of a first rough draft... --- examples/mcpwm-simple.rs | 36 +++ src/lib.rs | 2 + src/mcpwm.rs | 458 +++++++++++++++++++++++++++++++++++++++ src/peripherals.rs | 18 ++ 4 files changed, 514 insertions(+) create mode 100644 examples/mcpwm-simple.rs create mode 100644 src/mcpwm.rs diff --git a/examples/mcpwm-simple.rs b/examples/mcpwm-simple.rs new file mode 100644 index 00000000000..5633af0b1e3 --- /dev/null +++ b/examples/mcpwm-simple.rs @@ -0,0 +1,36 @@ +use embedded_hal::delay::blocking::DelayUs; + +use esp_idf_hal::delay::FreeRtos; +use esp_idf_hal::mcpwm::{Mcpwm, Operator, OperatorConfig}; +use esp_idf_hal::prelude::Peripherals; +use esp_idf_hal::units::FromValueType; + +fn main() -> anyhow::Result<()> { + esp_idf_sys::link_patches(); + + println!("Configuring MCPWM"); + + let peripherals = Peripherals::take().unwrap(); + let config = OperatorConfig::default().frequency(25.kHz().into()); + let mcpwm = Mcpwm::new(peripherals.mcpwm0.mcpwm)?; + let mut operator = Operator::new( + peripherals.mcpwm0.operator0, + &mcpwm, + &config, + peripherals.pins.gpio4, + peripherals.pins.gpio5, + )?; + + println!("Starting duty-cycle loop"); + + for &duty in [0.0, 20.0, 40.0, 60.0, 80.0, 100.0].iter().cycle() { + println!("Duty {}%", duty); + operator.set_duty_a(duty)?; + operator.set_duty_b(100.0 - duty)?; + FreeRtos.delay_ms(2000)?; + } + + loop { + FreeRtos.delay_ms(1000)?; + } +} diff --git a/src/lib.rs b/src/lib.rs index 3bf4c427026..9177bca93bc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -42,6 +42,8 @@ pub mod ledc; not(feature = "riscv-ulp-hal") ))] pub mod mac; +#[cfg(any(esp32, esp32s3))] +pub mod mcpwm; #[cfg(not(feature = "riscv-ulp-hal"))] pub mod modem; pub mod peripheral; diff --git a/src/mcpwm.rs b/src/mcpwm.rs new file mode 100644 index 00000000000..ea400e31d77 --- /dev/null +++ b/src/mcpwm.rs @@ -0,0 +1,458 @@ +//! Motor Control Pulse Width Modulator peripheral +//! +//! Interface to the [Motor Control Pulse Width Modulator peripheral (MCPWM) +//! peripheral](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/mcpwm.html) +//! +//! This is an initial implementation supporting !::!:!:!::!:!:!:! +//! TODO: write stuff here +//! +//! TODO: COme up with nice example +//! # Examples +//! +//! Create a 25 kHz PWM signal with 75 % duty cycle on GPIO 1 +//! ``` +//! +//! TODO: write example not stolen from ledc +//! ``` +//! +//! See the `examples/` folder of this repository for more. + +use core::borrow::Borrow; + +use crate::gpio::OutputPin; +use crate::units::{FromValueType, Hertz}; +use esp_idf_sys::{esp, mcpwm_config_t, EspError}; +use esp_idf_sys::{ + mcpwm_counter_type_t, + mcpwm_counter_type_t_MCPWM_DOWN_COUNTER, + //mcpwm_counter_type_t_MCPWM_FREEZE_COUNTER, + mcpwm_counter_type_t_MCPWM_UP_COUNTER, + mcpwm_counter_type_t_MCPWM_UP_DOWN_COUNTER, +}; +use esp_idf_sys::{ + mcpwm_duty_type_t, mcpwm_duty_type_t_MCPWM_DUTY_MODE_0, mcpwm_duty_type_t_MCPWM_DUTY_MODE_1, +}; +use esp_idf_sys::{ + mcpwm_io_signals_t, mcpwm_io_signals_t_MCPWM0A, mcpwm_io_signals_t_MCPWM0B, + mcpwm_io_signals_t_MCPWM1A, mcpwm_io_signals_t_MCPWM1B, mcpwm_io_signals_t_MCPWM2A, + mcpwm_io_signals_t_MCPWM2B, +}; +use esp_idf_sys::{ + mcpwm_timer_t, mcpwm_timer_t_MCPWM_TIMER_0, mcpwm_timer_t_MCPWM_TIMER_1, + mcpwm_timer_t_MCPWM_TIMER_2, +}; +use esp_idf_sys::{mcpwm_unit_t, mcpwm_unit_t_MCPWM_UNIT_0, mcpwm_unit_t_MCPWM_UNIT_1}; + +/// The Motor Control Pulse Width Modulator peripheral +pub struct Peripheral { + pub mcpwm: MCPWM, // TODO: Is there a better way to name or structure this? + pub operator0: OPERATOR0, + pub operator1: OPERATOR1, + pub operator2: OPERATOR2, +} + +impl Peripheral { + pub unsafe fn new() -> Self { + Self { + mcpwm: MCPWM::new(), + operator0: OPERATOR0::new(), + operator1: OPERATOR1::new(), + operator2: OPERATOR2::new(), + } + } +} + +#[derive(Clone, Copy, Debug)] +pub enum DutyMode { + ActiveHigh, + ActiveLow, +} + +impl Into for DutyMode { + fn into(self) -> mcpwm_duty_type_t { + match self { + DutyMode::ActiveHigh => mcpwm_duty_type_t_MCPWM_DUTY_MODE_0, + DutyMode::ActiveLow => mcpwm_duty_type_t_MCPWM_DUTY_MODE_1, + } + } +} + +// TODO: For UpDown, frequency is half of MCPWM frequency set +#[derive(Clone, Copy, Debug)] +pub enum CounterMode { + //Frozen, + Up, + Down, + UpDown, +} + +impl Into for CounterMode { + fn into(self) -> mcpwm_counter_type_t { + match self { + // TODO: This seems to be new to IDF 4.4? + //CounterMode::Frozen => mcpwm_counter_type_t_MCPWM_FREEZE_COUNTER, + CounterMode::Up => mcpwm_counter_type_t_MCPWM_UP_COUNTER, + CounterMode::Down => mcpwm_counter_type_t_MCPWM_DOWN_COUNTER, + CounterMode::UpDown => mcpwm_counter_type_t_MCPWM_UP_DOWN_COUNTER, + } + } +} + +pub struct OperatorConfig { + frequency: Hertz, + duty_a: Duty, + duty_b: Duty, + + #[cfg(idf_newer_than_or_equal_v4_4_0)] + lowest_frequency: Hertz, + duty_mode: DutyMode, + counter_mode: CounterMode, + //deadtime: DeadtimeConfig, +} + +impl OperatorConfig { + #[must_use] + pub fn frequency(mut self, frequency: Hertz) -> Self { + self.frequency = frequency; + self + } + + #[cfg(idf_newer_than_or_equal_v4_4_0)] + #[must_use] + pub fn lowest_frequency(mut self, lowest_frequency: Hertz) -> Self { + self.lowest_frequency = lowest_frequency; + self + } + + #[must_use] + pub fn duty_mode(mut self, duty_mode: DutyMode) -> Self { + self.duty_mode = duty_mode; + self + } + + #[must_use] + pub fn counter_mode(mut self, counter_mode: CounterMode) -> Self { + self.counter_mode = counter_mode; + self + } + + /* + #[must_use] + pub fn deadtime_config(mut self, deadtime_config: DeadtimeConfig) -> Self { + todo!() + }*/ +} + +impl Default for OperatorConfig { + fn default() -> Self { + Self { + frequency: 1000.Hz(), + duty_a: 50.0, + duty_b: 50.0, + + #[cfg(idf_newer_than_or_equal_v4_4_0)] + lowest_frequency: 1.Hz(), + + duty_mode: DutyMode::ActiveHigh, + counter_mode: CounterMode::Up, + } + } +} + +#[derive(Default)] +pub struct UnitZero; + +#[derive(Default)] +pub struct UnitOne; + +pub type Duty = f32; +const MAX_DUTY: Duty = 100.0; + +pub struct MCPWM { + unit: U, +} + +impl MCPWM { + /// # Safety + /// + /// It is safe to instantiate this exactly one time per `Unit`. + unsafe fn new() -> Self { + Self { unit: U::default() } + } +} + +pub trait Unit: Default { + fn unit() -> mcpwm_unit_t; +} + +impl Unit for UnitZero { + fn unit() -> mcpwm_unit_t { + mcpwm_unit_t_MCPWM_UNIT_0 + } +} + +impl Unit for UnitOne { + fn unit() -> mcpwm_unit_t { + mcpwm_unit_t_MCPWM_UNIT_1 + } +} + +// TODO: How do we want fault module to fit into this? +// TODO: How do we want capture module to fit into this? + +pub struct Mcpwm { + instance: MCPWM, +} + +impl Mcpwm { + pub fn new(instance: MCPWM) -> Result { + todo!(); + Ok(Self { instance }) + } + + // TODO: I do not at all understand the motivation behind exposing the group prescaler as + // "resolution"? From what i can see, duty is exposed as a percentage so we are not talking + // about the pulse length in terms of cycles as is the case for ledc, right? What am I missing? + // Anyway, my reasoning: + // * High res => small prescaler => better maintan accuracy at high frequency, unable to reach lower freq + // * Lower res => larger prescaler => worse accuracy at high frequency(still as good at low frequency), + // able to reach lower freq + // Would it make more sense to expose it as "lowest reachable frequency" + // Also why are there no checks other than prescaler >= 1 in the idf? + // What happens if prescaler register value does not fit into the least significant 8 bits? + #[cfg(idf_newer_than_or_equal_v4_4_0)] + pub fn lowest_frequency(mut self, lowest_frequency: Hertz) -> Result { + // MCPWM clock source frequency for ESP32 and ESP32-s3 + const MCPWM_CLOCK_SOURCE_FREQUENCY: u32 = 160_000_000; + // Max PWM timer prescaler + const MAX_PWM_TIMER_PRESCALE: u32 = 0x1_00; + // Max PWM timer period + const MAX_PWM_TIMER_PERIOD: u32 = 0x1_00_00; + + // let lowest_frequency = MCPWM_CLOCK_SOURCE_FREQUENCY / group_prescaler_factor / MAX_PWM_TIMER_PRESCALE / MAX_PWM_TIMER_PERIOD; + // let MAX_PWM_TIMER_PRESCALE * MAX_PWM_TIMER_PERIOD * frequency = MCPWM_CLOCK_SOURCE_FREQUENCY / group_prescaler_factor; + // let group_prescaler_factor = MCPWM_CLOCK_SOURCE_FREQUENCY / (MAX_PWM_TIMER_PRESCALE * MAX_PWM_TIMER_PERIOD * frequency); + + // let resolution = MCPWM_CLOCK_SOURCE_FREQUENCY / group_prescaler; + + // let resolution = MCPWM_CLOCK_SOURCE_FREQUENCY / (MCPWM_CLOCK_SOURCE_FREQUENCY / (MAX_PWM_TIMER_PRESCALE * MAX_PWM_TIMER_PERIOD * frequency)); + // let resolution = (MAX_PWM_TIMER_PRESCALE * MAX_PWM_TIMER_PERIOD * frequency) * 160_000_000 / MCPWM_CLOCK_SOURCE_FREQUENCY; + let resolution = MAX_PWM_TIMER_PRESCALE * MAX_PWM_TIMER_PERIOD * lowest_reachable_frequency; + + mcpwm_group_set_resolution(); + todo!() + } +} + +// The hardware for ESP32 and ESP32-S3 can associate any operator(within the mcpwm module) with any +// timer(within the mcpwm module) for example allowing using the same timer for all three operators. +// However at least as of IDF v4.4 timer0 is hardcoded to operator0 and timer1 to operator1 and so on... +pub trait HwOperator { + fn timer() -> mcpwm_timer_t; + fn signal_a() -> mcpwm_io_signals_t; + fn signal_b() -> mcpwm_io_signals_t; + fn unit() -> mcpwm_unit_t { + U::unit() + } +} + +macro_rules! impl_operator_helper { + ($instance:ident: $timer:expr, $signal_a:expr, $signal_b:expr, $unit:ty) => { + impl HwOperator<$unit> for $instance<$unit> { + fn timer() -> mcpwm_timer_t { + $timer + } + + fn signal_a() -> mcpwm_io_signals_t { + $signal_a + } + + fn signal_b() -> mcpwm_io_signals_t { + $signal_b + } + } + }; +} + +macro_rules! impl_operator { + ($instance:ident: $timer:expr, $signal_a:expr, $signal_b:expr) => { + pub struct $instance { + unit: U, + } + + impl $instance { + /// # Safety + /// + /// It is safe to instantiate this operator exactly one time. + pub unsafe fn new() -> Self { + $instance { unit: U::default() } + } + } + + impl_operator_helper!($instance: $timer, $signal_a, $signal_b, UnitZero); + impl_operator_helper!($instance: $timer, $signal_a, $signal_b, UnitOne); + }; +} + +impl_operator!( + OPERATOR0: mcpwm_timer_t_MCPWM_TIMER_0, + mcpwm_io_signals_t_MCPWM0A, + mcpwm_io_signals_t_MCPWM0B +); +impl_operator!( + OPERATOR1: mcpwm_timer_t_MCPWM_TIMER_1, + mcpwm_io_signals_t_MCPWM1A, + mcpwm_io_signals_t_MCPWM1B +); +impl_operator!( + OPERATOR2: mcpwm_timer_t_MCPWM_TIMER_2, + mcpwm_io_signals_t_MCPWM2A, + mcpwm_io_signals_t_MCPWM2B +); + +// TODO: How do we want syncing to fit in to this? +// TODO: How do we want deadtime to fit into this? +// TODO: How do we want carrier to fit into this? + +pub struct Operator, M: Borrow>, PA: OutputPin, PB: OutputPin> { + instance: O, + unit: U, + mcpwm_module: M, + pin_a: Option, + pin_b: Option, +} + +impl, M: Borrow>, PA: OutputPin, PB: OutputPin> + Operator +{ + pub fn new>, B: Into>>( + operator: O, + mcpwm_module: M, + config: &OperatorConfig, + pin_a: A, + pin_b: B, + ) -> Result { + // TODO: Dont forget to make these cases work + #[cfg(idf_newer_than_or_equal_v4_4_0)] + { + if config.frequency < config.lowest_frequency { + return panic!("TODO: Invalid parameter, should this be checked in OperatorConfig or here or hope that the IDF? will handle the error checking"); + } + unsafe { + esp_idf_sys::mcpwm_timer_set_resolution(); + } + } + unsafe { + // TODO: Handle half pwm frequency when counter_mode = UpDown here? + + esp!(unsafe { + esp_idf_sys::mcpwm_init( + U::unit(), + O::timer(), + &mcpwm_config_t { + frequency: config.frequency.into(), + cmpr_a: config.duty_a, + cmpr_b: config.duty_b, + duty_mode: config.duty_mode.into(), + counter_mode: config.counter_mode.into(), + }, + ) + })?; + + todo!(); + } + + let pin_a: Option = pin_a.into(); + let pin_b: Option = pin_b.into(); + + if let Some(pin_a) = &pin_a { + let io_signal = O::signal_a(); + esp!(unsafe { esp_idf_sys::mcpwm_gpio_init(U::unit(), io_signal, pin_a.pin()) })?; + } + + if let Some(pin_b) = &pin_b { + let io_signal = O::signal_b(); + esp!(unsafe { esp_idf_sys::mcpwm_gpio_init(U::unit(), io_signal, pin_b.pin()) })?; + } + + Ok(Self { + instance: operator, + unit: U::default(), + mcpwm_module, + pin_a, + pin_b, + }) + } + + pub fn get_duty_a(&self) -> Result { + todo!() + } + + pub fn get_duty_b(&self) -> Result { + todo!() + } + + pub fn set_duty_a(&mut self, duty: Duty) -> Result<(), EspError> { + todo!() + } + + pub fn set_duty_b(&mut self, duty: Duty) -> Result<(), EspError> { + todo!() + } + + pub fn set_frequency() -> Result<(), EspError> { + todo!() + } + pub fn get_frequency() -> Result<(), EspError> { + todo!() + } +} + +pub enum Generator { + A, + B, +} + +// TODO: would we like to also implement PwmPin for something like Foo<&Operator, G: GeneratorAorB> +// this would allow things like: +// let operator = Operator::new(...); +// let gen_a = operator.generator_a_as_pwm_pin(); +// let gen_b = operator.generator_b_as_pwm_pin(); +// function_expecting_pwm_pin(&gen_a); +// gen_a.set_duty() +// gen_a.set_duty() +impl, M: Borrow>, PA: OutputPin, PB: OutputPin> + embedded_hal_0_2::Pwm for Operator +{ + type Channel = Generator; + type Duty = Duty; + type Time = (); + + fn disable(&mut self, channel: Self::Channel) { + todo!() + } + + fn enable(&mut self, channel: Self::Channel) { + todo!() + } + + fn get_period(&self) -> Self::Time { + todo!() + } + + fn get_duty(&self, channel: Self::Channel) -> Self::Duty { + self.get_duty(channel) + } + + fn get_max_duty(&self) -> Self::Duty { + MAX_DUTY + } + + fn set_duty(&mut self, channel: Self::Channel, duty: Self::Duty) { + self.set_duty(channel, duty) + } + + fn set_period

(&mut self, period: P) { + todo!() + } +} diff --git a/src/peripherals.rs b/src/peripherals.rs index d14dde82363..bc2eb013a66 100644 --- a/src/peripherals.rs +++ b/src/peripherals.rs @@ -11,6 +11,8 @@ use crate::ledc; not(feature = "riscv-ulp-hal") ))] use crate::mac; +#[cfg(any(esp32, esp32s3))] +use crate::mcpwm; #[cfg(not(feature = "riscv-ulp-hal"))] use crate::modem; #[cfg(not(feature = "riscv-ulp-hal"))] @@ -53,7 +55,15 @@ pub struct Peripherals { #[cfg(not(feature = "riscv-ulp-hal"))] pub can: can::CAN, #[cfg(not(feature = "riscv-ulp-hal"))] +<<<<<<< HEAD pub ledc: ledc::LEDC, +======= + pub ledc: ledc::Peripheral, + #[cfg(any(esp32, esp32s3))] + pub mcpwm0: mcpwm::Peripheral, + #[cfg(any(esp32, esp32s3))] + pub mcpwm1: mcpwm::Peripheral, +>>>>>>> The beginning of a first rough draft... #[cfg(not(feature = "riscv-ulp-hal"))] pub rmt: rmt::RMT, #[cfg(all( @@ -161,7 +171,15 @@ impl Peripherals { #[cfg(not(feature = "riscv-ulp-hal"))] can: can::CAN::new(), #[cfg(not(feature = "riscv-ulp-hal"))] +<<<<<<< HEAD ledc: ledc::LEDC::new(), +======= + ledc: ledc::Peripheral::new(), + #[cfg(any(esp32, esp32s3))] + mcpwm0: mcpwm::Peripheral::new(), + #[cfg(any(esp32, esp32s3))] + mcpwm1: mcpwm::Peripheral::new(), +>>>>>>> The beginning of a first rough draft... #[cfg(not(feature = "riscv-ulp-hal"))] rmt: rmt::RMT::new(), #[cfg(all( From 5a86430bb3ec9a2c8aaaef54fed15e1b3c66b605 Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Thu, 30 Jun 2022 22:16:14 +0200 Subject: [PATCH 02/62] Fix some of the TODOs, disable example for esp variants without mcpwm --- examples/mcpwm-simple.rs | 2 + src/mcpwm.rs | 176 ++++++++++++++++++++++++++------------- 2 files changed, 122 insertions(+), 56 deletions(-) diff --git a/examples/mcpwm-simple.rs b/examples/mcpwm-simple.rs index 5633af0b1e3..e877f593335 100644 --- a/examples/mcpwm-simple.rs +++ b/examples/mcpwm-simple.rs @@ -1,3 +1,5 @@ +#![cfg(any(esp32, esp32s3))] + use embedded_hal::delay::blocking::DelayUs; use esp_idf_hal::delay::FreeRtos; diff --git a/src/mcpwm.rs b/src/mcpwm.rs index ea400e31d77..4d28e33e6cd 100644 --- a/src/mcpwm.rs +++ b/src/mcpwm.rs @@ -6,13 +6,31 @@ //! This is an initial implementation supporting !::!:!:!::!:!:!:! //! TODO: write stuff here //! -//! TODO: COme up with nice example +//! TODO: Come up with nice example //! # Examples //! -//! Create a 25 kHz PWM signal with 75 % duty cycle on GPIO 1 +//! Create a pair of PWM signals on pin 4 and 5. The duty on pin 4 will ramp from 0% to 100% +//! while pin 5 will ramp from 100% down to 0%. //! ``` +//! let peripherals = Peripherals::take().unwrap(); +//! let config = OperatorConfig::default().frequency(25.kHz().into()); +//! let mcpwm = Mcpwm::new(peripherals.mcpwm0.mcpwm)?; +//! let mut operator = Operator::new( +//! peripherals.mcpwm0.operator0, +//! &mcpwm, +//! &config, +//! peripherals.pins.gpio4, +//! peripherals.pins.gpio5, +//! )?; //! -//! TODO: write example not stolen from ledc +//! println!("Starting duty-cycle loop"); +//! +//! for &duty in [0.0, 20.0, 40.0, 60.0, 80.0, 100.0].iter() { +//! println!("Duty {}%", duty); +//! operator.set_duty_a(duty)?; +//! operator.set_duty_b(100.0 - duty)?; +//! FreeRtos.delay_ms(2000)?; +//! } //! ``` //! //! See the `examples/` folder of this repository for more. @@ -169,7 +187,7 @@ pub type Duty = f32; const MAX_DUTY: Duty = 100.0; pub struct MCPWM { - unit: U, + _unit: U, } impl MCPWM { @@ -177,7 +195,9 @@ impl MCPWM { /// /// It is safe to instantiate this exactly one time per `Unit`. unsafe fn new() -> Self { - Self { unit: U::default() } + Self { + _unit: U::default(), + } } } @@ -201,13 +221,14 @@ impl Unit for UnitOne { // TODO: How do we want capture module to fit into this? pub struct Mcpwm { - instance: MCPWM, + _instance: MCPWM, } impl Mcpwm { pub fn new(instance: MCPWM) -> Result { - todo!(); - Ok(Self { instance }) + Ok(Self { + _instance: instance, + }) } // TODO: I do not at all understand the motivation behind exposing the group prescaler as @@ -277,7 +298,7 @@ macro_rules! impl_operator_helper { macro_rules! impl_operator { ($instance:ident: $timer:expr, $signal_a:expr, $signal_b:expr) => { pub struct $instance { - unit: U, + _unit: U, } impl $instance { @@ -285,7 +306,9 @@ macro_rules! impl_operator { /// /// It is safe to instantiate this operator exactly one time. pub unsafe fn new() -> Self { - $instance { unit: U::default() } + $instance { + _unit: U::default(), + } } } @@ -315,11 +338,12 @@ impl_operator!( // TODO: How do we want carrier to fit into this? pub struct Operator, M: Borrow>, PA: OutputPin, PB: OutputPin> { - instance: O, - unit: U, - mcpwm_module: M, - pin_a: Option, - pin_b: Option, + _instance: O, + _unit: U, + _mcpwm_module: M, + + _pin_a: Option, + _pin_b: Option, } impl, M: Borrow>, PA: OutputPin, PB: OutputPin> @@ -336,31 +360,28 @@ impl, M: Borrow>, PA: OutputPin, PB: OutputPi #[cfg(idf_newer_than_or_equal_v4_4_0)] { if config.frequency < config.lowest_frequency { - return panic!("TODO: Invalid parameter, should this be checked in OperatorConfig or here or hope that the IDF? will handle the error checking"); + return Err(panic!("TODO: Invalid parameter, should this be checked in OperatorConfig or here or hope that the IDF? will handle the error checking")); } unsafe { esp_idf_sys::mcpwm_timer_set_resolution(); } } - unsafe { - // TODO: Handle half pwm frequency when counter_mode = UpDown here? - - esp!(unsafe { - esp_idf_sys::mcpwm_init( - U::unit(), - O::timer(), - &mcpwm_config_t { - frequency: config.frequency.into(), - cmpr_a: config.duty_a, - cmpr_b: config.duty_b, - duty_mode: config.duty_mode.into(), - counter_mode: config.counter_mode.into(), - }, - ) - })?; - - todo!(); - } + + // TODO: Handle half pwm frequency when counter_mode = UpDown here? + + esp!(unsafe { + esp_idf_sys::mcpwm_init( + U::unit(), + O::timer(), + &mcpwm_config_t { + frequency: config.frequency.into(), + cmpr_a: config.duty_a, + cmpr_b: config.duty_b, + duty_mode: config.duty_mode.into(), + counter_mode: config.counter_mode.into(), + }, + ) + })?; let pin_a: Option = pin_a.into(); let pin_b: Option = pin_b.into(); @@ -376,35 +397,68 @@ impl, M: Borrow>, PA: OutputPin, PB: OutputPi } Ok(Self { - instance: operator, - unit: U::default(), - mcpwm_module, - pin_a, - pin_b, + _instance: operator, + _unit: U::default(), + _mcpwm_module: mcpwm_module, + _pin_a: pin_a, + _pin_b: pin_b, }) } - pub fn get_duty_a(&self) -> Result { - todo!() + pub fn get_duty_a(&self) -> Duty { + unsafe { + esp_idf_sys::mcpwm_get_duty( + U::unit(), + O::timer(), + esp_idf_sys::mcpwm_generator_t_MCPWM_GEN_A, + ) + } } - pub fn get_duty_b(&self) -> Result { - todo!() + pub fn get_duty_b(&self) -> Duty { + unsafe { + esp_idf_sys::mcpwm_get_duty( + U::unit(), + O::timer(), + esp_idf_sys::mcpwm_generator_t_MCPWM_GEN_B, + ) + } } + /// Set duty as percentage between 0.0 and 100.0 pub fn set_duty_a(&mut self, duty: Duty) -> Result<(), EspError> { - todo!() + unsafe { + esp!(esp_idf_sys::mcpwm_set_duty( + U::unit(), + O::timer(), + esp_idf_sys::mcpwm_generator_t_MCPWM_GEN_A, + duty + )) + } } pub fn set_duty_b(&mut self, duty: Duty) -> Result<(), EspError> { - todo!() + unsafe { + esp!(esp_idf_sys::mcpwm_set_duty( + U::unit(), + O::timer(), + esp_idf_sys::mcpwm_generator_t_MCPWM_GEN_B, + duty + )) + } } - pub fn set_frequency() -> Result<(), EspError> { - todo!() + pub fn set_frequency(frequency: u32) -> Result<(), EspError> { + unsafe { + esp!(esp_idf_sys::mcpwm_set_frequency( + U::unit(), + O::timer(), + frequency + )) + } } - pub fn get_frequency() -> Result<(), EspError> { - todo!() + pub fn get_frequency() -> u32 { + unsafe { esp_idf_sys::mcpwm_get_frequency(U::unit(), O::timer()) } } } @@ -413,7 +467,7 @@ pub enum Generator { B, } -// TODO: would we like to also implement PwmPin for something like Foo<&Operator, G: GeneratorAorB> +// TODO: would we like to also implement PwmPin for something like Foo<&Operator, G: Generator> // this would allow things like: // let operator = Operator::new(...); // let gen_a = operator.generator_a_as_pwm_pin(); @@ -428,11 +482,11 @@ impl, M: Borrow>, PA: OutputPin, PB: OutputPi type Duty = Duty; type Time = (); - fn disable(&mut self, channel: Self::Channel) { + fn disable(&mut self, _channel: Self::Channel) { todo!() } - fn enable(&mut self, channel: Self::Channel) { + fn enable(&mut self, _channel: Self::Channel) { todo!() } @@ -441,7 +495,10 @@ impl, M: Borrow>, PA: OutputPin, PB: OutputPi } fn get_duty(&self, channel: Self::Channel) -> Self::Duty { - self.get_duty(channel) + match channel { + Generator::A => self.get_duty_a(), + Generator::B => self.get_duty_b(), + } } fn get_max_duty(&self) -> Self::Duty { @@ -449,10 +506,17 @@ impl, M: Borrow>, PA: OutputPin, PB: OutputPi } fn set_duty(&mut self, channel: Self::Channel, duty: Self::Duty) { - self.set_duty(channel, duty) + match channel { + Generator::A => self + .set_duty_a(duty) + .expect("Failed to set duty for generator A"), + Generator::B => self + .set_duty_b(duty) + .expect("Failed to set duty for generator B"), + } } - fn set_period

(&mut self, period: P) { + fn set_period

(&mut self, _period: P) { todo!() } } From bef1ea4458f8a61317f755d17ad4c0b605e51092 Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Fri, 1 Jul 2022 17:45:36 +0200 Subject: [PATCH 03/62] Fix clippy lints --- src/mcpwm.rs | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/mcpwm.rs b/src/mcpwm.rs index 4d28e33e6cd..e33cd3f60b3 100644 --- a/src/mcpwm.rs +++ b/src/mcpwm.rs @@ -70,6 +70,9 @@ pub struct Peripheral { } impl Peripheral { + /// # Safety + /// + /// It is safe to instantiate this exactly one time per `Unit`. pub unsafe fn new() -> Self { Self { mcpwm: MCPWM::new(), @@ -86,9 +89,9 @@ pub enum DutyMode { ActiveLow, } -impl Into for DutyMode { - fn into(self) -> mcpwm_duty_type_t { - match self { +impl From for mcpwm_duty_type_t { + fn from(val: DutyMode) -> Self { + match val { DutyMode::ActiveHigh => mcpwm_duty_type_t_MCPWM_DUTY_MODE_0, DutyMode::ActiveLow => mcpwm_duty_type_t_MCPWM_DUTY_MODE_1, } @@ -104,9 +107,9 @@ pub enum CounterMode { UpDown, } -impl Into for CounterMode { - fn into(self) -> mcpwm_counter_type_t { - match self { +impl From for mcpwm_counter_type_t { + fn from(val: CounterMode) -> Self { + match val { // TODO: This seems to be new to IDF 4.4? //CounterMode::Frozen => mcpwm_counter_type_t_MCPWM_FREEZE_COUNTER, CounterMode::Up => mcpwm_counter_type_t_MCPWM_UP_COUNTER, @@ -304,7 +307,7 @@ macro_rules! impl_operator { impl $instance { /// # Safety /// - /// It is safe to instantiate this operator exactly one time. + /// It is safe to instantiate this operator exactly one time per Unit. pub unsafe fn new() -> Self { $instance { _unit: U::default(), From 208bde091ee97419d34bd21a1a9d67953ab0972d Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Fri, 1 Jul 2022 21:47:44 +0200 Subject: [PATCH 04/62] Update cfg's --- src/lib.rs | 2 +- src/mcpwm.rs | 2 ++ src/peripherals.rs | 6 +++--- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 9177bca93bc..ad3db2c1444 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -42,7 +42,7 @@ pub mod ledc; not(feature = "riscv-ulp-hal") ))] pub mod mac; -#[cfg(any(esp32, esp32s3))] +#[cfg(all(any(esp32, esp32s3), not(feature = "riscv-ulp-hal")))] pub mod mcpwm; #[cfg(not(feature = "riscv-ulp-hal"))] pub mod modem; diff --git a/src/mcpwm.rs b/src/mcpwm.rs index e33cd3f60b3..e4b9258fed6 100644 --- a/src/mcpwm.rs +++ b/src/mcpwm.rs @@ -1,3 +1,5 @@ +#![cfg(all(any(esp32, esp32s3), not(feature = "riscv-ulp-hal")))] + //! Motor Control Pulse Width Modulator peripheral //! //! Interface to the [Motor Control Pulse Width Modulator peripheral (MCPWM) diff --git a/src/peripherals.rs b/src/peripherals.rs index bc2eb013a66..b03192c8410 100644 --- a/src/peripherals.rs +++ b/src/peripherals.rs @@ -11,7 +11,7 @@ use crate::ledc; not(feature = "riscv-ulp-hal") ))] use crate::mac; -#[cfg(any(esp32, esp32s3))] +#[cfg(all(any(esp32, esp32s3), not(feature = "riscv-ulp-hal")))] use crate::mcpwm; #[cfg(not(feature = "riscv-ulp-hal"))] use crate::modem; @@ -59,9 +59,9 @@ pub struct Peripherals { pub ledc: ledc::LEDC, ======= pub ledc: ledc::Peripheral, - #[cfg(any(esp32, esp32s3))] + #[cfg(all(any(esp32, esp32s3), not(feature = "riscv-ulp-hal")))] pub mcpwm0: mcpwm::Peripheral, - #[cfg(any(esp32, esp32s3))] + #[cfg(all(any(esp32, esp32s3), not(feature = "riscv-ulp-hal")))] pub mcpwm1: mcpwm::Peripheral, >>>>>>> The beginning of a first rough draft... #[cfg(not(feature = "riscv-ulp-hal"))] From ad91ea1039d62ac73e7070994733913f4b99a2bd Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Fri, 15 Jul 2022 21:24:56 +0200 Subject: [PATCH 05/62] use esp_idf_sys::* --- src/mcpwm.rs | 22 +--------------------- 1 file changed, 1 insertion(+), 21 deletions(-) diff --git a/src/mcpwm.rs b/src/mcpwm.rs index e4b9258fed6..2fa5b0661a1 100644 --- a/src/mcpwm.rs +++ b/src/mcpwm.rs @@ -41,27 +41,7 @@ use core::borrow::Borrow; use crate::gpio::OutputPin; use crate::units::{FromValueType, Hertz}; -use esp_idf_sys::{esp, mcpwm_config_t, EspError}; -use esp_idf_sys::{ - mcpwm_counter_type_t, - mcpwm_counter_type_t_MCPWM_DOWN_COUNTER, - //mcpwm_counter_type_t_MCPWM_FREEZE_COUNTER, - mcpwm_counter_type_t_MCPWM_UP_COUNTER, - mcpwm_counter_type_t_MCPWM_UP_DOWN_COUNTER, -}; -use esp_idf_sys::{ - mcpwm_duty_type_t, mcpwm_duty_type_t_MCPWM_DUTY_MODE_0, mcpwm_duty_type_t_MCPWM_DUTY_MODE_1, -}; -use esp_idf_sys::{ - mcpwm_io_signals_t, mcpwm_io_signals_t_MCPWM0A, mcpwm_io_signals_t_MCPWM0B, - mcpwm_io_signals_t_MCPWM1A, mcpwm_io_signals_t_MCPWM1B, mcpwm_io_signals_t_MCPWM2A, - mcpwm_io_signals_t_MCPWM2B, -}; -use esp_idf_sys::{ - mcpwm_timer_t, mcpwm_timer_t_MCPWM_TIMER_0, mcpwm_timer_t_MCPWM_TIMER_1, - mcpwm_timer_t_MCPWM_TIMER_2, -}; -use esp_idf_sys::{mcpwm_unit_t, mcpwm_unit_t_MCPWM_UNIT_0, mcpwm_unit_t_MCPWM_UNIT_1}; +use esp_idf_sys::*; /// The Motor Control Pulse Width Modulator peripheral pub struct Peripheral { From 4e3b09cd3a598a3e7958580bcaf5430fce2cae72 Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Tue, 19 Jul 2022 22:44:44 +0200 Subject: [PATCH 06/62] Address some of the comments and add deadtime support --- examples/mcpwm-simple.rs | 6 +- src/mcpwm.rs | 328 +++++++++++++++++++++++++-------------- 2 files changed, 213 insertions(+), 121 deletions(-) diff --git a/examples/mcpwm-simple.rs b/examples/mcpwm-simple.rs index e877f593335..5876511ce28 100644 --- a/examples/mcpwm-simple.rs +++ b/examples/mcpwm-simple.rs @@ -1,5 +1,3 @@ -#![cfg(any(esp32, esp32s3))] - use embedded_hal::delay::blocking::DelayUs; use esp_idf_hal::delay::FreeRtos; @@ -7,6 +5,7 @@ use esp_idf_hal::mcpwm::{Mcpwm, Operator, OperatorConfig}; use esp_idf_hal::prelude::Peripherals; use esp_idf_hal::units::FromValueType; +#[cfg(any(esp32, esp32s3))] fn main() -> anyhow::Result<()> { esp_idf_sys::link_patches(); @@ -36,3 +35,6 @@ fn main() -> anyhow::Result<()> { FreeRtos.delay_ms(1000)?; } } + +#[cfg(not(any(esp32, esp32s3)))] +fn main() {} diff --git a/src/mcpwm.rs b/src/mcpwm.rs index 2fa5b0661a1..3825eeca5fb 100644 --- a/src/mcpwm.rs +++ b/src/mcpwm.rs @@ -1,15 +1,23 @@ -#![cfg(all(any(esp32, esp32s3), not(feature = "riscv-ulp-hal")))] - //! Motor Control Pulse Width Modulator peripheral //! //! Interface to the [Motor Control Pulse Width Modulator peripheral (MCPWM) //! peripheral](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/mcpwm.html) //! -//! This is an initial implementation supporting !::!:!:!::!:!:!:! -//! TODO: write stuff here -//! -//! TODO: Come up with nice example -//! # Examples +//! ``` +//! --------------- --------------- +//! | MCPWM Unit 0 | | MCPWM Unit 1 | +//! | ------------ | | ------------ | +//! | | | | +//! | OPERATOR 0 |--> A | OPERATOR 0 |--> A +//! | |--> B | |--> B +//! | | | | +//! | OPERATOR 1 |--> A | OPERATOR 1 |--> A +//! | |--> B | |--> B +//! | | | | +//! | OPERATOR 2 |--> A | OPERATOR 2 |--> A +//! | |--> B | |--> B +//! --------------- --------------- +//! ``` //! //! Create a pair of PWM signals on pin 4 and 5. The duty on pin 4 will ramp from 0% to 100% //! while pin 5 will ramp from 100% down to 0%. @@ -43,9 +51,18 @@ use crate::gpio::OutputPin; use crate::units::{FromValueType, Hertz}; use esp_idf_sys::*; +// MCPWM clock source frequency for ESP32 and ESP32-s3 +const MCPWM_CLOCK_SOURCE_FREQUENCY: u32 = 160_000_000; + +// Max PWM timer prescaler +const MAX_PWM_TIMER_PRESCALE: u32 = 0x1_00; + +// Max PWM timer period +const MAX_PWM_TIMER_PERIOD: u32 = 0x1_00_00; + /// The Motor Control Pulse Width Modulator peripheral pub struct Peripheral { - pub mcpwm: MCPWM, // TODO: Is there a better way to name or structure this? + pub mcpwm: MCPWM, pub operator0: OPERATOR0, pub operator1: OPERATOR1, pub operator2: OPERATOR2, @@ -83,7 +100,8 @@ impl From for mcpwm_duty_type_t { // TODO: For UpDown, frequency is half of MCPWM frequency set #[derive(Clone, Copy, Debug)] pub enum CounterMode { - //Frozen, + #[cfg(not(esp_idf_version = "4.3"))] + Frozen, Up, Down, UpDown, @@ -92,8 +110,8 @@ pub enum CounterMode { impl From for mcpwm_counter_type_t { fn from(val: CounterMode) -> Self { match val { - // TODO: This seems to be new to IDF 4.4? - //CounterMode::Frozen => mcpwm_counter_type_t_MCPWM_FREEZE_COUNTER, + #[cfg(not(esp_idf_version = "4.3"))] + CounterMode::Frozen => mcpwm_counter_type_t_MCPWM_FREEZE_COUNTER, CounterMode::Up => mcpwm_counter_type_t_MCPWM_UP_COUNTER, CounterMode::Down => mcpwm_counter_type_t_MCPWM_DOWN_COUNTER, CounterMode::UpDown => mcpwm_counter_type_t_MCPWM_UP_DOWN_COUNTER, @@ -101,16 +119,108 @@ impl From for mcpwm_counter_type_t { } } +// TODO: Note that `red` and `fed` from the IDF's perspecitve is time as in number of clock cycles after the +// MCPWM modules group prescaler. How do we want to expose this? Do we expose it as just that, a cycle count? +// Or do we expose it as a time which we then calculate the cycle count from? +#[derive(Copy, Clone, PartialEq, Debug)] +pub enum DeadtimeConfig { + // TODO: Figure out what all of those options do and give them nice descriptions + /// MCPWM_BYPASS_RED + BypassRisingEdge { fed: u16 }, + + /// MCPWM_BYPASS_FED + BypassFallingEdge { red: u16 }, + + /// MCPWM_ACTIVE_HIGH_MODE + ActiveHigh { red: u16, fed: u16 }, + + /// MCPWM_ACTIVE_LOW_MODE + ActiveLow { red: u16, fed: u16 }, + + /// MCPWM_ACTIVE_HIGH_COMPLIMENT_MODE + ActiveHighComplement { red: u16, fed: u16 }, + + /// MCPWM_ACTIVE_LOW_COMPLIMENT_MODE + ActiveLowComplement { red: u16, fed: u16 }, + + /// MCPWM_ACTIVE_RED_FED_FROM_PWMXA + ActiveRedFedFromPwmxa { red: u16, fed: u16 }, + + /// MCPWM_ACTIVE_RED_FED_FROM_PWMXB + ActiveRedFedFromPwmxb { red: u16, fed: u16 }, +} + +struct DeadtimeArgs { + red: u16, + fed: u16, + mode: mcpwm_deadtime_type_t, +} + +impl DeadtimeConfig { + fn as_args(&self) -> DeadtimeArgs { + match *self { + DeadtimeConfig::BypassRisingEdge { fed } => DeadtimeArgs { + red: 0, + fed, + mode: mcpwm_deadtime_type_t_MCPWM_BYPASS_RED, + }, + + DeadtimeConfig::BypassFallingEdge { red } => DeadtimeArgs { + red, + fed: 0, + mode: mcpwm_deadtime_type_t_MCPWM_BYPASS_FED, + }, + + DeadtimeConfig::ActiveHigh { red, fed } => DeadtimeArgs { + red, + fed, + mode: mcpwm_deadtime_type_t_MCPWM_ACTIVE_HIGH_MODE, + }, + + DeadtimeConfig::ActiveLow { red, fed } => DeadtimeArgs { + red, + fed, + mode: mcpwm_deadtime_type_t_MCPWM_ACTIVE_LOW_MODE, + }, + + DeadtimeConfig::ActiveHighComplement { red, fed } => DeadtimeArgs { + red, + fed, + mode: mcpwm_deadtime_type_t_MCPWM_ACTIVE_HIGH_COMPLIMENT_MODE, + }, + + DeadtimeConfig::ActiveLowComplement { red, fed } => DeadtimeArgs { + red, + fed, + mode: mcpwm_deadtime_type_t_MCPWM_ACTIVE_LOW_COMPLIMENT_MODE, + }, + + DeadtimeConfig::ActiveRedFedFromPwmxa { red, fed } => DeadtimeArgs { + red, + fed, + mode: mcpwm_deadtime_type_t_MCPWM_ACTIVE_RED_FED_FROM_PWMXA, + }, + + DeadtimeConfig::ActiveRedFedFromPwmxb { red, fed } => DeadtimeArgs { + red, + fed, + mode: mcpwm_deadtime_type_t_MCPWM_ACTIVE_RED_FED_FROM_PWMXB, + }, + } + } +} + pub struct OperatorConfig { frequency: Hertz, duty_a: Duty, duty_b: Duty, - #[cfg(idf_newer_than_or_equal_v4_4_0)] + #[cfg(not(esp_idf_version = "4.3"))] lowest_frequency: Hertz, duty_mode: DutyMode, counter_mode: CounterMode, - //deadtime: DeadtimeConfig, + + deadtime: Option, } impl OperatorConfig { @@ -120,7 +230,7 @@ impl OperatorConfig { self } - #[cfg(idf_newer_than_or_equal_v4_4_0)] + #[cfg(not(esp_idf_version = "4.3"))] #[must_use] pub fn lowest_frequency(mut self, lowest_frequency: Hertz) -> Self { self.lowest_frequency = lowest_frequency; @@ -139,11 +249,11 @@ impl OperatorConfig { self } - /* #[must_use] - pub fn deadtime_config(mut self, deadtime_config: DeadtimeConfig) -> Self { - todo!() - }*/ + pub fn deadtime(mut self, deadtime: impl Into>) -> Self { + self.deadtime = deadtime.into(); + self + } } impl Default for OperatorConfig { @@ -153,11 +263,13 @@ impl Default for OperatorConfig { duty_a: 50.0, duty_b: 50.0, - #[cfg(idf_newer_than_or_equal_v4_4_0)] - lowest_frequency: 1.Hz(), + #[cfg(not(esp_idf_version = "4.3"))] + lowest_frequency: 16.Hz(), duty_mode: DutyMode::ActiveHigh, counter_mode: CounterMode::Up, + + deadtime: None, } } } @@ -169,7 +281,6 @@ pub struct UnitZero; pub struct UnitOne; pub type Duty = f32; -const MAX_DUTY: Duty = 100.0; pub struct MCPWM { _unit: U, @@ -203,50 +314,58 @@ impl Unit for UnitOne { } // TODO: How do we want fault module to fit into this? -// TODO: How do we want capture module to fit into this? pub struct Mcpwm { + #[cfg(not(esp_idf_version = "4.3"))] + /// This is the frequency of the clock signal passed on as clock source for the operators timers + /// Those timers in turn have their own prescalers to scale this down even further + /// + /// NOTE: This is only to be set by calling Self::lowest_frequency + operator_input_frequency: u32, _instance: MCPWM, } impl Mcpwm { pub fn new(instance: MCPWM) -> Result { - Ok(Self { + let res = Self { + #[cfg(not(esp_idf_version = "4.3"))] + operator_input_frequency: 0, _instance: instance, - }) - } + }; - // TODO: I do not at all understand the motivation behind exposing the group prescaler as - // "resolution"? From what i can see, duty is exposed as a percentage so we are not talking - // about the pulse length in terms of cycles as is the case for ledc, right? What am I missing? - // Anyway, my reasoning: - // * High res => small prescaler => better maintan accuracy at high frequency, unable to reach lower freq - // * Lower res => larger prescaler => worse accuracy at high frequency(still as good at low frequency), - // able to reach lower freq - // Would it make more sense to expose it as "lowest reachable frequency" - // Also why are there no checks other than prescaler >= 1 in the idf? - // What happens if prescaler register value does not fit into the least significant 8 bits? - #[cfg(idf_newer_than_or_equal_v4_4_0)] - pub fn lowest_frequency(mut self, lowest_frequency: Hertz) -> Result { - // MCPWM clock source frequency for ESP32 and ESP32-s3 - const MCPWM_CLOCK_SOURCE_FREQUENCY: u32 = 160_000_000; - // Max PWM timer prescaler - const MAX_PWM_TIMER_PRESCALE: u32 = 0x1_00; - // Max PWM timer period - const MAX_PWM_TIMER_PERIOD: u32 = 0x1_00_00; + #[cfg(not(esp_idf_version = "4.3"))] + { + res.lowest_frequency(15.Hz()) + } - // let lowest_frequency = MCPWM_CLOCK_SOURCE_FREQUENCY / group_prescaler_factor / MAX_PWM_TIMER_PRESCALE / MAX_PWM_TIMER_PERIOD; - // let MAX_PWM_TIMER_PRESCALE * MAX_PWM_TIMER_PERIOD * frequency = MCPWM_CLOCK_SOURCE_FREQUENCY / group_prescaler_factor; - // let group_prescaler_factor = MCPWM_CLOCK_SOURCE_FREQUENCY / (MAX_PWM_TIMER_PRESCALE * MAX_PWM_TIMER_PERIOD * frequency); + #[cfg(esp_idf_version = "4.3")] + { + Ok(res) + } + } - // let resolution = MCPWM_CLOCK_SOURCE_FREQUENCY / group_prescaler; + /// Specify lowest reachable frequency + /// + /// The lower this is set, the lower frequencies will be reachable. However, this is at the cost of worse + /// resolution at higher frequencies. + /// + /// Same thing goes for the other way. The higher value set here, the more resolution and so on. + #[cfg(not(esp_idf_version = "4.3"))] + pub fn lowest_frequency(mut self, lowest_frequency: Hertz) -> Result { + // TODO: Do we care about frequency < 1Hz? + let operator_input_frequency = + MAX_PWM_TIMER_PRESCALE * MAX_PWM_TIMER_PERIOD * u32::from(lowest_frequency); + let group_pre_scale = MCPWM_CLOCK_SOURCE_FREQUENCY / operator_input_frequency; + if group_pre_scale > 256 || group_pre_scale < 1 { + return Err(EspError::from(ESP_ERR_INVALID_ARG).unwrap()); + } - // let resolution = MCPWM_CLOCK_SOURCE_FREQUENCY / (MCPWM_CLOCK_SOURCE_FREQUENCY / (MAX_PWM_TIMER_PRESCALE * MAX_PWM_TIMER_PERIOD * frequency)); - // let resolution = (MAX_PWM_TIMER_PRESCALE * MAX_PWM_TIMER_PERIOD * frequency) * 160_000_000 / MCPWM_CLOCK_SOURCE_FREQUENCY; - let resolution = MAX_PWM_TIMER_PRESCALE * MAX_PWM_TIMER_PERIOD * lowest_reachable_frequency; + let resolution = + MAX_PWM_TIMER_PRESCALE * MAX_PWM_TIMER_PERIOD * u32::from(lowest_frequency); + esp!(unsafe { mcpwm_group_set_resolution(U::unit(), resolution) })?; + self.operator_input_frequency = operator_input_frequency; - mcpwm_group_set_resolution(); - todo!() + Ok(self) } } @@ -321,6 +440,7 @@ impl_operator!( // TODO: How do we want syncing to fit in to this? // TODO: How do we want deadtime to fit into this? // TODO: How do we want carrier to fit into this? +// TODO: How do we want capture to fit into this? pub struct Operator, M: Borrow>, PA: OutputPin, PB: OutputPin> { _instance: O, @@ -341,18 +461,28 @@ impl, M: Borrow>, PA: OutputPin, PB: OutputPi pin_a: A, pin_b: B, ) -> Result { - // TODO: Dont forget to make these cases work - #[cfg(idf_newer_than_or_equal_v4_4_0)] + #[cfg(not(esp_idf_version = "4.3"))] { if config.frequency < config.lowest_frequency { - return Err(panic!("TODO: Invalid parameter, should this be checked in OperatorConfig or here or hope that the IDF? will handle the error checking")); + // Can not specify a clock frequency lower then what has + // been configured as the lowest clock frequency + // Use `OperatorConfig::lowest_frequency` to enable lower frequencies + return Err(EspError::from(ESP_ERR_INVALID_ARG).unwrap()); } + + if config.lowest_frequency > mcpwm_module.borrow().operator_input_frequency.Hz() { + // Can not specify a lowest_frequency larger than the corresponding value for + // the parent MCPWM module. Use `Mcpwm::lowest_frequency` to enable higher frequencies + return Err(EspError::from(ESP_ERR_INVALID_ARG).unwrap()); + } + + let resolution = u32::from(config.lowest_frequency) * MAX_PWM_TIMER_PERIOD; unsafe { - esp_idf_sys::mcpwm_timer_set_resolution(); + esp_idf_sys::mcpwm_timer_set_resolution(U::unit(), O::timer(), resolution); } } - // TODO: Handle half pwm frequency when counter_mode = UpDown here? + // TODO: Handle/document half pwm frequency when counter_mode = UpDown? esp!(unsafe { esp_idf_sys::mcpwm_init( @@ -368,6 +498,20 @@ impl, M: Borrow>, PA: OutputPin, PB: OutputPi ) })?; + match config.deadtime { + None => unsafe { + // Only way this can faild is if an invalid timer or unit is specified + // which we know can not happen. So we don't have to check for errors + mcpwm_deadtime_disable(U::unit(), O::timer()); + }, + Some(config) => { + let DeadtimeArgs { red, fed, mode } = config.as_args(); + unsafe { + mcpwm_deadtime_enable(U::unit(), O::timer(), mode, red.into(), fed.into()); + } + } + } + let pin_a: Option = pin_a.into(); let pin_b: Option = pin_b.into(); @@ -390,6 +534,7 @@ impl, M: Borrow>, PA: OutputPin, PB: OutputPi }) } + /// Get duty as percentage between 0.0 and 100.0 pub fn get_duty_a(&self) -> Duty { unsafe { esp_idf_sys::mcpwm_get_duty( @@ -400,6 +545,7 @@ impl, M: Borrow>, PA: OutputPin, PB: OutputPi } } + /// Get duty as percentage between 0.0 and 100.0 pub fn get_duty_b(&self) -> Duty { unsafe { esp_idf_sys::mcpwm_get_duty( @@ -422,6 +568,7 @@ impl, M: Borrow>, PA: OutputPin, PB: OutputPi } } + /// Set duty as percentage between 0.0 and 100.0 pub fn set_duty_b(&mut self, duty: Duty) -> Result<(), EspError> { unsafe { esp!(esp_idf_sys::mcpwm_set_duty( @@ -433,75 +580,18 @@ impl, M: Borrow>, PA: OutputPin, PB: OutputPi } } - pub fn set_frequency(frequency: u32) -> Result<(), EspError> { + /// Set PWM frequency + pub fn set_frequency(frequency: Hertz) -> Result<(), EspError> { unsafe { esp!(esp_idf_sys::mcpwm_set_frequency( U::unit(), O::timer(), - frequency + frequency.into() )) } } - pub fn get_frequency() -> u32 { - unsafe { esp_idf_sys::mcpwm_get_frequency(U::unit(), O::timer()) } - } -} - -pub enum Generator { - A, - B, -} - -// TODO: would we like to also implement PwmPin for something like Foo<&Operator, G: Generator> -// this would allow things like: -// let operator = Operator::new(...); -// let gen_a = operator.generator_a_as_pwm_pin(); -// let gen_b = operator.generator_b_as_pwm_pin(); -// function_expecting_pwm_pin(&gen_a); -// gen_a.set_duty() -// gen_a.set_duty() -impl, M: Borrow>, PA: OutputPin, PB: OutputPin> - embedded_hal_0_2::Pwm for Operator -{ - type Channel = Generator; - type Duty = Duty; - type Time = (); - - fn disable(&mut self, _channel: Self::Channel) { - todo!() - } - - fn enable(&mut self, _channel: Self::Channel) { - todo!() - } - - fn get_period(&self) -> Self::Time { - todo!() - } - - fn get_duty(&self, channel: Self::Channel) -> Self::Duty { - match channel { - Generator::A => self.get_duty_a(), - Generator::B => self.get_duty_b(), - } - } - - fn get_max_duty(&self) -> Self::Duty { - MAX_DUTY - } - - fn set_duty(&mut self, channel: Self::Channel, duty: Self::Duty) { - match channel { - Generator::A => self - .set_duty_a(duty) - .expect("Failed to set duty for generator A"), - Generator::B => self - .set_duty_b(duty) - .expect("Failed to set duty for generator B"), - } - } - fn set_period

(&mut self, _period: P) { - todo!() + pub fn get_frequency() -> Hertz { + Hertz::from(unsafe { esp_idf_sys::mcpwm_get_frequency(U::unit(), O::timer()) }) } } From 8a1b0fdd5d53ba98272cb0458de22fd40cf86525 Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Tue, 19 Jul 2022 23:52:23 +0200 Subject: [PATCH 07/62] Clippy --- src/mcpwm.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mcpwm.rs b/src/mcpwm.rs index 3825eeca5fb..b8f883430aa 100644 --- a/src/mcpwm.rs +++ b/src/mcpwm.rs @@ -356,7 +356,7 @@ impl Mcpwm { let operator_input_frequency = MAX_PWM_TIMER_PRESCALE * MAX_PWM_TIMER_PERIOD * u32::from(lowest_frequency); let group_pre_scale = MCPWM_CLOCK_SOURCE_FREQUENCY / operator_input_frequency; - if group_pre_scale > 256 || group_pre_scale < 1 { + if !(1..=256).contains(&group_pre_scale) { return Err(EspError::from(ESP_ERR_INVALID_ARG).unwrap()); } From 17a3e63d80557dc682f9b4f5eca580809971e481 Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Wed, 20 Jul 2022 00:15:21 +0200 Subject: [PATCH 08/62] Fix warnings in example --- examples/mcpwm-simple.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/examples/mcpwm-simple.rs b/examples/mcpwm-simple.rs index 5876511ce28..259aed7451e 100644 --- a/examples/mcpwm-simple.rs +++ b/examples/mcpwm-simple.rs @@ -1,12 +1,12 @@ -use embedded_hal::delay::blocking::DelayUs; - -use esp_idf_hal::delay::FreeRtos; -use esp_idf_hal::mcpwm::{Mcpwm, Operator, OperatorConfig}; -use esp_idf_hal::prelude::Peripherals; -use esp_idf_hal::units::FromValueType; - #[cfg(any(esp32, esp32s3))] fn main() -> anyhow::Result<()> { + use embedded_hal::delay::blocking::DelayUs; + + use esp_idf_hal::delay::FreeRtos; + use esp_idf_hal::mcpwm::{Mcpwm, Operator, OperatorConfig}; + use esp_idf_hal::prelude::Peripherals; + use esp_idf_hal::units::FromValueType; + esp_idf_sys::link_patches(); println!("Configuring MCPWM"); From a428ed631a7f3e894ce4e0c4f97882f3ce01d62b Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Wed, 20 Jul 2022 12:25:04 +0200 Subject: [PATCH 09/62] Fix example --- examples/mcpwm-simple.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/examples/mcpwm-simple.rs b/examples/mcpwm-simple.rs index 259aed7451e..4683362804c 100644 --- a/examples/mcpwm-simple.rs +++ b/examples/mcpwm-simple.rs @@ -37,4 +37,6 @@ fn main() -> anyhow::Result<()> { } #[cfg(not(any(esp32, esp32s3)))] -fn main() {} +fn main() { + esp_idf_sys::link_patches(); +} From 8f2f559e2011e792a3b8b5158a888c484f2ad543 Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Sun, 24 Jul 2022 13:18:26 +0200 Subject: [PATCH 10/62] Add some docs for deadtime modes --- src/mcpwm.rs | 148 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 147 insertions(+), 1 deletion(-) diff --git a/src/mcpwm.rs b/src/mcpwm.rs index b8f883430aa..f2c6c6de925 100644 --- a/src/mcpwm.rs +++ b/src/mcpwm.rs @@ -122,25 +122,171 @@ impl From for mcpwm_counter_type_t { // TODO: Note that `red` and `fed` from the IDF's perspecitve is time as in number of clock cycles after the // MCPWM modules group prescaler. How do we want to expose this? Do we expose it as just that, a cycle count? // Or do we expose it as a time which we then calculate the cycle count from? +/// Deadtime config for MCPWM operator +/// Note that the dead times are calculated from MCPWMXA's flanks unless explicitly stated otherwise #[derive(Copy, Clone, PartialEq, Debug)] pub enum DeadtimeConfig { // TODO: Figure out what all of those options do and give them nice descriptions /// MCPWM_BYPASS_RED + /// + /// . . . + /// . . . + /// .--------------------. . + /// | | . + /// MCPWMXA in | | . + /// | | . + /// --------------- --------------------- + /// . . . + /// . . . + /// .--------------------. . + /// | | . + /// MCPWMXA out | | . + /// | | . + /// --------------- --------------------- + /// . . . + /// . . . + /// .------------------------. + /// | >. |< fed + /// MCPWMXB out | . | + /// | . | + /// --------------. . ----------------- + /// . . . BypassRisingEdge { fed: u16 }, /// MCPWM_BYPASS_FED + /// + /// . . . + /// . . . + /// .--------------------. + /// | . | + /// MCPWMXA in | . | + /// | . | + /// --------------- . --------------------- + /// . . . + /// . . . + /// . .----------------. + /// red >. |< | + /// MCPWMXA out . | | + /// . | | + /// ------------------- --------------------- + /// . . . + /// . . . + /// .--------------------. + /// | . | + /// MCPWMXB out | . | + /// | . | + /// --------------- . --------------------- + /// . . . BypassFallingEdge { red: u16 }, /// MCPWM_ACTIVE_HIGH_MODE + /// + /// . . . . + /// . . . . + /// .--------------------. . + /// | . | . + /// MCPWMXA in | . | . + /// | . | . + /// --------------- . --------------------- + /// . . . . + /// . . . . + /// . .----------------. . + /// red >. |< | . + /// MCPWMXA out . | | . + /// . | | . + /// ------------------- --------------------- + /// . . . . + /// . . . . + /// .------------------------. + /// | . >. |< fed + /// MCPWMXB out | . . | + /// | . . | + /// --------------. . . ----------------- + /// . . . . ActiveHigh { red: u16, fed: u16 }, /// MCPWM_ACTIVE_LOW_MODE + /// + /// . . . . + /// . . . . + /// .--------------------. . + /// | . | . + /// MCPWMXA in | . | . + /// | . | . + /// --------------- . --------------------- + /// . . . . + /// . . . . + /// ------------------. .-------------------- + /// red >. |< | . + /// MCPWMXA out . | | . + /// . | | . + /// . ------------------ + /// . . . . + /// . . . . + /// --------------. . . .---------------- + /// | . >. |< fed + /// MCPWMXB out | . . | + /// | . . | + /// -------------------------- + /// . . . . + /// . . . . ActiveLow { red: u16, fed: u16 }, - /// MCPWM_ACTIVE_HIGH_COMPLIMENT_MODE + // TODO: Is this actually true? -------- + // | + // v + /// MCPWM_ACTIVE_HIGH_COMPLIMENT_MODE - The most common deadtime mode + /// + /// . . . . + /// . . . . + /// .--------------------. . + /// | . | . + /// MCPWMXA in | . | . + /// | . | . + /// --------------- . --------------------- + /// . . . . + /// . . . . + /// . .----------------. . + /// red >. |< | . + /// MCPWMXA out . | | . + /// . | | . + /// ------------------- --------------------- + /// . . . . + /// . . . . + /// --------------. . . .---------------- + /// | . >. |< fed + /// MCPWMXB out | . . | + /// | . . | + /// -------------------------- + /// . . . . + /// . . . . ActiveHighComplement { red: u16, fed: u16 }, /// MCPWM_ACTIVE_LOW_COMPLIMENT_MODE + /// + /// . . . . + /// . . . . + /// .--------------------. . + /// | . | . + /// MCPWMXA in | . | . + /// | . | . + /// --------------- . --------------------- + /// . . . . + /// . . . . + /// ------------------. .-------------------- + /// red >. |< | . + /// MCPWMXA out . | | . + /// . | | . + /// . ------------------ + /// . . . . + /// . . . . + /// .------------------------. + /// | . >. |< fed + /// MCPWMXB out | . . | + /// | . . | + /// --------------- . . ----------------- + /// . . . . + /// . . . . ActiveLowComplement { red: u16, fed: u16 }, /// MCPWM_ACTIVE_RED_FED_FROM_PWMXA From d64529a8fceb907bbd01314eb1d9d45fc7183442 Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Mon, 25 Jul 2022 13:49:07 +0200 Subject: [PATCH 11/62] Added Mcpwm::operator_source_frequency --- src/mcpwm.rs | 100 +++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 78 insertions(+), 22 deletions(-) diff --git a/src/mcpwm.rs b/src/mcpwm.rs index f2c6c6de925..c9705722576 100644 --- a/src/mcpwm.rs +++ b/src/mcpwm.rs @@ -119,10 +119,10 @@ impl From for mcpwm_counter_type_t { } } -// TODO: Note that `red` and `fed` from the IDF's perspecitve is time as in number of clock cycles after the -// MCPWM modules group prescaler. How do we want to expose this? Do we expose it as just that, a cycle count? -// Or do we expose it as a time which we then calculate the cycle count from? -/// Deadtime config for MCPWM operator +/// Dead time config for MCPWM operator +/// +/// `red` and `fed` is time as in number of clock cycles after the MCPWM modules group prescaler. +/// /// Note that the dead times are calculated from MCPWMXA's flanks unless explicitly stated otherwise #[derive(Copy, Clone, PartialEq, Debug)] pub enum DeadtimeConfig { @@ -232,10 +232,7 @@ pub enum DeadtimeConfig { /// . . . . ActiveLow { red: u16, fed: u16 }, - // TODO: Is this actually true? -------- - // | - // v - /// MCPWM_ACTIVE_HIGH_COMPLIMENT_MODE - The most common deadtime mode + /// MCPWM_ACTIVE_HIGH_COMPLIMENT_MODE /// /// . . . . /// . . . . @@ -467,7 +464,7 @@ pub struct Mcpwm { /// Those timers in turn have their own prescalers to scale this down even further /// /// NOTE: This is only to be set by calling Self::lowest_frequency - operator_input_frequency: u32, + operator_source_frequency: u32, _instance: MCPWM, } @@ -475,13 +472,15 @@ impl Mcpwm { pub fn new(instance: MCPWM) -> Result { let res = Self { #[cfg(not(esp_idf_version = "4.3"))] - operator_input_frequency: 0, + operator_source_frequency: 0, _instance: instance, }; #[cfg(not(esp_idf_version = "4.3"))] { - res.lowest_frequency(15.Hz()) + // TODO: Do we want to make this into something more builder-pattern like to + // avoid this potentially redundant function call? + res.operator_source_frequency(10.MHz()) } #[cfg(esp_idf_version = "4.3")] @@ -490,26 +489,53 @@ impl Mcpwm { } } + pub fn release(self) -> MCPWM { + // TODO: Do we need to reset any state here such as group_prescaler? + self._instance + } + /// Specify lowest reachable frequency /// /// The lower this is set, the lower frequencies will be reachable. However, this is at the cost of worse /// resolution at higher frequencies. - /// + /// /// Same thing goes for the other way. The higher value set here, the more resolution and so on. #[cfg(not(esp_idf_version = "4.3"))] pub fn lowest_frequency(mut self, lowest_frequency: Hertz) -> Result { // TODO: Do we care about frequency < 1Hz? - let operator_input_frequency = + let operator_source_frequency = MAX_PWM_TIMER_PRESCALE * MAX_PWM_TIMER_PERIOD * u32::from(lowest_frequency); - let group_pre_scale = MCPWM_CLOCK_SOURCE_FREQUENCY / operator_input_frequency; + let group_pre_scale = MCPWM_CLOCK_SOURCE_FREQUENCY / operator_source_frequency; if !(1..=256).contains(&group_pre_scale) { return Err(EspError::from(ESP_ERR_INVALID_ARG).unwrap()); } - let resolution = - MAX_PWM_TIMER_PRESCALE * MAX_PWM_TIMER_PERIOD * u32::from(lowest_frequency); - esp!(unsafe { mcpwm_group_set_resolution(U::unit(), resolution) })?; - self.operator_input_frequency = operator_input_frequency; + esp!(unsafe { mcpwm_group_set_resolution(U::unit(), operator_source_frequency) })?; + self.operator_source_frequency = operator_source_frequency; + + Ok(self) + } + + /// Specify frequency passed to operators timers as clock source + /// + /// The timers of the operators can then in turn scale this frequency down further. + /// + /// The lower this is set, the lower frequencies will be reachable. However, this is + /// at the cost of worse resolution at higher frequencies. Same thing goes for the + /// other way. The higher value set here, the more resolution and so on. + #[cfg(not(esp_idf_version = "4.3"))] + pub fn operator_source_frequency(mut self, frequency: impl Into) -> Result { + let frequency: Hertz = frequency.into(); + let frequency: u32 = frequency.into(); + + // TODO: Do we care about frequency < 1Hz? + let group_pre_scale = MCPWM_CLOCK_SOURCE_FREQUENCY / frequency; + if !(1..=256).contains(&group_pre_scale) { + return Err(EspError::from(ESP_ERR_INVALID_ARG).unwrap()); + } + + esp!(unsafe { mcpwm_group_set_resolution(U::unit(), frequency) })?; + self.operator_source_frequency = frequency; Ok(self) } @@ -584,7 +610,7 @@ impl_operator!( ); // TODO: How do we want syncing to fit in to this? -// TODO: How do we want deadtime to fit into this? +// TODO: How do we want dead time to fit into this? // TODO: How do we want carrier to fit into this? // TODO: How do we want capture to fit into this? @@ -597,8 +623,13 @@ pub struct Operator, M: Borrow>, PA: OutputPi _pin_b: Option, } -impl, M: Borrow>, PA: OutputPin, PB: OutputPin> - Operator +impl Operator +where + U: Unit, + O: HwOperator, + M: Borrow>, + PA: OutputPin, + PB: OutputPin, { pub fn new>, B: Into>>( operator: O, @@ -616,7 +647,7 @@ impl, M: Borrow>, PA: OutputPin, PB: OutputPi return Err(EspError::from(ESP_ERR_INVALID_ARG).unwrap()); } - if config.lowest_frequency > mcpwm_module.borrow().operator_input_frequency.Hz() { + if config.lowest_frequency > mcpwm_module.borrow().operator_source_frequency.Hz() { // Can not specify a lowest_frequency larger than the corresponding value for // the parent MCPWM module. Use `Mcpwm::lowest_frequency` to enable higher frequencies return Err(EspError::from(ESP_ERR_INVALID_ARG).unwrap()); @@ -680,6 +711,31 @@ impl, M: Borrow>, PA: OutputPin, PB: OutputPi }) } + pub fn release(self) -> (O, Option, Option) { + // mcpwm_stop will only fail when invalid args are given + esp!(unsafe { mcpwm_stop(U::unit(), O::timer()) }).unwrap(); + + // TODO: Test and verify if this is the right way + if self._pin_a.is_some() { + // TODO: How to unset pin? + // let io_signal = O::signal_a(); + // mcpwm_gpio_init(U::unit(), io_signal, -1) + // does not seem to be it... + todo!(); + } + + if self._pin_b.is_some() { + // TODO: How to unset pin? + // let io_signal = O::signal_b(); + // mcpwm_gpio_init(U::unit(), io_signal, -1) + // does not seem to be it... + + todo!(); + } + // TODO: Do we need to reset any more state here such as dead time config? + (self._instance, self._pin_a, self._pin_b) + } + /// Get duty as percentage between 0.0 and 100.0 pub fn get_duty_a(&self) -> Duty { unsafe { From 729cb851315881cdcf1e8a4b1a7b4faadaf845f8 Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Mon, 25 Jul 2022 14:15:21 +0200 Subject: [PATCH 12/62] cargo fmt --- src/mcpwm.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/mcpwm.rs b/src/mcpwm.rs index c9705722576..d0e719937e3 100644 --- a/src/mcpwm.rs +++ b/src/mcpwm.rs @@ -520,11 +520,14 @@ impl Mcpwm { /// /// The timers of the operators can then in turn scale this frequency down further. /// - /// The lower this is set, the lower frequencies will be reachable. However, this is + /// The lower this is set, the lower frequencies will be reachable. However, this is /// at the cost of worse resolution at higher frequencies. Same thing goes for the /// other way. The higher value set here, the more resolution and so on. #[cfg(not(esp_idf_version = "4.3"))] - pub fn operator_source_frequency(mut self, frequency: impl Into) -> Result { + pub fn operator_source_frequency( + mut self, + frequency: impl Into, + ) -> Result { let frequency: Hertz = frequency.into(); let frequency: u32 = frequency.into(); From 15610a2e39309a4770d1bb3afba1548842066586 Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Wed, 27 Jul 2022 13:26:48 +0200 Subject: [PATCH 13/62] Add more documentation --- examples/mcpwm-simple.rs | 64 ++++++++++- src/mcpwm.rs | 242 +++++++++++++++++++++++++++++++-------- 2 files changed, 251 insertions(+), 55 deletions(-) diff --git a/examples/mcpwm-simple.rs b/examples/mcpwm-simple.rs index 4683362804c..bcc3d849e1d 100644 --- a/examples/mcpwm-simple.rs +++ b/examples/mcpwm-simple.rs @@ -1,3 +1,57 @@ +/// # x = 10 +/// +/// . . +/// . . +/// .------. .------. +/// | | | | +/// pin4 | | | | +/// | | | | +/// ----- -------------------------- -------------------------- +/// . . +/// . . +/// .------------------------. .------------------------. +/// | | | | +/// pin5 | | | | +/// | | | | +/// ----- -------- -------- +/// . . +/// +/// +/// # x = 50 +/// . . +/// . . +/// .---------------. .---------------. +/// | | | | +/// pin4 | | | | +/// | | | | +/// ----- ----------------- ----------------- +/// . . +/// . . +/// .---------------. .---------------. +/// | | | | +/// pin5 | | | | +/// | | | | +/// ----- ----------------- ----------------- +/// . . +/// +/// +/// # x = 90 +/// . . +/// . . +/// .------------------------. .------------------------. +/// | | | | +/// pin4 | | | | +/// | | | | +/// ----- -------- -------- +/// . . +/// . . +/// .------. .------. +/// | | | | +/// pin5 | | | | +/// | | | | +/// ----- -------------------------- -------------------------- +/// . . + #[cfg(any(esp32, esp32s3))] fn main() -> anyhow::Result<()> { use embedded_hal::delay::blocking::DelayUs; @@ -12,7 +66,7 @@ fn main() -> anyhow::Result<()> { println!("Configuring MCPWM"); let peripherals = Peripherals::take().unwrap(); - let config = OperatorConfig::default().frequency(25.kHz().into()); + let config = OperatorConfig::default().frequency(25.kHz()); let mcpwm = Mcpwm::new(peripherals.mcpwm0.mcpwm)?; let mut operator = Operator::new( peripherals.mcpwm0.operator0, @@ -24,10 +78,10 @@ fn main() -> anyhow::Result<()> { println!("Starting duty-cycle loop"); - for &duty in [0.0, 20.0, 40.0, 60.0, 80.0, 100.0].iter().cycle() { - println!("Duty {}%", duty); - operator.set_duty_a(duty)?; - operator.set_duty_b(100.0 - duty)?; + for &x in [0.0, 20.0, 40.0, 60.0, 80.0, 100.0].iter().cycle() { + println!("Duty {}%", x); + operator.set_duty_a(x)?; + operator.set_duty_b(100.0 - x)?; FreeRtos.delay_ms(2000)?; } diff --git a/src/mcpwm.rs b/src/mcpwm.rs index d0e719937e3..4ac16d9c512 100644 --- a/src/mcpwm.rs +++ b/src/mcpwm.rs @@ -19,6 +19,8 @@ //! --------------- --------------- //! ``` //! +//! # Example +//! //! Create a pair of PWM signals on pin 4 and 5. The duty on pin 4 will ramp from 0% to 100% //! while pin 5 will ramp from 100% down to 0%. //! ``` @@ -82,9 +84,19 @@ impl Peripheral { } } +/// Duty modes for operator #[derive(Clone, Copy, Debug)] pub enum DutyMode { + /// Active high + /// + /// Setting duty = 100% will result in a constant high output + /// Setting duty = 0% will result in a constant low output ActiveHigh, + + /// Active low + /// + /// Setting duty = 100% will result in a constant low output + /// Setting duty = 0% will result in a constant high output ActiveLow, } @@ -97,13 +109,64 @@ impl From for mcpwm_duty_type_t { } } +/// Counter mode for operator's timer for generating PWM signal // TODO: For UpDown, frequency is half of MCPWM frequency set #[derive(Clone, Copy, Debug)] pub enum CounterMode { + /// Timer is frozen or paused #[cfg(not(esp_idf_version = "4.3"))] Frozen, + /// Edge aligned. The counter will start from its lowest value and increment every clock cycle until the period is reached. + /// + /// The wave form will end up looking something like the following: + /// ``` + /// start, counter = 0 reset, counter = period + /// | | + /// | |*--- start, counter = 0 + /// v <---- duty ----> . v| + /// . . .v + /// .--------------------. ..---- + /// | Active | .| + /// | | .| + /// | | Not active .| + /// - --------------------- + /// ``` Up, + + /// Edge aligned. The counter will start from its highest value, period and decrement every clock cycle until the zero is reached + /// + /// The wave form will end up looking something like the following: + /// ``` + /// start, counter = period reset, counter = 0 + /// | | + /// | |*--- start, counter = period + /// v . v| + /// . . <---- duty ----> .v + /// . .--------------------.. + /// . Active | |. + /// . | |. + /// . Not active | Active |. + /// ---------------------- ---- + /// ``` Down, + + /// Symmetric mode. The counter will start from its lowest value and increment every clock cycle until the period is reached + /// + /// The wave form will end up looking something like the following: + /// ``` + /// change count dir to decrement, counter = period + /// start, counter = 0, incrementing | change count dir to increment, counter = 0 + /// | | | + /// | |*--- counter = period |*----- start, counter = 0, incrementing + /// v <---- duty ----> . v| . <---- duty ----> || + /// . . .v . vv + /// ---------------------. .. .-------------------------------------------. .. .-- + /// Active | .. | Active Active | .. | + /// | .. | | .. | + /// | Not active .. Not active | | Not active .. Not active | + /// ---------------------------------------- ---------------------------------------- + /// ``` + /// NOTE: That in this mode, the frequency will be half of that specified UpDown, } @@ -121,7 +184,7 @@ impl From for mcpwm_counter_type_t { /// Dead time config for MCPWM operator /// -/// `red` and `fed` is time as in number of clock cycles after the MCPWM modules group prescaler. +/// `rising_edge_delay` and `falling_edge_delay` is time as in number of clock cycles after the MCPWM modules group prescaler. /// /// Note that the dead times are calculated from MCPWMXA's flanks unless explicitly stated otherwise #[derive(Copy, Clone, PartialEq, Debug)] @@ -129,6 +192,7 @@ pub enum DeadtimeConfig { // TODO: Figure out what all of those options do and give them nice descriptions /// MCPWM_BYPASS_RED /// + /// ``` /// . . . /// . . . /// .--------------------. . @@ -151,10 +215,13 @@ pub enum DeadtimeConfig { /// | . | /// --------------. . ----------------- /// . . . - BypassRisingEdge { fed: u16 }, + /// . . . . + /// ``` + BypassRisingEdge { falling_edge_delay: u16 }, /// MCPWM_BYPASS_FED /// + /// ``` /// . . . /// . . . /// .--------------------. @@ -177,10 +244,13 @@ pub enum DeadtimeConfig { /// | . | /// --------------- . --------------------- /// . . . - BypassFallingEdge { red: u16 }, + /// . . . . + /// ``` + BypassFallingEdge { rising_edge_delay: u16 }, /// MCPWM_ACTIVE_HIGH_MODE /// + /// ``` /// . . . . /// . . . . /// .--------------------. . @@ -203,10 +273,16 @@ pub enum DeadtimeConfig { /// | . . | /// --------------. . . ----------------- /// . . . . - ActiveHigh { red: u16, fed: u16 }, + /// . . . . + /// ``` + ActiveHigh { + rising_edge_delay: u16, + falling_edge_delay: u16, + }, /// MCPWM_ACTIVE_LOW_MODE /// + /// ``` /// . . . . /// . . . . /// .--------------------. . @@ -230,10 +306,15 @@ pub enum DeadtimeConfig { /// -------------------------- /// . . . . /// . . . . - ActiveLow { red: u16, fed: u16 }, + /// ``` + ActiveLow { + rising_edge_delay: u16, + falling_edge_delay: u16, + }, /// MCPWM_ACTIVE_HIGH_COMPLIMENT_MODE /// + /// ``` /// . . . . /// . . . . /// .--------------------. . @@ -257,10 +338,15 @@ pub enum DeadtimeConfig { /// -------------------------- /// . . . . /// . . . . - ActiveHighComplement { red: u16, fed: u16 }, + /// ``` + ActiveHighComplement { + rising_edge_delay: u16, + falling_edge_delay: u16, + }, /// MCPWM_ACTIVE_LOW_COMPLIMENT_MODE /// + /// ``` /// . . . . /// . . . . /// .--------------------. . @@ -284,75 +370,104 @@ pub enum DeadtimeConfig { /// --------------- . . ----------------- /// . . . . /// . . . . - ActiveLowComplement { red: u16, fed: u16 }, + /// ``` + ActiveLowComplement { + rising_edge_delay: u16, + falling_edge_delay: u16, + }, /// MCPWM_ACTIVE_RED_FED_FROM_PWMXA - ActiveRedFedFromPwmxa { red: u16, fed: u16 }, + ActiveRedFedFromPwmxa { + rising_edge_delay: u16, + falling_edge_delay: u16, + }, /// MCPWM_ACTIVE_RED_FED_FROM_PWMXB - ActiveRedFedFromPwmxb { red: u16, fed: u16 }, -} - -struct DeadtimeArgs { - red: u16, - fed: u16, - mode: mcpwm_deadtime_type_t, + ActiveRedFedFromPwmxb { + rising_edge_delay: u16, + falling_edge_delay: u16, + }, } impl DeadtimeConfig { fn as_args(&self) -> DeadtimeArgs { match *self { - DeadtimeConfig::BypassRisingEdge { fed } => DeadtimeArgs { - red: 0, - fed, + DeadtimeConfig::BypassRisingEdge { falling_edge_delay } => DeadtimeArgs { + rising_edge_delay: 0, + falling_edge_delay, mode: mcpwm_deadtime_type_t_MCPWM_BYPASS_RED, }, - DeadtimeConfig::BypassFallingEdge { red } => DeadtimeArgs { - red, - fed: 0, + DeadtimeConfig::BypassFallingEdge { rising_edge_delay } => DeadtimeArgs { + rising_edge_delay, + falling_edge_delay: 0, mode: mcpwm_deadtime_type_t_MCPWM_BYPASS_FED, }, - DeadtimeConfig::ActiveHigh { red, fed } => DeadtimeArgs { - red, - fed, + DeadtimeConfig::ActiveHigh { + rising_edge_delay, + falling_edge_delay, + } => DeadtimeArgs { + rising_edge_delay, + falling_edge_delay, mode: mcpwm_deadtime_type_t_MCPWM_ACTIVE_HIGH_MODE, }, - DeadtimeConfig::ActiveLow { red, fed } => DeadtimeArgs { - red, - fed, + DeadtimeConfig::ActiveLow { + rising_edge_delay, + falling_edge_delay, + } => DeadtimeArgs { + rising_edge_delay, + falling_edge_delay, mode: mcpwm_deadtime_type_t_MCPWM_ACTIVE_LOW_MODE, }, - DeadtimeConfig::ActiveHighComplement { red, fed } => DeadtimeArgs { - red, - fed, + DeadtimeConfig::ActiveHighComplement { + rising_edge_delay, + falling_edge_delay, + } => DeadtimeArgs { + rising_edge_delay, + falling_edge_delay, mode: mcpwm_deadtime_type_t_MCPWM_ACTIVE_HIGH_COMPLIMENT_MODE, }, - DeadtimeConfig::ActiveLowComplement { red, fed } => DeadtimeArgs { - red, - fed, + DeadtimeConfig::ActiveLowComplement { + rising_edge_delay, + falling_edge_delay, + } => DeadtimeArgs { + rising_edge_delay, + falling_edge_delay, mode: mcpwm_deadtime_type_t_MCPWM_ACTIVE_LOW_COMPLIMENT_MODE, }, - DeadtimeConfig::ActiveRedFedFromPwmxa { red, fed } => DeadtimeArgs { - red, - fed, + DeadtimeConfig::ActiveRedFedFromPwmxa { + rising_edge_delay, + falling_edge_delay, + } => DeadtimeArgs { + rising_edge_delay, + falling_edge_delay, mode: mcpwm_deadtime_type_t_MCPWM_ACTIVE_RED_FED_FROM_PWMXA, }, - DeadtimeConfig::ActiveRedFedFromPwmxb { red, fed } => DeadtimeArgs { - red, - fed, + DeadtimeConfig::ActiveRedFedFromPwmxb { + rising_edge_delay, + falling_edge_delay, + } => DeadtimeArgs { + rising_edge_delay, + falling_edge_delay, mode: mcpwm_deadtime_type_t_MCPWM_ACTIVE_RED_FED_FROM_PWMXB, }, } } } +struct DeadtimeArgs { + rising_edge_delay: u16, + falling_edge_delay: u16, + mode: mcpwm_deadtime_type_t, +} + +/// Operator configuration pub struct OperatorConfig { frequency: Hertz, duty_a: Duty, @@ -367,19 +482,32 @@ pub struct OperatorConfig { } impl OperatorConfig { + /// Frequency which the operator will run at, can also be changed live later #[must_use] - pub fn frequency(mut self, frequency: Hertz) -> Self { - self.frequency = frequency; + pub fn frequency(mut self, frequency: impl Into) -> Self { + self.frequency = frequency.into(); self } + /// Lowest frequency which the operator needs to be able to reach + /// + /// This setting will limit what frequency range the operator can reach. + /// Setting a low value here will lead to worse resolution at higher + /// frequencies. A high value on the other hand will prevent any frequencies + /// lower than that. + /// + /// Thus, for maximum resolution set lowest_frequency to the lowest expected + /// frequency which will be used. + /// + /// NOTE: This value can currently not be changed live #[cfg(not(esp_idf_version = "4.3"))] #[must_use] - pub fn lowest_frequency(mut self, lowest_frequency: Hertz) -> Self { - self.lowest_frequency = lowest_frequency; + pub fn lowest_frequency(mut self, lowest_frequency: impl Into) -> Self { + self.lowest_frequency = lowest_frequency.into(); self } + /// Specify what duty mode to use for the operator. #[must_use] pub fn duty_mode(mut self, duty_mode: DutyMode) -> Self { self.duty_mode = duty_mode; @@ -457,7 +585,7 @@ impl Unit for UnitOne { } // TODO: How do we want fault module to fit into this? - +/// Motor Control PWM module abstraction pub struct Mcpwm { #[cfg(not(esp_idf_version = "4.3"))] /// This is the frequency of the clock signal passed on as clock source for the operators timers @@ -617,6 +745,10 @@ impl_operator!( // TODO: How do we want carrier to fit into this? // TODO: How do we want capture to fit into this? +/// Motor Control operator abstraction +/// +/// Every Motor Control module has three operators. Every operator can generate two output signals called A and B. +/// A and B share the same timer and thus frequency and phase but can have induvidual duty set. pub struct Operator, M: Borrow>, PA: OutputPin, PB: OutputPin> { _instance: O, _unit: U, @@ -685,9 +817,19 @@ where mcpwm_deadtime_disable(U::unit(), O::timer()); }, Some(config) => { - let DeadtimeArgs { red, fed, mode } = config.as_args(); + let DeadtimeArgs { + rising_edge_delay, + falling_edge_delay, + mode, + } = config.as_args(); unsafe { - mcpwm_deadtime_enable(U::unit(), O::timer(), mode, red.into(), fed.into()); + mcpwm_deadtime_enable( + U::unit(), + O::timer(), + mode, + rising_edge_delay.into(), + falling_edge_delay.into(), + ); } } } @@ -739,7 +881,7 @@ where (self._instance, self._pin_a, self._pin_b) } - /// Get duty as percentage between 0.0 and 100.0 + /// Get duty as percentage between 0.0 and 100.0 for output A pub fn get_duty_a(&self) -> Duty { unsafe { esp_idf_sys::mcpwm_get_duty( @@ -750,7 +892,7 @@ where } } - /// Get duty as percentage between 0.0 and 100.0 + /// Get duty as percentage between 0.0 and 100.0 for output B pub fn get_duty_b(&self) -> Duty { unsafe { esp_idf_sys::mcpwm_get_duty( @@ -761,7 +903,7 @@ where } } - /// Set duty as percentage between 0.0 and 100.0 + /// Set duty as percentage between 0.0 and 100.0 for output A pub fn set_duty_a(&mut self, duty: Duty) -> Result<(), EspError> { unsafe { esp!(esp_idf_sys::mcpwm_set_duty( @@ -773,7 +915,7 @@ where } } - /// Set duty as percentage between 0.0 and 100.0 + /// Set duty as percentage between 0.0 and 100.0 for output B pub fn set_duty_b(&mut self, duty: Duty) -> Result<(), EspError> { unsafe { esp!(esp_idf_sys::mcpwm_set_duty( From cc8589df9190fd6e753218ce1c726c5ad7ae3286 Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Fri, 5 Aug 2022 18:08:46 +0200 Subject: [PATCH 14/62] Update example(crashing) --- examples/mcpwm-simple.rs | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/examples/mcpwm-simple.rs b/examples/mcpwm-simple.rs index bcc3d849e1d..f91fd6c33a6 100644 --- a/examples/mcpwm-simple.rs +++ b/examples/mcpwm-simple.rs @@ -78,16 +78,19 @@ fn main() -> anyhow::Result<()> { println!("Starting duty-cycle loop"); - for &x in [0.0, 20.0, 40.0, 60.0, 80.0, 100.0].iter().cycle() { - println!("Duty {}%", x); - operator.set_duty_a(x)?; - operator.set_duty_b(100.0 - x)?; - FreeRtos.delay_ms(2000)?; - } + for x in (0..10000u16).cycle() { + let duty = f32::from(x) * 0.01; + + if x % 100 == 0 { + println!("Duty {}%", duty); + } - loop { - FreeRtos.delay_ms(1000)?; + operator.set_duty_a(duty)?; + operator.set_duty_b(100.0 - duty)?; + FreeRtos.delay_ms(10)?; } + + unreachable!() } #[cfg(not(any(esp32, esp32s3)))] From 8ca34ba365813dc7c5b93af3e6f472812539aa7c Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Mon, 8 Aug 2022 19:11:00 +0200 Subject: [PATCH 15/62] Work around float printing issue --- examples/mcpwm-simple.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/mcpwm-simple.rs b/examples/mcpwm-simple.rs index f91fd6c33a6..c99d3cac605 100644 --- a/examples/mcpwm-simple.rs +++ b/examples/mcpwm-simple.rs @@ -82,7 +82,7 @@ fn main() -> anyhow::Result<()> { let duty = f32::from(x) * 0.01; if x % 100 == 0 { - println!("Duty {}%", duty); + println!("Duty {}%", x / 100); } operator.set_duty_a(duty)?; From 4ed269675252b73de6765151876fe57f6d3506a4 Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Mon, 8 Aug 2022 19:20:50 +0200 Subject: [PATCH 16/62] Clarify deadtime --- src/mcpwm.rs | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/mcpwm.rs b/src/mcpwm.rs index 4ac16d9c512..38e65110d29 100644 --- a/src/mcpwm.rs +++ b/src/mcpwm.rs @@ -192,6 +192,9 @@ pub enum DeadtimeConfig { // TODO: Figure out what all of those options do and give them nice descriptions /// MCPWM_BYPASS_RED /// + /// Note that `MCPWMXB in` will be completely ignored. This means `Operator::set_duty_b` will + /// have no effect with this dead time mode + /// /// ``` /// . . . /// . . . @@ -221,6 +224,9 @@ pub enum DeadtimeConfig { /// MCPWM_BYPASS_FED /// + /// Note that `MCPWMXB in` will be completely ignored. This means `Operator::set_duty_b` will + /// have no effect with this dead time mode + /// /// ``` /// . . . /// . . . @@ -250,6 +256,9 @@ pub enum DeadtimeConfig { /// MCPWM_ACTIVE_HIGH_MODE /// + /// Note that `MCPWMXB in` will be completely ignored. This means `Operator::set_duty_b` will + /// have no effect with this dead time mode + /// /// ``` /// . . . . /// . . . . @@ -282,6 +291,9 @@ pub enum DeadtimeConfig { /// MCPWM_ACTIVE_LOW_MODE /// + /// Note that `MCPWMXB in` will be completely ignored. This means `Operator::set_duty_b` will + /// have no effect with this dead time mode + /// /// ``` /// . . . . /// . . . . @@ -314,6 +326,9 @@ pub enum DeadtimeConfig { /// MCPWM_ACTIVE_HIGH_COMPLIMENT_MODE /// + /// Note that `MCPWMXB in` will be completely ignored. This means `Operator::set_duty_b` will + /// have no effect with this dead time mode + /// /// ``` /// . . . . /// . . . . @@ -346,6 +361,9 @@ pub enum DeadtimeConfig { /// MCPWM_ACTIVE_LOW_COMPLIMENT_MODE /// + /// Note that `MCPWMXB in` will be completely ignored. This means `Operator::set_duty_b` will + /// have no effect with this dead time mode + /// /// ``` /// . . . . /// . . . . @@ -377,12 +395,18 @@ pub enum DeadtimeConfig { }, /// MCPWM_ACTIVE_RED_FED_FROM_PWMXA + /// + /// Note that `MCPWMXB in` will be completely ignored. This means `Operator::set_duty_b` will + /// have no effect with this dead time mode ActiveRedFedFromPwmxa { rising_edge_delay: u16, falling_edge_delay: u16, }, /// MCPWM_ACTIVE_RED_FED_FROM_PWMXB + /// + /// Note that `MCPWMXA in` will be completely ignored. This means `Operator::set_duty_a` will + /// have no effect with this dead time mode ActiveRedFedFromPwmxb { rising_edge_delay: u16, falling_edge_delay: u16, From 490a84acae2f95e8fedc96191fc33981ba8373d6 Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Mon, 8 Aug 2022 19:23:17 +0200 Subject: [PATCH 17/62] Some cleanup --- src/mcpwm.rs | 31 +++++++++++++------------------ 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/src/mcpwm.rs b/src/mcpwm.rs index 38e65110d29..27fa4715bda 100644 --- a/src/mcpwm.rs +++ b/src/mcpwm.rs @@ -659,7 +659,7 @@ impl Mcpwm { MAX_PWM_TIMER_PRESCALE * MAX_PWM_TIMER_PERIOD * u32::from(lowest_frequency); let group_pre_scale = MCPWM_CLOCK_SOURCE_FREQUENCY / operator_source_frequency; if !(1..=256).contains(&group_pre_scale) { - return Err(EspError::from(ESP_ERR_INVALID_ARG).unwrap()); + esp!(ESP_ERR_INVALID_ARG)?; } esp!(unsafe { mcpwm_group_set_resolution(U::unit(), operator_source_frequency) })?; @@ -686,7 +686,7 @@ impl Mcpwm { // TODO: Do we care about frequency < 1Hz? let group_pre_scale = MCPWM_CLOCK_SOURCE_FREQUENCY / frequency; if !(1..=256).contains(&group_pre_scale) { - return Err(EspError::from(ESP_ERR_INVALID_ARG).unwrap()); + esp!(ESP_ERR_INVALID_ARG)?; } esp!(unsafe { mcpwm_group_set_resolution(U::unit(), frequency) })?; @@ -803,13 +803,13 @@ where // Can not specify a clock frequency lower then what has // been configured as the lowest clock frequency // Use `OperatorConfig::lowest_frequency` to enable lower frequencies - return Err(EspError::from(ESP_ERR_INVALID_ARG).unwrap()); + esp!(ESP_ERR_INVALID_ARG)?; } if config.lowest_frequency > mcpwm_module.borrow().operator_source_frequency.Hz() { // Can not specify a lowest_frequency larger than the corresponding value for // the parent MCPWM module. Use `Mcpwm::lowest_frequency` to enable higher frequencies - return Err(EspError::from(ESP_ERR_INVALID_ARG).unwrap()); + esp!(ESP_ERR_INVALID_ARG)?; } let resolution = u32::from(config.lowest_frequency) * MAX_PWM_TIMER_PERIOD; @@ -884,23 +884,18 @@ where // mcpwm_stop will only fail when invalid args are given esp!(unsafe { mcpwm_stop(U::unit(), O::timer()) }).unwrap(); - // TODO: Test and verify if this is the right way - if self._pin_a.is_some() { - // TODO: How to unset pin? - // let io_signal = O::signal_a(); - // mcpwm_gpio_init(U::unit(), io_signal, -1) - // does not seem to be it... - todo!(); + // Detatch pins from MCPWM operator + if let Some(_pin) = &self._pin_a { + // TODO + //pin.reset(); } - if self._pin_b.is_some() { - // TODO: How to unset pin? - // let io_signal = O::signal_b(); - // mcpwm_gpio_init(U::unit(), io_signal, -1) - // does not seem to be it... - - todo!(); + // Detatch pins from MCPWM operator + if let Some(_pin) = &self._pin_b { + // TODO + //pin.reset(); } + // TODO: Do we need to reset any more state here such as dead time config? (self._instance, self._pin_a, self._pin_b) } From c874e78bab7640ae1cf93214930df9333899d279 Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Mon, 8 Aug 2022 19:24:03 +0200 Subject: [PATCH 18/62] Fix some methods missing their self --- src/mcpwm.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mcpwm.rs b/src/mcpwm.rs index 27fa4715bda..dcc0cd3216b 100644 --- a/src/mcpwm.rs +++ b/src/mcpwm.rs @@ -947,7 +947,7 @@ where } /// Set PWM frequency - pub fn set_frequency(frequency: Hertz) -> Result<(), EspError> { + pub fn set_frequency(&mut self, frequency: Hertz) -> Result<(), EspError> { unsafe { esp!(esp_idf_sys::mcpwm_set_frequency( U::unit(), @@ -957,7 +957,7 @@ where } } - pub fn get_frequency() -> Hertz { + pub fn get_frequency(&self) -> Hertz { Hertz::from(unsafe { esp_idf_sys::mcpwm_get_frequency(U::unit(), O::timer()) }) } } From 9ebbeee4453d7a4f1c807eac14dfcae92346fa57 Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Fri, 12 Aug 2022 17:16:39 +0200 Subject: [PATCH 19/62] Final(?) adjustments to doc --- src/mcpwm.rs | 63 +++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 57 insertions(+), 6 deletions(-) diff --git a/src/mcpwm.rs b/src/mcpwm.rs index dcc0cd3216b..3d22952aefc 100644 --- a/src/mcpwm.rs +++ b/src/mcpwm.rs @@ -218,7 +218,7 @@ pub enum DeadtimeConfig { /// | . | /// --------------. . ----------------- /// . . . - /// . . . . + /// . . . /// ``` BypassRisingEdge { falling_edge_delay: u16 }, @@ -250,7 +250,7 @@ pub enum DeadtimeConfig { /// | . | /// --------------- . --------------------- /// . . . - /// . . . . + /// . . . /// ``` BypassFallingEdge { rising_edge_delay: u16 }, @@ -378,7 +378,7 @@ pub enum DeadtimeConfig { /// red >. |< | . /// MCPWMXA out . | | . /// . | | . - /// . ------------------ + /// . ------------------ . /// . . . . /// . . . . /// .------------------------. @@ -398,6 +398,32 @@ pub enum DeadtimeConfig { /// /// Note that `MCPWMXB in` will be completely ignored. This means `Operator::set_duty_b` will /// have no effect with this dead time mode + /// + /// ``` + /// . . . . + /// . . . . + /// .--------------------. . + /// | . | . + /// MCPWMXA in | . | . + /// | . | . + /// --------------- . --------------------- + /// . . . . + /// . . . . + /// . .--------------------. + /// red >. |< . | + /// MCPWMXA out . | . | + /// . | . | + /// ------------------- . --------------------- + /// . . . . + /// . . . . + /// . .--------------------. + /// red >. |< . | + /// MCPWMXB out . | . | + /// . | . | + /// ------------------- . --------------------- + /// . . . . + /// . . . . + /// ``` ActiveRedFedFromPwmxa { rising_edge_delay: u16, falling_edge_delay: u16, @@ -407,6 +433,31 @@ pub enum DeadtimeConfig { /// /// Note that `MCPWMXA in` will be completely ignored. This means `Operator::set_duty_a` will /// have no effect with this dead time mode + /// ``` + /// . . . . + /// . . . . + /// .--------------------. . + /// | . | . + /// MCPWMXB in | . | . + /// | . | . + /// --------------- . --------------------- + /// . . . . + /// . . . . + /// . .--------------------. + /// red >. |< . | + /// MCPWMXA out . | . | + /// . | . | + /// ------------------- . --------------------- + /// . . . . + /// . . . . + /// . .--------------------. + /// red >. |< . | + /// MCPWMXB out . | . | + /// . | . | + /// ------------------- . --------------------- + /// . . . . + /// . . . . + /// ``` ActiveRedFedFromPwmxb { rising_edge_delay: u16, falling_edge_delay: u16, @@ -765,7 +816,6 @@ impl_operator!( ); // TODO: How do we want syncing to fit in to this? -// TODO: How do we want dead time to fit into this? // TODO: How do we want carrier to fit into this? // TODO: How do we want capture to fit into this? @@ -880,7 +930,7 @@ where }) } - pub fn release(self) -> (O, Option, Option) { + /*pub fn release(self) -> (O, Option, Option) { // mcpwm_stop will only fail when invalid args are given esp!(unsafe { mcpwm_stop(U::unit(), O::timer()) }).unwrap(); @@ -898,7 +948,7 @@ where // TODO: Do we need to reset any more state here such as dead time config? (self._instance, self._pin_a, self._pin_b) - } + }*/ /// Get duty as percentage between 0.0 and 100.0 for output A pub fn get_duty_a(&self) -> Duty { @@ -957,6 +1007,7 @@ where } } + /// Get PWM frequency pub fn get_frequency(&self) -> Hertz { Hertz::from(unsafe { esp_idf_sys::mcpwm_get_frequency(U::unit(), O::timer()) }) } From d05ae44bebd3fe0f97fd7273b991dbb602534215 Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Fri, 12 Aug 2022 17:23:45 +0200 Subject: [PATCH 20/62] Explain example mcpwm-simple --- examples/mcpwm-simple.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/examples/mcpwm-simple.rs b/examples/mcpwm-simple.rs index c99d3cac605..07f99d73676 100644 --- a/examples/mcpwm-simple.rs +++ b/examples/mcpwm-simple.rs @@ -1,4 +1,8 @@ -/// # x = 10 +/// Simple example showing how MCPWM may be used to generate two synchronized pwm signals with varying duty. +/// The duty on the pin4 will increase from 0% all the way up to 100% and repeat. At the same time the duty +/// on pin 5 will go from 100% down to 0% +/// +/// # duty = 10 /// /// . . /// . . @@ -17,7 +21,7 @@ /// . . /// /// -/// # x = 50 +/// # duty = 50 /// . . /// . . /// .---------------. .---------------. @@ -35,7 +39,7 @@ /// . . /// /// -/// # x = 90 +/// # duty = 90 /// . . /// . . /// .------------------------. .------------------------. From f5a13ca2f20b22e73c2134f1697e0b83cf826ff4 Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Wed, 31 Aug 2022 14:02:51 +0200 Subject: [PATCH 21/62] Allow setting initial duty in OperatorConfig --- src/mcpwm.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/mcpwm.rs b/src/mcpwm.rs index 3d22952aefc..3b037a3e13b 100644 --- a/src/mcpwm.rs +++ b/src/mcpwm.rs @@ -564,6 +564,20 @@ impl OperatorConfig { self } + /// Set initual duty cycle for output A as percentage + #[must_use] + pub fn duty_a(mut self, duty: Duty) -> Self { + self.duty_a = duty; + self + } + + /// Set initual duty cycle for output B as percentage + #[must_use] + pub fn duty_b(mut self, duty: Duty) -> Self { + self.duty_b = duty; + self + } + /// Lowest frequency which the operator needs to be able to reach /// /// This setting will limit what frequency range the operator can reach. From 6bc129a18ed2d611d2d4af3041a2d4cc3a8b1172 Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Wed, 31 Aug 2022 14:05:28 +0200 Subject: [PATCH 22/62] Clamp timer resolution to not be greater than timer group resolution --- src/mcpwm.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/mcpwm.rs b/src/mcpwm.rs index 3b037a3e13b..051df1868ef 100644 --- a/src/mcpwm.rs +++ b/src/mcpwm.rs @@ -870,13 +870,15 @@ where esp!(ESP_ERR_INVALID_ARG)?; } - if config.lowest_frequency > mcpwm_module.borrow().operator_source_frequency.Hz() { + let operator_source_frequency = mcpwm_module.borrow().operator_source_frequency; + if config.lowest_frequency > operator_source_frequency.Hz() { // Can not specify a lowest_frequency larger than the corresponding value for // the parent MCPWM module. Use `Mcpwm::lowest_frequency` to enable higher frequencies esp!(ESP_ERR_INVALID_ARG)?; } let resolution = u32::from(config.lowest_frequency) * MAX_PWM_TIMER_PERIOD; + let resolution = resolution.min(operator_source_frequency); unsafe { esp_idf_sys::mcpwm_timer_set_resolution(U::unit(), O::timer(), resolution); } From 1d779db75dcd032e8733d29369c89376ea1c5118 Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Wed, 31 Aug 2022 14:06:42 +0200 Subject: [PATCH 23/62] Expose Mcpwm's unit and Operator's timer --- src/mcpwm.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/mcpwm.rs b/src/mcpwm.rs index 051df1868ef..480f6f5e3eb 100644 --- a/src/mcpwm.rs +++ b/src/mcpwm.rs @@ -187,7 +187,7 @@ impl From for mcpwm_counter_type_t { /// `rising_edge_delay` and `falling_edge_delay` is time as in number of clock cycles after the MCPWM modules group prescaler. /// /// Note that the dead times are calculated from MCPWMXA's flanks unless explicitly stated otherwise -#[derive(Copy, Clone, PartialEq, Debug)] +#[derive(Copy, Clone, PartialEq, Eq, Debug)] pub enum DeadtimeConfig { // TODO: Figure out what all of those options do and give them nice descriptions /// MCPWM_BYPASS_RED @@ -759,6 +759,10 @@ impl Mcpwm { Ok(self) } + + pub fn unit(&self) -> mcpwm_unit_t { + U::unit() + } } // The hardware for ESP32 and ESP32-S3 can associate any operator(within the mcpwm module) with any @@ -1027,4 +1031,8 @@ where pub fn get_frequency(&self) -> Hertz { Hertz::from(unsafe { esp_idf_sys::mcpwm_get_frequency(U::unit(), O::timer()) }) } + + pub fn timer(&self) -> mcpwm_timer_t { + O::timer() + } } From 8384b06dfff59ef674ca523d1d630d2dd24d2b65 Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Fri, 16 Sep 2022 23:14:02 +0200 Subject: [PATCH 24/62] Very rough, not working draft of MCPWM 5.0 mostly stolen from MCPWM 4.4 --- src/mcpwm.rs | 371 +++++++++++++++++---------------------------------- 1 file changed, 119 insertions(+), 252 deletions(-) diff --git a/src/mcpwm.rs b/src/mcpwm.rs index 480f6f5e3eb..724d488212b 100644 --- a/src/mcpwm.rs +++ b/src/mcpwm.rs @@ -673,103 +673,112 @@ impl Unit for UnitOne { } } -// TODO: How do we want fault module to fit into this? -/// Motor Control PWM module abstraction -pub struct Mcpwm { - #[cfg(not(esp_idf_version = "4.3"))] - /// This is the frequency of the clock signal passed on as clock source for the operators timers - /// Those timers in turn have their own prescalers to scale this down even further - /// - /// NOTE: This is only to be set by calling Self::lowest_frequency - operator_source_frequency: u32, - _instance: MCPWM, +struct Timer { + } -impl Mcpwm { - pub fn new(instance: MCPWM) -> Result { - let res = Self { - #[cfg(not(esp_idf_version = "4.3"))] - operator_source_frequency: 0, - _instance: instance, - }; - - #[cfg(not(esp_idf_version = "4.3"))] - { - // TODO: Do we want to make this into something more builder-pattern like to - // avoid this potentially redundant function call? - res.operator_source_frequency(10.MHz()) - } +impl Timer { + /// Set PWM frequency + pub fn set_frequency(&mut self, frequency: Hertz) -> Result<(), EspError> { + todo!() + } - #[cfg(esp_idf_version = "4.3")] - { - Ok(res) - } + /// Get PWM frequency + pub fn get_frequency(&self) -> Hertz { + todo!() } - pub fn release(self) -> MCPWM { - // TODO: Do we need to reset any state here such as group_prescaler? - self._instance + pub fn timer(&self) -> mcpwm_timer_t { + T::timer() } +} - /// Specify lowest reachable frequency - /// - /// The lower this is set, the lower frequencies will be reachable. However, this is at the cost of worse - /// resolution at higher frequencies. - /// - /// Same thing goes for the other way. The higher value set here, the more resolution and so on. - #[cfg(not(esp_idf_version = "4.3"))] - pub fn lowest_frequency(mut self, lowest_frequency: Hertz) -> Result { - // TODO: Do we care about frequency < 1Hz? - let operator_source_frequency = - MAX_PWM_TIMER_PRESCALE * MAX_PWM_TIMER_PERIOD * u32::from(lowest_frequency); - let group_pre_scale = MCPWM_CLOCK_SOURCE_FREQUENCY / operator_source_frequency; - if !(1..=256).contains(&group_pre_scale) { - esp!(ESP_ERR_INVALID_ARG)?; - } +trait OptionalOperator> { - esp!(unsafe { mcpwm_group_set_resolution(U::unit(), operator_source_frequency) })?; - self.operator_source_frequency = operator_source_frequency; +} +struct NoOperator; +impl OptionalOperator for NoOperator {} - Ok(self) +// TODO: How do we want fault module to fit into this? +/// Motor Control PWM module abstraction +pub struct TimerConnection, O0, O1, O2> + where + O0: OptionalOperator, + O1: OptionalOperator, + O2: OptionalOperator, +{ + timer: Timer, + operator0: O0, + operator1: O1, + operator2: O2 +} + +// Since there can only ever by one instance of every operator type (except NoOperator) +// we know that there can be no mem::swap or similar to cause any problems. +// +// Thus we know that after split is called nothing can be added/removed while still having access to +// the individual objects. We also garantuee that the operators wont live longer than the timer +impl, O1: OptionalOperator, O2: OptionalOperator> TimerConnection { + fn split(&mut self) -> (&mut timer, &mut O0, &mut O1, &mut O2) { + ( + &mut self.timer, + &mut self.operator0, + &mut self.operator1, + &mut self.operator2, + ) } +} - /// Specify frequency passed to operators timers as clock source - /// - /// The timers of the operators can then in turn scale this frequency down further. - /// - /// The lower this is set, the lower frequencies will be reachable. However, this is - /// at the cost of worse resolution at higher frequencies. Same thing goes for the - /// other way. The higher value set here, the more resolution and so on. - #[cfg(not(esp_idf_version = "4.3"))] - pub fn operator_source_frequency( - mut self, - frequency: impl Into, - ) -> Result { - let frequency: Hertz = frequency.into(); - let frequency: u32 = frequency.into(); - - // TODO: Do we care about frequency < 1Hz? - let group_pre_scale = MCPWM_CLOCK_SOURCE_FREQUENCY / frequency; - if !(1..=256).contains(&group_pre_scale) { - esp!(ESP_ERR_INVALID_ARG)?; +impl TimerConnection { + fn new(timer: T) -> Self { + Self { + timer, + operator0: NoOperator, + operator1: NoOperator, + operator2: NoOperator } + } +} +impl TimerConnection { + fn attatch_operator0>(mut self, operator_cfg: O) -> TimerConnection { + let operator = self.init_and_attach_operator(operator_cfg); + TimerConnection { + timer: self.timer, + operator0: operator, + operator1: self.operator1, + operator2: self.operator2 + } + } +} - esp!(unsafe { mcpwm_group_set_resolution(U::unit(), frequency) })?; - self.operator_source_frequency = frequency; - - Ok(self) +impl TimerConnection { + fn attatch_operator1>(mut self, operator: O) -> TimerConnection { + let operator = self.init_and_attach_operator(operator_cfg); + TimerConnection { + timer: self.timer, + operator0: self.operator0, + operator1: operator, + operator2: self.operator2 + } } +} - pub fn unit(&self) -> mcpwm_unit_t { - U::unit() +impl TimerConnection { + fn attatch_operator2>(mut self, operator: O) -> TimerConnection { + let operator = self.init_and_attach_operator(operator_cfg); + TimerConnection { + timer: self.timer, + operator0: self.operator0, + operator1: self.operator1, + operator2: operator + } } } // The hardware for ESP32 and ESP32-S3 can associate any operator(within the mcpwm module) with any // timer(within the mcpwm module) for example allowing using the same timer for all three operators. // However at least as of IDF v4.4 timer0 is hardcoded to operator0 and timer1 to operator1 and so on... -pub trait HwOperator { - fn timer() -> mcpwm_timer_t; +pub trait HwOperator: Into> { fn signal_a() -> mcpwm_io_signals_t; fn signal_b() -> mcpwm_io_signals_t; fn unit() -> mcpwm_unit_t { @@ -780,10 +789,6 @@ pub trait HwOperator { macro_rules! impl_operator_helper { ($instance:ident: $timer:expr, $signal_a:expr, $signal_b:expr, $unit:ty) => { impl HwOperator<$unit> for $instance<$unit> { - fn timer() -> mcpwm_timer_t { - $timer - } - fn signal_a() -> mcpwm_io_signals_t { $signal_a } @@ -812,6 +817,16 @@ macro_rules! impl_operator { } } + impl Into> for $instance { + fn into(self) -> Operator { + Operator { + _instance: self, + pin_a: NoPin, + pin_b: NoPin, + } + } + } + impl_operator_helper!($instance: $timer, $signal_a, $signal_b, UnitZero); impl_operator_helper!($instance: $timer, $signal_a, $signal_b, UnitOne); }; @@ -841,13 +856,14 @@ impl_operator!( /// /// Every Motor Control module has three operators. Every operator can generate two output signals called A and B. /// A and B share the same timer and thus frequency and phase but can have induvidual duty set. -pub struct Operator, M: Borrow>, PA: OutputPin, PB: OutputPin> { +pub struct Operator, M: Borrow>, PA: OptionalPin, PB: OptionalPin, D> { + handle: mcpwm_operator_t, _instance: O, - _unit: U, - _mcpwm_module: M, - _pin_a: Option, - _pin_b: Option, + _pin_a: PA, + _pin_b: PB, + + deadtime: D } impl Operator @@ -856,183 +872,34 @@ where O: HwOperator, M: Borrow>, PA: OutputPin, - PB: OutputPin, + PB: OptionalOutputPin, { - pub fn new>, B: Into>>( - operator: O, - mcpwm_module: M, - config: &OperatorConfig, - pin_a: A, - pin_b: B, - ) -> Result { - #[cfg(not(esp_idf_version = "4.3"))] - { - if config.frequency < config.lowest_frequency { - // Can not specify a clock frequency lower then what has - // been configured as the lowest clock frequency - // Use `OperatorConfig::lowest_frequency` to enable lower frequencies - esp!(ESP_ERR_INVALID_ARG)?; - } - - let operator_source_frequency = mcpwm_module.borrow().operator_source_frequency; - if config.lowest_frequency > operator_source_frequency.Hz() { - // Can not specify a lowest_frequency larger than the corresponding value for - // the parent MCPWM module. Use `Mcpwm::lowest_frequency` to enable higher frequencies - esp!(ESP_ERR_INVALID_ARG)?; - } - - let resolution = u32::from(config.lowest_frequency) * MAX_PWM_TIMER_PERIOD; - let resolution = resolution.min(operator_source_frequency); - unsafe { - esp_idf_sys::mcpwm_timer_set_resolution(U::unit(), O::timer(), resolution); - } - } - - // TODO: Handle/document half pwm frequency when counter_mode = UpDown? - - esp!(unsafe { - esp_idf_sys::mcpwm_init( - U::unit(), - O::timer(), - &mcpwm_config_t { - frequency: config.frequency.into(), - cmpr_a: config.duty_a, - cmpr_b: config.duty_b, - duty_mode: config.duty_mode.into(), - counter_mode: config.counter_mode.into(), - }, - ) - })?; - - match config.deadtime { - None => unsafe { - // Only way this can faild is if an invalid timer or unit is specified - // which we know can not happen. So we don't have to check for errors - mcpwm_deadtime_disable(U::unit(), O::timer()); - }, - Some(config) => { - let DeadtimeArgs { - rising_edge_delay, - falling_edge_delay, - mode, - } = config.as_args(); - unsafe { - mcpwm_deadtime_enable( - U::unit(), - O::timer(), - mode, - rising_edge_delay.into(), - falling_edge_delay.into(), - ); - } - } - } - - let pin_a: Option = pin_a.into(); - let pin_b: Option = pin_b.into(); - - if let Some(pin_a) = &pin_a { - let io_signal = O::signal_a(); - esp!(unsafe { esp_idf_sys::mcpwm_gpio_init(U::unit(), io_signal, pin_a.pin()) })?; - } - - if let Some(pin_b) = &pin_b { - let io_signal = O::signal_b(); - esp!(unsafe { esp_idf_sys::mcpwm_gpio_init(U::unit(), io_signal, pin_b.pin()) })?; - } - - Ok(Self { - _instance: operator, - _unit: U::default(), - _mcpwm_module: mcpwm_module, - _pin_a: pin_a, - _pin_b: pin_b, - }) - } - - /*pub fn release(self) -> (O, Option, Option) { - // mcpwm_stop will only fail when invalid args are given - esp!(unsafe { mcpwm_stop(U::unit(), O::timer()) }).unwrap(); - - // Detatch pins from MCPWM operator - if let Some(_pin) = &self._pin_a { - // TODO - //pin.reset(); - } - - // Detatch pins from MCPWM operator - if let Some(_pin) = &self._pin_b { - // TODO - //pin.reset(); - } - - // TODO: Do we need to reset any more state here such as dead time config? - (self._instance, self._pin_a, self._pin_b) - }*/ - /// Get duty as percentage between 0.0 and 100.0 for output A pub fn get_duty_a(&self) -> Duty { - unsafe { - esp_idf_sys::mcpwm_get_duty( - U::unit(), - O::timer(), - esp_idf_sys::mcpwm_generator_t_MCPWM_GEN_A, - ) - } - } - - /// Get duty as percentage between 0.0 and 100.0 for output B - pub fn get_duty_b(&self) -> Duty { - unsafe { - esp_idf_sys::mcpwm_get_duty( - U::unit(), - O::timer(), - esp_idf_sys::mcpwm_generator_t_MCPWM_GEN_B, - ) - } + todo!() } /// Set duty as percentage between 0.0 and 100.0 for output A pub fn set_duty_a(&mut self, duty: Duty) -> Result<(), EspError> { - unsafe { - esp!(esp_idf_sys::mcpwm_set_duty( - U::unit(), - O::timer(), - esp_idf_sys::mcpwm_generator_t_MCPWM_GEN_A, - duty - )) - } - } - - /// Set duty as percentage between 0.0 and 100.0 for output B - pub fn set_duty_b(&mut self, duty: Duty) -> Result<(), EspError> { - unsafe { - esp!(esp_idf_sys::mcpwm_set_duty( - U::unit(), - O::timer(), - esp_idf_sys::mcpwm_generator_t_MCPWM_GEN_B, - duty - )) - } - } - - /// Set PWM frequency - pub fn set_frequency(&mut self, frequency: Hertz) -> Result<(), EspError> { - unsafe { - esp!(esp_idf_sys::mcpwm_set_frequency( - U::unit(), - O::timer(), - frequency.into() - )) - } + todo!() } +} - /// Get PWM frequency - pub fn get_frequency(&self) -> Hertz { - Hertz::from(unsafe { esp_idf_sys::mcpwm_get_frequency(U::unit(), O::timer()) }) +impl Operator +where + U: Unit, + O: HwOperator, + M: Borrow>, + PA: OptionalOutputPin, + PB: OutputPin, +{ + /// Get duty as percentage between 0.0 and 100.0 for output B + pub fn get_duty_b(&self) -> Duty { + todo!() } - pub fn timer(&self) -> mcpwm_timer_t { - O::timer() + /// Set duty as percentage between 0.0 and 100.0 for output B + pub fn set_duty_b(&mut self, duty: Duty) -> Result<(), EspError> { + todo!() } } From 5f018352d2a520e24741b4b4f8a13ba8d73089cb Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Sun, 25 Sep 2022 12:11:02 +0200 Subject: [PATCH 25/62] Refactor into different files and update example(still not compiling by far) --- examples/mcpwm-simple.rs | 28 +- src/mcpwm.rs | 905 ---------------------------------- src/mcpwm/mod.rs | 138 ++++++ src/mcpwm/operator.rs | 248 ++++++++++ src/mcpwm/timer.rs | 168 +++++++ src/mcpwm/timer_connection.rs | 76 +++ 6 files changed, 645 insertions(+), 918 deletions(-) delete mode 100644 src/mcpwm.rs create mode 100644 src/mcpwm/mod.rs create mode 100644 src/mcpwm/operator.rs create mode 100644 src/mcpwm/timer.rs create mode 100644 src/mcpwm/timer_connection.rs diff --git a/examples/mcpwm-simple.rs b/examples/mcpwm-simple.rs index 07f99d73676..d1d7790ab60 100644 --- a/examples/mcpwm-simple.rs +++ b/examples/mcpwm-simple.rs @@ -70,22 +70,24 @@ fn main() -> anyhow::Result<()> { println!("Configuring MCPWM"); let peripherals = Peripherals::take().unwrap(); - let config = OperatorConfig::default().frequency(25.kHz()); - let mcpwm = Mcpwm::new(peripherals.mcpwm0.mcpwm)?; - let mut operator = Operator::new( - peripherals.mcpwm0.operator0, - &mcpwm, - &config, - peripherals.pins.gpio4, - peripherals.pins.gpio5, - )?; + let timer_config = TimerConfig::default().frequency(25.kHz()); + let operator_config = OperatorConfig::default(); + let timer = Mcpwm::new(peripherals.mcpwm0.timer, timer_config)?; - println!("Starting duty-cycle loop"); + let timer = timer.into_connection() + .attatch_operator0( + peripherals.mcpwm0.operator0, + operator_config, + peripherals.pins.gpio4, + peripherals.pins.gpio5, + )?; + + let (timer, operator, _, _) = timer.split(); - for x in (0..10000u16).cycle() { - let duty = f32::from(x) * 0.01; + println!("Starting duty-cycle loop"); - if x % 100 == 0 { + for duty in (0..timer.get_top_value()).cycle() { + if duty % 100 == 0 { println!("Duty {}%", x / 100); } diff --git a/src/mcpwm.rs b/src/mcpwm.rs deleted file mode 100644 index 724d488212b..00000000000 --- a/src/mcpwm.rs +++ /dev/null @@ -1,905 +0,0 @@ -//! Motor Control Pulse Width Modulator peripheral -//! -//! Interface to the [Motor Control Pulse Width Modulator peripheral (MCPWM) -//! peripheral](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/mcpwm.html) -//! -//! ``` -//! --------------- --------------- -//! | MCPWM Unit 0 | | MCPWM Unit 1 | -//! | ------------ | | ------------ | -//! | | | | -//! | OPERATOR 0 |--> A | OPERATOR 0 |--> A -//! | |--> B | |--> B -//! | | | | -//! | OPERATOR 1 |--> A | OPERATOR 1 |--> A -//! | |--> B | |--> B -//! | | | | -//! | OPERATOR 2 |--> A | OPERATOR 2 |--> A -//! | |--> B | |--> B -//! --------------- --------------- -//! ``` -//! -//! # Example -//! -//! Create a pair of PWM signals on pin 4 and 5. The duty on pin 4 will ramp from 0% to 100% -//! while pin 5 will ramp from 100% down to 0%. -//! ``` -//! let peripherals = Peripherals::take().unwrap(); -//! let config = OperatorConfig::default().frequency(25.kHz().into()); -//! let mcpwm = Mcpwm::new(peripherals.mcpwm0.mcpwm)?; -//! let mut operator = Operator::new( -//! peripherals.mcpwm0.operator0, -//! &mcpwm, -//! &config, -//! peripherals.pins.gpio4, -//! peripherals.pins.gpio5, -//! )?; -//! -//! println!("Starting duty-cycle loop"); -//! -//! for &duty in [0.0, 20.0, 40.0, 60.0, 80.0, 100.0].iter() { -//! println!("Duty {}%", duty); -//! operator.set_duty_a(duty)?; -//! operator.set_duty_b(100.0 - duty)?; -//! FreeRtos.delay_ms(2000)?; -//! } -//! ``` -//! -//! See the `examples/` folder of this repository for more. - -use core::borrow::Borrow; - -use crate::gpio::OutputPin; -use crate::units::{FromValueType, Hertz}; -use esp_idf_sys::*; - -// MCPWM clock source frequency for ESP32 and ESP32-s3 -const MCPWM_CLOCK_SOURCE_FREQUENCY: u32 = 160_000_000; - -// Max PWM timer prescaler -const MAX_PWM_TIMER_PRESCALE: u32 = 0x1_00; - -// Max PWM timer period -const MAX_PWM_TIMER_PERIOD: u32 = 0x1_00_00; - -/// The Motor Control Pulse Width Modulator peripheral -pub struct Peripheral { - pub mcpwm: MCPWM, - pub operator0: OPERATOR0, - pub operator1: OPERATOR1, - pub operator2: OPERATOR2, -} - -impl Peripheral { - /// # Safety - /// - /// It is safe to instantiate this exactly one time per `Unit`. - pub unsafe fn new() -> Self { - Self { - mcpwm: MCPWM::new(), - operator0: OPERATOR0::new(), - operator1: OPERATOR1::new(), - operator2: OPERATOR2::new(), - } - } -} - -/// Duty modes for operator -#[derive(Clone, Copy, Debug)] -pub enum DutyMode { - /// Active high - /// - /// Setting duty = 100% will result in a constant high output - /// Setting duty = 0% will result in a constant low output - ActiveHigh, - - /// Active low - /// - /// Setting duty = 100% will result in a constant low output - /// Setting duty = 0% will result in a constant high output - ActiveLow, -} - -impl From for mcpwm_duty_type_t { - fn from(val: DutyMode) -> Self { - match val { - DutyMode::ActiveHigh => mcpwm_duty_type_t_MCPWM_DUTY_MODE_0, - DutyMode::ActiveLow => mcpwm_duty_type_t_MCPWM_DUTY_MODE_1, - } - } -} - -/// Counter mode for operator's timer for generating PWM signal -// TODO: For UpDown, frequency is half of MCPWM frequency set -#[derive(Clone, Copy, Debug)] -pub enum CounterMode { - /// Timer is frozen or paused - #[cfg(not(esp_idf_version = "4.3"))] - Frozen, - /// Edge aligned. The counter will start from its lowest value and increment every clock cycle until the period is reached. - /// - /// The wave form will end up looking something like the following: - /// ``` - /// start, counter = 0 reset, counter = period - /// | | - /// | |*--- start, counter = 0 - /// v <---- duty ----> . v| - /// . . .v - /// .--------------------. ..---- - /// | Active | .| - /// | | .| - /// | | Not active .| - /// - --------------------- - /// ``` - Up, - - /// Edge aligned. The counter will start from its highest value, period and decrement every clock cycle until the zero is reached - /// - /// The wave form will end up looking something like the following: - /// ``` - /// start, counter = period reset, counter = 0 - /// | | - /// | |*--- start, counter = period - /// v . v| - /// . . <---- duty ----> .v - /// . .--------------------.. - /// . Active | |. - /// . | |. - /// . Not active | Active |. - /// ---------------------- ---- - /// ``` - Down, - - /// Symmetric mode. The counter will start from its lowest value and increment every clock cycle until the period is reached - /// - /// The wave form will end up looking something like the following: - /// ``` - /// change count dir to decrement, counter = period - /// start, counter = 0, incrementing | change count dir to increment, counter = 0 - /// | | | - /// | |*--- counter = period |*----- start, counter = 0, incrementing - /// v <---- duty ----> . v| . <---- duty ----> || - /// . . .v . vv - /// ---------------------. .. .-------------------------------------------. .. .-- - /// Active | .. | Active Active | .. | - /// | .. | | .. | - /// | Not active .. Not active | | Not active .. Not active | - /// ---------------------------------------- ---------------------------------------- - /// ``` - /// NOTE: That in this mode, the frequency will be half of that specified - UpDown, -} - -impl From for mcpwm_counter_type_t { - fn from(val: CounterMode) -> Self { - match val { - #[cfg(not(esp_idf_version = "4.3"))] - CounterMode::Frozen => mcpwm_counter_type_t_MCPWM_FREEZE_COUNTER, - CounterMode::Up => mcpwm_counter_type_t_MCPWM_UP_COUNTER, - CounterMode::Down => mcpwm_counter_type_t_MCPWM_DOWN_COUNTER, - CounterMode::UpDown => mcpwm_counter_type_t_MCPWM_UP_DOWN_COUNTER, - } - } -} - -/// Dead time config for MCPWM operator -/// -/// `rising_edge_delay` and `falling_edge_delay` is time as in number of clock cycles after the MCPWM modules group prescaler. -/// -/// Note that the dead times are calculated from MCPWMXA's flanks unless explicitly stated otherwise -#[derive(Copy, Clone, PartialEq, Eq, Debug)] -pub enum DeadtimeConfig { - // TODO: Figure out what all of those options do and give them nice descriptions - /// MCPWM_BYPASS_RED - /// - /// Note that `MCPWMXB in` will be completely ignored. This means `Operator::set_duty_b` will - /// have no effect with this dead time mode - /// - /// ``` - /// . . . - /// . . . - /// .--------------------. . - /// | | . - /// MCPWMXA in | | . - /// | | . - /// --------------- --------------------- - /// . . . - /// . . . - /// .--------------------. . - /// | | . - /// MCPWMXA out | | . - /// | | . - /// --------------- --------------------- - /// . . . - /// . . . - /// .------------------------. - /// | >. |< fed - /// MCPWMXB out | . | - /// | . | - /// --------------. . ----------------- - /// . . . - /// . . . - /// ``` - BypassRisingEdge { falling_edge_delay: u16 }, - - /// MCPWM_BYPASS_FED - /// - /// Note that `MCPWMXB in` will be completely ignored. This means `Operator::set_duty_b` will - /// have no effect with this dead time mode - /// - /// ``` - /// . . . - /// . . . - /// .--------------------. - /// | . | - /// MCPWMXA in | . | - /// | . | - /// --------------- . --------------------- - /// . . . - /// . . . - /// . .----------------. - /// red >. |< | - /// MCPWMXA out . | | - /// . | | - /// ------------------- --------------------- - /// . . . - /// . . . - /// .--------------------. - /// | . | - /// MCPWMXB out | . | - /// | . | - /// --------------- . --------------------- - /// . . . - /// . . . - /// ``` - BypassFallingEdge { rising_edge_delay: u16 }, - - /// MCPWM_ACTIVE_HIGH_MODE - /// - /// Note that `MCPWMXB in` will be completely ignored. This means `Operator::set_duty_b` will - /// have no effect with this dead time mode - /// - /// ``` - /// . . . . - /// . . . . - /// .--------------------. . - /// | . | . - /// MCPWMXA in | . | . - /// | . | . - /// --------------- . --------------------- - /// . . . . - /// . . . . - /// . .----------------. . - /// red >. |< | . - /// MCPWMXA out . | | . - /// . | | . - /// ------------------- --------------------- - /// . . . . - /// . . . . - /// .------------------------. - /// | . >. |< fed - /// MCPWMXB out | . . | - /// | . . | - /// --------------. . . ----------------- - /// . . . . - /// . . . . - /// ``` - ActiveHigh { - rising_edge_delay: u16, - falling_edge_delay: u16, - }, - - /// MCPWM_ACTIVE_LOW_MODE - /// - /// Note that `MCPWMXB in` will be completely ignored. This means `Operator::set_duty_b` will - /// have no effect with this dead time mode - /// - /// ``` - /// . . . . - /// . . . . - /// .--------------------. . - /// | . | . - /// MCPWMXA in | . | . - /// | . | . - /// --------------- . --------------------- - /// . . . . - /// . . . . - /// ------------------. .-------------------- - /// red >. |< | . - /// MCPWMXA out . | | . - /// . | | . - /// . ------------------ - /// . . . . - /// . . . . - /// --------------. . . .---------------- - /// | . >. |< fed - /// MCPWMXB out | . . | - /// | . . | - /// -------------------------- - /// . . . . - /// . . . . - /// ``` - ActiveLow { - rising_edge_delay: u16, - falling_edge_delay: u16, - }, - - /// MCPWM_ACTIVE_HIGH_COMPLIMENT_MODE - /// - /// Note that `MCPWMXB in` will be completely ignored. This means `Operator::set_duty_b` will - /// have no effect with this dead time mode - /// - /// ``` - /// . . . . - /// . . . . - /// .--------------------. . - /// | . | . - /// MCPWMXA in | . | . - /// | . | . - /// --------------- . --------------------- - /// . . . . - /// . . . . - /// . .----------------. . - /// red >. |< | . - /// MCPWMXA out . | | . - /// . | | . - /// ------------------- --------------------- - /// . . . . - /// . . . . - /// --------------. . . .---------------- - /// | . >. |< fed - /// MCPWMXB out | . . | - /// | . . | - /// -------------------------- - /// . . . . - /// . . . . - /// ``` - ActiveHighComplement { - rising_edge_delay: u16, - falling_edge_delay: u16, - }, - - /// MCPWM_ACTIVE_LOW_COMPLIMENT_MODE - /// - /// Note that `MCPWMXB in` will be completely ignored. This means `Operator::set_duty_b` will - /// have no effect with this dead time mode - /// - /// ``` - /// . . . . - /// . . . . - /// .--------------------. . - /// | . | . - /// MCPWMXA in | . | . - /// | . | . - /// --------------- . --------------------- - /// . . . . - /// . . . . - /// ------------------. .-------------------- - /// red >. |< | . - /// MCPWMXA out . | | . - /// . | | . - /// . ------------------ . - /// . . . . - /// . . . . - /// .------------------------. - /// | . >. |< fed - /// MCPWMXB out | . . | - /// | . . | - /// --------------- . . ----------------- - /// . . . . - /// . . . . - /// ``` - ActiveLowComplement { - rising_edge_delay: u16, - falling_edge_delay: u16, - }, - - /// MCPWM_ACTIVE_RED_FED_FROM_PWMXA - /// - /// Note that `MCPWMXB in` will be completely ignored. This means `Operator::set_duty_b` will - /// have no effect with this dead time mode - /// - /// ``` - /// . . . . - /// . . . . - /// .--------------------. . - /// | . | . - /// MCPWMXA in | . | . - /// | . | . - /// --------------- . --------------------- - /// . . . . - /// . . . . - /// . .--------------------. - /// red >. |< . | - /// MCPWMXA out . | . | - /// . | . | - /// ------------------- . --------------------- - /// . . . . - /// . . . . - /// . .--------------------. - /// red >. |< . | - /// MCPWMXB out . | . | - /// . | . | - /// ------------------- . --------------------- - /// . . . . - /// . . . . - /// ``` - ActiveRedFedFromPwmxa { - rising_edge_delay: u16, - falling_edge_delay: u16, - }, - - /// MCPWM_ACTIVE_RED_FED_FROM_PWMXB - /// - /// Note that `MCPWMXA in` will be completely ignored. This means `Operator::set_duty_a` will - /// have no effect with this dead time mode - /// ``` - /// . . . . - /// . . . . - /// .--------------------. . - /// | . | . - /// MCPWMXB in | . | . - /// | . | . - /// --------------- . --------------------- - /// . . . . - /// . . . . - /// . .--------------------. - /// red >. |< . | - /// MCPWMXA out . | . | - /// . | . | - /// ------------------- . --------------------- - /// . . . . - /// . . . . - /// . .--------------------. - /// red >. |< . | - /// MCPWMXB out . | . | - /// . | . | - /// ------------------- . --------------------- - /// . . . . - /// . . . . - /// ``` - ActiveRedFedFromPwmxb { - rising_edge_delay: u16, - falling_edge_delay: u16, - }, -} - -impl DeadtimeConfig { - fn as_args(&self) -> DeadtimeArgs { - match *self { - DeadtimeConfig::BypassRisingEdge { falling_edge_delay } => DeadtimeArgs { - rising_edge_delay: 0, - falling_edge_delay, - mode: mcpwm_deadtime_type_t_MCPWM_BYPASS_RED, - }, - - DeadtimeConfig::BypassFallingEdge { rising_edge_delay } => DeadtimeArgs { - rising_edge_delay, - falling_edge_delay: 0, - mode: mcpwm_deadtime_type_t_MCPWM_BYPASS_FED, - }, - - DeadtimeConfig::ActiveHigh { - rising_edge_delay, - falling_edge_delay, - } => DeadtimeArgs { - rising_edge_delay, - falling_edge_delay, - mode: mcpwm_deadtime_type_t_MCPWM_ACTIVE_HIGH_MODE, - }, - - DeadtimeConfig::ActiveLow { - rising_edge_delay, - falling_edge_delay, - } => DeadtimeArgs { - rising_edge_delay, - falling_edge_delay, - mode: mcpwm_deadtime_type_t_MCPWM_ACTIVE_LOW_MODE, - }, - - DeadtimeConfig::ActiveHighComplement { - rising_edge_delay, - falling_edge_delay, - } => DeadtimeArgs { - rising_edge_delay, - falling_edge_delay, - mode: mcpwm_deadtime_type_t_MCPWM_ACTIVE_HIGH_COMPLIMENT_MODE, - }, - - DeadtimeConfig::ActiveLowComplement { - rising_edge_delay, - falling_edge_delay, - } => DeadtimeArgs { - rising_edge_delay, - falling_edge_delay, - mode: mcpwm_deadtime_type_t_MCPWM_ACTIVE_LOW_COMPLIMENT_MODE, - }, - - DeadtimeConfig::ActiveRedFedFromPwmxa { - rising_edge_delay, - falling_edge_delay, - } => DeadtimeArgs { - rising_edge_delay, - falling_edge_delay, - mode: mcpwm_deadtime_type_t_MCPWM_ACTIVE_RED_FED_FROM_PWMXA, - }, - - DeadtimeConfig::ActiveRedFedFromPwmxb { - rising_edge_delay, - falling_edge_delay, - } => DeadtimeArgs { - rising_edge_delay, - falling_edge_delay, - mode: mcpwm_deadtime_type_t_MCPWM_ACTIVE_RED_FED_FROM_PWMXB, - }, - } - } -} - -struct DeadtimeArgs { - rising_edge_delay: u16, - falling_edge_delay: u16, - mode: mcpwm_deadtime_type_t, -} - -/// Operator configuration -pub struct OperatorConfig { - frequency: Hertz, - duty_a: Duty, - duty_b: Duty, - - #[cfg(not(esp_idf_version = "4.3"))] - lowest_frequency: Hertz, - duty_mode: DutyMode, - counter_mode: CounterMode, - - deadtime: Option, -} - -impl OperatorConfig { - /// Frequency which the operator will run at, can also be changed live later - #[must_use] - pub fn frequency(mut self, frequency: impl Into) -> Self { - self.frequency = frequency.into(); - self - } - - /// Set initual duty cycle for output A as percentage - #[must_use] - pub fn duty_a(mut self, duty: Duty) -> Self { - self.duty_a = duty; - self - } - - /// Set initual duty cycle for output B as percentage - #[must_use] - pub fn duty_b(mut self, duty: Duty) -> Self { - self.duty_b = duty; - self - } - - /// Lowest frequency which the operator needs to be able to reach - /// - /// This setting will limit what frequency range the operator can reach. - /// Setting a low value here will lead to worse resolution at higher - /// frequencies. A high value on the other hand will prevent any frequencies - /// lower than that. - /// - /// Thus, for maximum resolution set lowest_frequency to the lowest expected - /// frequency which will be used. - /// - /// NOTE: This value can currently not be changed live - #[cfg(not(esp_idf_version = "4.3"))] - #[must_use] - pub fn lowest_frequency(mut self, lowest_frequency: impl Into) -> Self { - self.lowest_frequency = lowest_frequency.into(); - self - } - - /// Specify what duty mode to use for the operator. - #[must_use] - pub fn duty_mode(mut self, duty_mode: DutyMode) -> Self { - self.duty_mode = duty_mode; - self - } - - #[must_use] - pub fn counter_mode(mut self, counter_mode: CounterMode) -> Self { - self.counter_mode = counter_mode; - self - } - - #[must_use] - pub fn deadtime(mut self, deadtime: impl Into>) -> Self { - self.deadtime = deadtime.into(); - self - } -} - -impl Default for OperatorConfig { - fn default() -> Self { - Self { - frequency: 1000.Hz(), - duty_a: 50.0, - duty_b: 50.0, - - #[cfg(not(esp_idf_version = "4.3"))] - lowest_frequency: 16.Hz(), - - duty_mode: DutyMode::ActiveHigh, - counter_mode: CounterMode::Up, - - deadtime: None, - } - } -} - -#[derive(Default)] -pub struct UnitZero; - -#[derive(Default)] -pub struct UnitOne; - -pub type Duty = f32; - -pub struct MCPWM { - _unit: U, -} - -impl MCPWM { - /// # Safety - /// - /// It is safe to instantiate this exactly one time per `Unit`. - unsafe fn new() -> Self { - Self { - _unit: U::default(), - } - } -} - -pub trait Unit: Default { - fn unit() -> mcpwm_unit_t; -} - -impl Unit for UnitZero { - fn unit() -> mcpwm_unit_t { - mcpwm_unit_t_MCPWM_UNIT_0 - } -} - -impl Unit for UnitOne { - fn unit() -> mcpwm_unit_t { - mcpwm_unit_t_MCPWM_UNIT_1 - } -} - -struct Timer { - -} - -impl Timer { - /// Set PWM frequency - pub fn set_frequency(&mut self, frequency: Hertz) -> Result<(), EspError> { - todo!() - } - - /// Get PWM frequency - pub fn get_frequency(&self) -> Hertz { - todo!() - } - - pub fn timer(&self) -> mcpwm_timer_t { - T::timer() - } -} - -trait OptionalOperator> { - -} -struct NoOperator; -impl OptionalOperator for NoOperator {} - -// TODO: How do we want fault module to fit into this? -/// Motor Control PWM module abstraction -pub struct TimerConnection, O0, O1, O2> - where - O0: OptionalOperator, - O1: OptionalOperator, - O2: OptionalOperator, -{ - timer: Timer, - operator0: O0, - operator1: O1, - operator2: O2 -} - -// Since there can only ever by one instance of every operator type (except NoOperator) -// we know that there can be no mem::swap or similar to cause any problems. -// -// Thus we know that after split is called nothing can be added/removed while still having access to -// the individual objects. We also garantuee that the operators wont live longer than the timer -impl, O1: OptionalOperator, O2: OptionalOperator> TimerConnection { - fn split(&mut self) -> (&mut timer, &mut O0, &mut O1, &mut O2) { - ( - &mut self.timer, - &mut self.operator0, - &mut self.operator1, - &mut self.operator2, - ) - } -} - -impl TimerConnection { - fn new(timer: T) -> Self { - Self { - timer, - operator0: NoOperator, - operator1: NoOperator, - operator2: NoOperator - } - } -} -impl TimerConnection { - fn attatch_operator0>(mut self, operator_cfg: O) -> TimerConnection { - let operator = self.init_and_attach_operator(operator_cfg); - TimerConnection { - timer: self.timer, - operator0: operator, - operator1: self.operator1, - operator2: self.operator2 - } - } -} - -impl TimerConnection { - fn attatch_operator1>(mut self, operator: O) -> TimerConnection { - let operator = self.init_and_attach_operator(operator_cfg); - TimerConnection { - timer: self.timer, - operator0: self.operator0, - operator1: operator, - operator2: self.operator2 - } - } -} - -impl TimerConnection { - fn attatch_operator2>(mut self, operator: O) -> TimerConnection { - let operator = self.init_and_attach_operator(operator_cfg); - TimerConnection { - timer: self.timer, - operator0: self.operator0, - operator1: self.operator1, - operator2: operator - } - } -} - -// The hardware for ESP32 and ESP32-S3 can associate any operator(within the mcpwm module) with any -// timer(within the mcpwm module) for example allowing using the same timer for all three operators. -// However at least as of IDF v4.4 timer0 is hardcoded to operator0 and timer1 to operator1 and so on... -pub trait HwOperator: Into> { - fn signal_a() -> mcpwm_io_signals_t; - fn signal_b() -> mcpwm_io_signals_t; - fn unit() -> mcpwm_unit_t { - U::unit() - } -} - -macro_rules! impl_operator_helper { - ($instance:ident: $timer:expr, $signal_a:expr, $signal_b:expr, $unit:ty) => { - impl HwOperator<$unit> for $instance<$unit> { - fn signal_a() -> mcpwm_io_signals_t { - $signal_a - } - - fn signal_b() -> mcpwm_io_signals_t { - $signal_b - } - } - }; -} - -macro_rules! impl_operator { - ($instance:ident: $timer:expr, $signal_a:expr, $signal_b:expr) => { - pub struct $instance { - _unit: U, - } - - impl $instance { - /// # Safety - /// - /// It is safe to instantiate this operator exactly one time per Unit. - pub unsafe fn new() -> Self { - $instance { - _unit: U::default(), - } - } - } - - impl Into> for $instance { - fn into(self) -> Operator { - Operator { - _instance: self, - pin_a: NoPin, - pin_b: NoPin, - } - } - } - - impl_operator_helper!($instance: $timer, $signal_a, $signal_b, UnitZero); - impl_operator_helper!($instance: $timer, $signal_a, $signal_b, UnitOne); - }; -} - -impl_operator!( - OPERATOR0: mcpwm_timer_t_MCPWM_TIMER_0, - mcpwm_io_signals_t_MCPWM0A, - mcpwm_io_signals_t_MCPWM0B -); -impl_operator!( - OPERATOR1: mcpwm_timer_t_MCPWM_TIMER_1, - mcpwm_io_signals_t_MCPWM1A, - mcpwm_io_signals_t_MCPWM1B -); -impl_operator!( - OPERATOR2: mcpwm_timer_t_MCPWM_TIMER_2, - mcpwm_io_signals_t_MCPWM2A, - mcpwm_io_signals_t_MCPWM2B -); - -// TODO: How do we want syncing to fit in to this? -// TODO: How do we want carrier to fit into this? -// TODO: How do we want capture to fit into this? - -/// Motor Control operator abstraction -/// -/// Every Motor Control module has three operators. Every operator can generate two output signals called A and B. -/// A and B share the same timer and thus frequency and phase but can have induvidual duty set. -pub struct Operator, M: Borrow>, PA: OptionalPin, PB: OptionalPin, D> { - handle: mcpwm_operator_t, - _instance: O, - - _pin_a: PA, - _pin_b: PB, - - deadtime: D -} - -impl Operator -where - U: Unit, - O: HwOperator, - M: Borrow>, - PA: OutputPin, - PB: OptionalOutputPin, -{ - /// Get duty as percentage between 0.0 and 100.0 for output A - pub fn get_duty_a(&self) -> Duty { - todo!() - } - - /// Set duty as percentage between 0.0 and 100.0 for output A - pub fn set_duty_a(&mut self, duty: Duty) -> Result<(), EspError> { - todo!() - } -} - -impl Operator -where - U: Unit, - O: HwOperator, - M: Borrow>, - PA: OptionalOutputPin, - PB: OutputPin, -{ - /// Get duty as percentage between 0.0 and 100.0 for output B - pub fn get_duty_b(&self) -> Duty { - todo!() - } - - /// Set duty as percentage between 0.0 and 100.0 for output B - pub fn set_duty_b(&mut self, duty: Duty) -> Result<(), EspError> { - todo!() - } -} diff --git a/src/mcpwm/mod.rs b/src/mcpwm/mod.rs new file mode 100644 index 00000000000..9a6682c2160 --- /dev/null +++ b/src/mcpwm/mod.rs @@ -0,0 +1,138 @@ +//! Motor Control Pulse Width Modulator peripheral +//! +//! Interface to the [Motor Control Pulse Width Modulator peripheral (MCPWM) +//! peripheral](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/mcpwm.html) +//! +//! ``` +//! --------------- --------------- +//! | MCPWM Unit 0 | | MCPWM Unit 1 | +//! | ------------ | | ------------ | +//! | | | | +//! | OPERATOR 0 |--> A | OPERATOR 0 |--> A +//! | |--> B | |--> B +//! | | | | +//! | OPERATOR 1 |--> A | OPERATOR 1 |--> A +//! | |--> B | |--> B +//! | | | | +//! | OPERATOR 2 |--> A | OPERATOR 2 |--> A +//! | |--> B | |--> B +//! --------------- --------------- +//! ``` +//! +//! # Example +//! +//! Create a pair of PWM signals on pin 4 and 5. The duty on pin 4 will ramp from 0% to 100% +//! while pin 5 will ramp from 100% down to 0%. +//! ``` +//! let peripherals = Peripherals::take().unwrap(); +//! let timer_config = TimerConfig::default().frequency(25.kHz()); +//! let operator_config = OperatorConfig::default(); +//! let timer = Mcpwm::new(peripherals.mcpwm0.timer, timer_config)?; +//! +//! let timer = timer.into_connection() +//! .attatch_operator0( +//! peripherals.mcpwm0.operator0, +//! operator_config, +//! peripherals.pins.gpio4, +//! peripherals.pins.gpio5, +//! )?; +//! +//! let (timer, operator, _, _) = timer.split(); +//! +//! println!("Starting duty-cycle loop"); +//! +//! for &duty in [0.0, 20.0, 40.0, 60.0, 80.0, 100.0].iter() { +//! println!("Duty {}%", duty); +//! operator.set_duty_a(duty)?; +//! operator.set_duty_b(100.0 - duty)?; +//! FreeRtos.delay_ms(2000)?; +//! } +//! ``` +//! +//! See the `examples/` folder of this repository for more. + +mod timer; +mod operator; +mod timer_connection; + +use core::borrow::Borrow; + +use crate::gpio::OutputPin; +use crate::units::{FromValueType, Hertz}; +use esp_idf_sys::*; + +// MCPWM clock source frequency for ESP32 and ESP32-s3 +const MCPWM_CLOCK_SOURCE_FREQUENCY: u32 = 160_000_000; + +// Max PWM timer prescaler +const MAX_PWM_TIMER_PRESCALE: u32 = 0x1_00; + +// Max PWM timer period +const MAX_PWM_TIMER_PERIOD: u32 = 0x1_00_00; + +/// The Motor Control Pulse Width Modulator peripheral +pub struct Peripheral { + pub mcpwm: MCPWM, + pub operator0: OPERATOR0, + pub operator1: OPERATOR1, + pub operator2: OPERATOR2, +} + +impl Peripheral { + /// # Safety + /// + /// It is safe to instantiate this exactly one time per `Unit`. + pub unsafe fn new() -> Self { + Self { + mcpwm: MCPWM::new(), + operator0: OPERATOR0::new(), + operator1: OPERATOR1::new(), + operator2: OPERATOR2::new(), + } + } +} + +#[derive(Default)] +pub struct UnitZero; + +#[derive(Default)] +pub struct UnitOne; + +pub type Duty = u16; + +pub struct MCPWM { + _unit: U, +} + +impl MCPWM { + /// # Safety + /// + /// It is safe to instantiate this exactly one time per `Unit`. + unsafe fn new() -> Self { + Self { + _unit: U::default(), + } + } +} + +pub trait Unit: Default { + fn unit() -> mcpwm_unit_t; +} + +impl Unit for UnitZero { + fn unit() -> mcpwm_unit_t { + mcpwm_unit_t_MCPWM_UNIT_0 + } +} + +impl Unit for UnitOne { + fn unit() -> mcpwm_unit_t { + mcpwm_unit_t_MCPWM_UNIT_1 + } +} + +trait OptionalOperator> {} + +struct NoOperator; +impl OptionalOperator for NoOperator {} + diff --git a/src/mcpwm/operator.rs b/src/mcpwm/operator.rs new file mode 100644 index 00000000000..aafaac9536e --- /dev/null +++ b/src/mcpwm/operator.rs @@ -0,0 +1,248 @@ +// The hardware for ESP32 and ESP32-S3 can associate any operator(within the mcpwm module) with any +// timer(within the mcpwm module) for example allowing using the same timer for all three operators. +// However at least as of IDF v4.4 timer0 is hardcoded to operator0 and timer1 to operator1 and so on... +pub trait HwOperator: Into> { + fn signal_a() -> mcpwm_io_signals_t; + fn signal_b() -> mcpwm_io_signals_t; + fn unit() -> mcpwm_unit_t { + U::unit() + } +} + +macro_rules! impl_operator_helper { + ($instance:ident: $timer:expr, $signal_a:expr, $signal_b:expr, $unit:ty) => { + impl HwOperator<$unit> for $instance<$unit> { + fn signal_a() -> mcpwm_io_signals_t { + $signal_a + } + + fn signal_b() -> mcpwm_io_signals_t { + $signal_b + } + } + }; +} + +macro_rules! impl_operator { + ($instance:ident: $timer:expr, $signal_a:expr, $signal_b:expr) => { + pub struct $instance { + _unit: U, + } + + impl $instance { + /// # Safety + /// + /// It is safe to instantiate this operator exactly one time per Unit. + pub unsafe fn new() -> Self { + $instance { + _unit: U::default(), + } + } + } + + impl Into> for $instance { + fn into(self) -> Operator { + Operator { + _instance: self, + pin_a: NoPin, + pin_b: NoPin, + } + } + } + + impl_operator_helper!($instance: $timer, $signal_a, $signal_b, UnitZero); + impl_operator_helper!($instance: $timer, $signal_a, $signal_b, UnitOne); + }; +} + +impl_operator!( + OPERATOR0: mcpwm_timer_t_MCPWM_TIMER_0, + mcpwm_io_signals_t_MCPWM0A, + mcpwm_io_signals_t_MCPWM0B +); +impl_operator!( + OPERATOR1: mcpwm_timer_t_MCPWM_TIMER_1, + mcpwm_io_signals_t_MCPWM1A, + mcpwm_io_signals_t_MCPWM1B +); +impl_operator!( + OPERATOR2: mcpwm_timer_t_MCPWM_TIMER_2, + mcpwm_io_signals_t_MCPWM2A, + mcpwm_io_signals_t_MCPWM2B +); + +// TODO: How do we want syncing to fit in to this? +// TODO: How do we want carrier to fit into this? +// TODO: How do we want capture to fit into this? + +/// Motor Control operator abstraction +/// +/// Every Motor Control module has three operators. Every operator can generate two output signals called A and B. +/// A and B share the same timer and thus frequency and phase but can have induvidual duty set. +pub struct Operator, M: Borrow>, PA: OptionalPin, PB: OptionalPin, D> { + handle: mcpwm_operator_t, + _instance: O, + + _pin_a: PA, + _pin_b: PB, + + deadtime: D +} + +impl Operator +where + U: Unit, + O: HwOperator, + M: Borrow>, + PA: OutputPin, + PB: OptionalOutputPin, +{ + /// Get duty as percentage between 0.0 and 100.0 for output A + pub fn get_duty_a(&self) -> Duty { + todo!() + } + + /// Set duty as percentage between 0.0 and 100.0 for output A + pub fn set_duty_a(&mut self, duty: Duty) -> Result<(), EspError> { + todo!() + } +} + +impl Operator +where + U: Unit, + O: HwOperator, + M: Borrow>, + PA: OptionalOutputPin, + PB: OutputPin, +{ + /// Get duty as percentage between 0.0 and 100.0 for output B + pub fn get_duty_b(&self) -> Duty { + todo!() + } + + /// Set duty as percentage between 0.0 and 100.0 for output B + pub fn set_duty_b(&mut self, duty: Duty) -> Result<(), EspError> { + todo!() + } +} + +/// Operator configuration +pub struct OperatorConfig { + duty_a: Duty, + duty_b: Duty, + + duty_mode: DutyMode, + + deadtime: Option, +} + +impl OperatorConfig { + /// Set initual duty cycle for output A as percentage + #[must_use] + pub fn duty_a(mut self, duty: Duty) -> Self { + self.duty_a = duty; + self + } + + /// Set initual duty cycle for output B as percentage + #[must_use] + pub fn duty_b(mut self, duty: Duty) -> Self { + self.duty_b = duty; + self + } + + /// Specify what duty mode to use for the operator. + #[must_use] + pub fn duty_mode(mut self, duty_mode: DutyMode) -> Self { + self.duty_mode = duty_mode; + self + } + + #[must_use] + pub fn deadtime(mut self, deadtime: impl Into>) -> Self { + self.deadtime = deadtime.into(); + self + } +} + +impl Default for OperatorConfig { + fn default() -> Self { + Self { + frequency: 1000.Hz(), + duty_a: 50.0, + duty_b: 50.0, + + #[cfg(not(esp_idf_version = "4.3"))] + lowest_frequency: 16.Hz(), + + duty_mode: DutyMode::ActiveHigh, + + deadtime: None, + } + } +} + +/// Duty modes for operator +#[derive(Clone, Copy, Debug)] +pub enum DutyMode { + /// Active high + /// + /// Setting duty = 100% will result in a constant high output + /// Setting duty = 0% will result in a constant low output + ActiveHigh, + + /// Active low + /// + /// Setting duty = 100% will result in a constant low output + /// Setting duty = 0% will result in a constant high output + ActiveLow, +} + +#[derive(Default, Clone)] +struct DutyConfig { + on_matches_cmp_a: CountingDirection, + on_matches_cmp_b: CountingDirection, + on_is_empty: CountingDirection, + on_is_full: CountingDirection +} + +#[derive(Default, Clone, Copy)] +struct CountingDirection { + counting_up: GeneratorAction, + counting_down: GeneratorAction +} + +enum GeneratorAction { + Nothing, + SetLow, + SetHigh, + Toggle +} + +impl Default for GeneratorAction { + fn default() -> Self { + GeneratorAction::Nothing + } +} + +impl From for DutyConfig { + fn from(val: DutyMode) -> Self { + match val { + DutyMode::ActiveHigh => { + let mut duty_config: DutyConfig = Default::default(); + duty_config.on_is_empty.counting_up = GeneratorAction::SetHigh; + duty_config.on_matches_cmp_a.counting_up = GeneratorAction::SetLow; + + duty_config + }, + DutyMode::ActiveLow => { + let mut duty_config: DutyConfig = Default::default(); + duty_config.on_is_empty.counting_up = GeneratorAction::SetLow; + duty_config.on_matches_cmp_a.counting_up = GeneratorAction::SetHigh; + + duty_config + }, + } + } +} \ No newline at end of file diff --git a/src/mcpwm/timer.rs b/src/mcpwm/timer.rs new file mode 100644 index 00000000000..1baf98cfaa1 --- /dev/null +++ b/src/mcpwm/timer.rs @@ -0,0 +1,168 @@ +#[derive(Clone, Copy, Debug)] +struct TimerConfig { + frequency: Hertz, + resolution: Hertz, + counter_mode: CounterMode, + + // TODO + // on_full, + // on_empty, + // on_stop, +} + +impl TimerConfig { + #[must_use] + pub fn resolution(mut self, resolution: impl Into) -> Self { + self.resolution = resolution.into(); + self + } + + /// Frequency which the operator will run at, can also be changed live later + #[must_use] + pub fn frequency(mut self, frequency: impl Into) -> Self { + self.frequency = frequency.into(); + self + } + + #[must_use] + pub fn counter_mode(mut self, counter_mode: CounterMode) -> Self { + self.counter_mode = counter_mode; + self + } +} + +//TODO +//impl TimerConfig { + //#[must_use] + //pub fn on_full(mut self, on_full: CB) -> Self { + // self.on_full = on_full; + // self + //} +//} + +struct Timer> { + handle: mcpwm_timer_handle_t, + _timer: T, +} + +impl Timer { + pub fn new(timer: T, config: TimerConfig) -> Self { + let config = mcpwm_timer_config_t { + resolution + }; + let mut handle = ptr::null(); + mcpwm_new_timer(config, &mut handle); + + // TODO: note that this has to be called before mcpwm_timer_enable + // mcpwm_timer_register_event_callbacks() + + mcpwm_timer_enable(); + + Self { handle, _timer: timer } + } + /// Set PWM frequency + pub fn set_frequency(&mut self, frequency: Hertz) -> Result<(), EspError> { + todo!() + } + + /// Get PWM frequency + pub fn get_frequency(&self) -> Hertz { + todo!() + } + + pub fn timer(&self) -> mcpwm_timer_t { + T::timer() + } + + pub fn release(self) -> T { + let Self { + _timer, + handle + } = self; + mcpwm_del_timer(handle); + _timer + } + + fn into_connection(timer: T) -> TimerConnection { + TimerConnection::new(timer) + } +} + +impl Drop for Timer { + fn drop(self) { + mcpwm_del_timer(self.handle) + } +} + +/// Counter mode for operator's timer for generating PWM signal +// TODO: For UpDown, frequency is half of MCPWM frequency set +#[derive(Clone, Copy, Debug)] +pub enum CounterMode { + /// Timer is frozen or paused + #[cfg(not(esp_idf_version = "4.3"))] + Frozen, + /// Edge aligned. The counter will start from its lowest value and increment every clock cycle until the period is reached. + /// + /// The wave form will end up looking something like the following: + /// ``` + /// start, counter = 0 reset, counter = period + /// | | + /// | |*--- start, counter = 0 + /// v <---- duty ----> . v| + /// . . .v + /// .--------------------. ..---- + /// | Active | .| + /// | | .| + /// | | Not active .| + /// - --------------------- + /// ``` + Up, + + /// Edge aligned. The counter will start from its highest value, period and decrement every clock cycle until the zero is reached + /// + /// The wave form will end up looking something like the following: + /// ``` + /// start, counter = period reset, counter = 0 + /// | | + /// | |*--- start, counter = period + /// v . v| + /// . . <---- duty ----> .v + /// . .--------------------.. + /// . Active | |. + /// . | |. + /// . Not active | Active |. + /// ---------------------- ---- + /// ``` + Down, + + /// Symmetric mode. The counter will start from its lowest value and increment every clock cycle until the period is reached + /// + /// The wave form will end up looking something like the following: + /// ``` + /// change count dir to decrement, counter = period + /// start, counter = 0, incrementing | change count dir to increment, counter = 0 + /// | | | + /// | |*--- counter = period |*----- start, counter = 0, incrementing + /// v <---- duty ----> . v| . <---- duty ----> || + /// . . .v . vv + /// ---------------------. .. .-------------------------------------------. .. .-- + /// Active | .. | Active Active | .. | + /// | .. | | .. | + /// | Not active .. Not active | | Not active .. Not active | + /// ---------------------------------------- ---------------------------------------- + /// ``` + /// NOTE: That in this mode, the frequency will be half of that specified + UpDown, +} + +impl From for mcpwm_counter_type_t { + fn from(val: CounterMode) -> Self { + match val { + #[cfg(not(esp_idf_version = "4.3"))] + CounterMode::Frozen => mcpwm_counter_type_t_MCPWM_FREEZE_COUNTER, + CounterMode::Up => mcpwm_counter_type_t_MCPWM_UP_COUNTER, + CounterMode::Down => mcpwm_counter_type_t_MCPWM_DOWN_COUNTER, + CounterMode::UpDown => mcpwm_counter_type_t_MCPWM_UP_DOWN_COUNTER, + } + } +} \ No newline at end of file diff --git a/src/mcpwm/timer_connection.rs b/src/mcpwm/timer_connection.rs new file mode 100644 index 00000000000..ea9a25b5678 --- /dev/null +++ b/src/mcpwm/timer_connection.rs @@ -0,0 +1,76 @@ +// TODO: How do we want fault module to fit into this? +/// Created by `Timer::into_connection()` +pub struct TimerConnection, O0, O1, O2> + where + O0: OptionalOperator, + O1: OptionalOperator, + O2: OptionalOperator, +{ + timer: Timer, + operator0: O0, + operator1: O1, + operator2: O2 +} + +impl TimerConnection { + pub(crate) fn new(timer: T) -> Self { + Self { + timer, + operator0: NoOperator, + operator1: NoOperator, + operator2: NoOperator + } + } +} + +// Since there can only ever by one instance of every operator type (except NoOperator) +// we know that there can be no mem::swap or similar to cause any problems. +// +// Thus we know that after split is called nothing can be added/removed while still having access to +// the individual objects. We also garantuee that the operators wont live longer than the timer +impl, O1: OptionalOperator, O2: OptionalOperator> TimerConnection { + fn split(&mut self) -> (&mut timer, &mut O0, &mut O1, &mut O2) { + ( + &mut self.timer, + &mut self.operator0, + &mut self.operator1, + &mut self.operator2, + ) + } +} + +impl TimerConnection { + fn attatch_operator0>(mut self, operator_cfg: O) -> TimerConnection { + let operator = self.init_and_attach_operator(operator_cfg, pin_a, pin_b); + TimerConnection { + timer: self.timer, + operator0: operator, + operator1: self.operator1, + operator2: self.operator2 + } + } +} + +impl TimerConnection { + fn attatch_operator1>(mut self, operator: O) -> TimerConnection { + let operator = self.init_and_attach_operator(operator_cfg, pin_a, pin_b); + TimerConnection { + timer: self.timer, + operator0: self.operator0, + operator1: operator, + operator2: self.operator2 + } + } +} + +impl TimerConnection { + fn attatch_operator2>(mut self, operator: O) -> TimerConnection { + let operator = self.init_and_attach_operator(operator_cfg, pin_a, pin_b); + TimerConnection { + timer: self.timer, + operator0: self.operator0, + operator1: self.operator1, + operator2: operator + } + } +} \ No newline at end of file From 5acf5fcb10770e0c608d91552439b2fa49f33aab Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Sun, 2 Oct 2022 11:40:44 +0200 Subject: [PATCH 26/62] A bit less far from compiling... --- src/mcpwm/mod.rs | 19 ++----- src/mcpwm/operator.rs | 94 ++++++++++++++++++----------------- src/mcpwm/timer.rs | 51 ++++++++++++++----- src/mcpwm/timer_connection.rs | 30 +++++++---- 4 files changed, 111 insertions(+), 83 deletions(-) diff --git a/src/mcpwm/mod.rs b/src/mcpwm/mod.rs index 9a6682c2160..949445866ce 100644 --- a/src/mcpwm/mod.rs +++ b/src/mcpwm/mod.rs @@ -59,6 +59,7 @@ use core::borrow::Borrow; use crate::gpio::OutputPin; use crate::units::{FromValueType, Hertz}; +use crate::mcpwm::operator::{HwOperator, OPERATOR0, OPERATOR1, OPERATOR2}; use esp_idf_sys::*; // MCPWM clock source frequency for ESP32 and ESP32-s3 @@ -116,23 +117,13 @@ impl MCPWM { } pub trait Unit: Default { - fn unit() -> mcpwm_unit_t; + const ID: mcpwm_unit_t; } impl Unit for UnitZero { - fn unit() -> mcpwm_unit_t { - mcpwm_unit_t_MCPWM_UNIT_0 - } + const ID: mcpwm_unit_t = mcpwm_unit_t_MCPWM_UNIT_0; } impl Unit for UnitOne { - fn unit() -> mcpwm_unit_t { - mcpwm_unit_t_MCPWM_UNIT_1 - } -} - -trait OptionalOperator> {} - -struct NoOperator; -impl OptionalOperator for NoOperator {} - + const ID: mcpwm_unit_t = mcpwm_unit_t_MCPWM_UNIT_1; +} \ No newline at end of file diff --git a/src/mcpwm/operator.rs b/src/mcpwm/operator.rs index aafaac9536e..dfaab85935b 100644 --- a/src/mcpwm/operator.rs +++ b/src/mcpwm/operator.rs @@ -1,24 +1,28 @@ +use std::borrow::Borrow; + +use esp_idf_sys::{mcpwm_io_signals_t, mcpwm_unit_t, mcpwm_operator_t, + mcpwm_io_signals_t_MCPWM0A, mcpwm_io_signals_t_MCPWM0B, + mcpwm_io_signals_t_MCPWM1A, mcpwm_io_signals_t_MCPWM1B, + mcpwm_io_signals_t_MCPWM2A, mcpwm_io_signals_t_MCPWM2B, EspError +}; + +use crate::{mcpwm::{Unit, UnitZero, UnitOne}, gpio::OutputPin}; + +use super::{Duty, timer_connection::OptionalOutputPin}; + // The hardware for ESP32 and ESP32-S3 can associate any operator(within the mcpwm module) with any // timer(within the mcpwm module) for example allowing using the same timer for all three operators. -// However at least as of IDF v4.4 timer0 is hardcoded to operator0 and timer1 to operator1 and so on... -pub trait HwOperator: Into> { - fn signal_a() -> mcpwm_io_signals_t; - fn signal_b() -> mcpwm_io_signals_t; - fn unit() -> mcpwm_unit_t { - U::unit() - } +pub trait HwOperator { + const SIGNAL_A: mcpwm_io_signals_t; + const SIGNAL_B: mcpwm_io_signals_t; + const UNIT_ID: mcpwm_unit_t = U::ID; } macro_rules! impl_operator_helper { ($instance:ident: $timer:expr, $signal_a:expr, $signal_b:expr, $unit:ty) => { impl HwOperator<$unit> for $instance<$unit> { - fn signal_a() -> mcpwm_io_signals_t { - $signal_a - } - - fn signal_b() -> mcpwm_io_signals_t { - $signal_b - } + const SIGNAL_A: mcpwm_io_signals_t = $signal_a; + const SIGNAL_B: mcpwm_io_signals_t = $signal_b; } }; } @@ -40,16 +44,6 @@ macro_rules! impl_operator { } } - impl Into> for $instance { - fn into(self) -> Operator { - Operator { - _instance: self, - pin_a: NoPin, - pin_b: NoPin, - } - } - } - impl_operator_helper!($instance: $timer, $signal_a, $signal_b, UnitZero); impl_operator_helper!($instance: $timer, $signal_a, $signal_b, UnitOne); }; @@ -79,21 +73,20 @@ impl_operator!( /// /// Every Motor Control module has three operators. Every operator can generate two output signals called A and B. /// A and B share the same timer and thus frequency and phase but can have induvidual duty set. -pub struct Operator, M: Borrow>, PA: OptionalPin, PB: OptionalPin, D> { +pub struct Operator, PA: OptionalOutputPin, PB: OptionalOutputPin> { handle: mcpwm_operator_t, _instance: O, _pin_a: PA, _pin_b: PB, - deadtime: D + //deadtime: D } -impl Operator +impl Operator where U: Unit, O: HwOperator, - M: Borrow>, PA: OutputPin, PB: OptionalOutputPin, { @@ -108,11 +101,10 @@ where } } -impl Operator +impl Operator where U: Unit, O: HwOperator, - M: Borrow>, PA: OptionalOutputPin, PB: OutputPin, { @@ -134,7 +126,7 @@ pub struct OperatorConfig { duty_mode: DutyMode, - deadtime: Option, + //deadtime: Option, } impl OperatorConfig { @@ -159,26 +151,22 @@ impl OperatorConfig { self } - #[must_use] + /*#[must_use] pub fn deadtime(mut self, deadtime: impl Into>) -> Self { self.deadtime = deadtime.into(); self - } + }*/ } impl Default for OperatorConfig { fn default() -> Self { Self { - frequency: 1000.Hz(), - duty_a: 50.0, - duty_b: 50.0, - - #[cfg(not(esp_idf_version = "4.3"))] - lowest_frequency: 16.Hz(), + duty_a: 0.0, + duty_b: 0.0, duty_mode: DutyMode::ActiveHigh, - deadtime: None, + //deadtime: None, } } } @@ -199,6 +187,13 @@ pub enum DutyMode { ActiveLow, } +pub trait OptionalOperator> {} + +pub struct NoOperator; +impl> OptionalOperator for NoOperator {} + +/* + #[derive(Default, Clone)] struct DutyConfig { on_matches_cmp_a: CountingDirection, @@ -226,23 +221,30 @@ impl Default for GeneratorAction { } } -impl From for DutyConfig { - fn from(val: DutyMode) -> Self { +impl DutyMode { + fn into_duty_cfg(self) -> DutyConfig { match val { DutyMode::ActiveHigh => { let mut duty_config: DutyConfig = Default::default(); duty_config.on_is_empty.counting_up = GeneratorAction::SetHigh; - duty_config.on_matches_cmp_a.counting_up = GeneratorAction::SetLow; - + if G::IS_A { + duty_config.on_matches_cmp_a.counting_up = GeneratorAction::SetLow; + } else { + duty_config.on_matches_cmp_b.counting_up = GeneratorAction::SetLow; + } duty_config }, DutyMode::ActiveLow => { let mut duty_config: DutyConfig = Default::default(); duty_config.on_is_empty.counting_up = GeneratorAction::SetLow; - duty_config.on_matches_cmp_a.counting_up = GeneratorAction::SetHigh; + if G::IS_A { + duty_config.on_matches_cmp_a.counting_up = GeneratorAction::SetHigh; + } else { + duty_config.on_matches_cmp_b.counting_up = GeneratorAction::SetHigh; + } duty_config }, } } -} \ No newline at end of file +} */ \ No newline at end of file diff --git a/src/mcpwm/timer.rs b/src/mcpwm/timer.rs index 1baf98cfaa1..abed247e175 100644 --- a/src/mcpwm/timer.rs +++ b/src/mcpwm/timer.rs @@ -1,3 +1,14 @@ +use std::ptr; + +use esp_idf_sys::{mcpwm_counter_type_t_MCPWM_UP_DOWN_COUNTER, mcpwm_counter_type_t_MCPWM_DOWN_COUNTER, mcpwm_counter_type_t_MCPWM_UP_COUNTER, mcpwm_timer_t, mcpwm_timer_handle_t, mcpwm_timer_enable, mcpwm_timer_t_MCPWM_TIMER_0, mcpwm_timer_t_MCPWM_TIMER_1, mcpwm_timer_t_MCPWM_TIMER_2, mcpwm_counter_type_t}; + +use crate::units::Hertz; +use crate::mcpwm::Unit; + +use super::operator::NoOperator; +use super::timer_connection::TimerConnection; + + #[derive(Clone, Copy, Debug)] struct TimerConfig { frequency: Hertz, @@ -5,9 +16,9 @@ struct TimerConfig { counter_mode: CounterMode, // TODO - // on_full, - // on_empty, - // on_stop, + // on_full: FF, + // on_empty: FE, + // on_stop: FS, } impl TimerConfig { @@ -40,12 +51,12 @@ impl TimerConfig { //} //} -struct Timer> { +pub struct Timer> { handle: mcpwm_timer_handle_t, _timer: T, } -impl Timer { +impl> Timer { pub fn new(timer: T, config: TimerConfig) -> Self { let config = mcpwm_timer_config_t { resolution @@ -71,7 +82,7 @@ impl Timer { } pub fn timer(&self) -> mcpwm_timer_t { - T::timer() + T::ID } pub fn release(self) -> T { @@ -88,7 +99,7 @@ impl Timer { } } -impl Drop for Timer { +impl> Drop for Timer { fn drop(self) { mcpwm_del_timer(self.handle) } @@ -99,8 +110,8 @@ impl Drop for Timer { #[derive(Clone, Copy, Debug)] pub enum CounterMode { /// Timer is frozen or paused - #[cfg(not(esp_idf_version = "4.3"))] - Frozen, + //#[cfg(not(esp_idf_version = "4.3"))] + //Frozen, /// Edge aligned. The counter will start from its lowest value and increment every clock cycle until the period is reached. /// /// The wave form will end up looking something like the following: @@ -158,11 +169,27 @@ pub enum CounterMode { impl From for mcpwm_counter_type_t { fn from(val: CounterMode) -> Self { match val { - #[cfg(not(esp_idf_version = "4.3"))] - CounterMode::Frozen => mcpwm_counter_type_t_MCPWM_FREEZE_COUNTER, + //CounterMode::Frozen => mcpwm_counter_type_t_MCPWM_FREEZE_COUNTER, CounterMode::Up => mcpwm_counter_type_t_MCPWM_UP_COUNTER, CounterMode::Down => mcpwm_counter_type_t_MCPWM_DOWN_COUNTER, CounterMode::UpDown => mcpwm_counter_type_t_MCPWM_UP_DOWN_COUNTER, } } -} \ No newline at end of file +} + +unsafe trait HwTimer { + const ID: mcpwm_timer_t; +} + +macro_rules! impl_timer { + ($t:ident, $id:expr) => { + struct $t; + impl HwTimer for $t { + const ID: mcpwm_timer_t = $id; + } + }; +} + +impl_timer!(TIMER0, mcpwm_timer_t_MCPWM_TIMER_0); +impl_timer!(TIMER1, mcpwm_timer_t_MCPWM_TIMER_1); +impl_timer!(TIMER2, mcpwm_timer_t_MCPWM_TIMER_2); \ No newline at end of file diff --git a/src/mcpwm/timer_connection.rs b/src/mcpwm/timer_connection.rs index ea9a25b5678..02ee3d8daa6 100644 --- a/src/mcpwm/timer_connection.rs +++ b/src/mcpwm/timer_connection.rs @@ -1,15 +1,19 @@ +use crate::{mcpwm::Unit, gpio::OutputPin}; + +use super::operator::{OptionalOperator, OPERATOR0, OperatorConfig, NoOperator, OPERATOR2, OPERATOR1}; + // TODO: How do we want fault module to fit into this? /// Created by `Timer::into_connection()` pub struct TimerConnection, O0, O1, O2> where - O0: OptionalOperator, - O1: OptionalOperator, - O2: OptionalOperator, + O0: OptionalOperator>, + O1: OptionalOperator>, + O2: OptionalOperator>, { timer: Timer, - operator0: O0, - operator1: O1, - operator2: O2 + operator0: O0, + operator1: O1, + operator2: O2 } impl TimerConnection { @@ -38,9 +42,9 @@ impl, O1: OptionalOperator TimerConnection { - fn attatch_operator0>(mut self, operator_cfg: O) -> TimerConnection { + fn attatch_operator0(mut self, operator_handle: OPERATOR0, operator_cfg: OperatorConfig, pin_a: PA, pin_b: PB) -> TimerConnection { let operator = self.init_and_attach_operator(operator_cfg, pin_a, pin_b); TimerConnection { timer: self.timer, @@ -52,7 +56,7 @@ impl TimerConnection { } impl TimerConnection { - fn attatch_operator1>(mut self, operator: O) -> TimerConnection { + fn attatch_operator1(mut self, operator_handle: OPERATOR1, operator_cfg: OperatorConfig, pin_a: PA, pin_b: PB) -> TimerConnection { let operator = self.init_and_attach_operator(operator_cfg, pin_a, pin_b); TimerConnection { timer: self.timer, @@ -64,7 +68,7 @@ impl TimerConnection { } impl TimerConnection { - fn attatch_operator2>(mut self, operator: O) -> TimerConnection { + fn attatch_operator2(mut self, operator_handle: OPERATOR2, operator_cfg: OperatorConfig, pin_a: PA, pin_b: PB) -> TimerConnection { let operator = self.init_and_attach_operator(operator_cfg, pin_a, pin_b); TimerConnection { timer: self.timer, @@ -73,4 +77,8 @@ impl TimerConnection { operator2: operator } } -} \ No newline at end of file +} + +pub struct NoPin; + +pub trait OptionalOutputPin {} \ No newline at end of file From a10f4f3b97f21a4d95f0b71f91999c9bc1bf820e Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Sun, 9 Oct 2022 22:55:20 +0200 Subject: [PATCH 27/62] Almost compiling --- src/mcpwm/mod.rs | 93 +++++++++++++++++++++------------- src/mcpwm/operator.rs | 94 ++++++++++++++--------------------- src/mcpwm/timer.rs | 66 +++++++++++++++--------- src/mcpwm/timer_connection.rs | 68 ++++++++++++++++++------- src/peripherals.rs | 16 ++---- 5 files changed, 189 insertions(+), 148 deletions(-) diff --git a/src/mcpwm/mod.rs b/src/mcpwm/mod.rs index 949445866ce..a3cd07e397a 100644 --- a/src/mcpwm/mod.rs +++ b/src/mcpwm/mod.rs @@ -55,12 +55,18 @@ mod timer; mod operator; mod timer_connection; -use core::borrow::Borrow; +use core::ffi; -use crate::gpio::OutputPin; -use crate::units::{FromValueType, Hertz}; -use crate::mcpwm::operator::{HwOperator, OPERATOR0, OPERATOR1, OPERATOR2}; -use esp_idf_sys::*; +use crate::mcpwm::operator::{ + HwOperator0, HwOperator1, HwOperator2, + OPERATOR00, OPERATOR01, OPERATOR02, + OPERATOR10, OPERATOR11, OPERATOR12 +}; + +use crate::mcpwm::timer::{ + TIMER00, TIMER01, TIMER02, + TIMER10, TIMER11, TIMER12 +}; // MCPWM clock source frequency for ESP32 and ESP32-s3 const MCPWM_CLOCK_SOURCE_FREQUENCY: u32 = 160_000_000; @@ -72,58 +78,75 @@ const MAX_PWM_TIMER_PRESCALE: u32 = 0x1_00; const MAX_PWM_TIMER_PERIOD: u32 = 0x1_00_00; /// The Motor Control Pulse Width Modulator peripheral -pub struct Peripheral { - pub mcpwm: MCPWM, - pub operator0: OPERATOR0, - pub operator1: OPERATOR1, - pub operator2: OPERATOR2, +pub struct MCPWM0 { + timer0: TIMER00, + timer1: TIMER01, + timer2: TIMER02, + + pub operator0: OPERATOR00, + pub operator1: OPERATOR01, + pub operator2: OPERATOR02, } -impl Peripheral { +impl MCPWM0 { /// # Safety /// - /// It is safe to instantiate this exactly one time per `Unit`. + /// It is safe to instantiate this exactly one time per `Group`. pub unsafe fn new() -> Self { Self { - mcpwm: MCPWM::new(), - operator0: OPERATOR0::new(), - operator1: OPERATOR1::new(), - operator2: OPERATOR2::new(), + timer0: TIMER00::new(), + timer1: TIMER01::new(), + timer2: TIMER02::new(), + operator0: OPERATOR00::new(), + operator1: OPERATOR01::new(), + operator2: OPERATOR02::new(), } } } -#[derive(Default)] -pub struct UnitZero; - -#[derive(Default)] -pub struct UnitOne; +pub struct MCPWM1 { + timer0: TIMER10, + timer1: TIMER11, + timer2: TIMER12, -pub type Duty = u16; - -pub struct MCPWM { - _unit: U, + pub operator0: OPERATOR10, + pub operator1: OPERATOR11, + pub operator2: OPERATOR12, } -impl MCPWM { +impl MCPWM1 { /// # Safety /// - /// It is safe to instantiate this exactly one time per `Unit`. - unsafe fn new() -> Self { + /// It is safe to instantiate this exactly one time per `Group`. + pub unsafe fn new() -> Self { Self { - _unit: U::default(), + timer0: TIMER10::new(), + timer1: TIMER11::new(), + timer2: TIMER12::new(), + operator0: OPERATOR10::new(), + operator1: OPERATOR11::new(), + operator2: OPERATOR12::new(), } } } -pub trait Unit: Default { - const ID: mcpwm_unit_t; +#[derive(Default)] +pub struct Group0; + +#[derive(Default)] +pub struct Group1; + +pub type Duty = u16; + +// Note this was called `Unit` in IDF < 5.0 +pub trait Group: Default { + const ID: ffi::c_int; } -impl Unit for UnitZero { - const ID: mcpwm_unit_t = mcpwm_unit_t_MCPWM_UNIT_0; +impl Group for Group0 { + const ID: ffi::c_int = 0; } -impl Unit for UnitOne { - const ID: mcpwm_unit_t = mcpwm_unit_t_MCPWM_UNIT_1; +impl Group for Group1 { + const ID: ffi::c_int = 1; } \ No newline at end of file diff --git a/src/mcpwm/operator.rs b/src/mcpwm/operator.rs index dfaab85935b..95dfd97431d 100644 --- a/src/mcpwm/operator.rs +++ b/src/mcpwm/operator.rs @@ -1,69 +1,47 @@ -use std::borrow::Borrow; +use esp_idf_sys::{EspError, mcpwm_oper_handle_t}; -use esp_idf_sys::{mcpwm_io_signals_t, mcpwm_unit_t, mcpwm_operator_t, - mcpwm_io_signals_t_MCPWM0A, mcpwm_io_signals_t_MCPWM0B, - mcpwm_io_signals_t_MCPWM1A, mcpwm_io_signals_t_MCPWM1B, - mcpwm_io_signals_t_MCPWM2A, mcpwm_io_signals_t_MCPWM2B, EspError -}; - -use crate::{mcpwm::{Unit, UnitZero, UnitOne}, gpio::OutputPin}; +use crate::{mcpwm::{Group, Group0, Group1}, gpio::OutputPin}; use super::{Duty, timer_connection::OptionalOutputPin}; +use core::ffi; + // The hardware for ESP32 and ESP32-S3 can associate any operator(within the mcpwm module) with any // timer(within the mcpwm module) for example allowing using the same timer for all three operators. -pub trait HwOperator { - const SIGNAL_A: mcpwm_io_signals_t; - const SIGNAL_B: mcpwm_io_signals_t; - const UNIT_ID: mcpwm_unit_t = U::ID; +pub trait HwOperator { + const GROUP_ID: ffi::c_int = U::ID; } -macro_rules! impl_operator_helper { - ($instance:ident: $timer:expr, $signal_a:expr, $signal_b:expr, $unit:ty) => { - impl HwOperator<$unit> for $instance<$unit> { - const SIGNAL_A: mcpwm_io_signals_t = $signal_a; - const SIGNAL_B: mcpwm_io_signals_t = $signal_b; - } +macro_rules! impl_operator { + ($t:ident, $g:ty) => { + crate::impl_peripheral!($t); + + impl HwOperator<$g> for $t {} }; } -macro_rules! impl_operator { - ($instance:ident: $timer:expr, $signal_a:expr, $signal_b:expr) => { - pub struct $instance { - _unit: U, - } +pub trait HwOperator0: HwOperator {} +pub trait HwOperator1: HwOperator {} +pub trait HwOperator2: HwOperator {} - impl $instance { - /// # Safety - /// - /// It is safe to instantiate this operator exactly one time per Unit. - pub unsafe fn new() -> Self { - $instance { - _unit: U::default(), - } - } - } +// Group 0 +impl_operator!(OPERATOR00, Group0); +impl_operator!(OPERATOR01, Group0); +impl_operator!(OPERATOR02, Group0); - impl_operator_helper!($instance: $timer, $signal_a, $signal_b, UnitZero); - impl_operator_helper!($instance: $timer, $signal_a, $signal_b, UnitOne); - }; -} +// Group 1 +impl_operator!(OPERATOR10, Group1); +impl_operator!(OPERATOR11, Group1); +impl_operator!(OPERATOR12, Group1); + +impl HwOperator0 for OPERATOR00 {} +impl HwOperator0 for OPERATOR10 {} + +impl HwOperator1 for OPERATOR01 {} +impl HwOperator1 for OPERATOR11 {} -impl_operator!( - OPERATOR0: mcpwm_timer_t_MCPWM_TIMER_0, - mcpwm_io_signals_t_MCPWM0A, - mcpwm_io_signals_t_MCPWM0B -); -impl_operator!( - OPERATOR1: mcpwm_timer_t_MCPWM_TIMER_1, - mcpwm_io_signals_t_MCPWM1A, - mcpwm_io_signals_t_MCPWM1B -); -impl_operator!( - OPERATOR2: mcpwm_timer_t_MCPWM_TIMER_2, - mcpwm_io_signals_t_MCPWM2A, - mcpwm_io_signals_t_MCPWM2B -); +impl HwOperator2 for OPERATOR02 {} +impl HwOperator2 for OPERATOR12 {} // TODO: How do we want syncing to fit in to this? // TODO: How do we want carrier to fit into this? @@ -73,8 +51,8 @@ impl_operator!( /// /// Every Motor Control module has three operators. Every operator can generate two output signals called A and B. /// A and B share the same timer and thus frequency and phase but can have induvidual duty set. -pub struct Operator, PA: OptionalOutputPin, PB: OptionalOutputPin> { - handle: mcpwm_operator_t, +pub struct Operator, PA: OptionalOutputPin, PB: OptionalOutputPin> { + handle: mcpwm_oper_handle_t, _instance: O, _pin_a: PA, @@ -85,7 +63,7 @@ pub struct Operator, PA: OptionalOutputPin, PB: Option impl Operator where - U: Unit, + U: Group, O: HwOperator, PA: OutputPin, PB: OptionalOutputPin, @@ -103,7 +81,7 @@ where impl Operator where - U: Unit, + U: Group, O: HwOperator, PA: OptionalOutputPin, PB: OutputPin, @@ -187,10 +165,10 @@ pub enum DutyMode { ActiveLow, } -pub trait OptionalOperator> {} +pub trait OptionalOperator> {} pub struct NoOperator; -impl> OptionalOperator for NoOperator {} +impl> OptionalOperator for NoOperator {} /* diff --git a/src/mcpwm/timer.rs b/src/mcpwm/timer.rs index abed247e175..e116a5ff87f 100644 --- a/src/mcpwm/timer.rs +++ b/src/mcpwm/timer.rs @@ -1,9 +1,9 @@ use std::ptr; -use esp_idf_sys::{mcpwm_counter_type_t_MCPWM_UP_DOWN_COUNTER, mcpwm_counter_type_t_MCPWM_DOWN_COUNTER, mcpwm_counter_type_t_MCPWM_UP_COUNTER, mcpwm_timer_t, mcpwm_timer_handle_t, mcpwm_timer_enable, mcpwm_timer_t_MCPWM_TIMER_0, mcpwm_timer_t_MCPWM_TIMER_1, mcpwm_timer_t_MCPWM_TIMER_2, mcpwm_counter_type_t}; +use esp_idf_sys::{mcpwm_timer_t, mcpwm_timer_handle_t, mcpwm_timer_enable, mcpwm_timer_config_t, mcpwm_new_timer, mcpwm_del_timer, esp, EspError}; use crate::units::Hertz; -use crate::mcpwm::Unit; +use crate::mcpwm::{Group, Group0, Group1}; use super::operator::NoOperator; use super::timer_connection::TimerConnection; @@ -51,23 +51,30 @@ impl TimerConfig { //} //} -pub struct Timer> { +pub struct Timer> { handle: mcpwm_timer_handle_t, _timer: T, } -impl> Timer { +impl> Timer { pub fn new(timer: T, config: TimerConfig) -> Self { let config = mcpwm_timer_config_t { - resolution + group_id: todo!(), + clk_src: todo!(), + resolution_hz: todo!(), + count_mode: todo!(), + period_ticks: todo!(), + flags: todo!(), }; let mut handle = ptr::null(); - mcpwm_new_timer(config, &mut handle); - + unsafe { + esp!(mcpwm_new_timer(config, &mut handle)); + } // TODO: note that this has to be called before mcpwm_timer_enable // mcpwm_timer_register_event_callbacks() - - mcpwm_timer_enable(); + unsafe { + esp!(mcpwm_timer_enable(*handle)).unwrap(); + } Self { handle, _timer: timer } } @@ -81,8 +88,8 @@ impl> Timer { todo!() } - pub fn timer(&self) -> mcpwm_timer_t { - T::ID + pub fn timer(&self) -> mcpwm_timer_handle_t { + self.handle } pub fn release(self) -> T { @@ -90,7 +97,9 @@ impl> Timer { _timer, handle } = self; - mcpwm_del_timer(handle); + unsafe { + esp!(mcpwm_del_timer(handle)).unwrap(); + } _timer } @@ -99,9 +108,13 @@ impl> Timer { } } -impl> Drop for Timer { +impl> Drop for Timer { fn drop(self) { - mcpwm_del_timer(self.handle) + unsafe { + unsafe { + esp!(mcpwm_del_timer(self.handle)).unwrap(); + } + } } } @@ -166,6 +179,7 @@ pub enum CounterMode { UpDown, } +/* impl From for mcpwm_counter_type_t { fn from(val: CounterMode) -> Self { match val { @@ -175,21 +189,25 @@ impl From for mcpwm_counter_type_t { CounterMode::UpDown => mcpwm_counter_type_t_MCPWM_UP_DOWN_COUNTER, } } -} +}*/ -unsafe trait HwTimer { +pub unsafe trait HwTimer { const ID: mcpwm_timer_t; } macro_rules! impl_timer { - ($t:ident, $id:expr) => { - struct $t; - impl HwTimer for $t { - const ID: mcpwm_timer_t = $id; - } + ($t:ident, $g:ty) => { + crate::impl_peripheral!($t); + impl HwTimer<$g> for $t {} }; } -impl_timer!(TIMER0, mcpwm_timer_t_MCPWM_TIMER_0); -impl_timer!(TIMER1, mcpwm_timer_t_MCPWM_TIMER_1); -impl_timer!(TIMER2, mcpwm_timer_t_MCPWM_TIMER_2); \ No newline at end of file +// Group 0 +impl_timer!(TIMER00, Group0); +impl_timer!(TIMER01, Group0); +impl_timer!(TIMER02, Group0); + +// Group 1 +impl_timer!(TIMER10, Group1); +impl_timer!(TIMER11, Group1); +impl_timer!(TIMER12, Group1); \ No newline at end of file diff --git a/src/mcpwm/timer_connection.rs b/src/mcpwm/timer_connection.rs index 02ee3d8daa6..fc891c65e2d 100644 --- a/src/mcpwm/timer_connection.rs +++ b/src/mcpwm/timer_connection.rs @@ -1,19 +1,24 @@ -use crate::{mcpwm::Unit, gpio::OutputPin}; +use crate::mcpwm::Group; -use super::operator::{OptionalOperator, OPERATOR0, OperatorConfig, NoOperator, OPERATOR2, OPERATOR1}; +use super::{ + operator::{ + HwOperator0, HwOperator1, HwOperator2, NoOperator, OperatorConfig, OptionalOperator, + }, + timer::{HwTimer, Timer}, +}; // TODO: How do we want fault module to fit into this? /// Created by `Timer::into_connection()` -pub struct TimerConnection, O0, O1, O2> - where - O0: OptionalOperator>, - O1: OptionalOperator>, - O2: OptionalOperator>, +pub struct TimerConnection, O0, O1, O2> +where + O0: OptionalOperator>, + O1: OptionalOperator>, + O2: OptionalOperator>, { - timer: Timer, + timer: Timer, operator0: O0, operator1: O1, - operator2: O2 + operator2: O2, } impl TimerConnection { @@ -22,7 +27,7 @@ impl TimerConnection { timer, operator0: NoOperator, operator1: NoOperator, - operator2: NoOperator + operator2: NoOperator, } } } @@ -32,8 +37,15 @@ impl TimerConnection { // // Thus we know that after split is called nothing can be added/removed while still having access to // the individual objects. We also garantuee that the operators wont live longer than the timer -impl, O1: OptionalOperator, O2: OptionalOperator> TimerConnection { - fn split(&mut self) -> (&mut timer, &mut O0, &mut O1, &mut O2) { +impl< + U, + T, + O0: OptionalOperator, + O1: OptionalOperator, + O2: OptionalOperator, + > TimerConnection +{ + fn split(&mut self) -> (&mut Timer, &mut O0, &mut O1, &mut O2) { ( &mut self.timer, &mut self.operator0, @@ -44,41 +56,59 @@ impl, O1: OptionalOperator TimerConnection { - fn attatch_operator0(mut self, operator_handle: OPERATOR0, operator_cfg: OperatorConfig, pin_a: PA, pin_b: PB) -> TimerConnection { + fn attatch_operator0( + mut self, + operator_handle: HwOperator0, + operator_cfg: OperatorConfig, + pin_a: PA, + pin_b: PB, + ) -> TimerConnection, O1, O2> { let operator = self.init_and_attach_operator(operator_cfg, pin_a, pin_b); TimerConnection { timer: self.timer, operator0: operator, operator1: self.operator1, - operator2: self.operator2 + operator2: self.operator2, } } } impl TimerConnection { - fn attatch_operator1(mut self, operator_handle: OPERATOR1, operator_cfg: OperatorConfig, pin_a: PA, pin_b: PB) -> TimerConnection { + fn attatch_operator1( + mut self, + operator_handle: HwOperator1, + operator_cfg: OperatorConfig, + pin_a: PA, + pin_b: PB, + ) -> TimerConnection, O2> { let operator = self.init_and_attach_operator(operator_cfg, pin_a, pin_b); TimerConnection { timer: self.timer, operator0: self.operator0, operator1: operator, - operator2: self.operator2 + operator2: self.operator2, } } } impl TimerConnection { - fn attatch_operator2(mut self, operator_handle: OPERATOR2, operator_cfg: OperatorConfig, pin_a: PA, pin_b: PB) -> TimerConnection { + fn attatch_operator2( + mut self, + operator_handle: HwOperator2, + operator_cfg: OperatorConfig, + pin_a: PA, + pin_b: PB, + ) -> TimerConnection> { let operator = self.init_and_attach_operator(operator_cfg, pin_a, pin_b); TimerConnection { timer: self.timer, operator0: self.operator0, operator1: self.operator1, - operator2: operator + operator2: operator, } } } pub struct NoPin; -pub trait OptionalOutputPin {} \ No newline at end of file +pub trait OptionalOutputPin {} diff --git a/src/peripherals.rs b/src/peripherals.rs index b03192c8410..21d6c37c39b 100644 --- a/src/peripherals.rs +++ b/src/peripherals.rs @@ -55,15 +55,11 @@ pub struct Peripherals { #[cfg(not(feature = "riscv-ulp-hal"))] pub can: can::CAN, #[cfg(not(feature = "riscv-ulp-hal"))] -<<<<<<< HEAD pub ledc: ledc::LEDC, -======= - pub ledc: ledc::Peripheral, #[cfg(all(any(esp32, esp32s3), not(feature = "riscv-ulp-hal")))] - pub mcpwm0: mcpwm::Peripheral, + pub mcpwm0: mcpwm::MCPWM0, #[cfg(all(any(esp32, esp32s3), not(feature = "riscv-ulp-hal")))] - pub mcpwm1: mcpwm::Peripheral, ->>>>>>> The beginning of a first rough draft... + pub mcpwm1: mcpwm::MCPWM1, #[cfg(not(feature = "riscv-ulp-hal"))] pub rmt: rmt::RMT, #[cfg(all( @@ -171,15 +167,11 @@ impl Peripherals { #[cfg(not(feature = "riscv-ulp-hal"))] can: can::CAN::new(), #[cfg(not(feature = "riscv-ulp-hal"))] -<<<<<<< HEAD ledc: ledc::LEDC::new(), -======= - ledc: ledc::Peripheral::new(), #[cfg(any(esp32, esp32s3))] - mcpwm0: mcpwm::Peripheral::new(), + mcpwm0: mcpwm::MCPWM0::new(), #[cfg(any(esp32, esp32s3))] - mcpwm1: mcpwm::Peripheral::new(), ->>>>>>> The beginning of a first rough draft... + mcpwm1: mcpwm::MCPWM1::new(), #[cfg(not(feature = "riscv-ulp-hal"))] rmt: rmt::RMT::new(), #[cfg(all( From 12e34db9969bf90aedf21a763fc11e121a85a7ac Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Tue, 11 Oct 2022 22:22:03 +0200 Subject: [PATCH 28/62] Compiling!!! However far from working or even testable... --- src/mcpwm/deadtime.rs | 362 ++++++++++++++++++++++++++++++++++ src/mcpwm/mod.rs | 67 ++----- src/mcpwm/operator.rs | 79 ++++---- src/mcpwm/timer.rs | 69 ++++--- src/mcpwm/timer_connection.rs | 55 +++--- 5 files changed, 482 insertions(+), 150 deletions(-) create mode 100644 src/mcpwm/deadtime.rs diff --git a/src/mcpwm/deadtime.rs b/src/mcpwm/deadtime.rs new file mode 100644 index 00000000000..f8661689a53 --- /dev/null +++ b/src/mcpwm/deadtime.rs @@ -0,0 +1,362 @@ +// TODO: This is stolen from the MCPWM 4.4 PR, check if/how this should be changed for 5.0 + + +/// Dead time config for MCPWM operator +/// +/// `rising_edge_delay` and `falling_edge_delay` is time as in number of clock cycles after the MCPWM modules group prescaler. +/// +/// Note that the dead times are calculated from MCPWMXA's flanks unless explicitly stated otherwise +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub enum DeadtimeConfig { + // TODO: Figure out what all of those options do and give them nice descriptions + /// MCPWM_BYPASS_RED + /// + /// Note that `MCPWMXB in` will be completely ignored. This means `Operator::set_duty_b` will + /// have no effect with this dead time mode + /// + /// ``` + /// . . . + /// . . . + /// .--------------------. . + /// | | . + /// MCPWMXA in | | . + /// | | . + /// --------------- --------------------- + /// . . . + /// . . . + /// .--------------------. . + /// | | . + /// MCPWMXA out | | . + /// | | . + /// --------------- --------------------- + /// . . . + /// . . . + /// .------------------------. + /// | >. |< fed + /// MCPWMXB out | . | + /// | . | + /// --------------. . ----------------- + /// . . . + /// . . . + /// ``` + BypassRisingEdge { falling_edge_delay: u16 }, + + /// MCPWM_BYPASS_FED + /// + /// Note that `MCPWMXB in` will be completely ignored. This means `Operator::set_duty_b` will + /// have no effect with this dead time mode + /// + /// ``` + /// . . . + /// . . . + /// .--------------------. + /// | . | + /// MCPWMXA in | . | + /// | . | + /// --------------- . --------------------- + /// . . . + /// . . . + /// . .----------------. + /// red >. |< | + /// MCPWMXA out . | | + /// . | | + /// ------------------- --------------------- + /// . . . + /// . . . + /// .--------------------. + /// | . | + /// MCPWMXB out | . | + /// | . | + /// --------------- . --------------------- + /// . . . + /// . . . + /// ``` + BypassFallingEdge { rising_edge_delay: u16 }, + + /// MCPWM_ACTIVE_HIGH_MODE + /// + /// Note that `MCPWMXB in` will be completely ignored. This means `Operator::set_duty_b` will + /// have no effect with this dead time mode + /// + /// ``` + /// . . . . + /// . . . . + /// .--------------------. . + /// | . | . + /// MCPWMXA in | . | . + /// | . | . + /// --------------- . --------------------- + /// . . . . + /// . . . . + /// . .----------------. . + /// red >. |< | . + /// MCPWMXA out . | | . + /// . | | . + /// ------------------- --------------------- + /// . . . . + /// . . . . + /// .------------------------. + /// | . >. |< fed + /// MCPWMXB out | . . | + /// | . . | + /// --------------. . . ----------------- + /// . . . . + /// . . . . + /// ``` + ActiveHigh { + rising_edge_delay: u16, + falling_edge_delay: u16, + }, + + /// MCPWM_ACTIVE_LOW_MODE + /// + /// Note that `MCPWMXB in` will be completely ignored. This means `Operator::set_duty_b` will + /// have no effect with this dead time mode + /// + /// ``` + /// . . . . + /// . . . . + /// .--------------------. . + /// | . | . + /// MCPWMXA in | . | . + /// | . | . + /// --------------- . --------------------- + /// . . . . + /// . . . . + /// ------------------. .-------------------- + /// red >. |< | . + /// MCPWMXA out . | | . + /// . | | . + /// . ------------------ + /// . . . . + /// . . . . + /// --------------. . . .---------------- + /// | . >. |< fed + /// MCPWMXB out | . . | + /// | . . | + /// -------------------------- + /// . . . . + /// . . . . + /// ``` + ActiveLow { + rising_edge_delay: u16, + falling_edge_delay: u16, + }, + + /// MCPWM_ACTIVE_HIGH_COMPLIMENT_MODE + /// + /// Note that `MCPWMXB in` will be completely ignored. This means `Operator::set_duty_b` will + /// have no effect with this dead time mode + /// + /// ``` + /// . . . . + /// . . . . + /// .--------------------. . + /// | . | . + /// MCPWMXA in | . | . + /// | . | . + /// --------------- . --------------------- + /// . . . . + /// . . . . + /// . .----------------. . + /// red >. |< | . + /// MCPWMXA out . | | . + /// . | | . + /// ------------------- --------------------- + /// . . . . + /// . . . . + /// --------------. . . .---------------- + /// | . >. |< fed + /// MCPWMXB out | . . | + /// | . . | + /// -------------------------- + /// . . . . + /// . . . . + /// ``` + ActiveHighComplement { + rising_edge_delay: u16, + falling_edge_delay: u16, + }, + + /// MCPWM_ACTIVE_LOW_COMPLIMENT_MODE + /// + /// Note that `MCPWMXB in` will be completely ignored. This means `Operator::set_duty_b` will + /// have no effect with this dead time mode + /// + /// ``` + /// . . . . + /// . . . . + /// .--------------------. . + /// | . | . + /// MCPWMXA in | . | . + /// | . | . + /// --------------- . --------------------- + /// . . . . + /// . . . . + /// ------------------. .-------------------- + /// red >. |< | . + /// MCPWMXA out . | | . + /// . | | . + /// . ------------------ . + /// . . . . + /// . . . . + /// .------------------------. + /// | . >. |< fed + /// MCPWMXB out | . . | + /// | . . | + /// --------------- . . ----------------- + /// . . . . + /// . . . . + /// ``` + ActiveLowComplement { + rising_edge_delay: u16, + falling_edge_delay: u16, + }, + + /// MCPWM_ACTIVE_RED_FED_FROM_PWMXA + /// + /// Note that `MCPWMXB in` will be completely ignored. This means `Operator::set_duty_b` will + /// have no effect with this dead time mode + /// + /// ``` + /// . . . . + /// . . . . + /// .--------------------. . + /// | . | . + /// MCPWMXA in | . | . + /// | . | . + /// --------------- . --------------------- + /// . . . . + /// . . . . + /// . .--------------------. + /// red >. |< . | + /// MCPWMXA out . | . | + /// . | . | + /// ------------------- . --------------------- + /// . . . . + /// . . . . + /// . .--------------------. + /// red >. |< . | + /// MCPWMXB out . | . | + /// . | . | + /// ------------------- . --------------------- + /// . . . . + /// . . . . + /// ``` + ActiveRedFedFromPwmxa { + rising_edge_delay: u16, + falling_edge_delay: u16, + }, + + /// MCPWM_ACTIVE_RED_FED_FROM_PWMXB + /// + /// Note that `MCPWMXA in` will be completely ignored. This means `Operator::set_duty_a` will + /// have no effect with this dead time mode + /// ``` + /// . . . . + /// . . . . + /// .--------------------. . + /// | . | . + /// MCPWMXB in | . | . + /// | . | . + /// --------------- . --------------------- + /// . . . . + /// . . . . + /// . .--------------------. + /// red >. |< . | + /// MCPWMXA out . | . | + /// . | . | + /// ------------------- . --------------------- + /// . . . . + /// . . . . + /// . .--------------------. + /// red >. |< . | + /// MCPWMXB out . | . | + /// . | . | + /// ------------------- . --------------------- + /// . . . . + /// . . . . + /// ``` + ActiveRedFedFromPwmxb { + rising_edge_delay: u16, + falling_edge_delay: u16, + }, +} + +impl DeadtimeConfig { + fn as_args(&self) -> DeadtimeArgs { + match *self { + DeadtimeConfig::BypassRisingEdge { falling_edge_delay } => DeadtimeArgs { + rising_edge_delay: 0, + falling_edge_delay, + mode: mcpwm_deadtime_type_t_MCPWM_BYPASS_RED, + }, + + DeadtimeConfig::BypassFallingEdge { rising_edge_delay } => DeadtimeArgs { + rising_edge_delay, + falling_edge_delay: 0, + mode: mcpwm_deadtime_type_t_MCPWM_BYPASS_FED, + }, + + DeadtimeConfig::ActiveHigh { + rising_edge_delay, + falling_edge_delay, + } => DeadtimeArgs { + rising_edge_delay, + falling_edge_delay, + mode: mcpwm_deadtime_type_t_MCPWM_ACTIVE_HIGH_MODE, + }, + + DeadtimeConfig::ActiveLow { + rising_edge_delay, + falling_edge_delay, + } => DeadtimeArgs { + rising_edge_delay, + falling_edge_delay, + mode: mcpwm_deadtime_type_t_MCPWM_ACTIVE_LOW_MODE, + }, + + DeadtimeConfig::ActiveHighComplement { + rising_edge_delay, + falling_edge_delay, + } => DeadtimeArgs { + rising_edge_delay, + falling_edge_delay, + mode: mcpwm_deadtime_type_t_MCPWM_ACTIVE_HIGH_COMPLIMENT_MODE, + }, + + DeadtimeConfig::ActiveLowComplement { + rising_edge_delay, + falling_edge_delay, + } => DeadtimeArgs { + rising_edge_delay, + falling_edge_delay, + mode: mcpwm_deadtime_type_t_MCPWM_ACTIVE_LOW_COMPLIMENT_MODE, + }, + + DeadtimeConfig::ActiveRedFedFromPwmxa { + rising_edge_delay, + falling_edge_delay, + } => DeadtimeArgs { + rising_edge_delay, + falling_edge_delay, + mode: mcpwm_deadtime_type_t_MCPWM_ACTIVE_RED_FED_FROM_PWMXA, + }, + + DeadtimeConfig::ActiveRedFedFromPwmxb { + rising_edge_delay, + falling_edge_delay, + } => DeadtimeArgs { + rising_edge_delay, + falling_edge_delay, + mode: mcpwm_deadtime_type_t_MCPWM_ACTIVE_RED_FED_FROM_PWMXB, + }, + } + } +} + +struct DeadtimeArgs { + rising_edge_delay: u16, + falling_edge_delay: u16, + mode: mcpwm_deadtime_type_t, +} \ No newline at end of file diff --git a/src/mcpwm/mod.rs b/src/mcpwm/mod.rs index a3cd07e397a..38095609991 100644 --- a/src/mcpwm/mod.rs +++ b/src/mcpwm/mod.rs @@ -57,16 +57,7 @@ mod timer_connection; use core::ffi; -use crate::mcpwm::operator::{ - HwOperator0, HwOperator1, HwOperator2, - OPERATOR00, OPERATOR01, OPERATOR02, - OPERATOR10, OPERATOR11, OPERATOR12 -}; - -use crate::mcpwm::timer::{ - TIMER00, TIMER01, TIMER02, - TIMER10, TIMER11, TIMER12 -}; +use self::{operator::OPERATOR, timer::TIMER}; // MCPWM clock source frequency for ESP32 and ESP32-s3 const MCPWM_CLOCK_SOURCE_FREQUENCY: u32 = 160_000_000; @@ -78,54 +69,28 @@ const MAX_PWM_TIMER_PRESCALE: u32 = 0x1_00; const MAX_PWM_TIMER_PERIOD: u32 = 0x1_00_00; /// The Motor Control Pulse Width Modulator peripheral -pub struct MCPWM0 { - timer0: TIMER00, - timer1: TIMER01, - timer2: TIMER02, - - pub operator0: OPERATOR00, - pub operator1: OPERATOR01, - pub operator2: OPERATOR02, -} - -impl MCPWM0 { - /// # Safety - /// - /// It is safe to instantiate this exactly one time per `Group`. - pub unsafe fn new() -> Self { - Self { - timer0: TIMER00::new(), - timer1: TIMER01::new(), - timer2: TIMER02::new(), - operator0: OPERATOR00::new(), - operator1: OPERATOR01::new(), - operator2: OPERATOR02::new(), - } - } -} - -pub struct MCPWM1 { - timer0: TIMER10, - timer1: TIMER11, - timer2: TIMER12, - - pub operator0: OPERATOR10, - pub operator1: OPERATOR11, - pub operator2: OPERATOR12, +pub struct MCPWM { + timer0: TIMER<0, G>, + timer1: TIMER<1, G>, + timer2: TIMER<2, G>, + + pub operator0: OPERATOR<0, G>, + pub operator1: OPERATOR<1, G>, + pub operator2: OPERATOR<2, G>, } -impl MCPWM1 { +impl MCPWM { /// # Safety /// /// It is safe to instantiate this exactly one time per `Group`. pub unsafe fn new() -> Self { Self { - timer0: TIMER10::new(), - timer1: TIMER11::new(), - timer2: TIMER12::new(), - operator0: OPERATOR10::new(), - operator1: OPERATOR11::new(), - operator2: OPERATOR12::new(), + timer0: TIMER::new(), + timer1: TIMER::new(), + timer2: TIMER::new(), + operator0: OPERATOR::new(), + operator1: OPERATOR::new(), + operator2: OPERATOR::new(), } } } diff --git a/src/mcpwm/operator.rs b/src/mcpwm/operator.rs index 95dfd97431d..5dcd99fc97e 100644 --- a/src/mcpwm/operator.rs +++ b/src/mcpwm/operator.rs @@ -4,44 +4,38 @@ use crate::{mcpwm::{Group, Group0, Group1}, gpio::OutputPin}; use super::{Duty, timer_connection::OptionalOutputPin}; -use core::ffi; +use core::{ffi, marker::PhantomData}; -// The hardware for ESP32 and ESP32-S3 can associate any operator(within the mcpwm module) with any -// timer(within the mcpwm module) for example allowing using the same timer for all three operators. -pub trait HwOperator { - const GROUP_ID: ffi::c_int = U::ID; +pub struct OPERATOR{ + _ptr: PhantomData<*const ()>, + _group: PhantomData } -macro_rules! impl_operator { - ($t:ident, $g:ty) => { - crate::impl_peripheral!($t); - - impl HwOperator<$g> for $t {} - }; +impl OPERATOR { + /// # Safety + /// + /// Care should be taken not to instnatiate this peripheralinstance, if it is already instantiated and used elsewhere + #[inline(always)] + pub unsafe fn new() -> Self { + Self { + _ptr: PhantomData, + _group: PhantomData + } + } } -pub trait HwOperator0: HwOperator {} -pub trait HwOperator1: HwOperator {} -pub trait HwOperator2: HwOperator {} +unsafe impl Send for OPERATOR {} -// Group 0 -impl_operator!(OPERATOR00, Group0); -impl_operator!(OPERATOR01, Group0); -impl_operator!(OPERATOR02, Group0); +impl crate::peripheral::sealed::Sealed for OPERATOR {} -// Group 1 -impl_operator!(OPERATOR10, Group1); -impl_operator!(OPERATOR11, Group1); -impl_operator!(OPERATOR12, Group1); +impl crate::peripheral::Peripheral for OPERATOR { + type P = Self; -impl HwOperator0 for OPERATOR00 {} -impl HwOperator0 for OPERATOR10 {} - -impl HwOperator1 for OPERATOR01 {} -impl HwOperator1 for OPERATOR11 {} - -impl HwOperator2 for OPERATOR02 {} -impl HwOperator2 for OPERATOR12 {} + #[inline] + unsafe fn clone_unchecked(&mut self) -> Self::P { + Self { ..*self } + } +} // TODO: How do we want syncing to fit in to this? // TODO: How do we want carrier to fit into this? @@ -51,9 +45,10 @@ impl HwOperator2 for OPERATOR12 {} /// /// Every Motor Control module has three operators. Every operator can generate two output signals called A and B. /// A and B share the same timer and thus frequency and phase but can have induvidual duty set. -pub struct Operator, PA: OptionalOutputPin, PB: OptionalOutputPin> { +pub struct Operator { + _group: PhantomData, handle: mcpwm_oper_handle_t, - _instance: O, + _instance: OPERATOR, _pin_a: PA, _pin_b: PB, @@ -61,10 +56,9 @@ pub struct Operator, PA: OptionalOutputPin, PB: Optio //deadtime: D } -impl Operator +impl Operator where - U: Group, - O: HwOperator, + G: Group, PA: OutputPin, PB: OptionalOutputPin, { @@ -79,10 +73,9 @@ where } } -impl Operator +impl Operator where - U: Group, - O: HwOperator, + G: Group, PA: OptionalOutputPin, PB: OutputPin, { @@ -139,8 +132,8 @@ impl OperatorConfig { impl Default for OperatorConfig { fn default() -> Self { Self { - duty_a: 0.0, - duty_b: 0.0, + duty_a: 0, + duty_b: 0, duty_mode: DutyMode::ActiveHigh, @@ -165,10 +158,12 @@ pub enum DutyMode { ActiveLow, } -pub trait OptionalOperator> {} +pub trait OptionalOperator {} +impl OptionalOperator for OPERATOR {} pub struct NoOperator; -impl> OptionalOperator for NoOperator {} +impl OptionalOperator for NoOperator {} + /* diff --git a/src/mcpwm/timer.rs b/src/mcpwm/timer.rs index e116a5ff87f..43c50628ad3 100644 --- a/src/mcpwm/timer.rs +++ b/src/mcpwm/timer.rs @@ -1,3 +1,4 @@ +use core::marker::PhantomData; use std::ptr; use esp_idf_sys::{mcpwm_timer_t, mcpwm_timer_handle_t, mcpwm_timer_enable, mcpwm_timer_config_t, mcpwm_new_timer, mcpwm_del_timer, esp, EspError}; @@ -51,13 +52,14 @@ impl TimerConfig { //} //} -pub struct Timer> { +pub struct Timer { + _group: G, handle: mcpwm_timer_handle_t, - _timer: T, + _timer: TIMER, } -impl> Timer { - pub fn new(timer: T, config: TimerConfig) -> Self { +impl Timer { + pub fn new(timer: TIMER, config: TimerConfig) -> Self { let config = mcpwm_timer_config_t { group_id: todo!(), clk_src: todo!(), @@ -66,17 +68,17 @@ impl> Timer { period_ticks: todo!(), flags: todo!(), }; - let mut handle = ptr::null(); + let mut handle: mcpwm_timer_handle_t = ptr::null_mut(); unsafe { - esp!(mcpwm_new_timer(config, &mut handle)); + esp!(mcpwm_new_timer(&config, &mut handle)); } // TODO: note that this has to be called before mcpwm_timer_enable // mcpwm_timer_register_event_callbacks() unsafe { - esp!(mcpwm_timer_enable(*handle)).unwrap(); + esp!(mcpwm_timer_enable(handle)).unwrap(); } - Self { handle, _timer: timer } + Self { _group: G::default(), handle, _timer: timer } } /// Set PWM frequency pub fn set_frequency(&mut self, frequency: Hertz) -> Result<(), EspError> { @@ -92,8 +94,9 @@ impl> Timer { self.handle } - pub fn release(self) -> T { + pub fn release(self) -> TIMER { let Self { + _group, _timer, handle } = self; @@ -103,13 +106,13 @@ impl> Timer { _timer } - fn into_connection(timer: T) -> TimerConnection { - TimerConnection::new(timer) + fn into_connection(self) -> TimerConnection { + TimerConnection::new(self) } } -impl> Drop for Timer { - fn drop(self) { +impl Drop for Timer { + fn drop(&mut self) { unsafe { unsafe { esp!(mcpwm_del_timer(self.handle)).unwrap(); @@ -191,23 +194,33 @@ impl From for mcpwm_counter_type_t { } }*/ -pub unsafe trait HwTimer { - const ID: mcpwm_timer_t; +pub struct TIMER{ + _ptr: PhantomData<*const ()>, + _group: PhantomData } -macro_rules! impl_timer { - ($t:ident, $g:ty) => { - crate::impl_peripheral!($t); - impl HwTimer<$g> for $t {} - }; +impl TIMER { + /// # Safety + /// + /// Care should be taken not to instnatiate this peripheralinstance, if it is already instantiated and used elsewhere + #[inline(always)] + pub unsafe fn new() -> Self { + Self { + _ptr: PhantomData, + _group: PhantomData + } + } } -// Group 0 -impl_timer!(TIMER00, Group0); -impl_timer!(TIMER01, Group0); -impl_timer!(TIMER02, Group0); +unsafe impl Send for TIMER {} -// Group 1 -impl_timer!(TIMER10, Group1); -impl_timer!(TIMER11, Group1); -impl_timer!(TIMER12, Group1); \ No newline at end of file +impl crate::peripheral::sealed::Sealed for TIMER {} + +impl crate::peripheral::Peripheral for TIMER { + type P = Self; + + #[inline] + unsafe fn clone_unchecked(&mut self) -> Self::P { + Self { ..*self } + } +} \ No newline at end of file diff --git a/src/mcpwm/timer_connection.rs b/src/mcpwm/timer_connection.rs index fc891c65e2d..ee937b1d929 100644 --- a/src/mcpwm/timer_connection.rs +++ b/src/mcpwm/timer_connection.rs @@ -2,27 +2,27 @@ use crate::mcpwm::Group; use super::{ operator::{ - HwOperator0, HwOperator1, HwOperator2, NoOperator, OperatorConfig, OptionalOperator, + NoOperator, OperatorConfig, OptionalOperator, OPERATOR, }, - timer::{HwTimer, Timer}, + timer::{Timer, TIMER}, }; // TODO: How do we want fault module to fit into this? /// Created by `Timer::into_connection()` -pub struct TimerConnection, O0, O1, O2> +pub struct TimerConnection where - O0: OptionalOperator>, - O1: OptionalOperator>, - O2: OptionalOperator>, + O0: OptionalOperator<0, G>, + O1: OptionalOperator<1, G>, + O2: OptionalOperator<2, G>, { - timer: Timer, + timer: Timer, operator0: O0, operator1: O1, operator2: O2, } -impl TimerConnection { - pub(crate) fn new(timer: T) -> Self { +impl TimerConnection { + pub(crate) fn new(timer: Timer) -> Self { Self { timer, operator0: NoOperator, @@ -37,15 +37,10 @@ impl TimerConnection { // // Thus we know that after split is called nothing can be added/removed while still having access to // the individual objects. We also garantuee that the operators wont live longer than the timer -impl< - U, - T, - O0: OptionalOperator, - O1: OptionalOperator, - O2: OptionalOperator, - > TimerConnection +impl, O1: OptionalOperator<1, G>, O2: OptionalOperator<2, G>> + TimerConnection { - fn split(&mut self) -> (&mut Timer, &mut O0, &mut O1, &mut O2) { + fn split(&mut self) -> (&mut Timer, &mut O0, &mut O1, &mut O2) { ( &mut self.timer, &mut self.operator0, @@ -55,15 +50,15 @@ impl< } } // TODO: Do something more builder-pattern like for making the operator? -impl TimerConnection { +impl, O2: OptionalOperator<2, G>> TimerConnection { fn attatch_operator0( mut self, - operator_handle: HwOperator0, + operator_handle: OPERATOR<0, G>, operator_cfg: OperatorConfig, pin_a: PA, pin_b: PB, - ) -> TimerConnection, O1, O2> { - let operator = self.init_and_attach_operator(operator_cfg, pin_a, pin_b); + ) -> TimerConnection, O1, O2> { + let operator = todo!();//self.init_and_attach_operator(operator_cfg, pin_a, pin_b); TimerConnection { timer: self.timer, operator0: operator, @@ -73,15 +68,15 @@ impl TimerConnection { } } -impl TimerConnection { +impl, O2: OptionalOperator<2, G>> TimerConnection { fn attatch_operator1( mut self, - operator_handle: HwOperator1, + operator_handle: OPERATOR<1, G>, operator_cfg: OperatorConfig, pin_a: PA, pin_b: PB, - ) -> TimerConnection, O2> { - let operator = self.init_and_attach_operator(operator_cfg, pin_a, pin_b); + ) -> TimerConnection, O2> { + let operator = todo!();//self.init_and_attach_operator(operator_cfg, pin_a, pin_b); TimerConnection { timer: self.timer, operator0: self.operator0, @@ -91,15 +86,15 @@ impl TimerConnection { } } -impl TimerConnection { +impl, O1: OptionalOperator<1, G>> TimerConnection { fn attatch_operator2( mut self, - operator_handle: HwOperator2, + operator_handle: OPERATOR<2, G>, operator_cfg: OperatorConfig, pin_a: PA, pin_b: PB, - ) -> TimerConnection> { - let operator = self.init_and_attach_operator(operator_cfg, pin_a, pin_b); + ) -> TimerConnection> { + let operator = todo!();//self.init_and_attach_operator(operator_cfg, pin_a, pin_b); TimerConnection { timer: self.timer, operator0: self.operator0, @@ -112,3 +107,5 @@ impl TimerConnection { pub struct NoPin; pub trait OptionalOutputPin {} + +impl OptionalOutputPin for P {} \ No newline at end of file From 0caf311fe81e4f32059dd79727ac1943253f089e Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Thu, 13 Oct 2022 22:12:23 +0200 Subject: [PATCH 29/62] ...just kidding. However now it is compiling!! But now quite running --- examples/mcpwm-simple.rs | 19 ++++-- src/mcpwm/mod.rs | 22 ++++-- src/mcpwm/operator.rs | 28 ++++---- src/mcpwm/timer.rs | 122 +++++++++++++++++++++++----------- src/mcpwm/timer_connection.rs | 22 +++--- src/peripherals.rs | 8 +-- 6 files changed, 144 insertions(+), 77 deletions(-) diff --git a/examples/mcpwm-simple.rs b/examples/mcpwm-simple.rs index d1d7790ab60..31a50eaa775 100644 --- a/examples/mcpwm-simple.rs +++ b/examples/mcpwm-simple.rs @@ -61,7 +61,7 @@ fn main() -> anyhow::Result<()> { use embedded_hal::delay::blocking::DelayUs; use esp_idf_hal::delay::FreeRtos; - use esp_idf_hal::mcpwm::{Mcpwm, Operator, OperatorConfig}; + use esp_idf_hal::mcpwm::{Timer, TimerConfig, Operator, OperatorConfig}; use esp_idf_hal::prelude::Peripherals; use esp_idf_hal::units::FromValueType; @@ -72,27 +72,30 @@ fn main() -> anyhow::Result<()> { let peripherals = Peripherals::take().unwrap(); let timer_config = TimerConfig::default().frequency(25.kHz()); let operator_config = OperatorConfig::default(); - let timer = Mcpwm::new(peripherals.mcpwm0.timer, timer_config)?; + let timer = Timer::new(peripherals.mcpwm0.timer0, timer_config); - let timer = timer.into_connection() + let mut timer = timer.into_connection() .attatch_operator0( peripherals.mcpwm0.operator0, operator_config, peripherals.pins.gpio4, peripherals.pins.gpio5, - )?; + ); let (timer, operator, _, _) = timer.split(); println!("Starting duty-cycle loop"); - for duty in (0..timer.get_top_value()).cycle() { + let period_ticks = timer.get_period_peak(); + + // TODO: Will this work as expected in UP_DOWN counter mode? + for duty in (0..period_ticks).cycle() { if duty % 100 == 0 { - println!("Duty {}%", x / 100); + println!("Duty {}%", 100 * duty / period_ticks); } operator.set_duty_a(duty)?; - operator.set_duty_b(100.0 - duty)?; + operator.set_duty_b(period_ticks - duty)?; FreeRtos.delay_ms(10)?; } @@ -102,4 +105,6 @@ fn main() -> anyhow::Result<()> { #[cfg(not(any(esp32, esp32s3)))] fn main() { esp_idf_sys::link_patches(); + + println!("Sorry MCPWM is only supported on ESP32 and ESP32-S3"); } diff --git a/src/mcpwm/mod.rs b/src/mcpwm/mod.rs index 38095609991..7a61e7e1680 100644 --- a/src/mcpwm/mod.rs +++ b/src/mcpwm/mod.rs @@ -57,7 +57,21 @@ mod timer_connection; use core::ffi; -use self::{operator::OPERATOR, timer::TIMER}; +pub use self::{ + operator::{ + OPERATOR, + Operator, + OperatorConfig, + }, + timer::{ + TIMER, + Timer, + TimerConfig + }, + timer_connection::{ + TimerConnection + } +}; // MCPWM clock source frequency for ESP32 and ESP32-s3 const MCPWM_CLOCK_SOURCE_FREQUENCY: u32 = 160_000_000; @@ -70,9 +84,9 @@ const MAX_PWM_TIMER_PERIOD: u32 = 0x1_00_00; /// The Motor Control Pulse Width Modulator peripheral pub struct MCPWM { - timer0: TIMER<0, G>, - timer1: TIMER<1, G>, - timer2: TIMER<2, G>, + pub timer0: TIMER<0, G>, + pub timer1: TIMER<1, G>, + pub timer2: TIMER<2, G>, pub operator0: OPERATOR<0, G>, pub operator1: OPERATOR<1, G>, diff --git a/src/mcpwm/operator.rs b/src/mcpwm/operator.rs index 5dcd99fc97e..f9f0a3d7f00 100644 --- a/src/mcpwm/operator.rs +++ b/src/mcpwm/operator.rs @@ -1,14 +1,14 @@ -use esp_idf_sys::{EspError, mcpwm_oper_handle_t}; +use esp_idf_sys::{mcpwm_oper_handle_t, EspError}; -use crate::{mcpwm::{Group, Group0, Group1}, gpio::OutputPin}; +use crate::{gpio::OutputPin, mcpwm::Group}; -use super::{Duty, timer_connection::OptionalOutputPin}; +use super::{timer_connection::OptionalOutputPin, Duty}; use core::{ffi, marker::PhantomData}; -pub struct OPERATOR{ +pub struct OPERATOR { _ptr: PhantomData<*const ()>, - _group: PhantomData + _group: PhantomData, } impl OPERATOR { @@ -19,7 +19,7 @@ impl OPERATOR { pub unsafe fn new() -> Self { Self { _ptr: PhantomData, - _group: PhantomData + _group: PhantomData, } } } @@ -52,7 +52,6 @@ pub struct Operator Result<(), EspError> { todo!() } @@ -96,7 +96,6 @@ pub struct OperatorConfig { duty_b: Duty, duty_mode: DutyMode, - //deadtime: Option, } @@ -136,7 +135,6 @@ impl Default for OperatorConfig { duty_b: 0, duty_mode: DutyMode::ActiveHigh, - //deadtime: None, } } @@ -159,12 +157,14 @@ pub enum DutyMode { } pub trait OptionalOperator {} -impl OptionalOperator for OPERATOR {} +impl OptionalOperator + for Operator +{ +} pub struct NoOperator; impl OptionalOperator for NoOperator {} - /* #[derive(Default, Clone)] @@ -215,9 +215,9 @@ impl DutyMode { } else { duty_config.on_matches_cmp_b.counting_up = GeneratorAction::SetHigh; } - + duty_config }, } } -} */ \ No newline at end of file +} */ diff --git a/src/mcpwm/timer.rs b/src/mcpwm/timer.rs index 43c50628ad3..bb1b1d419ca 100644 --- a/src/mcpwm/timer.rs +++ b/src/mcpwm/timer.rs @@ -1,33 +1,46 @@ +use core::convert::TryInto; use core::marker::PhantomData; use std::ptr; -use esp_idf_sys::{mcpwm_timer_t, mcpwm_timer_handle_t, mcpwm_timer_enable, mcpwm_timer_config_t, mcpwm_new_timer, mcpwm_del_timer, esp, EspError}; +use esp_idf_sys::{ + esp, mcpwm_del_timer, mcpwm_new_timer, mcpwm_timer_config_t, mcpwm_timer_enable, + mcpwm_timer_handle_t, EspError, +}; +use crate::mcpwm::Group; +use crate::prelude::FromValueType; use crate::units::Hertz; -use crate::mcpwm::{Group, Group0, Group1}; use super::operator::NoOperator; use super::timer_connection::TimerConnection; - -#[derive(Clone, Copy, Debug)] -struct TimerConfig { +#[derive(Clone, Copy, Debug, PartialEq)] +pub struct TimerConfig { frequency: Hertz, - resolution: Hertz, - counter_mode: CounterMode, - + //resolution: Hertz, + count_mode: CountMode, // TODO // on_full: FF, // on_empty: FE, // on_stop: FS, } +impl Default for TimerConfig { + fn default() -> Self { + Self { + frequency: 1_u32.kHz().into(), + //resolution: Default::default(), + count_mode: CountMode::Up, + } + } +} + impl TimerConfig { - #[must_use] + /*#[must_use] pub fn resolution(mut self, resolution: impl Into) -> Self { self.resolution = resolution.into(); self - } + }*/ /// Frequency which the operator will run at, can also be changed live later #[must_use] @@ -37,30 +50,39 @@ impl TimerConfig { } #[must_use] - pub fn counter_mode(mut self, counter_mode: CounterMode) -> Self { - self.counter_mode = counter_mode; + pub fn counter_mode(mut self, counter_mode: CountMode) -> Self { + self.count_mode = counter_mode; self } } //TODO //impl TimerConfig { - //#[must_use] - //pub fn on_full(mut self, on_full: CB) -> Self { - // self.on_full = on_full; - // self - //} +//#[must_use] +//pub fn on_full(mut self, on_full: CB) -> Self { +// self.on_full = on_full; +// self +//} //} pub struct Timer { _group: G, handle: mcpwm_timer_handle_t, _timer: TIMER, + /// Number of ticks within a period + /// + /// See `Self::get_period_ticks` for more info + period_ticks: u32, + + /// This is the maximum value that the comparator will see + /// + /// See `Self::get_period_peak` for more info + period_peak: u16, } impl Timer { pub fn new(timer: TIMER, config: TimerConfig) -> Self { - let config = mcpwm_timer_config_t { + let cfg = mcpwm_timer_config_t { group_id: todo!(), clk_src: todo!(), resolution_hz: todo!(), @@ -70,7 +92,7 @@ impl Timer { }; let mut handle: mcpwm_timer_handle_t = ptr::null_mut(); unsafe { - esp!(mcpwm_new_timer(&config, &mut handle)); + esp!(mcpwm_new_timer(&cfg, &mut handle)); } // TODO: note that this has to be called before mcpwm_timer_enable // mcpwm_timer_register_event_callbacks() @@ -78,22 +100,50 @@ impl Timer { esp!(mcpwm_timer_enable(handle)).unwrap(); } - Self { _group: G::default(), handle, _timer: timer } + let period_peak = if config.count_mode == CountMode::UpDown { + (cfg.period_ticks / 2).try_into().unwrap() + } else { + cfg.period_ticks.try_into().unwrap() + }; + + Self { + _group: G::default(), + handle, + _timer: timer, + period_ticks: cfg.period_ticks, + period_peak + } } - /// Set PWM frequency - pub fn set_frequency(&mut self, frequency: Hertz) -> Result<(), EspError> { - todo!() + + /// Get number of ticks per period + /// + /// Use this when working with the frequency or the period + /// + /// NOTE: This will be the same as `Self::get_period_peak` for all `CounterMode` except for + /// `CounterMode::UpDown` where the period will be twice as large as the peak value since + /// the timer will count from zero to peak and then down to zero again + pub fn get_period_ticks(&self) -> u32 { + self.period_ticks } - /// Get PWM frequency - pub fn get_frequency(&self) -> Hertz { - todo!() + /// This is the maximum value that the comparator will see + /// + /// Use this working with the duty + /// + /// NOTE: This will not be the same as `Self::get_period_ticks` when using `CounterMode::UpDown` + /// See `Self::get_period_ticks` for more info + pub fn get_period_peak(&self) -> u16 { + self.period_peak } pub fn timer(&self) -> mcpwm_timer_handle_t { self.handle } + // TODO: It seems that we can't have both at the same time: + // a method for releasing its hardware resources + // and implementing Drop. + /* pub fn release(self) -> TIMER { let Self { _group, @@ -104,9 +154,9 @@ impl Timer { esp!(mcpwm_del_timer(handle)).unwrap(); } _timer - } + }*/ - fn into_connection(self) -> TimerConnection { + pub fn into_connection(self) -> TimerConnection { TimerConnection::new(self) } } @@ -114,17 +164,15 @@ impl Timer { impl Drop for Timer { fn drop(&mut self) { unsafe { - unsafe { - esp!(mcpwm_del_timer(self.handle)).unwrap(); - } + esp!(mcpwm_del_timer(self.handle)).unwrap(); } } } /// Counter mode for operator's timer for generating PWM signal // TODO: For UpDown, frequency is half of MCPWM frequency set -#[derive(Clone, Copy, Debug)] -pub enum CounterMode { +#[derive(Clone, Copy, Debug, PartialEq)] +pub enum CountMode { /// Timer is frozen or paused //#[cfg(not(esp_idf_version = "4.3"))] //Frozen, @@ -194,9 +242,9 @@ impl From for mcpwm_counter_type_t { } }*/ -pub struct TIMER{ +pub struct TIMER { _ptr: PhantomData<*const ()>, - _group: PhantomData + _group: PhantomData, } impl TIMER { @@ -207,7 +255,7 @@ impl TIMER { pub unsafe fn new() -> Self { Self { _ptr: PhantomData, - _group: PhantomData + _group: PhantomData, } } } @@ -223,4 +271,4 @@ impl crate::peripheral::Peripheral for TIMER { unsafe fn clone_unchecked(&mut self) -> Self::P { Self { ..*self } } -} \ No newline at end of file +} diff --git a/src/mcpwm/timer_connection.rs b/src/mcpwm/timer_connection.rs index ee937b1d929..b10e0dc9ed3 100644 --- a/src/mcpwm/timer_connection.rs +++ b/src/mcpwm/timer_connection.rs @@ -4,7 +4,7 @@ use super::{ operator::{ NoOperator, OperatorConfig, OptionalOperator, OPERATOR, }, - timer::{Timer, TIMER}, + timer::{Timer, TIMER}, Operator, }; // TODO: How do we want fault module to fit into this? @@ -40,7 +40,7 @@ impl TimerConnection, O1: OptionalOperator<1, G>, O2: OptionalOperator<2, G>> TimerConnection { - fn split(&mut self) -> (&mut Timer, &mut O0, &mut O1, &mut O2) { + pub fn split(&mut self) -> (&mut Timer, &mut O0, &mut O1, &mut O2) { ( &mut self.timer, &mut self.operator0, @@ -51,13 +51,13 @@ impl, O1: OptionalOperator<1, } // TODO: Do something more builder-pattern like for making the operator? impl, O2: OptionalOperator<2, G>> TimerConnection { - fn attatch_operator0( - mut self, + pub fn attatch_operator0( + self, operator_handle: OPERATOR<0, G>, operator_cfg: OperatorConfig, pin_a: PA, pin_b: PB, - ) -> TimerConnection, O1, O2> { + ) -> TimerConnection, O1, O2> { let operator = todo!();//self.init_and_attach_operator(operator_cfg, pin_a, pin_b); TimerConnection { timer: self.timer, @@ -69,13 +69,13 @@ impl, O2: OptionalOperator<2, } impl, O2: OptionalOperator<2, G>> TimerConnection { - fn attatch_operator1( - mut self, + pub fn attatch_operator1( + self, operator_handle: OPERATOR<1, G>, operator_cfg: OperatorConfig, pin_a: PA, pin_b: PB, - ) -> TimerConnection, O2> { + ) -> TimerConnection, O2> { let operator = todo!();//self.init_and_attach_operator(operator_cfg, pin_a, pin_b); TimerConnection { timer: self.timer, @@ -87,13 +87,13 @@ impl, O2: OptionalOperator<2, } impl, O1: OptionalOperator<1, G>> TimerConnection { - fn attatch_operator2( - mut self, + pub fn attatch_operator2( + self, operator_handle: OPERATOR<2, G>, operator_cfg: OperatorConfig, pin_a: PA, pin_b: PB, - ) -> TimerConnection> { + ) -> TimerConnection> { let operator = todo!();//self.init_and_attach_operator(operator_cfg, pin_a, pin_b); TimerConnection { timer: self.timer, diff --git a/src/peripherals.rs b/src/peripherals.rs index 21d6c37c39b..fb7c8d5e996 100644 --- a/src/peripherals.rs +++ b/src/peripherals.rs @@ -57,9 +57,9 @@ pub struct Peripherals { #[cfg(not(feature = "riscv-ulp-hal"))] pub ledc: ledc::LEDC, #[cfg(all(any(esp32, esp32s3), not(feature = "riscv-ulp-hal")))] - pub mcpwm0: mcpwm::MCPWM0, + pub mcpwm0: mcpwm::MCPWM::, #[cfg(all(any(esp32, esp32s3), not(feature = "riscv-ulp-hal")))] - pub mcpwm1: mcpwm::MCPWM1, + pub mcpwm1: mcpwm::MCPWM::, #[cfg(not(feature = "riscv-ulp-hal"))] pub rmt: rmt::RMT, #[cfg(all( @@ -169,9 +169,9 @@ impl Peripherals { #[cfg(not(feature = "riscv-ulp-hal"))] ledc: ledc::LEDC::new(), #[cfg(any(esp32, esp32s3))] - mcpwm0: mcpwm::MCPWM0::new(), + mcpwm0: mcpwm::MCPWM::::new(), #[cfg(any(esp32, esp32s3))] - mcpwm1: mcpwm::MCPWM1::new(), + mcpwm1: mcpwm::MCPWM::::new(), #[cfg(not(feature = "riscv-ulp-hal"))] rmt: rmt::RMT::new(), #[cfg(all( From 451dcd91a7596ae3e686c3f17c1ff9d1720da004 Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Thu, 13 Oct 2022 22:16:17 +0200 Subject: [PATCH 30/62] Add TODO comment --- src/mcpwm/operator.rs | 5 +++-- src/mcpwm/timer.rs | 2 ++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/mcpwm/operator.rs b/src/mcpwm/operator.rs index f9f0a3d7f00..02ffdf517ab 100644 --- a/src/mcpwm/operator.rs +++ b/src/mcpwm/operator.rs @@ -66,8 +66,9 @@ where todo!() } - // TODO: make sure the peak related description is accurate /// Set duty as in the range 0 to timers peak value + /// + /// NOTE: The compare value shouldn’t exceed timer’s count peak, otherwise, the compare event will never got triggered. pub fn set_duty_a(&mut self, duty: Duty) -> Result<(), EspError> { todo!() } @@ -84,7 +85,7 @@ where todo!() } - /// Set duty as percentage between 0.0 and 100.0 for output B + /// NOTE: The compare value shouldn’t exceed timer’s count peak, otherwise, the compare event will never got triggered. pub fn set_duty_b(&mut self, duty: Duty) -> Result<(), EspError> { todo!() } diff --git a/src/mcpwm/timer.rs b/src/mcpwm/timer.rs index bb1b1d419ca..736046bda55 100644 --- a/src/mcpwm/timer.rs +++ b/src/mcpwm/timer.rs @@ -115,6 +115,7 @@ impl Timer { } } + // TODO: make sure this description is accurate /// Get number of ticks per period /// /// Use this when working with the frequency or the period @@ -126,6 +127,7 @@ impl Timer { self.period_ticks } + // TODO: make sure this description is accurate /// This is the maximum value that the comparator will see /// /// Use this working with the duty From 952a36f807c3062ae4dc9882eb248cff3115f03a Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Thu, 13 Oct 2022 22:17:02 +0200 Subject: [PATCH 31/62] fmt --- examples/mcpwm-deadtime.rs | 106 ++++++++++++++++++++++++++++++++++ examples/mcpwm-simple.rs | 15 +++-- src/mcpwm/mod.rs | 24 +++----- src/mcpwm/operator.rs | 2 +- src/mcpwm/timer.rs | 14 ++--- src/mcpwm/timer_connection.rs | 36 +++++++----- src/peripherals.rs | 4 +- 7 files changed, 153 insertions(+), 48 deletions(-) create mode 100644 examples/mcpwm-deadtime.rs diff --git a/examples/mcpwm-deadtime.rs b/examples/mcpwm-deadtime.rs new file mode 100644 index 00000000000..fd40145497c --- /dev/null +++ b/examples/mcpwm-deadtime.rs @@ -0,0 +1,106 @@ +// TODO: Update those graphs with measured results + +/// # x = 10 +/// +/// . . +/// . . +/// .------. .------. +/// | | | | +/// pin4 | | | | +/// | | | | +/// ----- -------------------------- -------------------------- +/// . . +/// . . +/// .------------------------. .------------------------. +/// | | | | +/// pin5 | | | | +/// | | | | +/// ----- -------- -------- +/// . . +/// +/// +/// # x = 50 +/// . . +/// . . +/// .---------------. .---------------. +/// | | | | +/// pin4 | | | | +/// | | | | +/// ----- ----------------- ----------------- +/// . . +/// . . +/// .---------------. .---------------. +/// | | | | +/// pin5 | | | | +/// | | | | +/// ----- ----------------- ----------------- +/// . . +/// +/// +/// # x = 90 +/// . . +/// . . +/// .------------------------. .------------------------. +/// | | | | +/// pin4 | | | | +/// | | | | +/// ----- -------- -------- +/// . . +/// . . +/// .------. .------. +/// | | | | +/// pin5 | | | | +/// | | | | +/// ----- -------------------------- -------------------------- +/// . . + +#[cfg(any(esp32, esp32s3))] +fn main() -> anyhow::Result<()> { + use embedded_hal::delay::blocking::DelayUs; + + use esp_idf_hal::delay::FreeRtos; + use esp_idf_hal::mcpwm::{DeadtimeConfig, Mcpwm, Operator, OperatorConfig}; + use esp_idf_hal::prelude::Peripherals; + use esp_idf_hal::units::FromValueType; + + esp_idf_sys::link_patches(); + + println!("Configuring MCPWM"); + + let peripherals = Peripherals::take().unwrap(); + let config = OperatorConfig::default().frequency(1.kHz()).deadtime( + DeadtimeConfig::ActiveHighComplement { + rising_edge_delay: 1500, // 1500*100ns=150us or 15% of the period + falling_edge_delay: 3000, // 3000*100ns=300us or 30% of the period + }, + ); + let mcpwm = Mcpwm::new(peripherals.mcpwm0.mcpwm)?; + let mut operator = Operator::new( + peripherals.mcpwm0.operator0, + &mcpwm, + &config, + peripherals.pins.gpio4, + peripherals.pins.gpio5, + )?; + + println!("Starting duty-cycle loop"); + + for x in (0..10000u16).cycle() { + let duty = f32::from(x) * 0.01; + + if x % 100 == 0 { + println!("Duty {}%", duty); + } + + operator.set_duty_a(duty)?; + operator.set_duty_b(0.0)?; + FreeRtos.delay_ms(10)?; + } + + unreachable!() +} + +#[cfg(not(any(esp32, esp32s3)))] +fn main() { + esp_idf_sys::link_patches(); +} diff --git a/examples/mcpwm-simple.rs b/examples/mcpwm-simple.rs index 31a50eaa775..91057fc47a8 100644 --- a/examples/mcpwm-simple.rs +++ b/examples/mcpwm-simple.rs @@ -61,7 +61,7 @@ fn main() -> anyhow::Result<()> { use embedded_hal::delay::blocking::DelayUs; use esp_idf_hal::delay::FreeRtos; - use esp_idf_hal::mcpwm::{Timer, TimerConfig, Operator, OperatorConfig}; + use esp_idf_hal::mcpwm::{Operator, OperatorConfig, Timer, TimerConfig}; use esp_idf_hal::prelude::Peripherals; use esp_idf_hal::units::FromValueType; @@ -74,13 +74,12 @@ fn main() -> anyhow::Result<()> { let operator_config = OperatorConfig::default(); let timer = Timer::new(peripherals.mcpwm0.timer0, timer_config); - let mut timer = timer.into_connection() - .attatch_operator0( - peripherals.mcpwm0.operator0, - operator_config, - peripherals.pins.gpio4, - peripherals.pins.gpio5, - ); + let mut timer = timer.into_connection().attatch_operator0( + peripherals.mcpwm0.operator0, + operator_config, + peripherals.pins.gpio4, + peripherals.pins.gpio5, + ); let (timer, operator, _, _) = timer.split(); diff --git a/src/mcpwm/mod.rs b/src/mcpwm/mod.rs index 7a61e7e1680..fd59c47eb58 100644 --- a/src/mcpwm/mod.rs +++ b/src/mcpwm/mod.rs @@ -28,7 +28,7 @@ //! let timer_config = TimerConfig::default().frequency(25.kHz()); //! let operator_config = OperatorConfig::default(); //! let timer = Mcpwm::new(peripherals.mcpwm0.timer, timer_config)?; -//! +//! //! let timer = timer.into_connection() //! .attatch_operator0( //! peripherals.mcpwm0.operator0, @@ -36,7 +36,7 @@ //! peripherals.pins.gpio4, //! peripherals.pins.gpio5, //! )?; -//! +//! //! let (timer, operator, _, _) = timer.split(); //! //! println!("Starting duty-cycle loop"); @@ -51,26 +51,16 @@ //! //! See the `examples/` folder of this repository for more. -mod timer; mod operator; +mod timer; mod timer_connection; use core::ffi; pub use self::{ - operator::{ - OPERATOR, - Operator, - OperatorConfig, - }, - timer::{ - TIMER, - Timer, - TimerConfig - }, - timer_connection::{ - TimerConnection - } + operator::{Operator, OperatorConfig, OPERATOR}, + timer::{Timer, TimerConfig, TIMER}, + timer_connection::TimerConnection, }; // MCPWM clock source frequency for ESP32 and ESP32-s3 @@ -128,4 +118,4 @@ impl Group for Group0 { impl Group for Group1 { const ID: ffi::c_int = 1; -} \ No newline at end of file +} diff --git a/src/mcpwm/operator.rs b/src/mcpwm/operator.rs index 02ffdf517ab..5be42613b5b 100644 --- a/src/mcpwm/operator.rs +++ b/src/mcpwm/operator.rs @@ -67,7 +67,7 @@ where } /// Set duty as in the range 0 to timers peak value - /// + /// /// NOTE: The compare value shouldn’t exceed timer’s count peak, otherwise, the compare event will never got triggered. pub fn set_duty_a(&mut self, duty: Duty) -> Result<(), EspError> { todo!() diff --git a/src/mcpwm/timer.rs b/src/mcpwm/timer.rs index 736046bda55..e978a7835f5 100644 --- a/src/mcpwm/timer.rs +++ b/src/mcpwm/timer.rs @@ -70,12 +70,12 @@ pub struct Timer { handle: mcpwm_timer_handle_t, _timer: TIMER, /// Number of ticks within a period - /// + /// /// See `Self::get_period_ticks` for more info period_ticks: u32, /// This is the maximum value that the comparator will see - /// + /// /// See `Self::get_period_peak` for more info period_peak: u16, } @@ -111,15 +111,15 @@ impl Timer { handle, _timer: timer, period_ticks: cfg.period_ticks, - period_peak + period_peak, } } // TODO: make sure this description is accurate /// Get number of ticks per period - /// + /// /// Use this when working with the frequency or the period - /// + /// /// NOTE: This will be the same as `Self::get_period_peak` for all `CounterMode` except for /// `CounterMode::UpDown` where the period will be twice as large as the peak value since /// the timer will count from zero to peak and then down to zero again @@ -129,9 +129,9 @@ impl Timer { // TODO: make sure this description is accurate /// This is the maximum value that the comparator will see - /// + /// /// Use this working with the duty - /// + /// /// NOTE: This will not be the same as `Self::get_period_ticks` when using `CounterMode::UpDown` /// See `Self::get_period_ticks` for more info pub fn get_period_peak(&self) -> u16 { diff --git a/src/mcpwm/timer_connection.rs b/src/mcpwm/timer_connection.rs index b10e0dc9ed3..d1576116c19 100644 --- a/src/mcpwm/timer_connection.rs +++ b/src/mcpwm/timer_connection.rs @@ -1,10 +1,9 @@ use crate::mcpwm::Group; use super::{ - operator::{ - NoOperator, OperatorConfig, OptionalOperator, OPERATOR, - }, - timer::{Timer, TIMER}, Operator, + operator::{NoOperator, OperatorConfig, OptionalOperator, OPERATOR}, + timer::{Timer, TIMER}, + Operator, }; // TODO: How do we want fault module to fit into this? @@ -37,8 +36,13 @@ impl TimerConnection, O1: OptionalOperator<1, G>, O2: OptionalOperator<2, G>> - TimerConnection +impl< + const N: u8, + G: Group, + O0: OptionalOperator<0, G>, + O1: OptionalOperator<1, G>, + O2: OptionalOperator<2, G>, + > TimerConnection { pub fn split(&mut self) -> (&mut Timer, &mut O0, &mut O1, &mut O2) { ( @@ -50,7 +54,9 @@ impl, O1: OptionalOperator<1, } } // TODO: Do something more builder-pattern like for making the operator? -impl, O2: OptionalOperator<2, G>> TimerConnection { +impl, O2: OptionalOperator<2, G>> + TimerConnection +{ pub fn attatch_operator0( self, operator_handle: OPERATOR<0, G>, @@ -58,7 +64,7 @@ impl, O2: OptionalOperator<2, pin_a: PA, pin_b: PB, ) -> TimerConnection, O1, O2> { - let operator = todo!();//self.init_and_attach_operator(operator_cfg, pin_a, pin_b); + let operator = todo!(); //self.init_and_attach_operator(operator_cfg, pin_a, pin_b); TimerConnection { timer: self.timer, operator0: operator, @@ -68,7 +74,9 @@ impl, O2: OptionalOperator<2, } } -impl, O2: OptionalOperator<2, G>> TimerConnection { +impl, O2: OptionalOperator<2, G>> + TimerConnection +{ pub fn attatch_operator1( self, operator_handle: OPERATOR<1, G>, @@ -76,7 +84,7 @@ impl, O2: OptionalOperator<2, pin_a: PA, pin_b: PB, ) -> TimerConnection, O2> { - let operator = todo!();//self.init_and_attach_operator(operator_cfg, pin_a, pin_b); + let operator = todo!(); //self.init_and_attach_operator(operator_cfg, pin_a, pin_b); TimerConnection { timer: self.timer, operator0: self.operator0, @@ -86,7 +94,9 @@ impl, O2: OptionalOperator<2, } } -impl, O1: OptionalOperator<1, G>> TimerConnection { +impl, O1: OptionalOperator<1, G>> + TimerConnection +{ pub fn attatch_operator2( self, operator_handle: OPERATOR<2, G>, @@ -94,7 +104,7 @@ impl, O1: OptionalOperator<1, pin_a: PA, pin_b: PB, ) -> TimerConnection> { - let operator = todo!();//self.init_and_attach_operator(operator_cfg, pin_a, pin_b); + let operator = todo!(); //self.init_and_attach_operator(operator_cfg, pin_a, pin_b); TimerConnection { timer: self.timer, operator0: self.operator0, @@ -108,4 +118,4 @@ pub struct NoPin; pub trait OptionalOutputPin {} -impl OptionalOutputPin for P {} \ No newline at end of file +impl OptionalOutputPin for P {} diff --git a/src/peripherals.rs b/src/peripherals.rs index fb7c8d5e996..b6ec11ec654 100644 --- a/src/peripherals.rs +++ b/src/peripherals.rs @@ -57,9 +57,9 @@ pub struct Peripherals { #[cfg(not(feature = "riscv-ulp-hal"))] pub ledc: ledc::LEDC, #[cfg(all(any(esp32, esp32s3), not(feature = "riscv-ulp-hal")))] - pub mcpwm0: mcpwm::MCPWM::, + pub mcpwm0: mcpwm::MCPWM, #[cfg(all(any(esp32, esp32s3), not(feature = "riscv-ulp-hal")))] - pub mcpwm1: mcpwm::MCPWM::, + pub mcpwm1: mcpwm::MCPWM, #[cfg(not(feature = "riscv-ulp-hal"))] pub rmt: rmt::RMT, #[cfg(all( From 9ec6458feff781dd48896faae089f63804b9bf99 Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Thu, 13 Oct 2022 22:21:29 +0200 Subject: [PATCH 32/62] Fix some, far from all, warnings --- src/mcpwm/mod.rs | 9 --------- src/mcpwm/timer.rs | 8 ++++---- src/mcpwm/timer_connection.rs | 4 +++- 3 files changed, 7 insertions(+), 14 deletions(-) diff --git a/src/mcpwm/mod.rs b/src/mcpwm/mod.rs index fd59c47eb58..c8b2ee6904d 100644 --- a/src/mcpwm/mod.rs +++ b/src/mcpwm/mod.rs @@ -63,15 +63,6 @@ pub use self::{ timer_connection::TimerConnection, }; -// MCPWM clock source frequency for ESP32 and ESP32-s3 -const MCPWM_CLOCK_SOURCE_FREQUENCY: u32 = 160_000_000; - -// Max PWM timer prescaler -const MAX_PWM_TIMER_PRESCALE: u32 = 0x1_00; - -// Max PWM timer period -const MAX_PWM_TIMER_PERIOD: u32 = 0x1_00_00; - /// The Motor Control Pulse Width Modulator peripheral pub struct MCPWM { pub timer0: TIMER<0, G>, diff --git a/src/mcpwm/timer.rs b/src/mcpwm/timer.rs index e978a7835f5..c22e8d976d4 100644 --- a/src/mcpwm/timer.rs +++ b/src/mcpwm/timer.rs @@ -4,7 +4,7 @@ use std::ptr; use esp_idf_sys::{ esp, mcpwm_del_timer, mcpwm_new_timer, mcpwm_timer_config_t, mcpwm_timer_enable, - mcpwm_timer_handle_t, EspError, + mcpwm_timer_handle_t, }; use crate::mcpwm::Group; @@ -14,7 +14,7 @@ use crate::units::Hertz; use super::operator::NoOperator; use super::timer_connection::TimerConnection; -#[derive(Clone, Copy, Debug, PartialEq)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct TimerConfig { frequency: Hertz, //resolution: Hertz, @@ -92,7 +92,7 @@ impl Timer { }; let mut handle: mcpwm_timer_handle_t = ptr::null_mut(); unsafe { - esp!(mcpwm_new_timer(&cfg, &mut handle)); + esp!(mcpwm_new_timer(&cfg, &mut handle)).unwrap(); } // TODO: note that this has to be called before mcpwm_timer_enable // mcpwm_timer_register_event_callbacks() @@ -173,7 +173,7 @@ impl Drop for Timer { /// Counter mode for operator's timer for generating PWM signal // TODO: For UpDown, frequency is half of MCPWM frequency set -#[derive(Clone, Copy, Debug, PartialEq)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum CountMode { /// Timer is frozen or paused //#[cfg(not(esp_idf_version = "4.3"))] diff --git a/src/mcpwm/timer_connection.rs b/src/mcpwm/timer_connection.rs index d1576116c19..465c4699f29 100644 --- a/src/mcpwm/timer_connection.rs +++ b/src/mcpwm/timer_connection.rs @@ -2,7 +2,7 @@ use crate::mcpwm::Group; use super::{ operator::{NoOperator, OperatorConfig, OptionalOperator, OPERATOR}, - timer::{Timer, TIMER}, + timer::Timer, Operator, }; @@ -114,8 +114,10 @@ impl, O1: OptionalOperator<1, } } +// TODO: Should this be moved somewhere else? pub struct NoPin; +// TODO: Should this be moved somewhere else? pub trait OptionalOutputPin {} impl OptionalOutputPin for P {} From ca5685e2f894fbde237c9a08279f665cb91ee750 Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Mon, 14 Nov 2022 20:17:10 +0100 Subject: [PATCH 33/62] Unchecked stuff --- .cargo/config.toml | 11 +- Cargo.toml | 3 + src/mcpwm/comparator.rs | 28 ++++ src/mcpwm/generator.rs | 121 +++++++++++++++ src/mcpwm/mod.rs | 55 +++++-- src/mcpwm/operator.rs | 326 +++++++++++++++++++++++----------------- 6 files changed, 393 insertions(+), 151 deletions(-) create mode 100644 src/mcpwm/comparator.rs create mode 100644 src/mcpwm/generator.rs diff --git a/.cargo/config.toml b/.cargo/config.toml index f6ec1ef65fc..532d9c29f58 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,22 +1,31 @@ [target.xtensa-esp32-espidf] linker = "ldproxy" +rustflags = ["--cfg", "espidf_time64"] [target.xtensa-esp32s2-espidf] linker = "ldproxy" +rustflags = ["--cfg", "espidf_time64"] [target.xtensa-esp32s3-espidf] linker = "ldproxy" +rustflags = ["--cfg", "espidf_time64"] [target.riscv32imc-esp-espidf] linker = "ldproxy" # Future - necessary for the experimental "native build" of esp-idf-sys with ESP32C3 # See also https://github.com/ivmarkov/embuild/issues/16 -rustflags = ["-C", "default-linker-libraries"] +#rustflags = ["-C", "default-linker-libraries"] +rustflags = ["--cfg", "espidf_time64", "-C", "default-linker-libraries"] [env] ESP_IDF_SDKCONFIG_DEFAULTS = ".github/configs/sdkconfig.defaults" +#ESP_IDF_VERSION = { value = "branch:release/v5.0" } +ESP_IDF_VERSION = { value = "master" } [unstable] build-std = ["std", "panic_abort"] build-std-features = ["panic_immediate_abort"] + +[build] +target = "xtensa-esp32-espidf" diff --git a/Cargo.toml b/Cargo.toml index 0ce5171d30f..8f0820f0cca 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,6 +13,9 @@ readme = "README.md" documentation = "https://esp-rs.github.io/esp-idf-hal/" rust-version = "1.61" +[patch.crates-io] +esp-idf-sys = { git = "https://github.com/esp-rs/esp-idf-sys", branch = "master" } + [features] default = ["std", "alloc", "esp-idf-sys"] std = ["alloc", "esp-idf-sys/std", "edge-executor?/std"] diff --git a/src/mcpwm/comparator.rs b/src/mcpwm/comparator.rs new file mode 100644 index 00000000000..e1ed13e3edd --- /dev/null +++ b/src/mcpwm/comparator.rs @@ -0,0 +1,28 @@ + +trait ComparatorChannel{} + +pub struct CmpX; +impl ComparatorChannel for CmpX {} + +pub struct CmpY; +impl ComparatorChannel for CmpY {} + +trait OptionalCmp { + type OnMatchCfg: Into; +} + +impl OptionalCmp for Comparator { + type OnMatchCfg = super::generator::CountingDirection; +} + +struct NoCmp; +impl OptionalCmp for NoCmp { + type OnMatchCfg = super::generator::NoCmpMatchConfig; +} + +pub struct Comparator(pub(crate)mcpwm_cmpr_handle_t); + +#[derive(Debug, Clone, Copy)] +pub struct ComparatorConfig { + _: (), +} \ No newline at end of file diff --git a/src/mcpwm/generator.rs b/src/mcpwm/generator.rs new file mode 100644 index 00000000000..5914adafc7f --- /dev/null +++ b/src/mcpwm/generator.rs @@ -0,0 +1,121 @@ + +trait GeneratorChannel{ + const IS_A: bool; +} + +pub struct GenA; +impl GeneratorChannel for GenA { + const IS_A: bool = true; +} + +pub struct GenB; +impl GeneratorChannel for GenB { + const IS_A: bool = false; +} + +pub struct Generator(pub(crate)mcpwm_gen_handle_t); + +#[derive(Default, Clone)] +pub struct GeneratorConfig { + _channel: PhantomData, + on_matches_cmp_x: CMP_X, + on_matches_cmp_y: CMP_Y, + on_is_empty: CountingDirection, + on_is_full: CountingDirection +} + +impl GeneratorConfig { + fn empty() -> Self { + GeneratorConfig { + _channel: PhantomData, + on_matches_cmp_x: NoCmpMatchConfig, + on_matches_cmp_y: NoCmpMatchConfig, + on_is_empty: CountingDirection { + counting_up: GeneratorAction::Nothing, + counting_down: GeneratorAction::Nothing, + }, + on_is_full: CountingDirection { + counting_up: GeneratorAction::Nothing, + counting_down: GeneratorAction::Nothing, + } + } + } +} + +impl Into for NoCmpMatchConfig { + fn into(self) -> CountingDirection { + CountingDirection { + counting_up: GeneratorAction::Nothing, + counting_down: GeneratorAction::Nothing, + } + } +} + +impl Into> for GeneratorConfig + where + CMP_X: Into, + CMP_Y: Into +{ + fn into(self) -> GeneratorConfig { + GeneratorConfig { + _channel, + on_matches_cmp_x, + on_matches_cmp_y, + on_is_empty, + on_is_full + } = self; + + GeneratorConfig { + _channel, + on_matches_cmp_x: on_matches_cmp_x.into(), + on_matches_cmp_y: on_matches_cmp_y.into(), + on_is_empty, + on_is_full + } + } + +} + +pub struct NoCmpMatchConfig; + +// TODO: Come up with better name +#[derive(Clone, Copy)] +pub struct CountingDirection { + counting_up: GeneratorAction, + counting_down: GeneratorAction +} + +pub enum GeneratorAction { + Nothing, + SetLow, + SetHigh, + Toggle +} + +impl DutyMode { + fn into_duty_cfg(self) -> GeneratorConfig { + match val { + DutyMode::ActiveHigh => { + let mut duty_config = GeneratorConfig::empty(); + duty_config.on_is_empty.counting_up = GeneratorAction::SetHigh; + if G::IS_A { + duty_config.on_matches_cmp_a.counting_up = GeneratorAction::SetLow; + } else { + duty_config.on_matches_cmp_b.counting_up = GeneratorAction::SetLow; + } + duty_config + }, + DutyMode::ActiveLow => { + let mut duty_config = GeneratorConfig::empty(); + duty_config.on_is_empty.counting_up = GeneratorAction::SetLow; + if G::IS_A { + duty_config.on_matches_cmp_a.counting_up = GeneratorAction::SetHigh; + } else { + duty_config.on_matches_cmp_b.counting_up = GeneratorAction::SetHigh; + } + + duty_config + }, + } + } +} \ No newline at end of file diff --git a/src/mcpwm/mod.rs b/src/mcpwm/mod.rs index c8b2ee6904d..1669d00ae1e 100644 --- a/src/mcpwm/mod.rs +++ b/src/mcpwm/mod.rs @@ -4,19 +4,44 @@ //! peripheral](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/mcpwm.html) //! //! ``` -//! --------------- --------------- -//! | MCPWM Unit 0 | | MCPWM Unit 1 | -//! | ------------ | | ------------ | -//! | | | | -//! | OPERATOR 0 |--> A | OPERATOR 0 |--> A -//! | |--> B | |--> B -//! | | | | -//! | OPERATOR 1 |--> A | OPERATOR 1 |--> A -//! | |--> B | |--> B -//! | | | | -//! | OPERATOR 2 |--> A | OPERATOR 2 |--> A -//! | |--> B | |--> B -//! --------------- --------------- +//! -------------------------------------------------------------------------- +//! | MCPWM Group N | +//! | ------------------------- | +//! | | +//! | --------- ------------------------------------------------- | +//! | | Timer 0 |-------* | OPERATOR 0 | | +//! | --------- | | --------------- | | +//! | --------- | | ------------- | | +//! | | Timer 1 |----* | | *----------------------->| | | | +//! | --------- | | | | *------>| GENERATOR 0 |-|-|--> To Output pin +//! | --------- | *--|-|\ | | *---->| | | | +//! | | Timer 2 |-* | | | | \ *->Comparator 0>-* | ------------- | | +//! | --------- | *-----|-| |>-| | | | | +//! | | | | | | / *->Comparator 1>-|-* ------------- | | +//! | *--------|-|/ | *-|---->| | |-|--> To Output pin +//! | | | | | | *---->| GENERATOR 1 | | | +//! | | | | | *----------------------->| | | | +//! | | | | | ------------- | | +//! | | | | ------------------------------------------------- | +//! | | | | | +//! | | | | ------------------------------------------------- | +//! | | | *--| OPERATOR 1 | | +//! | | | | | --------------- |-|--> To Output pin +//! | | *-----| | | +//! | | | | | ... |-|--> To Output pin +//! | *--------| | | +//! | | | | ------------------------------------------------- | +//! | | | | | +//! | | | | ------------------------------------------------- | +//! | | | *--| OPERATOR 2 | | +//! | | | | | --------------- |-|--> To Output pin +//! | | *-----| | | +//! | | | | | ... |-|--> To Output pin +//! | *--------| | | +//! | ------------------------------------------------- | +//! | | +//! | | +//! ------------------------------------------------------------------------- //! ``` //! //! # Example @@ -52,6 +77,8 @@ //! See the `examples/` folder of this repository for more. mod operator; +mod generator; +mod comparator; mod timer; mod timer_connection; @@ -93,6 +120,7 @@ impl MCPWM { #[derive(Default)] pub struct Group0; +#[cfg(not(esp32c6))] #[derive(Default)] pub struct Group1; @@ -107,6 +135,7 @@ impl Group for Group0 { const ID: ffi::c_int = 0; } +#[cfg(not(esp32c6))] impl Group for Group1 { const ID: ffi::c_int = 1; } diff --git a/src/mcpwm/operator.rs b/src/mcpwm/operator.rs index 5be42613b5b..6ffa1d68a21 100644 --- a/src/mcpwm/operator.rs +++ b/src/mcpwm/operator.rs @@ -45,180 +45,232 @@ impl crate::peripheral::Peripheral for OPERATOR { /// /// Every Motor Control module has three operators. Every operator can generate two output signals called A and B. /// A and B share the same timer and thus frequency and phase but can have induvidual duty set. -pub struct Operator { - _group: PhantomData, - handle: mcpwm_oper_handle_t, +pub struct Operator< + const N: u8, + G: Group, + CMP_X: OptionalCmp, + CMP_Y: OptionalCmp, + GEN_A: OptionalGenerator, + GEN_B: OptionalGenerator, +> { _instance: OPERATOR, + handle: mcpwm_oper_handle_t, + + comparator_x: CMP_X, // SOC_MCPWM_COMPARATORS_PER_OPERATOR is 2 for ESP32 and ESP32-S3 + comparator_y: CMP_Y, - _pin_a: PA, - _pin_b: PB, + generator_a: GEN_A, // One generator per pin, with a maximum of two generators per Operator + generator_b: GEN_B, //deadtime: D } -impl Operator -where - G: Group, - PA: OutputPin, - PB: OptionalOutputPin, -{ - /// Get duty as percentage between 0.0 and 100.0 for output A - pub fn get_duty_a(&self) -> Duty { - todo!() +impl Operator { + pub fn default(instance: OPERATOR) -> _ { + Operator::custom(instance) + .cmp_x(Default::default()) + .cmp_y(Default::default()) + .gen_a(Default::default()) + .gen_b(Default::default()) } - /// Set duty as in the range 0 to timers peak value - /// - /// NOTE: The compare value shouldn’t exceed timer’s count peak, otherwise, the compare event will never got triggered. - pub fn set_duty_a(&mut self, duty: Duty) -> Result<(), EspError> { - todo!() + pub fn custom(instance: OPERATOR) -> Self { + Operator { + _instance: instance, + handle, + comparator_x: NoCmp, // SOC_MCPWM_COMPARATORS_PER_OPERATOR is 2 for ESP32 and ESP32-S3 + comparator_y: NoCmp, + + generator_a: NoGen, // One generator per pin, with a maximum of two generators per Operator + generator_b: NoGen, + } } } -impl Operator -where - G: Group, - PA: OptionalOutputPin, - PB: OutputPin, +impl< + const N: u8, + G: Group, + > Operator { - /// Get duty as percentage between 0.0 and 100.0 for output B - pub fn get_duty_b(&self) -> Duty { - todo!() - } + fn cmp_x(self, config: ComparatorConfig) -> Operator { + let cfg: mcpwm_comparator_config_t = todo!(); + let mut cmp = ptr::null_mut(); + esp!(mcpwm_new_comparator(self.handle, &cfg, &mut cmp)).unwrap(); - /// NOTE: The compare value shouldn’t exceed timer’s count peak, otherwise, the compare event will never got triggered. - pub fn set_duty_b(&mut self, duty: Duty) -> Result<(), EspError> { - todo!() + let comparator_x = Comparator(cmp); + + Operator { + _instance: instance, + handle, + comparator_x, + comparator_y: self.comparator_y, + + generator_a: self.generator_a, + generator_b: self.generator_b, + } } } -/// Operator configuration -pub struct OperatorConfig { - duty_a: Duty, - duty_b: Duty, +impl< + const N: u8, + G: Group, + > Operator +{ + fn cmp_y(self, config: ComparatorConfig) -> Operator { + let cfg: mcpwm_comparator_config_t = todo!(); + let mut cmp = ptr::null_mut(); + esp!(mcpwm_new_comparator(self.handle, &cfg, &mut cmp)).unwrap(); - duty_mode: DutyMode, - //deadtime: Option, -} + let comparator_y = Comparator(cmp); -impl OperatorConfig { - /// Set initual duty cycle for output A as percentage - #[must_use] - pub fn duty_a(mut self, duty: Duty) -> Self { - self.duty_a = duty; - self - } + Operator { + _instance: instance, + handle, + comparator_x: self.comparator_x, + comparator_y, - /// Set initual duty cycle for output B as percentage - #[must_use] - pub fn duty_b(mut self, duty: Duty) -> Self { - self.duty_b = duty; - self + generator_a: self.generator_a, + generator_b: self.generator_b, + } } +} - /// Specify what duty mode to use for the operator. - #[must_use] - pub fn duty_mode(mut self, duty_mode: DutyMode) -> Self { - self.duty_mode = duty_mode; - self - } +// TODO: Make sure that a generator config can only refer to comparators that are assigned to the operator +// TODO: Is there any point in letting the user provide the comparators or should two (the only two available +// for that operator in hardware) be automatically assigned in `Operator::new`? - /*#[must_use] - pub fn deadtime(mut self, deadtime: impl Into>) -> Self { - self.deadtime = deadtime.into(); - self - }*/ -} -impl Default for OperatorConfig { - fn default() -> Self { - Self { - duty_a: 0, - duty_b: 0, +impl< + const N: u8, + G: Group, + GEN_B: OptionalGen, + CMP_X: OptionalCmp, + CMP_Y: OptionalCmp, +> Operator +{ + fn gen_a(self, config: Into>) -> Operator<> { + let cfg: mcpwm_generator_config_t = todo!(); + let mut gen = ptr::null_mut(); + esp!(mcpwm_new_generator(self.handle, &cfg, &gen)).unwrap(); - duty_mode: DutyMode::ActiveHigh, - //deadtime: None, + esp_err_t mcpwm_generator_set_actions_on_timer_event(mcpwm_gen_handle_t gen, mcpwm_gen_timer_event_action_t ev_act, ...); + esp_err_t mcpwm_generator_set_actions_on_compare_event(mcpwm_gen_handle_t gen, mcpwm_gen_compare_event_action_t ev_act, ...) + + let generator_a = Generator(gen); + + Operator { + _instance: instance, + handle, + comparator_x: self.comparator_x, + comparator_y: self.comparator_y, + + generator_a, + generator_b: self.generator_b, } } } -/// Duty modes for operator -#[derive(Clone, Copy, Debug)] -pub enum DutyMode { - /// Active high - /// - /// Setting duty = 100% will result in a constant high output - /// Setting duty = 0% will result in a constant low output - ActiveHigh, +impl< + const N: u8, + G: Group, + GEN_A: OptionalGen, + CMP_X: OptionalCmp, + CMP_Y: OptionalCmp, +> Operator +{ + fn gen_b(self, config: Into>) -> Operator<> { + let cfg: mcpwm_generator_config_t = todo!(); + let mut gen = ptr::null_mut(); + esp!(mcpwm_new_generator(self.handle, &cfg, &gen)).unwrap(); - /// Active low - /// - /// Setting duty = 100% will result in a constant low output - /// Setting duty = 0% will result in a constant high output - ActiveLow, -} + esp_err_t mcpwm_generator_set_actions_on_timer_event(mcpwm_gen_handle_t gen, mcpwm_gen_timer_event_action_t ev_act, ...); + esp_err_t mcpwm_generator_set_actions_on_compare_event(mcpwm_gen_handle_t gen, mcpwm_gen_compare_event_action_t ev_act, ...) -pub trait OptionalOperator {} -impl OptionalOperator - for Operator -{ + let generator_b = Generator(gen); + + Operator { + _instance: instance, + handle, + comparator_x: self.comparator_x, + comparator_y: self.comparator_y, + + generator_a: self.generator_a, + generator_b, + } + } } -pub struct NoOperator; -impl OptionalOperator for NoOperator {} +impl Operator +where + G: Group, + GEN_A: OptionalGen, + GEN_B: OptionalGen, + CMP_Y: OptionalCmp, +{ -/* + // TODO: Note that this is the comparator we are affecting, not the generator. Generator A may not necessarily have + // anything to do with comparator A. How do we best convay that? Should we call them Generator X/Y and Comparator A/B? + // + // Again should we always provide two comparators? That would make both set/get_duty_a/b always available... Should we + // instead let the user only provide only as few or many (max 2 as of ESP32/ESP32-S3) as they want. And depending on + // expose only the corresponding set/get_duty? + // + // Once again to clarify, set_duty affects the comparator. The generators(booth or none) may then choose to use that + // event, as well as timer events, to change the level of the pin. + // + /// Get compare value, often times same as the duty for output A. + /// + /// See `Self::set_compare_value_x` for more info + pub fn get_compare_value_x(&self) -> u16 { + todo!() + } -#[derive(Default, Clone)] -struct DutyConfig { - on_matches_cmp_a: CountingDirection, - on_matches_cmp_b: CountingDirection, - on_is_empty: CountingDirection, - on_is_full: CountingDirection + /// Set compare value, often times same as the duty for output A. + /// + /// Depending on how the generators are configured this is, using the most common configuration, the duty of output A. + /// `value` is from the range 0 to timers peak value. However do note that if using a custom configuration this might + /// control something else like for example the phase. Look into Generator::TODO for more details + /// + /// TODO: what about CountMode::UpDown? + /// + /// NOTE: The compare value shouldn’t exceed timer’s count peak, otherwise, the compare event will never got triggered. + pub fn set_compare_value_x(&mut self, value: u16) -> Result<(), EspError> { + esp!(mcpwm_comparator_set_compare_value(&self.comparator_x.0, value.into())) + } } -#[derive(Default, Clone, Copy)] -struct CountingDirection { - counting_up: GeneratorAction, - counting_down: GeneratorAction -} +impl Operator +where + G: Group, + GEN_A: OptionalGen, + GEN_B: OptionalGen, + CMP_X: OptionalCmp, +{ + /// Get compare value, often times same as the duty for output B. + /// + /// See `Self::set_compare_value_x` for more info + pub fn get_compare_value_y(&self) -> u16 { + todo!() + } -enum GeneratorAction { - Nothing, - SetLow, - SetHigh, - Toggle + /// Set compare value, often times same as the duty for output A. + /// + /// Depending on how the generators are configured this is, using the most common configuration, the duty of output A. + /// `value` is from the range 0 to timers peak value. However do note that if using a custom configuration this might + /// control something else like for example the phase. Look into Generator::TODO for more details + /// + /// TODO: what about CountMode::UpDown? + /// + /// NOTE: The compare value shouldn’t exceed timer’s count peak, otherwise, the compare event will never got triggered. + pub fn set_compare_value_y(&mut self, value: u16) -> Result<(), EspError> { + esp!(mcpwm_comparator_set_compare_value(&self.comparator_x.0, value.into())) + } } -impl Default for GeneratorAction { - fn default() -> Self { - GeneratorAction::Nothing - } +pub trait OptionalOperator {} +impl OptionalOperator + for Operator +{ } -impl DutyMode { - fn into_duty_cfg(self) -> DutyConfig { - match val { - DutyMode::ActiveHigh => { - let mut duty_config: DutyConfig = Default::default(); - duty_config.on_is_empty.counting_up = GeneratorAction::SetHigh; - if G::IS_A { - duty_config.on_matches_cmp_a.counting_up = GeneratorAction::SetLow; - } else { - duty_config.on_matches_cmp_b.counting_up = GeneratorAction::SetLow; - } - duty_config - }, - DutyMode::ActiveLow => { - let mut duty_config: DutyConfig = Default::default(); - duty_config.on_is_empty.counting_up = GeneratorAction::SetLow; - if G::IS_A { - duty_config.on_matches_cmp_a.counting_up = GeneratorAction::SetHigh; - } else { - duty_config.on_matches_cmp_b.counting_up = GeneratorAction::SetHigh; - } - - duty_config - }, - } - } -} */ +pub struct NoOperator; +impl OptionalOperator for NoOperator {} \ No newline at end of file From dc0588449b43349f433225b850db17771ef90f90 Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Sun, 27 Nov 2022 18:22:21 +0100 Subject: [PATCH 34/62] Compiling again after refactor, still far from working --- examples/mcpwm-simple.rs | 8 +- src/mcpwm/comparator.rs | 29 +++- src/mcpwm/generator.rs | 174 +++++++++++--------- src/mcpwm/mod.rs | 2 +- src/mcpwm/operator.rs | 291 ++++++++++++++++++++++++---------- src/mcpwm/timer_connection.rs | 59 +++++-- 6 files changed, 381 insertions(+), 182 deletions(-) diff --git a/examples/mcpwm-simple.rs b/examples/mcpwm-simple.rs index 91057fc47a8..426850edf2c 100644 --- a/examples/mcpwm-simple.rs +++ b/examples/mcpwm-simple.rs @@ -58,10 +58,10 @@ #[cfg(any(esp32, esp32s3))] fn main() -> anyhow::Result<()> { - use embedded_hal::delay::blocking::DelayUs; + use embedded_hal::delay::DelayUs; use esp_idf_hal::delay::FreeRtos; - use esp_idf_hal::mcpwm::{Operator, OperatorConfig, Timer, TimerConfig}; + use esp_idf_hal::mcpwm::{Operator, Timer, TimerConfig, OperatorConfig}; use esp_idf_hal::prelude::Peripherals; use esp_idf_hal::units::FromValueType; @@ -93,8 +93,8 @@ fn main() -> anyhow::Result<()> { println!("Duty {}%", 100 * duty / period_ticks); } - operator.set_duty_a(duty)?; - operator.set_duty_b(period_ticks - duty)?; + operator.set_compare_value_x(duty)?; + operator.set_compare_value_y(period_ticks - duty)?; FreeRtos.delay_ms(10)?; } diff --git a/src/mcpwm/comparator.rs b/src/mcpwm/comparator.rs index e1ed13e3edd..e6f6a2ff134 100644 --- a/src/mcpwm/comparator.rs +++ b/src/mcpwm/comparator.rs @@ -1,3 +1,7 @@ +use esp_idf_sys::{mcpwm_cmpr_handle_t, mcpwm_gen_t, mcpwm_generator_set_actions_on_compare_event, mcpwm_timer_direction_t_MCPWM_TIMER_DIRECTION_UP, mcpwm_generator_action_t_MCPWM_GEN_ACTION_KEEP, mcpwm_timer_direction_t_MCPWM_TIMER_DIRECTION_DOWN}; + +use super::generator::{CountingDirection, OnMatchCfg}; + trait ComparatorChannel{} @@ -7,22 +11,37 @@ impl ComparatorChannel for CmpX {} pub struct CmpY; impl ComparatorChannel for CmpY {} -trait OptionalCmp { - type OnMatchCfg: Into; +pub trait OptionalCmp { + type OnMatchCfg: OnMatchCfg; + unsafe fn _configure(&mut self, gen: &mut mcpwm_gen_t, cfg: Self::OnMatchCfg); } impl OptionalCmp for Comparator { type OnMatchCfg = super::generator::CountingDirection; + + unsafe fn _configure(&mut self, gen: &mut mcpwm_gen_t, cfg: Self::OnMatchCfg) { + // TODO: "must be terminated by MCPWM_GEN_TIMER_EVENT_ACTION_END()" + mcpwm_generator_set_actions_on_compare_event(gen, esp_idf_sys::mcpwm_gen_compare_event_action_t { + direction: mcpwm_timer_direction_t_MCPWM_TIMER_DIRECTION_UP, comparator: self.0, action: cfg.counting_up.into() + }); + mcpwm_generator_set_actions_on_compare_event(gen, esp_idf_sys::mcpwm_gen_compare_event_action_t { + direction: mcpwm_timer_direction_t_MCPWM_TIMER_DIRECTION_DOWN, comparator: self.0, action: cfg.counting_down.into() + }); + } } -struct NoCmp; +pub struct NoCmp; impl OptionalCmp for NoCmp { type OnMatchCfg = super::generator::NoCmpMatchConfig; + + unsafe fn _configure(&mut self, gen: &mut mcpwm_gen_t, cfg: Self::OnMatchCfg) { + // Do nothing + } } pub struct Comparator(pub(crate)mcpwm_cmpr_handle_t); -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, Default)] pub struct ComparatorConfig { - _: (), + _todo: (), } \ No newline at end of file diff --git a/src/mcpwm/generator.rs b/src/mcpwm/generator.rs index 5914adafc7f..753847f5c0c 100644 --- a/src/mcpwm/generator.rs +++ b/src/mcpwm/generator.rs @@ -1,5 +1,21 @@ +use core::marker::PhantomData; -trait GeneratorChannel{ +use esp_idf_sys::{ + mcpwm_gen_handle_t, mcpwm_generator_action_t, mcpwm_generator_action_t_MCPWM_GEN_ACTION_HIGH, + mcpwm_generator_action_t_MCPWM_GEN_ACTION_KEEP, mcpwm_generator_action_t_MCPWM_GEN_ACTION_LOW, + mcpwm_generator_action_t_MCPWM_GEN_ACTION_TOGGLE, +}; + +use crate::gpio::OutputPin; + +pub struct NoGen; + +impl OptionalGen for NoGen {} +pub trait OptionalGen {} + +impl OptionalGen for Generator

{} + +pub trait GeneratorChannel { const IS_A: bool; } @@ -13,109 +29,121 @@ impl GeneratorChannel for GenB { const IS_A: bool = false; } -pub struct Generator(pub(crate)mcpwm_gen_handle_t); +// TODO: Allow OptionalOutputPin? +pub struct Generator { + pub(crate) handle: mcpwm_gen_handle_t, + pub(crate) pin: P, +} -#[derive(Default, Clone)] +#[derive(Clone)] pub struct GeneratorConfig { _channel: PhantomData, - on_matches_cmp_x: CMP_X, - on_matches_cmp_y: CMP_Y, - on_is_empty: CountingDirection, - on_is_full: CountingDirection + pub(crate) invert: bool, + pub(crate) on_matches_cmp_x: CMP_X, + pub(crate) on_matches_cmp_y: CMP_Y, + pub(crate) on_is_empty: CountingDirection, + pub(crate) on_is_full: CountingDirection, +} + +impl GeneratorConfig { + pub fn active_high() -> Self { + let mut result: Self = GeneratorConfig::empty(); + + result.on_is_empty.counting_up = GeneratorAction::SetHigh; + if G::IS_A { + result.on_matches_cmp_x.counting_up = GeneratorAction::SetLow; + } else { + result.on_matches_cmp_y.counting_up = GeneratorAction::SetLow; + } + result + } + + pub fn active_low() -> Self { + let mut result: Self = GeneratorConfig::empty(); + result.on_is_empty.counting_up = GeneratorAction::SetLow; + if G::IS_A { + result.on_matches_cmp_x.counting_up = GeneratorAction::SetHigh; + } else { + result.on_matches_cmp_y.counting_up = GeneratorAction::SetHigh; + } + result + } } -impl GeneratorConfig { +// TODO: Do we have any use for this? +impl GeneratorConfig +where + G: GeneratorChannel, + CMP_X: OnMatchCfg, + CMP_Y: OnMatchCfg, +{ fn empty() -> Self { GeneratorConfig { _channel: PhantomData, - on_matches_cmp_x: NoCmpMatchConfig, - on_matches_cmp_y: NoCmpMatchConfig, - on_is_empty: CountingDirection { + invert: false, + on_matches_cmp_x: OnMatchCfg::empty(), + on_matches_cmp_y: OnMatchCfg::empty(), + on_is_empty: CountingDirection { counting_up: GeneratorAction::Nothing, counting_down: GeneratorAction::Nothing, }, - on_is_full: CountingDirection { + on_is_full: CountingDirection { counting_up: GeneratorAction::Nothing, counting_down: GeneratorAction::Nothing, - } + }, } } } -impl Into for NoCmpMatchConfig { - fn into(self) -> CountingDirection { - CountingDirection { - counting_up: GeneratorAction::Nothing, - counting_down: GeneratorAction::Nothing, - } +pub struct NoCmpMatchConfig; + +impl OnMatchCfg for NoCmpMatchConfig { + fn empty() -> Self { + NoCmpMatchConfig } } -impl Into> for GeneratorConfig - where - CMP_X: Into, - CMP_Y: Into -{ - fn into(self) -> GeneratorConfig { - GeneratorConfig { - _channel, - on_matches_cmp_x, - on_matches_cmp_y, - on_is_empty, - on_is_full - } = self; +// TODO: Come up with better name +#[derive(Clone, Copy, Debug, PartialEq)] +pub struct CountingDirection { + pub(crate) counting_up: GeneratorAction, + pub(crate) counting_down: GeneratorAction, +} - GeneratorConfig { - _channel, - on_matches_cmp_x: on_matches_cmp_x.into(), - on_matches_cmp_y: on_matches_cmp_y.into(), - on_is_empty, - on_is_full +impl CountingDirection { + pub fn empty() -> Self { + CountingDirection { + counting_up: GeneratorAction::Nothing, + counting_down: GeneratorAction::Nothing, } } +} +impl OnMatchCfg for CountingDirection { + fn empty() -> Self { + CountingDirection::empty() + } } -pub struct NoCmpMatchConfig; +pub trait OnMatchCfg { + fn empty() -> Self; +} -// TODO: Come up with better name -#[derive(Clone, Copy)] -pub struct CountingDirection { - counting_up: GeneratorAction, - counting_down: GeneratorAction +impl Into for GeneratorAction { + fn into(self) -> mcpwm_generator_action_t { + match self { + GeneratorAction::Nothing => mcpwm_generator_action_t_MCPWM_GEN_ACTION_KEEP, + GeneratorAction::SetLow => mcpwm_generator_action_t_MCPWM_GEN_ACTION_LOW, + GeneratorAction::SetHigh => mcpwm_generator_action_t_MCPWM_GEN_ACTION_HIGH, + GeneratorAction::Toggle => mcpwm_generator_action_t_MCPWM_GEN_ACTION_TOGGLE, + } + } } +#[derive(Clone, Copy, Debug, PartialEq)] pub enum GeneratorAction { Nothing, SetLow, SetHigh, - Toggle + Toggle, } - -impl DutyMode { - fn into_duty_cfg(self) -> GeneratorConfig { - match val { - DutyMode::ActiveHigh => { - let mut duty_config = GeneratorConfig::empty(); - duty_config.on_is_empty.counting_up = GeneratorAction::SetHigh; - if G::IS_A { - duty_config.on_matches_cmp_a.counting_up = GeneratorAction::SetLow; - } else { - duty_config.on_matches_cmp_b.counting_up = GeneratorAction::SetLow; - } - duty_config - }, - DutyMode::ActiveLow => { - let mut duty_config = GeneratorConfig::empty(); - duty_config.on_is_empty.counting_up = GeneratorAction::SetLow; - if G::IS_A { - duty_config.on_matches_cmp_a.counting_up = GeneratorAction::SetHigh; - } else { - duty_config.on_matches_cmp_b.counting_up = GeneratorAction::SetHigh; - } - - duty_config - }, - } - } -} \ No newline at end of file diff --git a/src/mcpwm/mod.rs b/src/mcpwm/mod.rs index 1669d00ae1e..80240af68b9 100644 --- a/src/mcpwm/mod.rs +++ b/src/mcpwm/mod.rs @@ -85,7 +85,7 @@ mod timer_connection; use core::ffi; pub use self::{ - operator::{Operator, OperatorConfig, OPERATOR}, + operator::{Operator, OPERATOR, OperatorConfig}, timer::{Timer, TimerConfig, TIMER}, timer_connection::TimerConnection, }; diff --git a/src/mcpwm/operator.rs b/src/mcpwm/operator.rs index 6ffa1d68a21..3b36170abe7 100644 --- a/src/mcpwm/operator.rs +++ b/src/mcpwm/operator.rs @@ -1,10 +1,23 @@ -use esp_idf_sys::{mcpwm_oper_handle_t, EspError}; +use esp_idf_sys::{ + esp, mcpwm_comparator_config_t, mcpwm_comparator_config_t__bindgen_ty_1, + mcpwm_comparator_set_compare_value, mcpwm_gen_timer_event_action_t, mcpwm_generator_config_t, + mcpwm_generator_config_t__bindgen_ty_1, mcpwm_generator_set_actions_on_compare_event, + mcpwm_generator_set_actions_on_timer_event, mcpwm_new_comparator, mcpwm_new_generator, + mcpwm_oper_handle_t, mcpwm_operator_config_t, mcpwm_operator_config_t__bindgen_ty_1, + mcpwm_timer_direction_t_MCPWM_TIMER_DIRECTION_DOWN, + mcpwm_timer_direction_t_MCPWM_TIMER_DIRECTION_UP, mcpwm_timer_event_t_MCPWM_TIMER_EVENT_EMPTY, + mcpwm_timer_event_t_MCPWM_TIMER_EVENT_FULL, EspError, +}; use crate::{gpio::OutputPin, mcpwm::Group}; -use super::{timer_connection::OptionalOutputPin, Duty}; +use super::{ + comparator::{Comparator, ComparatorConfig, NoCmp, OptionalCmp}, + generator::{GenA, GenB, Generator, GeneratorConfig, NoGen, OptionalGen, GeneratorChannel}, + timer_connection::OptionalOutputPin, +}; -use core::{ffi, marker::PhantomData}; +use core::{marker::PhantomData, ptr}; pub struct OPERATOR { _ptr: PhantomData<*const ()>, @@ -50,8 +63,8 @@ pub struct Operator< G: Group, CMP_X: OptionalCmp, CMP_Y: OptionalCmp, - GEN_A: OptionalGenerator, - GEN_B: OptionalGenerator, + GEN_A: OptionalGen, + GEN_B: OptionalGen, > { _instance: OPERATOR, handle: mcpwm_oper_handle_t, @@ -64,16 +77,40 @@ pub struct Operator< //deadtime: D } -impl Operator { - pub fn default(instance: OPERATOR) -> _ { - Operator::custom(instance) +impl Operator { + pub fn default(instance: OPERATOR, pin_a: PA, pin_b: PB, cfg: OperatorConfig) -> Operator, Generator> { + Operator::custom(instance, cfg) .cmp_x(Default::default()) .cmp_y(Default::default()) - .gen_a(Default::default()) - .gen_b(Default::default()) + .gen_a(pin_a, GeneratorConfig::active_high()) + .gen_b(pin_b, GeneratorConfig::active_high()) } - pub fn custom(instance: OPERATOR) -> Self { + pub fn custom(instance: OPERATOR, cfg: OperatorConfig) -> Self { + let mut handle = ptr::null_mut(); + let mut flags: mcpwm_operator_config_t__bindgen_ty_1 = Default::default(); + + flags.set_update_gen_action_on_tez(todo!()); + flags.set_update_gen_action_on_tep(todo!()); + flags.set_update_gen_action_on_sync(todo!()); + + flags.set_update_dead_time_on_tez(todo!()); + flags.set_update_dead_time_on_tep(todo!()); + flags.set_update_dead_time_on_sync(todo!()); + + let config = mcpwm_operator_config_t { + group_id: G::ID, + flags, + }; + + unsafe { + esp!(esp_idf_sys::mcpwm_new_operator( + &config, + &mut handle, + )) + .unwrap(); + } + Operator { _instance: instance, handle, @@ -86,21 +123,23 @@ impl Operator { } } -impl< - const N: u8, - G: Group, - > Operator -{ - fn cmp_x(self, config: ComparatorConfig) -> Operator { - let cfg: mcpwm_comparator_config_t = todo!(); - let mut cmp = ptr::null_mut(); - esp!(mcpwm_new_comparator(self.handle, &cfg, &mut cmp)).unwrap(); +impl Operator { + fn cmp_x(self, config: ComparatorConfig) -> Operator { + let mut flags: mcpwm_comparator_config_t__bindgen_ty_1 = Default::default(); + flags.set_update_cmp_on_tep(todo!()); + flags.set_update_cmp_on_tez(todo!()); + flags.set_update_cmp_on_sync(todo!()); + let cfg = mcpwm_comparator_config_t { flags }; + let mut cmp = ptr::null_mut(); + unsafe { + esp!(mcpwm_new_comparator(self.handle, &cfg, &mut cmp)).unwrap(); + } let comparator_x = Comparator(cmp); Operator { - _instance: instance, - handle, + _instance: self._instance, + handle: self.handle, comparator_x, comparator_y: self.comparator_y, @@ -110,21 +149,24 @@ impl< } } -impl< - const N: u8, - G: Group, - > Operator -{ - fn cmp_y(self, config: ComparatorConfig) -> Operator { - let cfg: mcpwm_comparator_config_t = todo!(); +impl Operator { + fn cmp_y(self, config: ComparatorConfig) -> Operator { + let mut flags: mcpwm_comparator_config_t__bindgen_ty_1 = Default::default(); + flags.set_update_cmp_on_tep(todo!()); + flags.set_update_cmp_on_tez(todo!()); + flags.set_update_cmp_on_sync(todo!()); + + let cfg = mcpwm_comparator_config_t { flags }; let mut cmp = ptr::null_mut(); - esp!(mcpwm_new_comparator(self.handle, &cfg, &mut cmp)).unwrap(); + unsafe { + esp!(mcpwm_new_comparator(self.handle, &cfg, &mut cmp)).unwrap(); + } let comparator_y = Comparator(cmp); Operator { - _instance: instance, - handle, + _instance: self._instance, + handle: self.handle, comparator_x: self.comparator_x, comparator_y, @@ -138,28 +180,25 @@ impl< // TODO: Is there any point in letting the user provide the comparators or should two (the only two available // for that operator in hardware) be automatically assigned in `Operator::new`? - -impl< - const N: u8, - G: Group, - GEN_B: OptionalGen, - CMP_X: OptionalCmp, - CMP_Y: OptionalCmp, -> Operator +impl + Operator { - fn gen_a(self, config: Into>) -> Operator<> { - let cfg: mcpwm_generator_config_t = todo!(); - let mut gen = ptr::null_mut(); - esp!(mcpwm_new_generator(self.handle, &cfg, &gen)).unwrap(); - - esp_err_t mcpwm_generator_set_actions_on_timer_event(mcpwm_gen_handle_t gen, mcpwm_gen_timer_event_action_t ev_act, ...); - esp_err_t mcpwm_generator_set_actions_on_compare_event(mcpwm_gen_handle_t gen, mcpwm_gen_compare_event_action_t ev_act, ...) - - let generator_a = Generator(gen); + fn gen_a( + mut self, + pin: P, + config: GeneratorConfig, + ) -> Operator, GEN_B> { + let generator_a = setup_generator( + &mut self.handle, + &mut self.comparator_x, + &mut self.comparator_y, + pin, + config, + ); Operator { - _instance: instance, - handle, + _instance: self._instance, + handle: self.handle, comparator_x: self.comparator_x, comparator_y: self.comparator_y, @@ -169,27 +208,25 @@ impl< } } -impl< - const N: u8, - G: Group, - GEN_A: OptionalGen, - CMP_X: OptionalCmp, - CMP_Y: OptionalCmp, -> Operator +impl + Operator { - fn gen_b(self, config: Into>) -> Operator<> { - let cfg: mcpwm_generator_config_t = todo!(); - let mut gen = ptr::null_mut(); - esp!(mcpwm_new_generator(self.handle, &cfg, &gen)).unwrap(); - - esp_err_t mcpwm_generator_set_actions_on_timer_event(mcpwm_gen_handle_t gen, mcpwm_gen_timer_event_action_t ev_act, ...); - esp_err_t mcpwm_generator_set_actions_on_compare_event(mcpwm_gen_handle_t gen, mcpwm_gen_compare_event_action_t ev_act, ...) - - let generator_b = Generator(gen); + fn gen_b( + mut self, + pin: P, + config: GeneratorConfig, + ) -> Operator> { + let generator_b = setup_generator( + &mut self.handle, + &mut self.comparator_x, + &mut self.comparator_y, + pin, + config, + ); Operator { - _instance: instance, - handle, + _instance: self._instance, + handle: self.handle, comparator_x: self.comparator_x, comparator_y: self.comparator_y, @@ -199,14 +236,72 @@ impl< } } -impl Operator +fn setup_generator( + operator: &mut mcpwm_oper_handle_t, + comparator_x: &mut CMP_X, + comparator_y: &mut CMP_Y, + pin: P, + config: GeneratorConfig, +) -> Generator

{ + let mut flags: mcpwm_generator_config_t__bindgen_ty_1 = Default::default(); + flags.set_invert_pwm(config.invert.into()); + flags.set_io_loop_back(0); + let cfg = mcpwm_generator_config_t { + gen_gpio_num: pin.pin(), + flags, + }; + let mut gen = ptr::null_mut(); + unsafe { + esp!(mcpwm_new_generator(*operator, &cfg, &mut gen)).unwrap(); + + // TODO: "must be terminated by MCPWM_GEN_TIMER_EVENT_ACTION_END()" + esp!(mcpwm_generator_set_actions_on_timer_event( + gen, + mcpwm_gen_timer_event_action_t { + direction: mcpwm_timer_direction_t_MCPWM_TIMER_DIRECTION_UP, + event: mcpwm_timer_event_t_MCPWM_TIMER_EVENT_EMPTY, + action: config.on_is_empty.counting_up.into(), + } + )).unwrap(); + esp!(mcpwm_generator_set_actions_on_timer_event( + gen, + mcpwm_gen_timer_event_action_t { + direction: mcpwm_timer_direction_t_MCPWM_TIMER_DIRECTION_DOWN, + event: mcpwm_timer_event_t_MCPWM_TIMER_EVENT_EMPTY, + action: config.on_is_empty.counting_down.into(), + } + )).unwrap(); + esp!(mcpwm_generator_set_actions_on_timer_event( + gen, + mcpwm_gen_timer_event_action_t { + direction: mcpwm_timer_direction_t_MCPWM_TIMER_DIRECTION_UP, + event: mcpwm_timer_event_t_MCPWM_TIMER_EVENT_FULL, + action: config.on_is_full.counting_up.into(), + } + )).unwrap(); + esp!(mcpwm_generator_set_actions_on_timer_event( + gen, + mcpwm_gen_timer_event_action_t { + direction: mcpwm_timer_direction_t_MCPWM_TIMER_DIRECTION_DOWN, + event: mcpwm_timer_event_t_MCPWM_TIMER_EVENT_FULL, + action: config.on_is_full.counting_down.into(), + } + )).unwrap(); + + comparator_x._configure(&mut *gen, config.on_matches_cmp_x); + comparator_y._configure(&mut *gen, config.on_matches_cmp_y); + } + + Generator { handle: gen, pin } +} + +impl Operator where G: Group, + CMP_Y: OptionalCmp, GEN_A: OptionalGen, GEN_B: OptionalGen, - CMP_Y: OptionalCmp, { - // TODO: Note that this is the comparator we are affecting, not the generator. Generator A may not necessarily have // anything to do with comparator A. How do we best convay that? Should we call them Generator X/Y and Comparator A/B? // @@ -218,59 +313,81 @@ where // event, as well as timer events, to change the level of the pin. // /// Get compare value, often times same as the duty for output A. - /// + /// /// See `Self::set_compare_value_x` for more info pub fn get_compare_value_x(&self) -> u16 { todo!() } /// Set compare value, often times same as the duty for output A. - /// + /// /// Depending on how the generators are configured this is, using the most common configuration, the duty of output A. /// `value` is from the range 0 to timers peak value. However do note that if using a custom configuration this might /// control something else like for example the phase. Look into Generator::TODO for more details - /// + /// /// TODO: what about CountMode::UpDown? - /// + /// /// NOTE: The compare value shouldn’t exceed timer’s count peak, otherwise, the compare event will never got triggered. pub fn set_compare_value_x(&mut self, value: u16) -> Result<(), EspError> { - esp!(mcpwm_comparator_set_compare_value(&self.comparator_x.0, value.into())) + unsafe { + esp!(mcpwm_comparator_set_compare_value( + self.comparator_x.0, + value.into() + )) + } } } -impl Operator +impl Operator where G: Group, + CMP_X: OptionalCmp, GEN_A: OptionalGen, GEN_B: OptionalGen, - CMP_X: OptionalCmp, { /// Get compare value, often times same as the duty for output B. - /// + /// /// See `Self::set_compare_value_x` for more info pub fn get_compare_value_y(&self) -> u16 { todo!() } /// Set compare value, often times same as the duty for output A. - /// + /// /// Depending on how the generators are configured this is, using the most common configuration, the duty of output A. /// `value` is from the range 0 to timers peak value. However do note that if using a custom configuration this might /// control something else like for example the phase. Look into Generator::TODO for more details - /// + /// /// TODO: what about CountMode::UpDown? - /// + /// /// NOTE: The compare value shouldn’t exceed timer’s count peak, otherwise, the compare event will never got triggered. pub fn set_compare_value_y(&mut self, value: u16) -> Result<(), EspError> { - esp!(mcpwm_comparator_set_compare_value(&self.comparator_x.0, value.into())) + unsafe { + esp!(mcpwm_comparator_set_compare_value( + self.comparator_y.0, + value.into() + )) + } } } pub trait OptionalOperator {} -impl OptionalOperator - for Operator +impl OptionalOperator + for Operator +where + G: Group, + CMP_X: OptionalCmp, + CMP_Y: OptionalCmp, + GEN_A: OptionalGen, + GEN_B: OptionalGen, { + // TODO: } pub struct NoOperator; -impl OptionalOperator for NoOperator {} \ No newline at end of file +impl OptionalOperator for NoOperator {} + +#[derive(Default)] +pub struct OperatorConfig { + _todo: (), +} diff --git a/src/mcpwm/timer_connection.rs b/src/mcpwm/timer_connection.rs index 465c4699f29..c8d51825e46 100644 --- a/src/mcpwm/timer_connection.rs +++ b/src/mcpwm/timer_connection.rs @@ -1,6 +1,8 @@ use crate::mcpwm::Group; use super::{ + comparator::OptionalCmp, + generator::OptionalGen, operator::{NoOperator, OperatorConfig, OptionalOperator, OPERATOR}, timer::Timer, Operator, @@ -54,16 +56,27 @@ impl< } } // TODO: Do something more builder-pattern like for making the operator? -impl, O2: OptionalOperator<2, G>> - TimerConnection +impl TimerConnection +where + G: Group, + O1: OptionalOperator<1, G>, + O2: OptionalOperator<2, G>, { - pub fn attatch_operator0( + pub fn attatch_operator0( self, operator_handle: OPERATOR<0, G>, operator_cfg: OperatorConfig, pin_a: PA, pin_b: PB, - ) -> TimerConnection, O1, O2> { + ) -> TimerConnection, O1, O2> + where + CMP_X: OptionalCmp, + CMP_Y: OptionalCmp, + GEN_A: OptionalGen, + GEN_B: OptionalGen, + PA: OptionalOutputPin, + PB: OptionalOutputPin, + { let operator = todo!(); //self.init_and_attach_operator(operator_cfg, pin_a, pin_b); TimerConnection { timer: self.timer, @@ -74,16 +87,27 @@ impl, O2: OptionalOperator<2, } } -impl, O2: OptionalOperator<2, G>> - TimerConnection +impl TimerConnection +where + G: Group, + O0: OptionalOperator<0, G>, + O2: OptionalOperator<2, G>, { - pub fn attatch_operator1( + pub fn attatch_operator1( self, operator_handle: OPERATOR<1, G>, operator_cfg: OperatorConfig, pin_a: PA, pin_b: PB, - ) -> TimerConnection, O2> { + ) -> TimerConnection, O2> + where + CMP_X: OptionalCmp, + CMP_Y: OptionalCmp, + GEN_A: OptionalGen, + GEN_B: OptionalGen, + PA: OptionalOutputPin, + PB: OptionalOutputPin, + { let operator = todo!(); //self.init_and_attach_operator(operator_cfg, pin_a, pin_b); TimerConnection { timer: self.timer, @@ -94,16 +118,27 @@ impl, O2: OptionalOperator<2, } } -impl, O1: OptionalOperator<1, G>> - TimerConnection +impl TimerConnection +where + G: Group, + O0: OptionalOperator<0, G>, + O1: OptionalOperator<1, G>, { - pub fn attatch_operator2( + pub fn attatch_operator2( self, operator_handle: OPERATOR<2, G>, operator_cfg: OperatorConfig, pin_a: PA, pin_b: PB, - ) -> TimerConnection> { + ) -> TimerConnection> + where + CMP_X: OptionalCmp, + CMP_Y: OptionalCmp, + GEN_A: OptionalGen, + GEN_B: OptionalGen, + PA: OptionalOutputPin, + PB: OptionalOutputPin, + { let operator = todo!(); //self.init_and_attach_operator(operator_cfg, pin_a, pin_b); TimerConnection { timer: self.timer, From d932af453d2898b768342b34762f991f228932c5 Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Sun, 27 Nov 2022 23:06:04 +0100 Subject: [PATCH 35/62] Do all configuration before building the real types(not compiling) --- examples/mcpwm-simple.rs | 13 +- src/mcpwm/comparator.rs | 55 +++++++- src/mcpwm/generator.rs | 158 +++++++++++++++++++-- src/mcpwm/mod.rs | 4 +- src/mcpwm/operator.rs | 249 +++++++++------------------------- src/mcpwm/operator_config.rs | 224 ++++++++++++++++++++++++++++++ src/mcpwm/timer_connection.rs | 14 +- 7 files changed, 499 insertions(+), 218 deletions(-) create mode 100644 src/mcpwm/operator_config.rs diff --git a/examples/mcpwm-simple.rs b/examples/mcpwm-simple.rs index 426850edf2c..cbb7ccf3e94 100644 --- a/examples/mcpwm-simple.rs +++ b/examples/mcpwm-simple.rs @@ -61,7 +61,7 @@ fn main() -> anyhow::Result<()> { use embedded_hal::delay::DelayUs; use esp_idf_hal::delay::FreeRtos; - use esp_idf_hal::mcpwm::{Operator, Timer, TimerConfig, OperatorConfig}; + use esp_idf_hal::mcpwm::{Operator, OperatorConfig, Timer, TimerConfig}; use esp_idf_hal::prelude::Peripherals; use esp_idf_hal::units::FromValueType; @@ -71,15 +71,12 @@ fn main() -> anyhow::Result<()> { let peripherals = Peripherals::take().unwrap(); let timer_config = TimerConfig::default().frequency(25.kHz()); - let operator_config = OperatorConfig::default(); + let operator_config = OperatorConfig::default(peripherals.pins.gpio4, peripherals.pins.gpio5); let timer = Timer::new(peripherals.mcpwm0.timer0, timer_config); - let mut timer = timer.into_connection().attatch_operator0( - peripherals.mcpwm0.operator0, - operator_config, - peripherals.pins.gpio4, - peripherals.pins.gpio5, - ); + let mut timer = timer + .into_connection() + .attatch_operator0(peripherals.mcpwm0.operator0, operator_config); let (timer, operator, _, _) = timer.split(); diff --git a/src/mcpwm/comparator.rs b/src/mcpwm/comparator.rs index e6f6a2ff134..e2c52a7eca7 100644 --- a/src/mcpwm/comparator.rs +++ b/src/mcpwm/comparator.rs @@ -1,6 +1,8 @@ -use esp_idf_sys::{mcpwm_cmpr_handle_t, mcpwm_gen_t, mcpwm_generator_set_actions_on_compare_event, mcpwm_timer_direction_t_MCPWM_TIMER_DIRECTION_UP, mcpwm_generator_action_t_MCPWM_GEN_ACTION_KEEP, mcpwm_timer_direction_t_MCPWM_TIMER_DIRECTION_DOWN}; +use core::ptr; -use super::generator::{CountingDirection, OnMatchCfg}; +use esp_idf_sys::{mcpwm_cmpr_handle_t, mcpwm_gen_t, mcpwm_generator_set_actions_on_compare_event, mcpwm_timer_direction_t_MCPWM_TIMER_DIRECTION_UP, mcpwm_generator_action_t_MCPWM_GEN_ACTION_KEEP, mcpwm_timer_direction_t_MCPWM_TIMER_DIRECTION_DOWN, mcpwm_comparator_config_t__bindgen_ty_1, esp, mcpwm_comparator_config_t, mcpwm_new_comparator, mcpwm_oper_handle_t}; + +use super::generator::{CountingDirection, OnMatchCfg, NoCmpMatchConfig}; trait ComparatorChannel{} @@ -13,11 +15,13 @@ impl ComparatorChannel for CmpY {} pub trait OptionalCmp { type OnMatchCfg: OnMatchCfg; + type Cfg: OptionalCmpCfg; unsafe fn _configure(&mut self, gen: &mut mcpwm_gen_t, cfg: Self::OnMatchCfg); } impl OptionalCmp for Comparator { type OnMatchCfg = super::generator::CountingDirection; + type Cfg = ComparatorConfig; unsafe fn _configure(&mut self, gen: &mut mcpwm_gen_t, cfg: Self::OnMatchCfg) { // TODO: "must be terminated by MCPWM_GEN_TIMER_EVENT_ACTION_END()" @@ -33,6 +37,7 @@ impl OptionalCmp for Comparator { pub struct NoCmp; impl OptionalCmp for NoCmp { type OnMatchCfg = super::generator::NoCmpMatchConfig; + type Cfg = NoCmpCfg; unsafe fn _configure(&mut self, gen: &mut mcpwm_gen_t, cfg: Self::OnMatchCfg) { // Do nothing @@ -41,7 +46,49 @@ impl OptionalCmp for NoCmp { pub struct Comparator(pub(crate)mcpwm_cmpr_handle_t); -#[derive(Debug, Clone, Copy, Default)] +#[derive(Debug, Clone, Copy)] pub struct ComparatorConfig { - _todo: (), + flags: mcpwm_comparator_config_t__bindgen_ty_1, +} + +impl Default for ComparatorConfig { + fn default() -> Self { + let mut flags: mcpwm_comparator_config_t__bindgen_ty_1 = Default::default(); + flags.set_update_cmp_on_tep(todo!()); + flags.set_update_cmp_on_tez(todo!()); + flags.set_update_cmp_on_sync(todo!()); + Self { flags } + } +} + +pub struct NoCmpCfg; + +pub trait OptionalCmpCfg{ + type OnMatchCfg: OnMatchCfg; + type Cmp: OptionalCmp; + unsafe fn init(self, operator_handle: mcpwm_oper_handle_t) -> Self::Cmp; +} + +impl OptionalCmpCfg for NoCmpCfg { + type OnMatchCfg = NoCmpMatchConfig; + type Cmp = NoCmp; + + unsafe fn init(self, _operator_handle: mcpwm_oper_handle_t) -> NoCmp { + NoCmp + } +} +impl OptionalCmpCfg for ComparatorConfig{ + type OnMatchCfg = CountingDirection; + type Cmp = Comparator; + + unsafe fn init(self, operator_handle: mcpwm_oper_handle_t) -> Comparator { + let cfg = mcpwm_comparator_config_t { flags: self.flags }; + + let mut cmp = ptr::null_mut(); + unsafe { + esp!(mcpwm_new_comparator(operator_handle, &cfg, &mut cmp)).unwrap(); + } + + Comparator(cmp) + } } \ No newline at end of file diff --git a/src/mcpwm/generator.rs b/src/mcpwm/generator.rs index 753847f5c0c..2ad2d354bb4 100644 --- a/src/mcpwm/generator.rs +++ b/src/mcpwm/generator.rs @@ -1,13 +1,23 @@ -use core::marker::PhantomData; +use core::{marker::PhantomData, ptr}; use esp_idf_sys::{ - mcpwm_gen_handle_t, mcpwm_generator_action_t, mcpwm_generator_action_t_MCPWM_GEN_ACTION_HIGH, - mcpwm_generator_action_t_MCPWM_GEN_ACTION_KEEP, mcpwm_generator_action_t_MCPWM_GEN_ACTION_LOW, - mcpwm_generator_action_t_MCPWM_GEN_ACTION_TOGGLE, + esp, mcpwm_gen_handle_t, mcpwm_gen_timer_event_action_t, mcpwm_generator_action_t, + mcpwm_generator_action_t_MCPWM_GEN_ACTION_HIGH, mcpwm_generator_action_t_MCPWM_GEN_ACTION_KEEP, + mcpwm_generator_action_t_MCPWM_GEN_ACTION_LOW, + mcpwm_generator_action_t_MCPWM_GEN_ACTION_TOGGLE, mcpwm_generator_config_t, + mcpwm_generator_config_t__bindgen_ty_1, mcpwm_generator_set_actions_on_timer_event, + mcpwm_new_generator, mcpwm_oper_handle_t, mcpwm_timer_direction_t_MCPWM_TIMER_DIRECTION_DOWN, + mcpwm_timer_direction_t_MCPWM_TIMER_DIRECTION_UP, mcpwm_timer_event_t_MCPWM_TIMER_EVENT_EMPTY, + mcpwm_timer_event_t_MCPWM_TIMER_EVENT_FULL, }; use crate::gpio::OutputPin; +use super::{ + comparator::OptionalCmp, + timer_connection::{NoPin, OptionalOutputPin}, +}; + pub struct NoGen; impl OptionalGen for NoGen {} @@ -15,6 +25,20 @@ pub trait OptionalGen {} impl OptionalGen for Generator

{} +pub trait ToGenCfg { + type Cfg: OptionalGenCfg; +} + +impl ToGenCfg + for (CMP_X, CMP_Y, G, Generator

) +{ + type Cfg = GeneratorConfig; +} + +impl ToGenCfg for (CMP_X, CMP_Y, G, NoGenCfg) { + type Cfg = NoGenCfg; +} + pub trait GeneratorChannel { const IS_A: bool; } @@ -35,19 +59,126 @@ pub struct Generator { pub(crate) pin: P, } -#[derive(Clone)] -pub struct GeneratorConfig { +pub struct GeneratorConfig { _channel: PhantomData, pub(crate) invert: bool, pub(crate) on_matches_cmp_x: CMP_X, pub(crate) on_matches_cmp_y: CMP_Y, pub(crate) on_is_empty: CountingDirection, pub(crate) on_is_full: CountingDirection, + pub(crate) pin: P, +} + +pub struct NoGenCfg; + +pub trait OptionalGenCfg {} + +impl OptionalGenCfg for NoGenCfg {} + +impl OptionalGenCfg + for GeneratorConfig +{ +} + +pub trait GenInit { + type Gen: OptionalGen; + + /// This is only to be used internally by esp-idf-hal + unsafe fn init(self, operator_handle: mcpwm_oper_handle_t) -> Self::Gen; } -impl GeneratorConfig { - pub fn active_high() -> Self { - let mut result: Self = GeneratorConfig::empty(); +impl GenInit + for ( + &mut CMP_X, + &mut CMP_Y, + NoGenCfg, + ) +where + CMP_X: OptionalCmp, + CMP_Y: OptionalCmp, +{ + type Gen = NoGen; + + unsafe fn init(self, operator_handle: mcpwm_oper_handle_t) -> Self::Gen { + NoGen + } +} + +impl GenInit + for ( + &mut CMP_X, + &mut CMP_Y, + GeneratorConfig, + ) +where + CMP_X: OptionalCmp, + CMP_Y: OptionalCmp, +{ + type Gen = Generator

; + + unsafe fn init(mut self, operator_handle: mcpwm_oper_handle_t) -> Generator

{ + let (cmp_x, cmp_y, generator_config) = self; + + let cfg = mcpwm_generator_config_t { + gen_gpio_num: generator_config.pin.pin(), + flags: todo!(),//generator_config.flags, + }; + let mut gen = ptr::null_mut(); + unsafe { + esp!(mcpwm_new_generator(operator_handle, &cfg, &mut gen)).unwrap(); + + // TODO: "must be terminated by MCPWM_GEN_TIMER_EVENT_ACTION_END()" + esp!(mcpwm_generator_set_actions_on_timer_event( + gen, + mcpwm_gen_timer_event_action_t { + direction: mcpwm_timer_direction_t_MCPWM_TIMER_DIRECTION_UP, + event: mcpwm_timer_event_t_MCPWM_TIMER_EVENT_EMPTY, + action: generator_config.on_is_empty.counting_up.into(), + } + )) + .unwrap(); + esp!(mcpwm_generator_set_actions_on_timer_event( + gen, + mcpwm_gen_timer_event_action_t { + direction: mcpwm_timer_direction_t_MCPWM_TIMER_DIRECTION_DOWN, + event: mcpwm_timer_event_t_MCPWM_TIMER_EVENT_EMPTY, + action: generator_config.on_is_empty.counting_down.into(), + } + )) + .unwrap(); + esp!(mcpwm_generator_set_actions_on_timer_event( + gen, + mcpwm_gen_timer_event_action_t { + direction: mcpwm_timer_direction_t_MCPWM_TIMER_DIRECTION_UP, + event: mcpwm_timer_event_t_MCPWM_TIMER_EVENT_FULL, + action: generator_config.on_is_full.counting_up.into(), + } + )) + .unwrap(); + esp!(mcpwm_generator_set_actions_on_timer_event( + gen, + mcpwm_gen_timer_event_action_t { + direction: mcpwm_timer_direction_t_MCPWM_TIMER_DIRECTION_DOWN, + event: mcpwm_timer_event_t_MCPWM_TIMER_EVENT_FULL, + action: generator_config.on_is_full.counting_down.into(), + } + )) + .unwrap(); + + cmp_x._configure(&mut *gen, generator_config.on_matches_cmp_x); + cmp_y._configure(&mut *gen, generator_config.on_matches_cmp_y); + } + + Generator { + handle: gen, + pin: generator_config.pin, + } + } +} + +impl GeneratorConfig { + pub fn active_high(pin: P) -> Self { + let mut result: Self = GeneratorConfig::empty(pin); result.on_is_empty.counting_up = GeneratorAction::SetHigh; if G::IS_A { @@ -58,8 +189,8 @@ impl GeneratorConfig Self { - let mut result: Self = GeneratorConfig::empty(); + pub fn active_low(pin: P) -> Self { + let mut result: Self = GeneratorConfig::empty(pin); result.on_is_empty.counting_up = GeneratorAction::SetLow; if G::IS_A { result.on_matches_cmp_x.counting_up = GeneratorAction::SetHigh; @@ -71,13 +202,13 @@ impl GeneratorConfig GeneratorConfig +impl GeneratorConfig where G: GeneratorChannel, CMP_X: OnMatchCfg, CMP_Y: OnMatchCfg, { - fn empty() -> Self { + fn empty(pin: P) -> Self { GeneratorConfig { _channel: PhantomData, invert: false, @@ -91,6 +222,7 @@ where counting_up: GeneratorAction::Nothing, counting_down: GeneratorAction::Nothing, }, + pin, } } } diff --git a/src/mcpwm/mod.rs b/src/mcpwm/mod.rs index 80240af68b9..e17acaeb42f 100644 --- a/src/mcpwm/mod.rs +++ b/src/mcpwm/mod.rs @@ -77,6 +77,7 @@ //! See the `examples/` folder of this repository for more. mod operator; +mod operator_config; mod generator; mod comparator; mod timer; @@ -85,7 +86,8 @@ mod timer_connection; use core::ffi; pub use self::{ - operator::{Operator, OPERATOR, OperatorConfig}, + operator::{Operator, OPERATOR}, + operator_config::OperatorConfig, timer::{Timer, TimerConfig, TIMER}, timer_connection::TimerConnection, }; diff --git a/src/mcpwm/operator.rs b/src/mcpwm/operator.rs index 3b36170abe7..ce2749ffcdb 100644 --- a/src/mcpwm/operator.rs +++ b/src/mcpwm/operator.rs @@ -4,17 +4,20 @@ use esp_idf_sys::{ mcpwm_generator_config_t__bindgen_ty_1, mcpwm_generator_set_actions_on_compare_event, mcpwm_generator_set_actions_on_timer_event, mcpwm_new_comparator, mcpwm_new_generator, mcpwm_oper_handle_t, mcpwm_operator_config_t, mcpwm_operator_config_t__bindgen_ty_1, - mcpwm_timer_direction_t_MCPWM_TIMER_DIRECTION_DOWN, + mcpwm_operator_connect_timer, mcpwm_timer_direction_t_MCPWM_TIMER_DIRECTION_DOWN, mcpwm_timer_direction_t_MCPWM_TIMER_DIRECTION_UP, mcpwm_timer_event_t_MCPWM_TIMER_EVENT_EMPTY, - mcpwm_timer_event_t_MCPWM_TIMER_EVENT_FULL, EspError, + mcpwm_timer_event_t_MCPWM_TIMER_EVENT_FULL, mcpwm_timer_handle_t, EspError, }; -use crate::{gpio::OutputPin, mcpwm::Group}; +use crate::mcpwm::Group; use super::{ - comparator::{Comparator, ComparatorConfig, NoCmp, OptionalCmp}, - generator::{GenA, GenB, Generator, GeneratorConfig, NoGen, OptionalGen, GeneratorChannel}, - timer_connection::OptionalOutputPin, + comparator::{Comparator, ComparatorConfig, NoCmp, OptionalCmp, OptionalCmpCfg}, + generator::{ + GenA, GenB, GenInit, NoGen, OptionalGen, + ToGenCfg, + }, + OperatorConfig, }; use core::{marker::PhantomData, ptr}; @@ -67,26 +70,47 @@ pub struct Operator< GEN_B: OptionalGen, > { _instance: OPERATOR, - handle: mcpwm_oper_handle_t, + _handle: mcpwm_oper_handle_t, comparator_x: CMP_X, // SOC_MCPWM_COMPARATORS_PER_OPERATOR is 2 for ESP32 and ESP32-S3 comparator_y: CMP_Y, - generator_a: GEN_A, // One generator per pin, with a maximum of two generators per Operator - generator_b: GEN_B, + _generator_a: GEN_A, // One generator per pin, with a maximum of two generators per Operator + _generator_b: GEN_B, //deadtime: D } -impl Operator { - pub fn default(instance: OPERATOR, pin_a: PA, pin_b: PB, cfg: OperatorConfig) -> Operator, Generator> { - Operator::custom(instance, cfg) - .cmp_x(Default::default()) - .cmp_y(Default::default()) - .gen_a(pin_a, GeneratorConfig::active_high()) - .gen_b(pin_b, GeneratorConfig::active_high()) - } +impl<'a, const N: u8, G: Group, CMP_X, CMP_Y, GEN_A, GEN_B> + Operator +where + CMP_X: OptionalCmp, + CMP_Y: OptionalCmp, - pub fn custom(instance: OPERATOR, cfg: OperatorConfig) -> Self { + GEN_A: OptionalGen, + GEN_B: OptionalGen, + (CMP_X, CMP_Y, GenA, GEN_A): ToGenCfg, // TODO: Is there a less hacky way to do this? + (CMP_X, CMP_Y, GenB, GEN_B): ToGenCfg, + ( + &'a mut CMP_X, + &'a mut CMP_Y, + <(CMP_X, CMP_Y, GenA, GEN_A) as ToGenCfg>::Cfg, + ): GenInit + 'a, + ( + &'a mut CMP_X, + &'a mut CMP_Y, + <(CMP_X, CMP_Y, GenB, GEN_B) as ToGenCfg>::Cfg, + ): GenInit + 'a, +{ + pub unsafe fn new( + instance: OPERATOR, + timer_handle: mcpwm_timer_handle_t, + cfg: OperatorConfig< + CMP_X::Cfg, + CMP_Y::Cfg, + <(CMP_X, CMP_Y, GenA, GEN_A) as ToGenCfg>::Cfg, + <(CMP_X, CMP_Y, GenB, GEN_B) as ToGenCfg>::Cfg, + >, + ) -> Self { let mut handle = ptr::null_mut(); let mut flags: mcpwm_operator_config_t__bindgen_ty_1 = Default::default(); @@ -97,60 +121,43 @@ impl Operator { flags.set_update_dead_time_on_tez(todo!()); flags.set_update_dead_time_on_tep(todo!()); flags.set_update_dead_time_on_sync(todo!()); - + let config = mcpwm_operator_config_t { group_id: G::ID, flags, }; unsafe { - esp!(esp_idf_sys::mcpwm_new_operator( - &config, - &mut handle, - )) - .unwrap(); + esp!(esp_idf_sys::mcpwm_new_operator(&config, &mut handle,)).unwrap(); } - Operator { - _instance: instance, - handle, - comparator_x: NoCmp, // SOC_MCPWM_COMPARATORS_PER_OPERATOR is 2 for ESP32 and ESP32-S3 - comparator_y: NoCmp, - - generator_a: NoGen, // One generator per pin, with a maximum of two generators per Operator - generator_b: NoGen, - } - } -} + let comparator_x = unsafe { cfg.comparator_x.init(handle) }; + let comparator_y = unsafe { cfg.comparator_y.init(handle) }; -impl Operator { - fn cmp_x(self, config: ComparatorConfig) -> Operator { - let mut flags: mcpwm_comparator_config_t__bindgen_ty_1 = Default::default(); - flags.set_update_cmp_on_tep(todo!()); - flags.set_update_cmp_on_tez(todo!()); - flags.set_update_cmp_on_sync(todo!()); + let generator_a = + unsafe { (&mut comparator_x, &mut comparator_y, cfg.generator_a).init(handle) }; + let generator_b = + unsafe { (&mut comparator_x, &mut comparator_y, cfg.generator_b).init(handle) }; - let cfg = mcpwm_comparator_config_t { flags }; - let mut cmp = ptr::null_mut(); + // Connect operator to timer unsafe { - esp!(mcpwm_new_comparator(self.handle, &cfg, &mut cmp)).unwrap(); + esp!(mcpwm_operator_connect_timer(handle, timer_handle)).unwrap(); } - let comparator_x = Comparator(cmp); Operator { - _instance: self._instance, - handle: self.handle, + _instance: instance, + _handle: handle, comparator_x, - comparator_y: self.comparator_y, + comparator_y, - generator_a: self.generator_a, - generator_b: self.generator_b, + _generator_a: generator_a, + _generator_b: generator_b, } } } -impl Operator { - fn cmp_y(self, config: ComparatorConfig) -> Operator { +impl Operator { + fn cmp_x(self, config: ComparatorConfig) -> Operator { let mut flags: mcpwm_comparator_config_t__bindgen_ty_1 = Default::default(); flags.set_update_cmp_on_tep(todo!()); flags.set_update_cmp_on_tez(todo!()); @@ -159,142 +166,22 @@ impl Operator - Operator -{ - fn gen_a( - mut self, - pin: P, - config: GeneratorConfig, - ) -> Operator, GEN_B> { - let generator_a = setup_generator( - &mut self.handle, - &mut self.comparator_x, - &mut self.comparator_y, - pin, - config, - ); - - Operator { - _instance: self._instance, - handle: self.handle, - comparator_x: self.comparator_x, - comparator_y: self.comparator_y, - - generator_a, - generator_b: self.generator_b, - } - } -} - -impl - Operator -{ - fn gen_b( - mut self, - pin: P, - config: GeneratorConfig, - ) -> Operator> { - let generator_b = setup_generator( - &mut self.handle, - &mut self.comparator_x, - &mut self.comparator_y, - pin, - config, - ); + let comparator_x = Comparator(cmp); Operator { _instance: self._instance, - handle: self.handle, - comparator_x: self.comparator_x, + _handle: self._handle, + comparator_x, comparator_y: self.comparator_y, - generator_a: self.generator_a, - generator_b, + _generator_a: self._generator_a, + _generator_b: self._generator_b, } } } -fn setup_generator( - operator: &mut mcpwm_oper_handle_t, - comparator_x: &mut CMP_X, - comparator_y: &mut CMP_Y, - pin: P, - config: GeneratorConfig, -) -> Generator

{ - let mut flags: mcpwm_generator_config_t__bindgen_ty_1 = Default::default(); - flags.set_invert_pwm(config.invert.into()); - flags.set_io_loop_back(0); - let cfg = mcpwm_generator_config_t { - gen_gpio_num: pin.pin(), - flags, - }; - let mut gen = ptr::null_mut(); - unsafe { - esp!(mcpwm_new_generator(*operator, &cfg, &mut gen)).unwrap(); - - // TODO: "must be terminated by MCPWM_GEN_TIMER_EVENT_ACTION_END()" - esp!(mcpwm_generator_set_actions_on_timer_event( - gen, - mcpwm_gen_timer_event_action_t { - direction: mcpwm_timer_direction_t_MCPWM_TIMER_DIRECTION_UP, - event: mcpwm_timer_event_t_MCPWM_TIMER_EVENT_EMPTY, - action: config.on_is_empty.counting_up.into(), - } - )).unwrap(); - esp!(mcpwm_generator_set_actions_on_timer_event( - gen, - mcpwm_gen_timer_event_action_t { - direction: mcpwm_timer_direction_t_MCPWM_TIMER_DIRECTION_DOWN, - event: mcpwm_timer_event_t_MCPWM_TIMER_EVENT_EMPTY, - action: config.on_is_empty.counting_down.into(), - } - )).unwrap(); - esp!(mcpwm_generator_set_actions_on_timer_event( - gen, - mcpwm_gen_timer_event_action_t { - direction: mcpwm_timer_direction_t_MCPWM_TIMER_DIRECTION_UP, - event: mcpwm_timer_event_t_MCPWM_TIMER_EVENT_FULL, - action: config.on_is_full.counting_up.into(), - } - )).unwrap(); - esp!(mcpwm_generator_set_actions_on_timer_event( - gen, - mcpwm_gen_timer_event_action_t { - direction: mcpwm_timer_direction_t_MCPWM_TIMER_DIRECTION_DOWN, - event: mcpwm_timer_event_t_MCPWM_TIMER_EVENT_FULL, - action: config.on_is_full.counting_down.into(), - } - )).unwrap(); - - comparator_x._configure(&mut *gen, config.on_matches_cmp_x); - comparator_y._configure(&mut *gen, config.on_matches_cmp_y); - } - - Generator { handle: gen, pin } -} - impl Operator where G: Group, @@ -381,13 +268,7 @@ where GEN_A: OptionalGen, GEN_B: OptionalGen, { - // TODO: } pub struct NoOperator; impl OptionalOperator for NoOperator {} - -#[derive(Default)] -pub struct OperatorConfig { - _todo: (), -} diff --git a/src/mcpwm/operator_config.rs b/src/mcpwm/operator_config.rs new file mode 100644 index 00000000000..fb2aedc8c93 --- /dev/null +++ b/src/mcpwm/operator_config.rs @@ -0,0 +1,224 @@ +use core::ptr; + +use esp_idf_sys::{esp, mcpwm_operator_config_t__bindgen_ty_1}; + +use crate::gpio::OutputPin; + +use super::{ + comparator::{ComparatorConfig, NoCmpCfg, OptionalCmpCfg}, + generator::{GenA, GenB, GeneratorConfig, NoGenCfg, OptionalGenCfg, CountingDirection}, + OPERATOR, +}; + +#[derive(Default)] +pub struct OperatorConfig { + // TODO: When, how and who should set the flags? + // Should that be done automagically, manually or does some specific setting cover all cases? + /// Flags for Operator + pub(crate) flags: mcpwm_operator_config_t__bindgen_ty_1, + + /// Configuration for Comparator X + pub(crate) comparator_x: CMP_X, + + /// Configuration for Comparator Y + pub(crate) comparator_y: CMP_Y, + + /// Configuration for Generator A + pub(crate) generator_a: GEN_A, + + /// Configuration for Generator B + pub(crate) generator_b: GEN_B, +} + +impl OperatorConfig { + pub fn default( + pin_a: PA, + pin_b: PB, + ) -> OperatorConfig< + ComparatorConfig, + ComparatorConfig, + GeneratorConfig, + GeneratorConfig, + > { + OperatorConfig::empty() + .cmp_x(Default::default()) + .cmp_y(Default::default()) + .gen_a(GeneratorConfig::active_high(pin_a)) + .gen_b(GeneratorConfig::active_high(pin_b)) + } + + pub fn empty() -> Self { + let mut flags: mcpwm_operator_config_t__bindgen_ty_1 = Default::default(); + + // TODO: What value makes most sense as default here? + flags.set_update_gen_action_on_tez(1); + flags.set_update_gen_action_on_tep(1); + flags.set_update_gen_action_on_sync(1); + + flags.set_update_dead_time_on_tez(1); + flags.set_update_dead_time_on_tep(1); + flags.set_update_dead_time_on_sync(1); + + OperatorConfig { + flags, + comparator_x: NoCmpCfg, // SOC_MCPWM_COMPARATORS_PER_OPERATOR is 2 for ESP32 and ESP32-S3 + comparator_y: NoCmpCfg, + + generator_a: NoGenCfg, // One generator per pin, with a maximum of two generators per Operator + generator_b: NoGenCfg, + } + } +} + +impl OperatorConfig +where + CMP_X: OptionalCmpCfg, + CMP_Y: OptionalCmpCfg, + GEN_A: OptionalGenCfg, + GEN_B: OptionalGenCfg, +{ + fn set_update_dead_time_on_tez(mut self, update_dead_time_on_tez: bool) -> Self { + self.flags + .set_update_dead_time_on_tez(update_dead_time_on_tez.into()); + self + } + + fn set_update_dead_time_on_tep(mut self, set_update_dead_time_on_tep: bool) -> Self { + self.flags + .set_update_dead_time_on_tep(set_update_dead_time_on_tep.into()); + self + } + + fn set_update_dead_time_on_sync(mut self, update_dead_time_on_sync: bool) -> Self { + self.flags + .set_update_dead_time_on_sync(update_dead_time_on_sync.into()); + self + } + + fn set_update_gen_action_on_tez(mut self, update_gen_action_on_tez: bool) -> Self { + self.flags + .set_update_gen_action_on_tez(update_gen_action_on_tez.into()); + self + } + + fn set_update_gen_action_on_tep(mut self, set_update_gen_action_on_tep: bool) -> Self { + self.flags + .set_update_gen_action_on_tep(set_update_gen_action_on_tep.into()); + self + } + + fn set_update_gen_action_on_sync(mut self, update_gen_action_on_sync: bool) -> Self { + self.flags + .set_update_gen_action_on_sync(update_gen_action_on_sync.into()); + self + } +} + +impl OperatorConfig { + fn cmp_x( + self, + config: ComparatorConfig, + ) -> OperatorConfig { + /* + let mut flags: mcpwm_comparator_config_t__bindgen_ty_1 = Default::default(); + flags.set_update_cmp_on_tep(todo!()); + flags.set_update_cmp_on_tez(todo!()); + flags.set_update_cmp_on_sync(todo!()); + + let cfg = mcpwm_comparator_config_t { flags }; + + let mut cmp = ptr::null_mut(); + unsafe { + esp!(mcpwm_new_comparator(self.handle, &cfg, &mut cmp)).unwrap(); + } + let comparator_x = Comparator(cmp);*/ + + OperatorConfig { + flags: self.flags, + comparator_x: config, + comparator_y: self.comparator_y, + + generator_a: self.generator_a, + generator_b: self.generator_b, + } + } +} + +impl OperatorConfig { + fn cmp_y( + self, + config: ComparatorConfig, + ) -> OperatorConfig { + /*let mut flags: mcpwm_comparator_config_t__bindgen_ty_1 = Default::default(); + flags.set_update_cmp_on_tep(todo!()); + flags.set_update_cmp_on_tez(todo!()); + flags.set_update_cmp_on_sync(todo!()); + + let cfg = mcpwm_comparator_config_t { flags }; + let mut cmp = ptr::null_mut(); + unsafe { + esp!(mcpwm_new_comparator(self.handle, &cfg, &mut cmp)).unwrap(); + } + let comparator_y = Comparator(cmp); + */ + + OperatorConfig { + flags: self.flags, + comparator_x: self.comparator_x, + comparator_y: config, + + generator_a: self.generator_a, + generator_b: self.generator_b, + } + } +} + +// TODO: Make sure that a generator config can only refer to comparators that are assigned to the operator +// TODO: Is there any point in letting the user provide the comparators or should two (the only two available +// for that operator in hardware) be automatically assigned in `Operator::new`? + +impl + OperatorConfig +{ + fn gen_a( + mut self, + config: GeneratorConfig, + ) -> OperatorConfig< + CMP_X, + CMP_Y, + GeneratorConfig, + GEN_B, + > { + OperatorConfig { + flags: self.flags, + comparator_x: self.comparator_x, + comparator_y: self.comparator_y, + + generator_a: config, + generator_b: self.generator_b, + } + } +} + +impl + OperatorConfig +{ + fn gen_b( + mut self, + config: GeneratorConfig, + ) -> OperatorConfig< + CMP_X, + CMP_Y, + GEN_A, + GeneratorConfig, + > { + OperatorConfig { + flags: self.flags, + comparator_x: self.comparator_x, + comparator_y: self.comparator_y, + + generator_a: self.generator_a, + generator_b: config, + } + } +} \ No newline at end of file diff --git a/src/mcpwm/timer_connection.rs b/src/mcpwm/timer_connection.rs index c8d51825e46..63851effc88 100644 --- a/src/mcpwm/timer_connection.rs +++ b/src/mcpwm/timer_connection.rs @@ -3,9 +3,9 @@ use crate::mcpwm::Group; use super::{ comparator::OptionalCmp, generator::OptionalGen, - operator::{NoOperator, OperatorConfig, OptionalOperator, OPERATOR}, + operator::{NoOperator, OptionalOperator, OPERATOR}, timer::Timer, - Operator, + Operator, OperatorConfig, }; // TODO: How do we want fault module to fit into this? @@ -65,9 +65,7 @@ where pub fn attatch_operator0( self, operator_handle: OPERATOR<0, G>, - operator_cfg: OperatorConfig, - pin_a: PA, - pin_b: PB, + operator_cfg: OperatorConfig, ) -> TimerConnection, O1, O2> where CMP_X: OptionalCmp, @@ -77,7 +75,7 @@ where PA: OptionalOutputPin, PB: OptionalOutputPin, { - let operator = todo!(); //self.init_and_attach_operator(operator_cfg, pin_a, pin_b); + let operator = unsafe { Operator::new(operator_handle, self.timer.timer(), operator_config) }; TimerConnection { timer: self.timer, operator0: operator, @@ -96,7 +94,7 @@ where pub fn attatch_operator1( self, operator_handle: OPERATOR<1, G>, - operator_cfg: OperatorConfig, + operator_cfg: OperatorConfig, pin_a: PA, pin_b: PB, ) -> TimerConnection, O2> @@ -127,7 +125,7 @@ where pub fn attatch_operator2( self, operator_handle: OPERATOR<2, G>, - operator_cfg: OperatorConfig, + operator_cfg: OperatorConfig, pin_a: PA, pin_b: PB, ) -> TimerConnection> From 8d8076090d7a8e566888fe664ae5d0b51c67331d Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Mon, 28 Nov 2022 19:19:26 +0100 Subject: [PATCH 36/62] Compiling again, still some work left to do before working --- src/mcpwm/comparator.rs | 63 ++++++++++----- src/mcpwm/generator.rs | 147 +++++++++++++++++++--------------- src/mcpwm/operator.rs | 124 +++++++++++++--------------- src/mcpwm/timer_connection.rs | 32 ++++---- 4 files changed, 200 insertions(+), 166 deletions(-) diff --git a/src/mcpwm/comparator.rs b/src/mcpwm/comparator.rs index e2c52a7eca7..2649cf322da 100644 --- a/src/mcpwm/comparator.rs +++ b/src/mcpwm/comparator.rs @@ -1,11 +1,15 @@ use core::ptr; -use esp_idf_sys::{mcpwm_cmpr_handle_t, mcpwm_gen_t, mcpwm_generator_set_actions_on_compare_event, mcpwm_timer_direction_t_MCPWM_TIMER_DIRECTION_UP, mcpwm_generator_action_t_MCPWM_GEN_ACTION_KEEP, mcpwm_timer_direction_t_MCPWM_TIMER_DIRECTION_DOWN, mcpwm_comparator_config_t__bindgen_ty_1, esp, mcpwm_comparator_config_t, mcpwm_new_comparator, mcpwm_oper_handle_t}; +use esp_idf_sys::{ + esp, mcpwm_cmpr_handle_t, mcpwm_comparator_config_t, mcpwm_comparator_config_t__bindgen_ty_1, + mcpwm_gen_t, mcpwm_generator_set_actions_on_compare_event, mcpwm_new_comparator, + mcpwm_oper_handle_t, mcpwm_timer_direction_t_MCPWM_TIMER_DIRECTION_DOWN, + mcpwm_timer_direction_t_MCPWM_TIMER_DIRECTION_UP, +}; -use super::generator::{CountingDirection, OnMatchCfg, NoCmpMatchConfig}; +use super::generator::{CountingDirection, NoCmpMatchConfig, OnMatchCfg}; - -trait ComparatorChannel{} +trait ComparatorChannel {} pub struct CmpX; impl ComparatorChannel for CmpX {} @@ -15,22 +19,17 @@ impl ComparatorChannel for CmpY {} pub trait OptionalCmp { type OnMatchCfg: OnMatchCfg; - type Cfg: OptionalCmpCfg; - unsafe fn _configure(&mut self, gen: &mut mcpwm_gen_t, cfg: Self::OnMatchCfg); + type Cfg: OptionalCmpCfg; + + fn get_comparator_mut(&mut self) -> Option<&mut Comparator>; } impl OptionalCmp for Comparator { type OnMatchCfg = super::generator::CountingDirection; type Cfg = ComparatorConfig; - unsafe fn _configure(&mut self, gen: &mut mcpwm_gen_t, cfg: Self::OnMatchCfg) { - // TODO: "must be terminated by MCPWM_GEN_TIMER_EVENT_ACTION_END()" - mcpwm_generator_set_actions_on_compare_event(gen, esp_idf_sys::mcpwm_gen_compare_event_action_t { - direction: mcpwm_timer_direction_t_MCPWM_TIMER_DIRECTION_UP, comparator: self.0, action: cfg.counting_up.into() - }); - mcpwm_generator_set_actions_on_compare_event(gen, esp_idf_sys::mcpwm_gen_compare_event_action_t { - direction: mcpwm_timer_direction_t_MCPWM_TIMER_DIRECTION_DOWN, comparator: self.0, action: cfg.counting_down.into() - }); + fn get_comparator_mut(&mut self) -> Option<&mut Comparator> { + Some(self) } } @@ -39,12 +38,34 @@ impl OptionalCmp for NoCmp { type OnMatchCfg = super::generator::NoCmpMatchConfig; type Cfg = NoCmpCfg; - unsafe fn _configure(&mut self, gen: &mut mcpwm_gen_t, cfg: Self::OnMatchCfg) { - // Do nothing + fn get_comparator_mut(&mut self) -> Option<&mut Comparator> { + None } } -pub struct Comparator(pub(crate)mcpwm_cmpr_handle_t); +pub struct Comparator(pub(crate) mcpwm_cmpr_handle_t); + +impl Comparator { + pub(crate) unsafe fn configure(&mut self, gen: &mut mcpwm_gen_t, cfg: CountingDirection) { + // TODO: "must be terminated by MCPWM_GEN_TIMER_EVENT_ACTION_END()" + mcpwm_generator_set_actions_on_compare_event( + gen, + esp_idf_sys::mcpwm_gen_compare_event_action_t { + direction: mcpwm_timer_direction_t_MCPWM_TIMER_DIRECTION_UP, + comparator: self.0, + action: cfg.counting_up.into(), + }, + ); + mcpwm_generator_set_actions_on_compare_event( + gen, + esp_idf_sys::mcpwm_gen_compare_event_action_t { + direction: mcpwm_timer_direction_t_MCPWM_TIMER_DIRECTION_DOWN, + comparator: self.0, + action: cfg.counting_down.into(), + }, + ); + } +} #[derive(Debug, Clone, Copy)] pub struct ComparatorConfig { @@ -63,7 +84,7 @@ impl Default for ComparatorConfig { pub struct NoCmpCfg; -pub trait OptionalCmpCfg{ +pub trait OptionalCmpCfg { type OnMatchCfg: OnMatchCfg; type Cmp: OptionalCmp; unsafe fn init(self, operator_handle: mcpwm_oper_handle_t) -> Self::Cmp; @@ -77,7 +98,7 @@ impl OptionalCmpCfg for NoCmpCfg { NoCmp } } -impl OptionalCmpCfg for ComparatorConfig{ +impl OptionalCmpCfg for ComparatorConfig { type OnMatchCfg = CountingDirection; type Cmp = Comparator; @@ -88,7 +109,7 @@ impl OptionalCmpCfg for ComparatorConfig{ unsafe { esp!(mcpwm_new_comparator(operator_handle, &cfg, &mut cmp)).unwrap(); } - + Comparator(cmp) } -} \ No newline at end of file +} diff --git a/src/mcpwm/generator.rs b/src/mcpwm/generator.rs index 2ad2d354bb4..67ac485bd91 100644 --- a/src/mcpwm/generator.rs +++ b/src/mcpwm/generator.rs @@ -5,7 +5,7 @@ use esp_idf_sys::{ mcpwm_generator_action_t_MCPWM_GEN_ACTION_HIGH, mcpwm_generator_action_t_MCPWM_GEN_ACTION_KEEP, mcpwm_generator_action_t_MCPWM_GEN_ACTION_LOW, mcpwm_generator_action_t_MCPWM_GEN_ACTION_TOGGLE, mcpwm_generator_config_t, - mcpwm_generator_config_t__bindgen_ty_1, mcpwm_generator_set_actions_on_timer_event, + mcpwm_generator_set_actions_on_timer_event, mcpwm_new_generator, mcpwm_oper_handle_t, mcpwm_timer_direction_t_MCPWM_TIMER_DIRECTION_DOWN, mcpwm_timer_direction_t_MCPWM_TIMER_DIRECTION_UP, mcpwm_timer_event_t_MCPWM_TIMER_EVENT_EMPTY, mcpwm_timer_event_t_MCPWM_TIMER_EVENT_FULL, @@ -14,29 +14,23 @@ use esp_idf_sys::{ use crate::gpio::OutputPin; use super::{ - comparator::OptionalCmp, - timer_connection::{NoPin, OptionalOutputPin}, + comparator::{Comparator, OptionalCmp}, }; pub struct NoGen; -impl OptionalGen for NoGen {} -pub trait OptionalGen {} - -impl OptionalGen for Generator

{} - -pub trait ToGenCfg { - type Cfg: OptionalGenCfg; +impl OptionalGen for NoGen { } - -impl ToGenCfg - for (CMP_X, CMP_Y, G, Generator

) -{ - type Cfg = GeneratorConfig; +pub trait OptionalGen { } -impl ToGenCfg for (CMP_X, CMP_Y, G, NoGenCfg) { - type Cfg = NoGenCfg; +impl OptionalGen for Generator +where + G: GeneratorChannel, + CMP_X: OnMatchCfg, + CMP_Y: OnMatchCfg, + P: OutputPin, +{ } pub trait GeneratorChannel { @@ -54,7 +48,10 @@ impl GeneratorChannel for GenB { } // TODO: Allow OptionalOutputPin? -pub struct Generator { +pub struct Generator { + channel: PhantomData, + cmp_x: PhantomData, + cmp_y: PhantomData, pub(crate) handle: mcpwm_gen_handle_t, pub(crate) pin: P, } @@ -71,57 +68,45 @@ pub struct GeneratorConfig { pub struct NoGenCfg; -pub trait OptionalGenCfg {} - -impl OptionalGenCfg for NoGenCfg {} - -impl OptionalGenCfg - for GeneratorConfig -{ -} - -pub trait GenInit { +pub trait OptionalGenCfg { type Gen: OptionalGen; /// This is only to be used internally by esp-idf-hal - unsafe fn init(self, operator_handle: mcpwm_oper_handle_t) -> Self::Gen; + unsafe fn init( + self, + operator_handle: mcpwm_oper_handle_t, + cmp_x: Option<&mut Comparator>, + cmp_y: Option<&mut Comparator>, + ) -> Self::Gen; } -impl GenInit - for ( - &mut CMP_X, - &mut CMP_Y, - NoGenCfg, - ) -where - CMP_X: OptionalCmp, - CMP_Y: OptionalCmp, -{ +impl OptionalGenCfg for NoGenCfg { type Gen = NoGen; - unsafe fn init(self, operator_handle: mcpwm_oper_handle_t) -> Self::Gen { + unsafe fn init( + self, + _operator_handle: mcpwm_oper_handle_t, + _cmp_x: Option<&mut Comparator>, + _cmp_y: Option<&mut Comparator>, + ) -> NoGen { NoGen } } -impl GenInit - for ( - &mut CMP_X, - &mut CMP_Y, - GeneratorConfig, - ) -where - CMP_X: OptionalCmp, - CMP_Y: OptionalCmp, +impl OptionalGenCfg + for GeneratorConfig { - type Gen = Generator

; - - unsafe fn init(mut self, operator_handle: mcpwm_oper_handle_t) -> Generator

{ - let (cmp_x, cmp_y, generator_config) = self; - + type Gen = Generator; + + unsafe fn init( + self, + operator_handle: mcpwm_oper_handle_t, + cmp_x: Option<&mut Comparator>, + cmp_y: Option<&mut Comparator>, + ) -> Self::Gen { let cfg = mcpwm_generator_config_t { - gen_gpio_num: generator_config.pin.pin(), - flags: todo!(),//generator_config.flags, + gen_gpio_num: self.pin.pin(), + flags: todo!(), //generator_config.flags, }; let mut gen = ptr::null_mut(); unsafe { @@ -133,7 +118,7 @@ where mcpwm_gen_timer_event_action_t { direction: mcpwm_timer_direction_t_MCPWM_TIMER_DIRECTION_UP, event: mcpwm_timer_event_t_MCPWM_TIMER_EVENT_EMPTY, - action: generator_config.on_is_empty.counting_up.into(), + action: self.on_is_empty.counting_up.into(), } )) .unwrap(); @@ -142,7 +127,7 @@ where mcpwm_gen_timer_event_action_t { direction: mcpwm_timer_direction_t_MCPWM_TIMER_DIRECTION_DOWN, event: mcpwm_timer_event_t_MCPWM_TIMER_EVENT_EMPTY, - action: generator_config.on_is_empty.counting_down.into(), + action: self.on_is_empty.counting_down.into(), } )) .unwrap(); @@ -151,7 +136,7 @@ where mcpwm_gen_timer_event_action_t { direction: mcpwm_timer_direction_t_MCPWM_TIMER_DIRECTION_UP, event: mcpwm_timer_event_t_MCPWM_TIMER_EVENT_FULL, - action: generator_config.on_is_full.counting_up.into(), + action: self.on_is_full.counting_up.into(), } )) .unwrap(); @@ -160,22 +145,49 @@ where mcpwm_gen_timer_event_action_t { direction: mcpwm_timer_direction_t_MCPWM_TIMER_DIRECTION_DOWN, event: mcpwm_timer_event_t_MCPWM_TIMER_EVENT_FULL, - action: generator_config.on_is_full.counting_down.into(), + action: self.on_is_full.counting_down.into(), } )) .unwrap(); - cmp_x._configure(&mut *gen, generator_config.on_matches_cmp_x); - cmp_y._configure(&mut *gen, generator_config.on_matches_cmp_y); + if let Some(cmp_x) = cmp_x { + cmp_x.configure(&mut *gen, self.on_matches_cmp_x.to_counting_direction()); + } + + if let Some(cmp_y) = cmp_y { + cmp_y.configure(&mut *gen, self.on_matches_cmp_y.to_counting_direction()); + } } Generator { + channel: PhantomData, + cmp_x: PhantomData, + cmp_y: PhantomData, handle: gen, - pin: generator_config.pin, + pin: self.pin, } } } +pub trait GenInit { + type Gen: OptionalGen; + + /// This is only to be used internally by esp-idf-hal + unsafe fn init(self, operator_handle: mcpwm_oper_handle_t) -> Self::Gen; +} + +impl GenInit for (&mut CMP_X, &mut CMP_Y, NoGenCfg) +where + CMP_X: OptionalCmp, + CMP_Y: OptionalCmp, +{ + type Gen = NoGen; + + unsafe fn init(self, operator_handle: mcpwm_oper_handle_t) -> Self::Gen { + NoGen + } +} + impl GeneratorConfig { pub fn active_high(pin: P) -> Self { let mut result: Self = GeneratorConfig::empty(pin); @@ -233,6 +245,10 @@ impl OnMatchCfg for NoCmpMatchConfig { fn empty() -> Self { NoCmpMatchConfig } + + fn to_counting_direction(self) -> CountingDirection { + CountingDirection::empty() + } } // TODO: Come up with better name @@ -255,10 +271,15 @@ impl OnMatchCfg for CountingDirection { fn empty() -> Self { CountingDirection::empty() } + + fn to_counting_direction(self) -> CountingDirection { + self + } } pub trait OnMatchCfg { fn empty() -> Self; + fn to_counting_direction(self) -> CountingDirection; } impl Into for GeneratorAction { diff --git a/src/mcpwm/operator.rs b/src/mcpwm/operator.rs index ce2749ffcdb..2da02f8594f 100644 --- a/src/mcpwm/operator.rs +++ b/src/mcpwm/operator.rs @@ -13,10 +13,7 @@ use crate::mcpwm::Group; use super::{ comparator::{Comparator, ComparatorConfig, NoCmp, OptionalCmp, OptionalCmpCfg}, - generator::{ - GenA, GenB, GenInit, NoGen, OptionalGen, - ToGenCfg, - }, + generator::{GenA, GenB, GenInit, NoGen, OptionalGen, OptionalGenCfg}, OperatorConfig, }; @@ -80,79 +77,70 @@ pub struct Operator< //deadtime: D } -impl<'a, const N: u8, G: Group, CMP_X, CMP_Y, GEN_A, GEN_B> - Operator +pub unsafe fn new( + instance: OPERATOR, + timer_handle: mcpwm_timer_handle_t, + cfg: OperatorConfig, +) -> Operator where - CMP_X: OptionalCmp, - CMP_Y: OptionalCmp, + G: Group, + CMP_X: OptionalCmpCfg, + CMP_Y: OptionalCmpCfg, - GEN_A: OptionalGen, - GEN_B: OptionalGen, - (CMP_X, CMP_Y, GenA, GEN_A): ToGenCfg, // TODO: Is there a less hacky way to do this? - (CMP_X, CMP_Y, GenB, GEN_B): ToGenCfg, - ( - &'a mut CMP_X, - &'a mut CMP_Y, - <(CMP_X, CMP_Y, GenA, GEN_A) as ToGenCfg>::Cfg, - ): GenInit + 'a, - ( - &'a mut CMP_X, - &'a mut CMP_Y, - <(CMP_X, CMP_Y, GenB, GEN_B) as ToGenCfg>::Cfg, - ): GenInit + 'a, + GEN_A: OptionalGenCfg, + GEN_B: OptionalGenCfg, { - pub unsafe fn new( - instance: OPERATOR, - timer_handle: mcpwm_timer_handle_t, - cfg: OperatorConfig< - CMP_X::Cfg, - CMP_Y::Cfg, - <(CMP_X, CMP_Y, GenA, GEN_A) as ToGenCfg>::Cfg, - <(CMP_X, CMP_Y, GenB, GEN_B) as ToGenCfg>::Cfg, - >, - ) -> Self { - let mut handle = ptr::null_mut(); - let mut flags: mcpwm_operator_config_t__bindgen_ty_1 = Default::default(); - - flags.set_update_gen_action_on_tez(todo!()); - flags.set_update_gen_action_on_tep(todo!()); - flags.set_update_gen_action_on_sync(todo!()); - - flags.set_update_dead_time_on_tez(todo!()); - flags.set_update_dead_time_on_tep(todo!()); - flags.set_update_dead_time_on_sync(todo!()); - - let config = mcpwm_operator_config_t { - group_id: G::ID, - flags, - }; + let mut handle = ptr::null_mut(); + let mut flags: mcpwm_operator_config_t__bindgen_ty_1 = Default::default(); - unsafe { - esp!(esp_idf_sys::mcpwm_new_operator(&config, &mut handle,)).unwrap(); - } + flags.set_update_gen_action_on_tez(todo!()); + flags.set_update_gen_action_on_tep(todo!()); + flags.set_update_gen_action_on_sync(todo!()); - let comparator_x = unsafe { cfg.comparator_x.init(handle) }; - let comparator_y = unsafe { cfg.comparator_y.init(handle) }; + flags.set_update_dead_time_on_tez(todo!()); + flags.set_update_dead_time_on_tep(todo!()); + flags.set_update_dead_time_on_sync(todo!()); - let generator_a = - unsafe { (&mut comparator_x, &mut comparator_y, cfg.generator_a).init(handle) }; - let generator_b = - unsafe { (&mut comparator_x, &mut comparator_y, cfg.generator_b).init(handle) }; + let config = mcpwm_operator_config_t { + group_id: G::ID, + flags, + }; - // Connect operator to timer - unsafe { - esp!(mcpwm_operator_connect_timer(handle, timer_handle)).unwrap(); - } + unsafe { + esp!(esp_idf_sys::mcpwm_new_operator(&config, &mut handle,)).unwrap(); + } - Operator { - _instance: instance, - _handle: handle, - comparator_x, - comparator_y, + let comparator_x = unsafe { cfg.comparator_x.init(handle) }; + let comparator_y = unsafe { cfg.comparator_y.init(handle) }; + + let generator_a = unsafe { + cfg.generator_a.init( + handle, + comparator_x.get_comparator_mut(), + comparator_y.get_comparator_mut(), + ) + }; + let generator_b = unsafe { + cfg.generator_b.init( + handle, + comparator_x.get_comparator_mut(), + comparator_y.get_comparator_mut(), + ) + }; + + // Connect operator to timer + unsafe { + esp!(mcpwm_operator_connect_timer(handle, timer_handle)).unwrap(); + } - _generator_a: generator_a, - _generator_b: generator_b, - } + Operator { + _instance: instance, + _handle: handle, + comparator_x, + comparator_y, + + _generator_a: generator_a, + _generator_b: generator_b, } } diff --git a/src/mcpwm/timer_connection.rs b/src/mcpwm/timer_connection.rs index 63851effc88..d5790c560a8 100644 --- a/src/mcpwm/timer_connection.rs +++ b/src/mcpwm/timer_connection.rs @@ -1,9 +1,9 @@ use crate::mcpwm::Group; use super::{ - comparator::OptionalCmp, - generator::OptionalGen, - operator::{NoOperator, OptionalOperator, OPERATOR}, + comparator::{OptionalCmp, OptionalCmpCfg}, + generator::{OptionalGen, GenA, GenB, GenInit, OptionalGenCfg}, + operator::{NoOperator, OptionalOperator, OPERATOR, self}, timer::Timer, Operator, OperatorConfig, }; @@ -62,20 +62,24 @@ where O1: OptionalOperator<1, G>, O2: OptionalOperator<2, G>, { - pub fn attatch_operator0( + pub fn attatch_operator0( self, operator_handle: OPERATOR<0, G>, - operator_cfg: OperatorConfig, - ) -> TimerConnection, O1, O2> - where - CMP_X: OptionalCmp, - CMP_Y: OptionalCmp, - GEN_A: OptionalGen, - GEN_B: OptionalGen, - PA: OptionalOutputPin, - PB: OptionalOutputPin, + operator_cfg: OperatorConfig< + CMP_X, + CMP_Y, + GEN_A, + GEN_B, + >, + ) -> TimerConnection, O1, O2> + where// TODO: Any ideas on how to simplify this mess? + CMP_X: OptionalCmpCfg, + CMP_Y: OptionalCmpCfg, + GEN_A: OptionalGenCfg, + GEN_B: OptionalGenCfg, { - let operator = unsafe { Operator::new(operator_handle, self.timer.timer(), operator_config) }; + let operator = + unsafe { operator::new(operator_handle, self.timer.timer(), operator_cfg) }; TimerConnection { timer: self.timer, operator0: operator, From 1907eef7f9e4303021be1cec9a2d11040751a6c2 Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Mon, 28 Nov 2022 20:11:03 +0100 Subject: [PATCH 37/62] Clean up, fix some todos, might be testable now/soon! --- src/mcpwm/comparator.rs | 7 +- src/mcpwm/generator.rs | 58 +++++++++-------- src/mcpwm/operator.rs | 116 +++++++++++++--------------------- src/mcpwm/operator_config.rs | 68 ++++++++++---------- src/mcpwm/timer.rs | 22 +++++-- src/mcpwm/timer_connection.rs | 86 ++++++++++++------------- 6 files changed, 168 insertions(+), 189 deletions(-) diff --git a/src/mcpwm/comparator.rs b/src/mcpwm/comparator.rs index 2649cf322da..5495da0a066 100644 --- a/src/mcpwm/comparator.rs +++ b/src/mcpwm/comparator.rs @@ -75,9 +75,10 @@ pub struct ComparatorConfig { impl Default for ComparatorConfig { fn default() -> Self { let mut flags: mcpwm_comparator_config_t__bindgen_ty_1 = Default::default(); - flags.set_update_cmp_on_tep(todo!()); - flags.set_update_cmp_on_tez(todo!()); - flags.set_update_cmp_on_sync(todo!()); + // TODO: What should be set here? + flags.set_update_cmp_on_tep(1); + flags.set_update_cmp_on_tez(1); + flags.set_update_cmp_on_sync(1); Self { flags } } } diff --git a/src/mcpwm/generator.rs b/src/mcpwm/generator.rs index 67ac485bd91..7890fcc9deb 100644 --- a/src/mcpwm/generator.rs +++ b/src/mcpwm/generator.rs @@ -8,7 +8,7 @@ use esp_idf_sys::{ mcpwm_generator_set_actions_on_timer_event, mcpwm_new_generator, mcpwm_oper_handle_t, mcpwm_timer_direction_t_MCPWM_TIMER_DIRECTION_DOWN, mcpwm_timer_direction_t_MCPWM_TIMER_DIRECTION_UP, mcpwm_timer_event_t_MCPWM_TIMER_EVENT_EMPTY, - mcpwm_timer_event_t_MCPWM_TIMER_EVENT_FULL, + mcpwm_timer_event_t_MCPWM_TIMER_EVENT_FULL, mcpwm_generator_config_t__bindgen_ty_1, }; use crate::gpio::OutputPin; @@ -24,11 +24,11 @@ impl OptionalGen for NoGen { pub trait OptionalGen { } -impl OptionalGen for Generator +impl OptionalGen for Generator where G: GeneratorChannel, - CMP_X: OnMatchCfg, - CMP_Y: OnMatchCfg, + CMPX: OnMatchCfg, + CMPY: OnMatchCfg, P: OutputPin, { } @@ -48,19 +48,19 @@ impl GeneratorChannel for GenB { } // TODO: Allow OptionalOutputPin? -pub struct Generator { +pub struct Generator { channel: PhantomData, - cmp_x: PhantomData, - cmp_y: PhantomData, - pub(crate) handle: mcpwm_gen_handle_t, - pub(crate) pin: P, + cmp_x: PhantomData, + cmp_y: PhantomData, + pub(crate) _handle: mcpwm_gen_handle_t, + pub(crate) _pin: P, } -pub struct GeneratorConfig { +pub struct GeneratorConfig { _channel: PhantomData, - pub(crate) invert: bool, - pub(crate) on_matches_cmp_x: CMP_X, - pub(crate) on_matches_cmp_y: CMP_Y, + pub(crate) flags: mcpwm_generator_config_t__bindgen_ty_1, + pub(crate) on_matches_cmp_x: CMPX, + pub(crate) on_matches_cmp_y: CMPY, pub(crate) on_is_empty: CountingDirection, pub(crate) on_is_full: CountingDirection, pub(crate) pin: P, @@ -93,10 +93,10 @@ impl OptionalGenCfg for NoGenCfg { } } -impl OptionalGenCfg - for GeneratorConfig +impl OptionalGenCfg + for GeneratorConfig { - type Gen = Generator; + type Gen = Generator; unsafe fn init( self, @@ -106,7 +106,7 @@ impl Op ) -> Self::Gen { let cfg = mcpwm_generator_config_t { gen_gpio_num: self.pin.pin(), - flags: todo!(), //generator_config.flags, + flags: self.flags, }; let mut gen = ptr::null_mut(); unsafe { @@ -163,8 +163,8 @@ impl Op channel: PhantomData, cmp_x: PhantomData, cmp_y: PhantomData, - handle: gen, - pin: self.pin, + _handle: gen, + _pin: self.pin, } } } @@ -176,14 +176,14 @@ pub trait GenInit { unsafe fn init(self, operator_handle: mcpwm_oper_handle_t) -> Self::Gen; } -impl GenInit for (&mut CMP_X, &mut CMP_Y, NoGenCfg) +impl GenInit for (&mut CMPX, &mut CMPY, NoGenCfg) where - CMP_X: OptionalCmp, - CMP_Y: OptionalCmp, + CMPX: OptionalCmp, + CMPY: OptionalCmp, { type Gen = NoGen; - unsafe fn init(self, operator_handle: mcpwm_oper_handle_t) -> Self::Gen { + unsafe fn init(self, _operator_handle: mcpwm_oper_handle_t) -> Self::Gen { NoGen } } @@ -214,16 +214,20 @@ impl GeneratorConfig GeneratorConfig +impl GeneratorConfig where G: GeneratorChannel, - CMP_X: OnMatchCfg, - CMP_Y: OnMatchCfg, + CMPX: OnMatchCfg, + CMPY: OnMatchCfg, { fn empty(pin: P) -> Self { + let mut flags: mcpwm_generator_config_t__bindgen_ty_1 = Default::default(); + flags.set_invert_pwm(0); + flags.set_io_loop_back(0); + GeneratorConfig { _channel: PhantomData, - invert: false, + flags, on_matches_cmp_x: OnMatchCfg::empty(), on_matches_cmp_y: OnMatchCfg::empty(), on_is_empty: CountingDirection { diff --git a/src/mcpwm/operator.rs b/src/mcpwm/operator.rs index 2da02f8594f..99e47a1908c 100644 --- a/src/mcpwm/operator.rs +++ b/src/mcpwm/operator.rs @@ -1,19 +1,14 @@ use esp_idf_sys::{ - esp, mcpwm_comparator_config_t, mcpwm_comparator_config_t__bindgen_ty_1, - mcpwm_comparator_set_compare_value, mcpwm_gen_timer_event_action_t, mcpwm_generator_config_t, - mcpwm_generator_config_t__bindgen_ty_1, mcpwm_generator_set_actions_on_compare_event, - mcpwm_generator_set_actions_on_timer_event, mcpwm_new_comparator, mcpwm_new_generator, - mcpwm_oper_handle_t, mcpwm_operator_config_t, mcpwm_operator_config_t__bindgen_ty_1, - mcpwm_operator_connect_timer, mcpwm_timer_direction_t_MCPWM_TIMER_DIRECTION_DOWN, - mcpwm_timer_direction_t_MCPWM_TIMER_DIRECTION_UP, mcpwm_timer_event_t_MCPWM_TIMER_EVENT_EMPTY, - mcpwm_timer_event_t_MCPWM_TIMER_EVENT_FULL, mcpwm_timer_handle_t, EspError, + esp, mcpwm_comparator_set_compare_value, mcpwm_oper_handle_t, mcpwm_operator_config_t, + mcpwm_operator_config_t__bindgen_ty_1, mcpwm_operator_connect_timer, mcpwm_timer_handle_t, + EspError, }; use crate::mcpwm::Group; use super::{ - comparator::{Comparator, ComparatorConfig, NoCmp, OptionalCmp, OptionalCmpCfg}, - generator::{GenA, GenB, GenInit, NoGen, OptionalGen, OptionalGenCfg}, + comparator::{Comparator, OptionalCmp, OptionalCmpCfg}, + generator::{OptionalGen, OptionalGenCfg}, OperatorConfig, }; @@ -61,45 +56,46 @@ impl crate::peripheral::Peripheral for OPERATOR { pub struct Operator< const N: u8, G: Group, - CMP_X: OptionalCmp, - CMP_Y: OptionalCmp, - GEN_A: OptionalGen, - GEN_B: OptionalGen, + CMPX: OptionalCmp, + CMPY: OptionalCmp, + GENA: OptionalGen, + GENB: OptionalGen, > { _instance: OPERATOR, _handle: mcpwm_oper_handle_t, - comparator_x: CMP_X, // SOC_MCPWM_COMPARATORS_PER_OPERATOR is 2 for ESP32 and ESP32-S3 - comparator_y: CMP_Y, + comparator_x: CMPX, // SOC_MCPWM_COMPARATORS_PER_OPERATOR is 2 for ESP32 and ESP32-S3 + comparator_y: CMPY, - _generator_a: GEN_A, // One generator per pin, with a maximum of two generators per Operator - _generator_b: GEN_B, + _generator_a: GENA, // One generator per pin, with a maximum of two generators per Operator + _generator_b: GENB, //deadtime: D } -pub unsafe fn new( +pub unsafe fn new( instance: OPERATOR, timer_handle: mcpwm_timer_handle_t, - cfg: OperatorConfig, -) -> Operator + cfg: OperatorConfig, +) -> Operator where G: Group, - CMP_X: OptionalCmpCfg, - CMP_Y: OptionalCmpCfg, + CMPX: OptionalCmpCfg, + CMPY: OptionalCmpCfg, - GEN_A: OptionalGenCfg, - GEN_B: OptionalGenCfg, + GENA: OptionalGenCfg, + GENB: OptionalGenCfg, { let mut handle = ptr::null_mut(); let mut flags: mcpwm_operator_config_t__bindgen_ty_1 = Default::default(); - flags.set_update_gen_action_on_tez(todo!()); - flags.set_update_gen_action_on_tep(todo!()); - flags.set_update_gen_action_on_sync(todo!()); + // TODO: What should these be set to? + flags.set_update_gen_action_on_tez(1); + flags.set_update_gen_action_on_tep(1); + flags.set_update_gen_action_on_sync(1); - flags.set_update_dead_time_on_tez(todo!()); - flags.set_update_dead_time_on_tep(todo!()); - flags.set_update_dead_time_on_sync(todo!()); + flags.set_update_dead_time_on_tez(1); + flags.set_update_dead_time_on_tep(1); + flags.set_update_dead_time_on_sync(1); let config = mcpwm_operator_config_t { group_id: G::ID, @@ -110,8 +106,8 @@ where esp!(esp_idf_sys::mcpwm_new_operator(&config, &mut handle,)).unwrap(); } - let comparator_x = unsafe { cfg.comparator_x.init(handle) }; - let comparator_y = unsafe { cfg.comparator_y.init(handle) }; + let mut comparator_x = unsafe { cfg.comparator_x.init(handle) }; + let mut comparator_y = unsafe { cfg.comparator_y.init(handle) }; let generator_a = unsafe { cfg.generator_a.init( @@ -144,38 +140,12 @@ where } } -impl Operator { - fn cmp_x(self, config: ComparatorConfig) -> Operator { - let mut flags: mcpwm_comparator_config_t__bindgen_ty_1 = Default::default(); - flags.set_update_cmp_on_tep(todo!()); - flags.set_update_cmp_on_tez(todo!()); - flags.set_update_cmp_on_sync(todo!()); - - let cfg = mcpwm_comparator_config_t { flags }; - let mut cmp = ptr::null_mut(); - unsafe { - esp!(mcpwm_new_comparator(self._handle, &cfg, &mut cmp)).unwrap(); - } - let comparator_x = Comparator(cmp); - - Operator { - _instance: self._instance, - _handle: self._handle, - comparator_x, - comparator_y: self.comparator_y, - - _generator_a: self._generator_a, - _generator_b: self._generator_b, - } - } -} - -impl Operator +impl Operator where G: Group, - CMP_Y: OptionalCmp, - GEN_A: OptionalGen, - GEN_B: OptionalGen, + CMPY: OptionalCmp, + GENA: OptionalGen, + GENB: OptionalGen, { // TODO: Note that this is the comparator we are affecting, not the generator. Generator A may not necessarily have // anything to do with comparator A. How do we best convay that? Should we call them Generator X/Y and Comparator A/B? @@ -213,12 +183,12 @@ where } } -impl Operator +impl Operator where G: Group, - CMP_X: OptionalCmp, - GEN_A: OptionalGen, - GEN_B: OptionalGen, + CMPX: OptionalCmp, + GENA: OptionalGen, + GENB: OptionalGen, { /// Get compare value, often times same as the duty for output B. /// @@ -247,14 +217,14 @@ where } pub trait OptionalOperator {} -impl OptionalOperator - for Operator +impl OptionalOperator + for Operator where G: Group, - CMP_X: OptionalCmp, - CMP_Y: OptionalCmp, - GEN_A: OptionalGen, - GEN_B: OptionalGen, + CMPX: OptionalCmp, + CMPY: OptionalCmp, + GENA: OptionalGen, + GENB: OptionalGen, { } diff --git a/src/mcpwm/operator_config.rs b/src/mcpwm/operator_config.rs index fb2aedc8c93..5836af9c987 100644 --- a/src/mcpwm/operator_config.rs +++ b/src/mcpwm/operator_config.rs @@ -1,33 +1,31 @@ -use core::ptr; -use esp_idf_sys::{esp, mcpwm_operator_config_t__bindgen_ty_1}; +use esp_idf_sys::mcpwm_operator_config_t__bindgen_ty_1; use crate::gpio::OutputPin; use super::{ comparator::{ComparatorConfig, NoCmpCfg, OptionalCmpCfg}, generator::{GenA, GenB, GeneratorConfig, NoGenCfg, OptionalGenCfg, CountingDirection}, - OPERATOR, }; #[derive(Default)] -pub struct OperatorConfig { +pub struct OperatorConfig { // TODO: When, how and who should set the flags? // Should that be done automagically, manually or does some specific setting cover all cases? /// Flags for Operator pub(crate) flags: mcpwm_operator_config_t__bindgen_ty_1, /// Configuration for Comparator X - pub(crate) comparator_x: CMP_X, + pub(crate) comparator_x: CMPX, /// Configuration for Comparator Y - pub(crate) comparator_y: CMP_Y, + pub(crate) comparator_y: CMPY, /// Configuration for Generator A - pub(crate) generator_a: GEN_A, + pub(crate) generator_a: GENA, /// Configuration for Generator B - pub(crate) generator_b: GEN_B, + pub(crate) generator_b: GENB, } impl OperatorConfig { @@ -70,14 +68,14 @@ impl OperatorConfig { } } -impl OperatorConfig +impl OperatorConfig where - CMP_X: OptionalCmpCfg, - CMP_Y: OptionalCmpCfg, - GEN_A: OptionalGenCfg, - GEN_B: OptionalGenCfg, + CMPX: OptionalCmpCfg, + CMPY: OptionalCmpCfg, + GENA: OptionalGenCfg, + GENB: OptionalGenCfg, { - fn set_update_dead_time_on_tez(mut self, update_dead_time_on_tez: bool) -> Self { + /*fn set_update_dead_time_on_tez(mut self, update_dead_time_on_tez: bool) -> Self { self.flags .set_update_dead_time_on_tez(update_dead_time_on_tez.into()); self @@ -111,14 +109,14 @@ where self.flags .set_update_gen_action_on_sync(update_gen_action_on_sync.into()); self - } + }*/ } -impl OperatorConfig { +impl OperatorConfig { fn cmp_x( self, config: ComparatorConfig, - ) -> OperatorConfig { + ) -> OperatorConfig { /* let mut flags: mcpwm_comparator_config_t__bindgen_ty_1 = Default::default(); flags.set_update_cmp_on_tep(todo!()); @@ -144,11 +142,11 @@ impl OperatorConfig } } -impl OperatorConfig { +impl OperatorConfig { fn cmp_y( self, config: ComparatorConfig, - ) -> OperatorConfig { + ) -> OperatorConfig { /*let mut flags: mcpwm_comparator_config_t__bindgen_ty_1 = Default::default(); flags.set_update_cmp_on_tep(todo!()); flags.set_update_cmp_on_tez(todo!()); @@ -177,17 +175,17 @@ impl OperatorConfig // TODO: Is there any point in letting the user provide the comparators or should two (the only two available // for that operator in hardware) be automatically assigned in `Operator::new`? -impl - OperatorConfig +impl + OperatorConfig { fn gen_a( - mut self, - config: GeneratorConfig, + self, + config: GeneratorConfig, ) -> OperatorConfig< - CMP_X, - CMP_Y, - GeneratorConfig, - GEN_B, + CMPX, + CMPY, + GeneratorConfig, + GENB, > { OperatorConfig { flags: self.flags, @@ -200,17 +198,17 @@ impl } } -impl - OperatorConfig +impl + OperatorConfig { fn gen_b( - mut self, - config: GeneratorConfig, + self, + config: GeneratorConfig, ) -> OperatorConfig< - CMP_X, - CMP_Y, - GEN_A, - GeneratorConfig, + CMPX, + CMPY, + GENA, + GeneratorConfig, > { OperatorConfig { flags: self.flags, diff --git a/src/mcpwm/timer.rs b/src/mcpwm/timer.rs index c22e8d976d4..7a2197f5524 100644 --- a/src/mcpwm/timer.rs +++ b/src/mcpwm/timer.rs @@ -4,7 +4,7 @@ use std::ptr; use esp_idf_sys::{ esp, mcpwm_del_timer, mcpwm_new_timer, mcpwm_timer_config_t, mcpwm_timer_enable, - mcpwm_timer_handle_t, + mcpwm_timer_handle_t, mcpwm_timer_config_t__bindgen_ty_1, mcpwm_timer_sync_src_config_t__bindgen_ty_1, soc_periph_mcpwm_timer_clk_src_t_MCPWM_TIMER_CLK_SRC_DEFAULT, mcpwm_timer_count_mode_t_MCPWM_TIMER_COUNT_MODE_UP, }; use crate::mcpwm::Group; @@ -82,13 +82,19 @@ pub struct Timer { impl Timer { pub fn new(timer: TIMER, config: TimerConfig) -> Self { + let mut flags: mcpwm_timer_config_t__bindgen_ty_1 = Default::default(); + + // TODO: What should these be set to? + flags.set_update_period_on_empty(1); + flags.set_update_period_on_sync(0); + let cfg = mcpwm_timer_config_t { - group_id: todo!(), - clk_src: todo!(), - resolution_hz: todo!(), - count_mode: todo!(), - period_ticks: todo!(), - flags: todo!(), + group_id: G::ID, + clk_src: soc_periph_mcpwm_timer_clk_src_t_MCPWM_TIMER_CLK_SRC_DEFAULT, + resolution_hz: 160_000_000, // 160MHz + count_mode: mcpwm_timer_count_mode_t_MCPWM_TIMER_COUNT_MODE_UP, + period_ticks: 16_000, // 10kHz + flags, }; let mut handle: mcpwm_timer_handle_t = ptr::null_mut(); unsafe { @@ -163,6 +169,8 @@ impl Timer { } } +// TODO: Should this be done in TimerConnection instead to ensure everything is taken down +// in the correct order? impl Drop for Timer { fn drop(&mut self) { unsafe { diff --git a/src/mcpwm/timer_connection.rs b/src/mcpwm/timer_connection.rs index d5790c560a8..43a9065c8ac 100644 --- a/src/mcpwm/timer_connection.rs +++ b/src/mcpwm/timer_connection.rs @@ -1,9 +1,9 @@ use crate::mcpwm::Group; use super::{ - comparator::{OptionalCmp, OptionalCmpCfg}, - generator::{OptionalGen, GenA, GenB, GenInit, OptionalGenCfg}, - operator::{NoOperator, OptionalOperator, OPERATOR, self}, + comparator::OptionalCmpCfg, + generator::OptionalGenCfg, + operator::{self, NoOperator, OptionalOperator, OPERATOR}, timer::Timer, Operator, OperatorConfig, }; @@ -62,24 +62,18 @@ where O1: OptionalOperator<1, G>, O2: OptionalOperator<2, G>, { - pub fn attatch_operator0( + pub fn attatch_operator0( self, operator_handle: OPERATOR<0, G>, - operator_cfg: OperatorConfig< - CMP_X, - CMP_Y, - GEN_A, - GEN_B, - >, - ) -> TimerConnection, O1, O2> - where// TODO: Any ideas on how to simplify this mess? - CMP_X: OptionalCmpCfg, - CMP_Y: OptionalCmpCfg, - GEN_A: OptionalGenCfg, - GEN_B: OptionalGenCfg, + operator_cfg: OperatorConfig, + ) -> TimerConnection, O1, O2> + where + CMPX: OptionalCmpCfg, + CMPY: OptionalCmpCfg, + GENA: OptionalGenCfg, + GENB: OptionalGenCfg, { - let operator = - unsafe { operator::new(operator_handle, self.timer.timer(), operator_cfg) }; + let operator = unsafe { operator::new(operator_handle, self.timer.timer(), operator_cfg) }; TimerConnection { timer: self.timer, operator0: operator, @@ -95,22 +89,18 @@ where O0: OptionalOperator<0, G>, O2: OptionalOperator<2, G>, { - pub fn attatch_operator1( + pub fn attatch_operator1( self, operator_handle: OPERATOR<1, G>, - operator_cfg: OperatorConfig, - pin_a: PA, - pin_b: PB, - ) -> TimerConnection, O2> + operator_cfg: OperatorConfig, + ) -> TimerConnection, O2> where - CMP_X: OptionalCmp, - CMP_Y: OptionalCmp, - GEN_A: OptionalGen, - GEN_B: OptionalGen, - PA: OptionalOutputPin, - PB: OptionalOutputPin, + CMPX: OptionalCmpCfg, + CMPY: OptionalCmpCfg, + GENA: OptionalGenCfg, + GENB: OptionalGenCfg, { - let operator = todo!(); //self.init_and_attach_operator(operator_cfg, pin_a, pin_b); + let operator = unsafe { operator::new(operator_handle, self.timer.timer(), operator_cfg) }; TimerConnection { timer: self.timer, operator0: self.operator0, @@ -126,22 +116,18 @@ where O0: OptionalOperator<0, G>, O1: OptionalOperator<1, G>, { - pub fn attatch_operator2( + pub fn attatch_operator2( self, operator_handle: OPERATOR<2, G>, - operator_cfg: OperatorConfig, - pin_a: PA, - pin_b: PB, - ) -> TimerConnection> + operator_cfg: OperatorConfig, + ) -> TimerConnection> where - CMP_X: OptionalCmp, - CMP_Y: OptionalCmp, - GEN_A: OptionalGen, - GEN_B: OptionalGen, - PA: OptionalOutputPin, - PB: OptionalOutputPin, + CMPX: OptionalCmpCfg, + CMPY: OptionalCmpCfg, + GENA: OptionalGenCfg, + GENB: OptionalGenCfg, { - let operator = todo!(); //self.init_and_attach_operator(operator_cfg, pin_a, pin_b); + let operator = unsafe { operator::new(operator_handle, self.timer.timer(), operator_cfg) }; TimerConnection { timer: self.timer, operator0: self.operator0, @@ -151,8 +137,20 @@ where } } -// TODO: Should this be moved somewhere else? -pub struct NoPin; +/* +// TODO: Adding this prevents moving values out of the type as is done in attatch_operator() +// how do we make this work? +impl Drop for TimerConnection + where G: Group, + O0: OptionalOperator<0, G>, + O1: OptionalOperator<1, G>, + O2: OptionalOperator<2, G>, +{ + fn drop(&mut self) { + todo!() + } +} +*/ // TODO: Should this be moved somewhere else? pub trait OptionalOutputPin {} From bb8d9578f7cb70cce7e6ed5bb9798f26f4bc5817 Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Mon, 28 Nov 2022 21:15:39 +0100 Subject: [PATCH 38/62] Fix some more warnings, and some cleanup --- src/mcpwm/generator.rs | 22 +++++++--------- src/mcpwm/mod.rs | 5 ++-- src/mcpwm/operator_config.rs | 5 ++-- src/mcpwm/timer.rs | 50 ++++++++++++++++++++---------------- 4 files changed, 42 insertions(+), 40 deletions(-) diff --git a/src/mcpwm/generator.rs b/src/mcpwm/generator.rs index 7890fcc9deb..b8671ce96cf 100644 --- a/src/mcpwm/generator.rs +++ b/src/mcpwm/generator.rs @@ -5,24 +5,20 @@ use esp_idf_sys::{ mcpwm_generator_action_t_MCPWM_GEN_ACTION_HIGH, mcpwm_generator_action_t_MCPWM_GEN_ACTION_KEEP, mcpwm_generator_action_t_MCPWM_GEN_ACTION_LOW, mcpwm_generator_action_t_MCPWM_GEN_ACTION_TOGGLE, mcpwm_generator_config_t, - mcpwm_generator_set_actions_on_timer_event, + mcpwm_generator_config_t__bindgen_ty_1, mcpwm_generator_set_actions_on_timer_event, mcpwm_new_generator, mcpwm_oper_handle_t, mcpwm_timer_direction_t_MCPWM_TIMER_DIRECTION_DOWN, mcpwm_timer_direction_t_MCPWM_TIMER_DIRECTION_UP, mcpwm_timer_event_t_MCPWM_TIMER_EVENT_EMPTY, - mcpwm_timer_event_t_MCPWM_TIMER_EVENT_FULL, mcpwm_generator_config_t__bindgen_ty_1, + mcpwm_timer_event_t_MCPWM_TIMER_EVENT_FULL, }; use crate::gpio::OutputPin; -use super::{ - comparator::{Comparator, OptionalCmp}, -}; +use super::comparator::{Comparator, OptionalCmp}; pub struct NoGen; -impl OptionalGen for NoGen { -} -pub trait OptionalGen { -} +impl OptionalGen for NoGen {} +pub trait OptionalGen {} impl OptionalGen for Generator where @@ -286,9 +282,9 @@ pub trait OnMatchCfg { fn to_counting_direction(self) -> CountingDirection; } -impl Into for GeneratorAction { - fn into(self) -> mcpwm_generator_action_t { - match self { +impl From for mcpwm_generator_action_t { + fn from(val: GeneratorAction) -> Self { + match val { GeneratorAction::Nothing => mcpwm_generator_action_t_MCPWM_GEN_ACTION_KEEP, GeneratorAction::SetLow => mcpwm_generator_action_t_MCPWM_GEN_ACTION_LOW, GeneratorAction::SetHigh => mcpwm_generator_action_t_MCPWM_GEN_ACTION_HIGH, @@ -297,7 +293,7 @@ impl Into for GeneratorAction { } } -#[derive(Clone, Copy, Debug, PartialEq)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum GeneratorAction { Nothing, SetLow, diff --git a/src/mcpwm/mod.rs b/src/mcpwm/mod.rs index e17acaeb42f..045ff54e2ea 100644 --- a/src/mcpwm/mod.rs +++ b/src/mcpwm/mod.rs @@ -76,10 +76,10 @@ //! //! See the `examples/` folder of this repository for more. +mod comparator; +mod generator; mod operator; mod operator_config; -mod generator; -mod comparator; mod timer; mod timer_connection; @@ -88,6 +88,7 @@ use core::ffi; pub use self::{ operator::{Operator, OPERATOR}, operator_config::OperatorConfig, + generator::{GeneratorConfig, GeneratorAction}, timer::{Timer, TimerConfig, TIMER}, timer_connection::TimerConnection, }; diff --git a/src/mcpwm/operator_config.rs b/src/mcpwm/operator_config.rs index 5836af9c987..4d44c2eda30 100644 --- a/src/mcpwm/operator_config.rs +++ b/src/mcpwm/operator_config.rs @@ -1,11 +1,10 @@ - use esp_idf_sys::mcpwm_operator_config_t__bindgen_ty_1; use crate::gpio::OutputPin; use super::{ comparator::{ComparatorConfig, NoCmpCfg, OptionalCmpCfg}, - generator::{GenA, GenB, GeneratorConfig, NoGenCfg, OptionalGenCfg, CountingDirection}, + generator::{CountingDirection, GenA, GenB, GeneratorConfig, NoGenCfg, OptionalGenCfg}, }; #[derive(Default)] @@ -219,4 +218,4 @@ impl generator_b: config, } } -} \ No newline at end of file +} diff --git a/src/mcpwm/timer.rs b/src/mcpwm/timer.rs index 7a2197f5524..150531ef89a 100644 --- a/src/mcpwm/timer.rs +++ b/src/mcpwm/timer.rs @@ -3,21 +3,23 @@ use core::marker::PhantomData; use std::ptr; use esp_idf_sys::{ - esp, mcpwm_del_timer, mcpwm_new_timer, mcpwm_timer_config_t, mcpwm_timer_enable, - mcpwm_timer_handle_t, mcpwm_timer_config_t__bindgen_ty_1, mcpwm_timer_sync_src_config_t__bindgen_ty_1, soc_periph_mcpwm_timer_clk_src_t_MCPWM_TIMER_CLK_SRC_DEFAULT, mcpwm_timer_count_mode_t_MCPWM_TIMER_COUNT_MODE_UP, + esp, mcpwm_del_timer, mcpwm_new_timer, mcpwm_timer_config_t, + mcpwm_timer_config_t__bindgen_ty_1, mcpwm_timer_count_mode_t, mcpwm_timer_count_mode_t_MCPWM_TIMER_COUNT_MODE_DOWN, + mcpwm_timer_count_mode_t_MCPWM_TIMER_COUNT_MODE_PAUSE, + mcpwm_timer_count_mode_t_MCPWM_TIMER_COUNT_MODE_UP, + mcpwm_timer_count_mode_t_MCPWM_TIMER_COUNT_MODE_UP_DOWN, mcpwm_timer_enable, + mcpwm_timer_handle_t, soc_periph_mcpwm_timer_clk_src_t_MCPWM_TIMER_CLK_SRC_DEFAULT, }; use crate::mcpwm::Group; -use crate::prelude::FromValueType; -use crate::units::Hertz; use super::operator::NoOperator; use super::timer_connection::TimerConnection; #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct TimerConfig { - frequency: Hertz, - //resolution: Hertz, + resolution: u32, + period_ticks: u16, count_mode: CountMode, // TODO // on_full: FF, @@ -28,7 +30,8 @@ pub struct TimerConfig { impl Default for TimerConfig { fn default() -> Self { Self { - frequency: 1_u32.kHz().into(), + resolution: 160_000_000, + period_ticks: 16_000, // 10kHz //resolution: Default::default(), count_mode: CountMode::Up, } @@ -44,13 +47,13 @@ impl TimerConfig { /// Frequency which the operator will run at, can also be changed live later #[must_use] - pub fn frequency(mut self, frequency: impl Into) -> Self { - self.frequency = frequency.into(); + pub fn period_ticks(mut self, period_ticks: u16) -> Self { + self.period_ticks = period_ticks; self } #[must_use] - pub fn counter_mode(mut self, counter_mode: CountMode) -> Self { + pub fn count_mode(mut self, counter_mode: CountMode) -> Self { self.count_mode = counter_mode; self } @@ -83,17 +86,17 @@ pub struct Timer { impl Timer { pub fn new(timer: TIMER, config: TimerConfig) -> Self { let mut flags: mcpwm_timer_config_t__bindgen_ty_1 = Default::default(); - + // TODO: What should these be set to? flags.set_update_period_on_empty(1); flags.set_update_period_on_sync(0); let cfg = mcpwm_timer_config_t { group_id: G::ID, - clk_src: soc_periph_mcpwm_timer_clk_src_t_MCPWM_TIMER_CLK_SRC_DEFAULT, - resolution_hz: 160_000_000, // 160MHz - count_mode: mcpwm_timer_count_mode_t_MCPWM_TIMER_COUNT_MODE_UP, - period_ticks: 16_000, // 10kHz + clk_src: soc_periph_mcpwm_timer_clk_src_t_MCPWM_TIMER_CLK_SRC_DEFAULT, + resolution_hz: config.resolution, + count_mode: config.count_mode.into(), + period_ticks: config.period_ticks.into(), flags, }; let mut handle: mcpwm_timer_handle_t = ptr::null_mut(); @@ -238,19 +241,22 @@ pub enum CountMode { /// ``` /// NOTE: That in this mode, the frequency will be half of that specified UpDown, + + /// Timer paused + Pause, } -/* -impl From for mcpwm_counter_type_t { - fn from(val: CounterMode) -> Self { +impl From for mcpwm_timer_count_mode_t { + fn from(val: CountMode) -> Self { match val { //CounterMode::Frozen => mcpwm_counter_type_t_MCPWM_FREEZE_COUNTER, - CounterMode::Up => mcpwm_counter_type_t_MCPWM_UP_COUNTER, - CounterMode::Down => mcpwm_counter_type_t_MCPWM_DOWN_COUNTER, - CounterMode::UpDown => mcpwm_counter_type_t_MCPWM_UP_DOWN_COUNTER, + CountMode::Up => mcpwm_timer_count_mode_t_MCPWM_TIMER_COUNT_MODE_UP, + CountMode::Down => mcpwm_timer_count_mode_t_MCPWM_TIMER_COUNT_MODE_DOWN, + CountMode::UpDown => mcpwm_timer_count_mode_t_MCPWM_TIMER_COUNT_MODE_UP_DOWN, + CountMode::Pause => mcpwm_timer_count_mode_t_MCPWM_TIMER_COUNT_MODE_PAUSE, } } -}*/ +} pub struct TIMER { _ptr: PhantomData<*const ()>, From e7e755e590b341cb758e4f81b683a636a7f4c36a Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Mon, 28 Nov 2022 21:23:06 +0100 Subject: [PATCH 39/62] Remove deadtime example --- examples/mcpwm-deadtime.rs | 106 ------------------------------------- 1 file changed, 106 deletions(-) delete mode 100644 examples/mcpwm-deadtime.rs diff --git a/examples/mcpwm-deadtime.rs b/examples/mcpwm-deadtime.rs deleted file mode 100644 index fd40145497c..00000000000 --- a/examples/mcpwm-deadtime.rs +++ /dev/null @@ -1,106 +0,0 @@ -// TODO: Update those graphs with measured results - -/// # x = 10 -/// -/// . . -/// . . -/// .------. .------. -/// | | | | -/// pin4 | | | | -/// | | | | -/// ----- -------------------------- -------------------------- -/// . . -/// . . -/// .------------------------. .------------------------. -/// | | | | -/// pin5 | | | | -/// | | | | -/// ----- -------- -------- -/// . . -/// -/// -/// # x = 50 -/// . . -/// . . -/// .---------------. .---------------. -/// | | | | -/// pin4 | | | | -/// | | | | -/// ----- ----------------- ----------------- -/// . . -/// . . -/// .---------------. .---------------. -/// | | | | -/// pin5 | | | | -/// | | | | -/// ----- ----------------- ----------------- -/// . . -/// -/// -/// # x = 90 -/// . . -/// . . -/// .------------------------. .------------------------. -/// | | | | -/// pin4 | | | | -/// | | | | -/// ----- -------- -------- -/// . . -/// . . -/// .------. .------. -/// | | | | -/// pin5 | | | | -/// | | | | -/// ----- -------------------------- -------------------------- -/// . . - -#[cfg(any(esp32, esp32s3))] -fn main() -> anyhow::Result<()> { - use embedded_hal::delay::blocking::DelayUs; - - use esp_idf_hal::delay::FreeRtos; - use esp_idf_hal::mcpwm::{DeadtimeConfig, Mcpwm, Operator, OperatorConfig}; - use esp_idf_hal::prelude::Peripherals; - use esp_idf_hal::units::FromValueType; - - esp_idf_sys::link_patches(); - - println!("Configuring MCPWM"); - - let peripherals = Peripherals::take().unwrap(); - let config = OperatorConfig::default().frequency(1.kHz()).deadtime( - DeadtimeConfig::ActiveHighComplement { - rising_edge_delay: 1500, // 1500*100ns=150us or 15% of the period - falling_edge_delay: 3000, // 3000*100ns=300us or 30% of the period - }, - ); - let mcpwm = Mcpwm::new(peripherals.mcpwm0.mcpwm)?; - let mut operator = Operator::new( - peripherals.mcpwm0.operator0, - &mcpwm, - &config, - peripherals.pins.gpio4, - peripherals.pins.gpio5, - )?; - - println!("Starting duty-cycle loop"); - - for x in (0..10000u16).cycle() { - let duty = f32::from(x) * 0.01; - - if x % 100 == 0 { - println!("Duty {}%", duty); - } - - operator.set_duty_a(duty)?; - operator.set_duty_b(0.0)?; - FreeRtos.delay_ms(10)?; - } - - unreachable!() -} - -#[cfg(not(any(esp32, esp32s3)))] -fn main() { - esp_idf_sys::link_patches(); -} From d20ae6218d75f103610207876ea543e9641fc37a Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Mon, 28 Nov 2022 22:34:44 +0100 Subject: [PATCH 40/62] Fix example and more cleanup --- examples/mcpwm-simple.rs | 15 +++++++-------- src/mcpwm/mod.rs | 2 +- src/mcpwm/timer.rs | 3 ++- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/examples/mcpwm-simple.rs b/examples/mcpwm-simple.rs index cbb7ccf3e94..02eee815045 100644 --- a/examples/mcpwm-simple.rs +++ b/examples/mcpwm-simple.rs @@ -2,7 +2,7 @@ /// The duty on the pin4 will increase from 0% all the way up to 100% and repeat. At the same time the duty /// on pin 5 will go from 100% down to 0% /// -/// # duty = 10 +/// # duty = 10% /// /// . . /// . . @@ -21,7 +21,7 @@ /// . . /// /// -/// # duty = 50 +/// # duty = 50% /// . . /// . . /// .---------------. .---------------. @@ -39,7 +39,7 @@ /// . . /// /// -/// # duty = 90 +/// # duty = 90% /// . . /// . . /// .------------------------. .------------------------. @@ -61,16 +61,15 @@ fn main() -> anyhow::Result<()> { use embedded_hal::delay::DelayUs; use esp_idf_hal::delay::FreeRtos; - use esp_idf_hal::mcpwm::{Operator, OperatorConfig, Timer, TimerConfig}; + use esp_idf_hal::mcpwm::{OperatorConfig, Timer, TimerConfig}; use esp_idf_hal::prelude::Peripherals; - use esp_idf_hal::units::FromValueType; esp_idf_sys::link_patches(); println!("Configuring MCPWM"); let peripherals = Peripherals::take().unwrap(); - let timer_config = TimerConfig::default().frequency(25.kHz()); + let timer_config = TimerConfig::default().period_ticks(16_000); // 10kHz let operator_config = OperatorConfig::default(peripherals.pins.gpio4, peripherals.pins.gpio5); let timer = Timer::new(peripherals.mcpwm0.timer0, timer_config); @@ -90,8 +89,8 @@ fn main() -> anyhow::Result<()> { println!("Duty {}%", 100 * duty / period_ticks); } - operator.set_compare_value_x(duty)?; - operator.set_compare_value_y(period_ticks - duty)?; + operator.set_compare_value_x(duty)?; // In this configuration this controls the duty on pin4 + operator.set_compare_value_y(period_ticks - duty)?; // and this controls pin5 FreeRtos.delay_ms(10)?; } diff --git a/src/mcpwm/mod.rs b/src/mcpwm/mod.rs index 045ff54e2ea..9760b470a0b 100644 --- a/src/mcpwm/mod.rs +++ b/src/mcpwm/mod.rs @@ -86,9 +86,9 @@ mod timer_connection; use core::ffi; pub use self::{ + generator::{GeneratorAction, GeneratorConfig}, operator::{Operator, OPERATOR}, operator_config::OperatorConfig, - generator::{GeneratorConfig, GeneratorAction}, timer::{Timer, TimerConfig, TIMER}, timer_connection::TimerConnection, }; diff --git a/src/mcpwm/timer.rs b/src/mcpwm/timer.rs index 150531ef89a..ffa461c1626 100644 --- a/src/mcpwm/timer.rs +++ b/src/mcpwm/timer.rs @@ -4,7 +4,8 @@ use std::ptr; use esp_idf_sys::{ esp, mcpwm_del_timer, mcpwm_new_timer, mcpwm_timer_config_t, - mcpwm_timer_config_t__bindgen_ty_1, mcpwm_timer_count_mode_t, mcpwm_timer_count_mode_t_MCPWM_TIMER_COUNT_MODE_DOWN, + mcpwm_timer_config_t__bindgen_ty_1, mcpwm_timer_count_mode_t, + mcpwm_timer_count_mode_t_MCPWM_TIMER_COUNT_MODE_DOWN, mcpwm_timer_count_mode_t_MCPWM_TIMER_COUNT_MODE_PAUSE, mcpwm_timer_count_mode_t_MCPWM_TIMER_COUNT_MODE_UP, mcpwm_timer_count_mode_t_MCPWM_TIMER_COUNT_MODE_UP_DOWN, mcpwm_timer_enable, From 7b2a929b6600f0e40abfc0897e2354ea8a2fae9b Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Mon, 28 Nov 2022 22:40:05 +0100 Subject: [PATCH 41/62] Dead time should probably wait... --- src/mcpwm/deadtime.rs | 362 ------------------------------------------ 1 file changed, 362 deletions(-) delete mode 100644 src/mcpwm/deadtime.rs diff --git a/src/mcpwm/deadtime.rs b/src/mcpwm/deadtime.rs deleted file mode 100644 index f8661689a53..00000000000 --- a/src/mcpwm/deadtime.rs +++ /dev/null @@ -1,362 +0,0 @@ -// TODO: This is stolen from the MCPWM 4.4 PR, check if/how this should be changed for 5.0 - - -/// Dead time config for MCPWM operator -/// -/// `rising_edge_delay` and `falling_edge_delay` is time as in number of clock cycles after the MCPWM modules group prescaler. -/// -/// Note that the dead times are calculated from MCPWMXA's flanks unless explicitly stated otherwise -#[derive(Copy, Clone, PartialEq, Eq, Debug)] -pub enum DeadtimeConfig { - // TODO: Figure out what all of those options do and give them nice descriptions - /// MCPWM_BYPASS_RED - /// - /// Note that `MCPWMXB in` will be completely ignored. This means `Operator::set_duty_b` will - /// have no effect with this dead time mode - /// - /// ``` - /// . . . - /// . . . - /// .--------------------. . - /// | | . - /// MCPWMXA in | | . - /// | | . - /// --------------- --------------------- - /// . . . - /// . . . - /// .--------------------. . - /// | | . - /// MCPWMXA out | | . - /// | | . - /// --------------- --------------------- - /// . . . - /// . . . - /// .------------------------. - /// | >. |< fed - /// MCPWMXB out | . | - /// | . | - /// --------------. . ----------------- - /// . . . - /// . . . - /// ``` - BypassRisingEdge { falling_edge_delay: u16 }, - - /// MCPWM_BYPASS_FED - /// - /// Note that `MCPWMXB in` will be completely ignored. This means `Operator::set_duty_b` will - /// have no effect with this dead time mode - /// - /// ``` - /// . . . - /// . . . - /// .--------------------. - /// | . | - /// MCPWMXA in | . | - /// | . | - /// --------------- . --------------------- - /// . . . - /// . . . - /// . .----------------. - /// red >. |< | - /// MCPWMXA out . | | - /// . | | - /// ------------------- --------------------- - /// . . . - /// . . . - /// .--------------------. - /// | . | - /// MCPWMXB out | . | - /// | . | - /// --------------- . --------------------- - /// . . . - /// . . . - /// ``` - BypassFallingEdge { rising_edge_delay: u16 }, - - /// MCPWM_ACTIVE_HIGH_MODE - /// - /// Note that `MCPWMXB in` will be completely ignored. This means `Operator::set_duty_b` will - /// have no effect with this dead time mode - /// - /// ``` - /// . . . . - /// . . . . - /// .--------------------. . - /// | . | . - /// MCPWMXA in | . | . - /// | . | . - /// --------------- . --------------------- - /// . . . . - /// . . . . - /// . .----------------. . - /// red >. |< | . - /// MCPWMXA out . | | . - /// . | | . - /// ------------------- --------------------- - /// . . . . - /// . . . . - /// .------------------------. - /// | . >. |< fed - /// MCPWMXB out | . . | - /// | . . | - /// --------------. . . ----------------- - /// . . . . - /// . . . . - /// ``` - ActiveHigh { - rising_edge_delay: u16, - falling_edge_delay: u16, - }, - - /// MCPWM_ACTIVE_LOW_MODE - /// - /// Note that `MCPWMXB in` will be completely ignored. This means `Operator::set_duty_b` will - /// have no effect with this dead time mode - /// - /// ``` - /// . . . . - /// . . . . - /// .--------------------. . - /// | . | . - /// MCPWMXA in | . | . - /// | . | . - /// --------------- . --------------------- - /// . . . . - /// . . . . - /// ------------------. .-------------------- - /// red >. |< | . - /// MCPWMXA out . | | . - /// . | | . - /// . ------------------ - /// . . . . - /// . . . . - /// --------------. . . .---------------- - /// | . >. |< fed - /// MCPWMXB out | . . | - /// | . . | - /// -------------------------- - /// . . . . - /// . . . . - /// ``` - ActiveLow { - rising_edge_delay: u16, - falling_edge_delay: u16, - }, - - /// MCPWM_ACTIVE_HIGH_COMPLIMENT_MODE - /// - /// Note that `MCPWMXB in` will be completely ignored. This means `Operator::set_duty_b` will - /// have no effect with this dead time mode - /// - /// ``` - /// . . . . - /// . . . . - /// .--------------------. . - /// | . | . - /// MCPWMXA in | . | . - /// | . | . - /// --------------- . --------------------- - /// . . . . - /// . . . . - /// . .----------------. . - /// red >. |< | . - /// MCPWMXA out . | | . - /// . | | . - /// ------------------- --------------------- - /// . . . . - /// . . . . - /// --------------. . . .---------------- - /// | . >. |< fed - /// MCPWMXB out | . . | - /// | . . | - /// -------------------------- - /// . . . . - /// . . . . - /// ``` - ActiveHighComplement { - rising_edge_delay: u16, - falling_edge_delay: u16, - }, - - /// MCPWM_ACTIVE_LOW_COMPLIMENT_MODE - /// - /// Note that `MCPWMXB in` will be completely ignored. This means `Operator::set_duty_b` will - /// have no effect with this dead time mode - /// - /// ``` - /// . . . . - /// . . . . - /// .--------------------. . - /// | . | . - /// MCPWMXA in | . | . - /// | . | . - /// --------------- . --------------------- - /// . . . . - /// . . . . - /// ------------------. .-------------------- - /// red >. |< | . - /// MCPWMXA out . | | . - /// . | | . - /// . ------------------ . - /// . . . . - /// . . . . - /// .------------------------. - /// | . >. |< fed - /// MCPWMXB out | . . | - /// | . . | - /// --------------- . . ----------------- - /// . . . . - /// . . . . - /// ``` - ActiveLowComplement { - rising_edge_delay: u16, - falling_edge_delay: u16, - }, - - /// MCPWM_ACTIVE_RED_FED_FROM_PWMXA - /// - /// Note that `MCPWMXB in` will be completely ignored. This means `Operator::set_duty_b` will - /// have no effect with this dead time mode - /// - /// ``` - /// . . . . - /// . . . . - /// .--------------------. . - /// | . | . - /// MCPWMXA in | . | . - /// | . | . - /// --------------- . --------------------- - /// . . . . - /// . . . . - /// . .--------------------. - /// red >. |< . | - /// MCPWMXA out . | . | - /// . | . | - /// ------------------- . --------------------- - /// . . . . - /// . . . . - /// . .--------------------. - /// red >. |< . | - /// MCPWMXB out . | . | - /// . | . | - /// ------------------- . --------------------- - /// . . . . - /// . . . . - /// ``` - ActiveRedFedFromPwmxa { - rising_edge_delay: u16, - falling_edge_delay: u16, - }, - - /// MCPWM_ACTIVE_RED_FED_FROM_PWMXB - /// - /// Note that `MCPWMXA in` will be completely ignored. This means `Operator::set_duty_a` will - /// have no effect with this dead time mode - /// ``` - /// . . . . - /// . . . . - /// .--------------------. . - /// | . | . - /// MCPWMXB in | . | . - /// | . | . - /// --------------- . --------------------- - /// . . . . - /// . . . . - /// . .--------------------. - /// red >. |< . | - /// MCPWMXA out . | . | - /// . | . | - /// ------------------- . --------------------- - /// . . . . - /// . . . . - /// . .--------------------. - /// red >. |< . | - /// MCPWMXB out . | . | - /// . | . | - /// ------------------- . --------------------- - /// . . . . - /// . . . . - /// ``` - ActiveRedFedFromPwmxb { - rising_edge_delay: u16, - falling_edge_delay: u16, - }, -} - -impl DeadtimeConfig { - fn as_args(&self) -> DeadtimeArgs { - match *self { - DeadtimeConfig::BypassRisingEdge { falling_edge_delay } => DeadtimeArgs { - rising_edge_delay: 0, - falling_edge_delay, - mode: mcpwm_deadtime_type_t_MCPWM_BYPASS_RED, - }, - - DeadtimeConfig::BypassFallingEdge { rising_edge_delay } => DeadtimeArgs { - rising_edge_delay, - falling_edge_delay: 0, - mode: mcpwm_deadtime_type_t_MCPWM_BYPASS_FED, - }, - - DeadtimeConfig::ActiveHigh { - rising_edge_delay, - falling_edge_delay, - } => DeadtimeArgs { - rising_edge_delay, - falling_edge_delay, - mode: mcpwm_deadtime_type_t_MCPWM_ACTIVE_HIGH_MODE, - }, - - DeadtimeConfig::ActiveLow { - rising_edge_delay, - falling_edge_delay, - } => DeadtimeArgs { - rising_edge_delay, - falling_edge_delay, - mode: mcpwm_deadtime_type_t_MCPWM_ACTIVE_LOW_MODE, - }, - - DeadtimeConfig::ActiveHighComplement { - rising_edge_delay, - falling_edge_delay, - } => DeadtimeArgs { - rising_edge_delay, - falling_edge_delay, - mode: mcpwm_deadtime_type_t_MCPWM_ACTIVE_HIGH_COMPLIMENT_MODE, - }, - - DeadtimeConfig::ActiveLowComplement { - rising_edge_delay, - falling_edge_delay, - } => DeadtimeArgs { - rising_edge_delay, - falling_edge_delay, - mode: mcpwm_deadtime_type_t_MCPWM_ACTIVE_LOW_COMPLIMENT_MODE, - }, - - DeadtimeConfig::ActiveRedFedFromPwmxa { - rising_edge_delay, - falling_edge_delay, - } => DeadtimeArgs { - rising_edge_delay, - falling_edge_delay, - mode: mcpwm_deadtime_type_t_MCPWM_ACTIVE_RED_FED_FROM_PWMXA, - }, - - DeadtimeConfig::ActiveRedFedFromPwmxb { - rising_edge_delay, - falling_edge_delay, - } => DeadtimeArgs { - rising_edge_delay, - falling_edge_delay, - mode: mcpwm_deadtime_type_t_MCPWM_ACTIVE_RED_FED_FROM_PWMXB, - }, - } - } -} - -struct DeadtimeArgs { - rising_edge_delay: u16, - falling_edge_delay: u16, - mode: mcpwm_deadtime_type_t, -} \ No newline at end of file From 6e8d31e3211d1ef2bc8a4c8ff407139195efb574 Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Mon, 28 Nov 2022 22:55:40 +0100 Subject: [PATCH 42/62] Operators constructor does not need to be pub, Timer::resolution is a frequency --- src/mcpwm/operator.rs | 4 ++-- src/mcpwm/timer.rs | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/mcpwm/operator.rs b/src/mcpwm/operator.rs index 99e47a1908c..6071941d12a 100644 --- a/src/mcpwm/operator.rs +++ b/src/mcpwm/operator.rs @@ -72,7 +72,7 @@ pub struct Operator< //deadtime: D } -pub unsafe fn new( +pub(crate) unsafe fn new( instance: OPERATOR, timer_handle: mcpwm_timer_handle_t, cfg: OperatorConfig, @@ -197,7 +197,7 @@ where todo!() } - /// Set compare value, often times same as the duty for output A. + /// Set compare value, often times same as the duty for output B. /// /// Depending on how the generators are configured this is, using the most common configuration, the duty of output A. /// `value` is from the range 0 to timers peak value. However do note that if using a custom configuration this might diff --git a/src/mcpwm/timer.rs b/src/mcpwm/timer.rs index ffa461c1626..ed5ed7f03e2 100644 --- a/src/mcpwm/timer.rs +++ b/src/mcpwm/timer.rs @@ -13,13 +13,14 @@ use esp_idf_sys::{ }; use crate::mcpwm::Group; +use crate::prelude::{Hertz, FromValueType}; use super::operator::NoOperator; use super::timer_connection::TimerConnection; #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct TimerConfig { - resolution: u32, + resolution: Hertz, period_ticks: u16, count_mode: CountMode, // TODO @@ -31,9 +32,8 @@ pub struct TimerConfig { impl Default for TimerConfig { fn default() -> Self { Self { - resolution: 160_000_000, + resolution: 160.MHz().into(), period_ticks: 16_000, // 10kHz - //resolution: Default::default(), count_mode: CountMode::Up, } } @@ -95,7 +95,7 @@ impl Timer { let cfg = mcpwm_timer_config_t { group_id: G::ID, clk_src: soc_periph_mcpwm_timer_clk_src_t_MCPWM_TIMER_CLK_SRC_DEFAULT, - resolution_hz: config.resolution, + resolution_hz: config.resolution.0, count_mode: config.count_mode.into(), period_ticks: config.period_ticks.into(), flags, From d3e44c13d938fc2b044e5f240670981ea977eb18 Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Sat, 3 Dec 2022 11:04:53 +0100 Subject: [PATCH 43/62] Some cleanup, added comments and removed temp changes in .cargo --- .cargo/config.toml | 11 +---------- examples/mcpwm-simple.rs | 1 + src/lib.rs | 2 +- src/mcpwm/timer.rs | 24 ++++++++++++++---------- 4 files changed, 17 insertions(+), 21 deletions(-) diff --git a/.cargo/config.toml b/.cargo/config.toml index 532d9c29f58..f6ec1ef65fc 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,31 +1,22 @@ [target.xtensa-esp32-espidf] linker = "ldproxy" -rustflags = ["--cfg", "espidf_time64"] [target.xtensa-esp32s2-espidf] linker = "ldproxy" -rustflags = ["--cfg", "espidf_time64"] [target.xtensa-esp32s3-espidf] linker = "ldproxy" -rustflags = ["--cfg", "espidf_time64"] [target.riscv32imc-esp-espidf] linker = "ldproxy" # Future - necessary for the experimental "native build" of esp-idf-sys with ESP32C3 # See also https://github.com/ivmarkov/embuild/issues/16 -#rustflags = ["-C", "default-linker-libraries"] -rustflags = ["--cfg", "espidf_time64", "-C", "default-linker-libraries"] +rustflags = ["-C", "default-linker-libraries"] [env] ESP_IDF_SDKCONFIG_DEFAULTS = ".github/configs/sdkconfig.defaults" -#ESP_IDF_VERSION = { value = "branch:release/v5.0" } -ESP_IDF_VERSION = { value = "master" } [unstable] build-std = ["std", "panic_abort"] build-std-features = ["panic_immediate_abort"] - -[build] -target = "xtensa-esp32-espidf" diff --git a/examples/mcpwm-simple.rs b/examples/mcpwm-simple.rs index 02eee815045..ddacfc88783 100644 --- a/examples/mcpwm-simple.rs +++ b/examples/mcpwm-simple.rs @@ -77,6 +77,7 @@ fn main() -> anyhow::Result<()> { .into_connection() .attatch_operator0(peripherals.mcpwm0.operator0, operator_config); + // Borrow references to the contained timer and operator let (timer, operator, _, _) = timer.split(); println!("Starting duty-cycle loop"); diff --git a/src/lib.rs b/src/lib.rs index ad3db2c1444..9e6763e654d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -42,7 +42,7 @@ pub mod ledc; not(feature = "riscv-ulp-hal") ))] pub mod mac; -#[cfg(all(any(esp32, esp32s3), not(feature = "riscv-ulp-hal")))] +#[cfg(all(any(esp32, esp32s3, esp_idf_version_major = "5"), not(feature = "riscv-ulp-hal")))] pub mod mcpwm; #[cfg(not(feature = "riscv-ulp-hal"))] pub mod modem; diff --git a/src/mcpwm/timer.rs b/src/mcpwm/timer.rs index ed5ed7f03e2..82ea901ccb3 100644 --- a/src/mcpwm/timer.rs +++ b/src/mcpwm/timer.rs @@ -46,7 +46,20 @@ impl TimerConfig { self }*/ - /// Frequency which the operator will run at, can also be changed live later + // TODO: make sure this description is accurate + /// Set number of ticks per period + /// + /// This is inversely proportional to the frequency of the signal + /// + /// You can calculate the frequency as + /// `frequency = resolution / period_ticks` + /// + /// For example a resolution of 160MHz and a period_ticks of 16_000: + /// `10kHz = 160MHz / 16_000` + /// + /// NOTE: This will be the same as `Self::get_period_peak` for all `CounterMode` except for + /// `CounterMode::UpDown` where the period will be twice as large as the peak value since + /// the timer will count from zero to peak and then down to zero again #[must_use] pub fn period_ticks(mut self, period_ticks: u16) -> Self { self.period_ticks = period_ticks; @@ -60,15 +73,6 @@ impl TimerConfig { } } -//TODO -//impl TimerConfig { -//#[must_use] -//pub fn on_full(mut self, on_full: CB) -> Self { -// self.on_full = on_full; -// self -//} -//} - pub struct Timer { _group: G, handle: mcpwm_timer_handle_t, From c15c69fa26cb1d042e607318b998b3e65ea459bb Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Sat, 3 Dec 2022 11:06:45 +0100 Subject: [PATCH 44/62] fmt --- src/lib.rs | 5 ++++- src/mcpwm/timer.rs | 6 +++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 9e6763e654d..99498b1a31e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -42,7 +42,10 @@ pub mod ledc; not(feature = "riscv-ulp-hal") ))] pub mod mac; -#[cfg(all(any(esp32, esp32s3, esp_idf_version_major = "5"), not(feature = "riscv-ulp-hal")))] +#[cfg(all( + any(esp32, esp32s3, esp_idf_version_major = "5"), + not(feature = "riscv-ulp-hal") +))] pub mod mcpwm; #[cfg(not(feature = "riscv-ulp-hal"))] pub mod modem; diff --git a/src/mcpwm/timer.rs b/src/mcpwm/timer.rs index 82ea901ccb3..251eb030f67 100644 --- a/src/mcpwm/timer.rs +++ b/src/mcpwm/timer.rs @@ -13,7 +13,7 @@ use esp_idf_sys::{ }; use crate::mcpwm::Group; -use crate::prelude::{Hertz, FromValueType}; +use crate::prelude::{FromValueType, Hertz}; use super::operator::NoOperator; use super::timer_connection::TimerConnection; @@ -50,10 +50,10 @@ impl TimerConfig { /// Set number of ticks per period /// /// This is inversely proportional to the frequency of the signal - /// + /// /// You can calculate the frequency as /// `frequency = resolution / period_ticks` - /// + /// /// For example a resolution of 160MHz and a period_ticks of 16_000: /// `10kHz = 160MHz / 16_000` /// From f053be1bc0ff0290bed0063afd77bdb00e8caf3c Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Sat, 3 Dec 2022 11:33:04 +0100 Subject: [PATCH 45/62] Remove some dead code, fix clippy warnings and silent those that cant be fixed --- src/mcpwm/generator.rs | 2 +- src/mcpwm/operator_config.rs | 84 +++-------------------------------- src/mcpwm/timer_connection.rs | 3 ++ 3 files changed, 11 insertions(+), 78 deletions(-) diff --git a/src/mcpwm/generator.rs b/src/mcpwm/generator.rs index b8671ce96cf..a491ea8b73c 100644 --- a/src/mcpwm/generator.rs +++ b/src/mcpwm/generator.rs @@ -252,7 +252,7 @@ impl OnMatchCfg for NoCmpMatchConfig { } // TODO: Come up with better name -#[derive(Clone, Copy, Debug, PartialEq)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct CountingDirection { pub(crate) counting_up: GeneratorAction, pub(crate) counting_down: GeneratorAction, diff --git a/src/mcpwm/operator_config.rs b/src/mcpwm/operator_config.rs index 4d44c2eda30..7a75e06c31b 100644 --- a/src/mcpwm/operator_config.rs +++ b/src/mcpwm/operator_config.rs @@ -7,6 +7,9 @@ use super::{ generator::{CountingDirection, GenA, GenB, GeneratorConfig, NoGenCfg, OptionalGenCfg}, }; +type DefaultGeneratorConfigA = GeneratorConfig; +type DefaultGeneratorConfigB = GeneratorConfig; + #[derive(Default)] pub struct OperatorConfig { // TODO: When, how and who should set the flags? @@ -34,8 +37,8 @@ impl OperatorConfig { ) -> OperatorConfig< ComparatorConfig, ComparatorConfig, - GeneratorConfig, - GeneratorConfig, + DefaultGeneratorConfigA, + DefaultGeneratorConfigB, > { OperatorConfig::empty() .cmp_x(Default::default()) @@ -67,69 +70,11 @@ impl OperatorConfig { } } -impl OperatorConfig -where - CMPX: OptionalCmpCfg, - CMPY: OptionalCmpCfg, - GENA: OptionalGenCfg, - GENB: OptionalGenCfg, -{ - /*fn set_update_dead_time_on_tez(mut self, update_dead_time_on_tez: bool) -> Self { - self.flags - .set_update_dead_time_on_tez(update_dead_time_on_tez.into()); - self - } - - fn set_update_dead_time_on_tep(mut self, set_update_dead_time_on_tep: bool) -> Self { - self.flags - .set_update_dead_time_on_tep(set_update_dead_time_on_tep.into()); - self - } - - fn set_update_dead_time_on_sync(mut self, update_dead_time_on_sync: bool) -> Self { - self.flags - .set_update_dead_time_on_sync(update_dead_time_on_sync.into()); - self - } - - fn set_update_gen_action_on_tez(mut self, update_gen_action_on_tez: bool) -> Self { - self.flags - .set_update_gen_action_on_tez(update_gen_action_on_tez.into()); - self - } - - fn set_update_gen_action_on_tep(mut self, set_update_gen_action_on_tep: bool) -> Self { - self.flags - .set_update_gen_action_on_tep(set_update_gen_action_on_tep.into()); - self - } - - fn set_update_gen_action_on_sync(mut self, update_gen_action_on_sync: bool) -> Self { - self.flags - .set_update_gen_action_on_sync(update_gen_action_on_sync.into()); - self - }*/ -} - impl OperatorConfig { fn cmp_x( self, config: ComparatorConfig, ) -> OperatorConfig { - /* - let mut flags: mcpwm_comparator_config_t__bindgen_ty_1 = Default::default(); - flags.set_update_cmp_on_tep(todo!()); - flags.set_update_cmp_on_tez(todo!()); - flags.set_update_cmp_on_sync(todo!()); - - let cfg = mcpwm_comparator_config_t { flags }; - - let mut cmp = ptr::null_mut(); - unsafe { - esp!(mcpwm_new_comparator(self.handle, &cfg, &mut cmp)).unwrap(); - } - let comparator_x = Comparator(cmp);*/ - OperatorConfig { flags: self.flags, comparator_x: config, @@ -146,19 +91,6 @@ impl OperatorConfig { self, config: ComparatorConfig, ) -> OperatorConfig { - /*let mut flags: mcpwm_comparator_config_t__bindgen_ty_1 = Default::default(); - flags.set_update_cmp_on_tep(todo!()); - flags.set_update_cmp_on_tez(todo!()); - flags.set_update_cmp_on_sync(todo!()); - - let cfg = mcpwm_comparator_config_t { flags }; - let mut cmp = ptr::null_mut(); - unsafe { - esp!(mcpwm_new_comparator(self.handle, &cfg, &mut cmp)).unwrap(); - } - let comparator_y = Comparator(cmp); - */ - OperatorConfig { flags: self.flags, comparator_x: self.comparator_x, @@ -170,13 +102,10 @@ impl OperatorConfig { } } -// TODO: Make sure that a generator config can only refer to comparators that are assigned to the operator -// TODO: Is there any point in letting the user provide the comparators or should two (the only two available -// for that operator in hardware) be automatically assigned in `Operator::new`? - impl OperatorConfig { + #[allow(clippy::type_complexity)] fn gen_a( self, config: GeneratorConfig, @@ -200,6 +129,7 @@ impl impl OperatorConfig { + #[allow(clippy::type_complexity)] fn gen_b( self, config: GeneratorConfig, diff --git a/src/mcpwm/timer_connection.rs b/src/mcpwm/timer_connection.rs index 43a9065c8ac..15047ea7370 100644 --- a/src/mcpwm/timer_connection.rs +++ b/src/mcpwm/timer_connection.rs @@ -62,6 +62,7 @@ where O1: OptionalOperator<1, G>, O2: OptionalOperator<2, G>, { + #[allow(clippy::type_complexity)] pub fn attatch_operator0( self, operator_handle: OPERATOR<0, G>, @@ -89,6 +90,7 @@ where O0: OptionalOperator<0, G>, O2: OptionalOperator<2, G>, { + #[allow(clippy::type_complexity)] pub fn attatch_operator1( self, operator_handle: OPERATOR<1, G>, @@ -116,6 +118,7 @@ where O0: OptionalOperator<0, G>, O1: OptionalOperator<1, G>, { + #[allow(clippy::type_complexity)] pub fn attatch_operator2( self, operator_handle: OPERATOR<2, G>, From 326299f8cb0089e0635b6e54512eca740ee382a3 Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Sat, 3 Dec 2022 11:40:34 +0100 Subject: [PATCH 46/62] Update cfgs to require IDF 5.0 for MCPWM (for now) --- src/lib.rs | 3 ++- src/peripherals.rs | 10 +++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 99498b1a31e..cd74d91f7ec 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -43,7 +43,8 @@ pub mod ledc; ))] pub mod mac; #[cfg(all( - any(esp32, esp32s3, esp_idf_version_major = "5"), + any(esp32, esp32s3), + esp_idf_version_major = "5", not(feature = "riscv-ulp-hal") ))] pub mod mcpwm; diff --git a/src/peripherals.rs b/src/peripherals.rs index b6ec11ec654..bbf0c315500 100644 --- a/src/peripherals.rs +++ b/src/peripherals.rs @@ -11,7 +11,7 @@ use crate::ledc; not(feature = "riscv-ulp-hal") ))] use crate::mac; -#[cfg(all(any(esp32, esp32s3), not(feature = "riscv-ulp-hal")))] +#[cfg(all(any(esp32, esp32s3), esp_idf_version_major = "5", not(feature = "riscv-ulp-hal")))] use crate::mcpwm; #[cfg(not(feature = "riscv-ulp-hal"))] use crate::modem; @@ -56,9 +56,9 @@ pub struct Peripherals { pub can: can::CAN, #[cfg(not(feature = "riscv-ulp-hal"))] pub ledc: ledc::LEDC, - #[cfg(all(any(esp32, esp32s3), not(feature = "riscv-ulp-hal")))] + #[cfg(all(any(esp32, esp32s3), esp_idf_version_major = "5", not(feature = "riscv-ulp-hal")))] pub mcpwm0: mcpwm::MCPWM, - #[cfg(all(any(esp32, esp32s3), not(feature = "riscv-ulp-hal")))] + #[cfg(all(any(esp32, esp32s3), esp_idf_version_major = "5", not(feature = "riscv-ulp-hal")))] pub mcpwm1: mcpwm::MCPWM, #[cfg(not(feature = "riscv-ulp-hal"))] pub rmt: rmt::RMT, @@ -168,9 +168,9 @@ impl Peripherals { can: can::CAN::new(), #[cfg(not(feature = "riscv-ulp-hal"))] ledc: ledc::LEDC::new(), - #[cfg(any(esp32, esp32s3))] + #[cfg(all(any(esp32, esp32s3), esp_idf_version_major = "5"))] mcpwm0: mcpwm::MCPWM::::new(), - #[cfg(any(esp32, esp32s3))] + #[cfg(all(any(esp32, esp32s3), esp_idf_version_major = "5"))] mcpwm1: mcpwm::MCPWM::::new(), #[cfg(not(feature = "riscv-ulp-hal"))] rmt: rmt::RMT::new(), From 44cd1d8652c7f305b64e5eaba1c4a878d7620eef Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Sat, 3 Dec 2022 11:44:13 +0100 Subject: [PATCH 47/62] fmt --- src/peripherals.rs | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/peripherals.rs b/src/peripherals.rs index bbf0c315500..497206c1bfb 100644 --- a/src/peripherals.rs +++ b/src/peripherals.rs @@ -11,7 +11,11 @@ use crate::ledc; not(feature = "riscv-ulp-hal") ))] use crate::mac; -#[cfg(all(any(esp32, esp32s3), esp_idf_version_major = "5", not(feature = "riscv-ulp-hal")))] +#[cfg(all( + any(esp32, esp32s3), + esp_idf_version_major = "5", + not(feature = "riscv-ulp-hal") +))] use crate::mcpwm; #[cfg(not(feature = "riscv-ulp-hal"))] use crate::modem; @@ -56,9 +60,17 @@ pub struct Peripherals { pub can: can::CAN, #[cfg(not(feature = "riscv-ulp-hal"))] pub ledc: ledc::LEDC, - #[cfg(all(any(esp32, esp32s3), esp_idf_version_major = "5", not(feature = "riscv-ulp-hal")))] + #[cfg(all( + any(esp32, esp32s3), + esp_idf_version_major = "5", + not(feature = "riscv-ulp-hal") + ))] pub mcpwm0: mcpwm::MCPWM, - #[cfg(all(any(esp32, esp32s3), esp_idf_version_major = "5", not(feature = "riscv-ulp-hal")))] + #[cfg(all( + any(esp32, esp32s3), + esp_idf_version_major = "5", + not(feature = "riscv-ulp-hal") + ))] pub mcpwm1: mcpwm::MCPWM, #[cfg(not(feature = "riscv-ulp-hal"))] pub rmt: rmt::RMT, From 5c4738b7ffdaca9a322d0e0e4ccb782fa95acc1e Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Sat, 3 Dec 2022 13:11:27 +0100 Subject: [PATCH 48/62] Put example behind cfg --- examples/mcpwm-simple.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/mcpwm-simple.rs b/examples/mcpwm-simple.rs index ddacfc88783..4709486857e 100644 --- a/examples/mcpwm-simple.rs +++ b/examples/mcpwm-simple.rs @@ -56,7 +56,7 @@ /// ----- -------------------------- -------------------------- /// . . -#[cfg(any(esp32, esp32s3))] +#[cfg(all(any(esp32, esp32s3), esp_idf_version_major = "5"))] fn main() -> anyhow::Result<()> { use embedded_hal::delay::DelayUs; @@ -98,7 +98,7 @@ fn main() -> anyhow::Result<()> { unreachable!() } -#[cfg(not(any(esp32, esp32s3)))] +#[cfg(not(all(any(esp32, esp32s3), esp_idf_version_major = "5")))] fn main() { esp_idf_sys::link_patches(); From 7ab93579a87ec83f755c2366a42102c09df36ada Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Tue, 6 Dec 2022 21:58:08 +0100 Subject: [PATCH 49/62] Add extern functions for vararg --- src/mcpwm/comparator.rs | 50 ++++++++++++++++++++++++++--------------- src/mcpwm/generator.rs | 49 ++++++++++++++++++++++++---------------- 2 files changed, 62 insertions(+), 37 deletions(-) diff --git a/src/mcpwm/comparator.rs b/src/mcpwm/comparator.rs index 5495da0a066..9acf8d62d8d 100644 --- a/src/mcpwm/comparator.rs +++ b/src/mcpwm/comparator.rs @@ -2,7 +2,7 @@ use core::ptr; use esp_idf_sys::{ esp, mcpwm_cmpr_handle_t, mcpwm_comparator_config_t, mcpwm_comparator_config_t__bindgen_ty_1, - mcpwm_gen_t, mcpwm_generator_set_actions_on_compare_event, mcpwm_new_comparator, + mcpwm_gen_compare_event_action_t, mcpwm_gen_handle_t, mcpwm_gen_t, mcpwm_new_comparator, mcpwm_oper_handle_t, mcpwm_timer_direction_t_MCPWM_TIMER_DIRECTION_DOWN, mcpwm_timer_direction_t_MCPWM_TIMER_DIRECTION_UP, }; @@ -47,23 +47,37 @@ pub struct Comparator(pub(crate) mcpwm_cmpr_handle_t); impl Comparator { pub(crate) unsafe fn configure(&mut self, gen: &mut mcpwm_gen_t, cfg: CountingDirection) { - // TODO: "must be terminated by MCPWM_GEN_TIMER_EVENT_ACTION_END()" - mcpwm_generator_set_actions_on_compare_event( - gen, - esp_idf_sys::mcpwm_gen_compare_event_action_t { - direction: mcpwm_timer_direction_t_MCPWM_TIMER_DIRECTION_UP, - comparator: self.0, - action: cfg.counting_up.into(), - }, - ); - mcpwm_generator_set_actions_on_compare_event( - gen, - esp_idf_sys::mcpwm_gen_compare_event_action_t { - direction: mcpwm_timer_direction_t_MCPWM_TIMER_DIRECTION_DOWN, - comparator: self.0, - action: cfg.counting_down.into(), - }, - ); + extern "C" { + fn mcpwm_generator_set_actions_on_compare_event( + gen: mcpwm_gen_handle_t, + ev_act: mcpwm_gen_compare_event_action_t, + ev_act_end: mcpwm_gen_compare_event_action_t, + ) -> esp_idf_sys::esp_err_t; + } + + let mut set_actions_on_compare_event = |action| { + esp!(mcpwm_generator_set_actions_on_compare_event( + gen, // mcpwm_generator_set_actions_on_compare_event + action, // is a variadic function in C. + mcpwm_gen_compare_event_action_t { + // <-- This marks the last argument in the variadic list + comparator: ptr::null_mut(), + ..Default::default() + } + )) + .unwrap() + }; + + set_actions_on_compare_event(mcpwm_gen_compare_event_action_t { + direction: mcpwm_timer_direction_t_MCPWM_TIMER_DIRECTION_UP, + comparator: self.0, + action: cfg.counting_up.into(), + }); + set_actions_on_compare_event(mcpwm_gen_compare_event_action_t { + direction: mcpwm_timer_direction_t_MCPWM_TIMER_DIRECTION_DOWN, + comparator: self.0, + action: cfg.counting_down.into(), + }); } } diff --git a/src/mcpwm/generator.rs b/src/mcpwm/generator.rs index a491ea8b73c..1c40b9d9002 100644 --- a/src/mcpwm/generator.rs +++ b/src/mcpwm/generator.rs @@ -5,10 +5,10 @@ use esp_idf_sys::{ mcpwm_generator_action_t_MCPWM_GEN_ACTION_HIGH, mcpwm_generator_action_t_MCPWM_GEN_ACTION_KEEP, mcpwm_generator_action_t_MCPWM_GEN_ACTION_LOW, mcpwm_generator_action_t_MCPWM_GEN_ACTION_TOGGLE, mcpwm_generator_config_t, - mcpwm_generator_config_t__bindgen_ty_1, mcpwm_generator_set_actions_on_timer_event, + mcpwm_generator_config_t__bindgen_ty_1, mcpwm_new_generator, mcpwm_oper_handle_t, mcpwm_timer_direction_t_MCPWM_TIMER_DIRECTION_DOWN, mcpwm_timer_direction_t_MCPWM_TIMER_DIRECTION_UP, mcpwm_timer_event_t_MCPWM_TIMER_EVENT_EMPTY, - mcpwm_timer_event_t_MCPWM_TIMER_EVENT_FULL, + mcpwm_timer_event_t_MCPWM_TIMER_EVENT_FULL, mcpwm_timer_event_t_MCPWM_TIMER_EVENT_INVALID, }; use crate::gpio::OutputPin; @@ -105,46 +105,57 @@ impl Opti flags: self.flags, }; let mut gen = ptr::null_mut(); + + extern "C" { + fn mcpwm_generator_set_actions_on_timer_event( + gen: mcpwm_gen_handle_t, + ev_act: mcpwm_gen_timer_event_action_t, + ev_act_end: mcpwm_gen_timer_event_action_t, + ) -> esp_idf_sys::esp_err_t; + } + unsafe { esp!(mcpwm_new_generator(operator_handle, &cfg, &mut gen)).unwrap(); - // TODO: "must be terminated by MCPWM_GEN_TIMER_EVENT_ACTION_END()" - esp!(mcpwm_generator_set_actions_on_timer_event( - gen, + let set_actions_on_timer_event = |action| { + esp!(mcpwm_generator_set_actions_on_timer_event( + gen, // mcpwm_generator_set_actions_on_timer_event + action, // is a variadic function in C. + mcpwm_gen_timer_event_action_t { // <-- This marks the last argument in the variadic list + event: mcpwm_timer_event_t_MCPWM_TIMER_EVENT_INVALID, + ..Default::default() + } + )).unwrap() + }; + + set_actions_on_timer_event( mcpwm_gen_timer_event_action_t { direction: mcpwm_timer_direction_t_MCPWM_TIMER_DIRECTION_UP, event: mcpwm_timer_event_t_MCPWM_TIMER_EVENT_EMPTY, action: self.on_is_empty.counting_up.into(), } - )) - .unwrap(); - esp!(mcpwm_generator_set_actions_on_timer_event( - gen, + ); + set_actions_on_timer_event( mcpwm_gen_timer_event_action_t { direction: mcpwm_timer_direction_t_MCPWM_TIMER_DIRECTION_DOWN, event: mcpwm_timer_event_t_MCPWM_TIMER_EVENT_EMPTY, action: self.on_is_empty.counting_down.into(), } - )) - .unwrap(); - esp!(mcpwm_generator_set_actions_on_timer_event( - gen, + ); + set_actions_on_timer_event( mcpwm_gen_timer_event_action_t { direction: mcpwm_timer_direction_t_MCPWM_TIMER_DIRECTION_UP, event: mcpwm_timer_event_t_MCPWM_TIMER_EVENT_FULL, action: self.on_is_full.counting_up.into(), } - )) - .unwrap(); - esp!(mcpwm_generator_set_actions_on_timer_event( - gen, + ); + set_actions_on_timer_event( mcpwm_gen_timer_event_action_t { direction: mcpwm_timer_direction_t_MCPWM_TIMER_DIRECTION_DOWN, event: mcpwm_timer_event_t_MCPWM_TIMER_EVENT_FULL, action: self.on_is_full.counting_down.into(), } - )) - .unwrap(); + ); if let Some(cmp_x) = cmp_x { cmp_x.configure(&mut *gen, self.on_matches_cmp_x.to_counting_direction()); From aa1970378282968ecd822c4102296073e9077cf2 Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Tue, 6 Dec 2022 22:05:09 +0100 Subject: [PATCH 50/62] One large variadic call instead of multiple small --- src/mcpwm/comparator.rs | 45 +++++++++++++++++++---------------------- src/mcpwm/generator.rs | 44 +++++++++++++++++----------------------- 2 files changed, 40 insertions(+), 49 deletions(-) diff --git a/src/mcpwm/comparator.rs b/src/mcpwm/comparator.rs index 9acf8d62d8d..b1a673b46de 100644 --- a/src/mcpwm/comparator.rs +++ b/src/mcpwm/comparator.rs @@ -50,34 +50,31 @@ impl Comparator { extern "C" { fn mcpwm_generator_set_actions_on_compare_event( gen: mcpwm_gen_handle_t, - ev_act: mcpwm_gen_compare_event_action_t, + ev_act0: mcpwm_gen_compare_event_action_t, + ev_act1: mcpwm_gen_compare_event_action_t, ev_act_end: mcpwm_gen_compare_event_action_t, ) -> esp_idf_sys::esp_err_t; } - let mut set_actions_on_compare_event = |action| { - esp!(mcpwm_generator_set_actions_on_compare_event( - gen, // mcpwm_generator_set_actions_on_compare_event - action, // is a variadic function in C. - mcpwm_gen_compare_event_action_t { - // <-- This marks the last argument in the variadic list - comparator: ptr::null_mut(), - ..Default::default() - } - )) - .unwrap() - }; - - set_actions_on_compare_event(mcpwm_gen_compare_event_action_t { - direction: mcpwm_timer_direction_t_MCPWM_TIMER_DIRECTION_UP, - comparator: self.0, - action: cfg.counting_up.into(), - }); - set_actions_on_compare_event(mcpwm_gen_compare_event_action_t { - direction: mcpwm_timer_direction_t_MCPWM_TIMER_DIRECTION_DOWN, - comparator: self.0, - action: cfg.counting_down.into(), - }); + esp!(mcpwm_generator_set_actions_on_compare_event( + gen, + mcpwm_gen_compare_event_action_t { + direction: mcpwm_timer_direction_t_MCPWM_TIMER_DIRECTION_UP, + comparator: self.0, + action: cfg.counting_up.into(), + }, + mcpwm_gen_compare_event_action_t { + direction: mcpwm_timer_direction_t_MCPWM_TIMER_DIRECTION_DOWN, + comparator: self.0, + action: cfg.counting_down.into(), + }, + mcpwm_gen_compare_event_action_t { + // <-- This marks the last argument in the variadic list + comparator: ptr::null_mut(), + ..Default::default() + } + )) + .unwrap(); } } diff --git a/src/mcpwm/generator.rs b/src/mcpwm/generator.rs index 1c40b9d9002..05b8b405574 100644 --- a/src/mcpwm/generator.rs +++ b/src/mcpwm/generator.rs @@ -5,8 +5,8 @@ use esp_idf_sys::{ mcpwm_generator_action_t_MCPWM_GEN_ACTION_HIGH, mcpwm_generator_action_t_MCPWM_GEN_ACTION_KEEP, mcpwm_generator_action_t_MCPWM_GEN_ACTION_LOW, mcpwm_generator_action_t_MCPWM_GEN_ACTION_TOGGLE, mcpwm_generator_config_t, - mcpwm_generator_config_t__bindgen_ty_1, - mcpwm_new_generator, mcpwm_oper_handle_t, mcpwm_timer_direction_t_MCPWM_TIMER_DIRECTION_DOWN, + mcpwm_generator_config_t__bindgen_ty_1, mcpwm_new_generator, mcpwm_oper_handle_t, + mcpwm_timer_direction_t_MCPWM_TIMER_DIRECTION_DOWN, mcpwm_timer_direction_t_MCPWM_TIMER_DIRECTION_UP, mcpwm_timer_event_t_MCPWM_TIMER_EVENT_EMPTY, mcpwm_timer_event_t_MCPWM_TIMER_EVENT_FULL, mcpwm_timer_event_t_MCPWM_TIMER_EVENT_INVALID, }; @@ -109,7 +109,11 @@ impl Opti extern "C" { fn mcpwm_generator_set_actions_on_timer_event( gen: mcpwm_gen_handle_t, - ev_act: mcpwm_gen_timer_event_action_t, + ev_act0: mcpwm_gen_timer_event_action_t, + ev_act1: mcpwm_gen_timer_event_action_t, + ev_act2: mcpwm_gen_timer_event_action_t, + ev_act3: mcpwm_gen_timer_event_action_t, + ev_act_end: mcpwm_gen_timer_event_action_t, ) -> esp_idf_sys::esp_err_t; } @@ -117,45 +121,35 @@ impl Opti unsafe { esp!(mcpwm_new_generator(operator_handle, &cfg, &mut gen)).unwrap(); - let set_actions_on_timer_event = |action| { - esp!(mcpwm_generator_set_actions_on_timer_event( - gen, // mcpwm_generator_set_actions_on_timer_event - action, // is a variadic function in C. - mcpwm_gen_timer_event_action_t { // <-- This marks the last argument in the variadic list - event: mcpwm_timer_event_t_MCPWM_TIMER_EVENT_INVALID, - ..Default::default() - } - )).unwrap() - }; - - set_actions_on_timer_event( + esp!(mcpwm_generator_set_actions_on_timer_event( + gen, mcpwm_gen_timer_event_action_t { direction: mcpwm_timer_direction_t_MCPWM_TIMER_DIRECTION_UP, event: mcpwm_timer_event_t_MCPWM_TIMER_EVENT_EMPTY, action: self.on_is_empty.counting_up.into(), - } - ); - set_actions_on_timer_event( + }, mcpwm_gen_timer_event_action_t { direction: mcpwm_timer_direction_t_MCPWM_TIMER_DIRECTION_DOWN, event: mcpwm_timer_event_t_MCPWM_TIMER_EVENT_EMPTY, action: self.on_is_empty.counting_down.into(), - } - ); - set_actions_on_timer_event( + }, mcpwm_gen_timer_event_action_t { direction: mcpwm_timer_direction_t_MCPWM_TIMER_DIRECTION_UP, event: mcpwm_timer_event_t_MCPWM_TIMER_EVENT_FULL, action: self.on_is_full.counting_up.into(), - } - ); - set_actions_on_timer_event( + }, mcpwm_gen_timer_event_action_t { direction: mcpwm_timer_direction_t_MCPWM_TIMER_DIRECTION_DOWN, event: mcpwm_timer_event_t_MCPWM_TIMER_EVENT_FULL, action: self.on_is_full.counting_down.into(), + }, + mcpwm_gen_timer_event_action_t { + // <-- This marks the last argument in the variadic list + event: mcpwm_timer_event_t_MCPWM_TIMER_EVENT_INVALID, + ..Default::default() } - ); + )) + .unwrap(); if let Some(cmp_x) = cmp_x { cmp_x.configure(&mut *gen, self.on_matches_cmp_x.to_counting_direction()); From 55745e67b64d0b7a64da17632f2b9f85e3cf2476 Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Sat, 10 Dec 2022 15:38:41 +0100 Subject: [PATCH 51/62] Avoid std --- src/mcpwm/timer.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mcpwm/timer.rs b/src/mcpwm/timer.rs index 251eb030f67..6d8776a97e9 100644 --- a/src/mcpwm/timer.rs +++ b/src/mcpwm/timer.rs @@ -1,6 +1,6 @@ use core::convert::TryInto; use core::marker::PhantomData; -use std::ptr; +use code::ptr; use esp_idf_sys::{ esp, mcpwm_del_timer, mcpwm_new_timer, mcpwm_timer_config_t, From 1c3dae44618e4a2cb253100733b7439a9b345a0c Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Sat, 10 Dec 2022 15:40:04 +0100 Subject: [PATCH 52/62] fmt --- src/mcpwm/timer.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mcpwm/timer.rs b/src/mcpwm/timer.rs index 6d8776a97e9..a052881e52a 100644 --- a/src/mcpwm/timer.rs +++ b/src/mcpwm/timer.rs @@ -1,6 +1,6 @@ +use code::ptr; use core::convert::TryInto; use core::marker::PhantomData; -use code::ptr; use esp_idf_sys::{ esp, mcpwm_del_timer, mcpwm_new_timer, mcpwm_timer_config_t, From 8bca808d5d196ba44b7fc6fc5b6bfbebc2d2758d Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Sat, 10 Dec 2022 15:49:04 +0100 Subject: [PATCH 53/62] Fix typo --- src/mcpwm/timer.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mcpwm/timer.rs b/src/mcpwm/timer.rs index a052881e52a..3249a93b5f9 100644 --- a/src/mcpwm/timer.rs +++ b/src/mcpwm/timer.rs @@ -1,6 +1,6 @@ -use code::ptr; use core::convert::TryInto; use core::marker::PhantomData; +use core::ptr; use esp_idf_sys::{ esp, mcpwm_del_timer, mcpwm_new_timer, mcpwm_timer_config_t, From 7063f05a3ddfbdfdaa005db5eedcd6ce1e555672 Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Sat, 10 Dec 2022 18:11:18 +0100 Subject: [PATCH 54/62] Fixes after testing on real hardware (ESP32-S3). Flags set to match IDF mcpwm servo example --- examples/mcpwm-simple.rs | 11 ++++++++--- src/mcpwm/comparator.rs | 4 ++-- src/mcpwm/operator.rs | 26 +++++++++++++++----------- src/mcpwm/timer.rs | 18 +++++++++++++----- 4 files changed, 38 insertions(+), 21 deletions(-) diff --git a/examples/mcpwm-simple.rs b/examples/mcpwm-simple.rs index 4709486857e..6b719600f3e 100644 --- a/examples/mcpwm-simple.rs +++ b/examples/mcpwm-simple.rs @@ -69,7 +69,7 @@ fn main() -> anyhow::Result<()> { println!("Configuring MCPWM"); let peripherals = Peripherals::take().unwrap(); - let timer_config = TimerConfig::default().period_ticks(16_000); // 10kHz + let timer_config = TimerConfig::default().period_ticks(8_000); // 10kHz let operator_config = OperatorConfig::default(peripherals.pins.gpio4, peripherals.pins.gpio5); let timer = Timer::new(peripherals.mcpwm0.timer0, timer_config); @@ -83,11 +83,16 @@ fn main() -> anyhow::Result<()> { println!("Starting duty-cycle loop"); let period_ticks = timer.get_period_peak(); + println!("period_ticks: {period_ticks}"); // TODO: Will this work as expected in UP_DOWN counter mode? - for duty in (0..period_ticks).cycle() { + for duty in (0..period_ticks).step_by(10).cycle() { if duty % 100 == 0 { - println!("Duty {}%", 100 * duty / period_ticks); + println!( + "cmp: {}, duty {}%", + duty, + 100 * u32::from(duty) / u32::from(period_ticks) + ); } operator.set_compare_value_x(duty)?; // In this configuration this controls the duty on pin4 diff --git a/src/mcpwm/comparator.rs b/src/mcpwm/comparator.rs index b1a673b46de..5dcb6f5a761 100644 --- a/src/mcpwm/comparator.rs +++ b/src/mcpwm/comparator.rs @@ -87,9 +87,9 @@ impl Default for ComparatorConfig { fn default() -> Self { let mut flags: mcpwm_comparator_config_t__bindgen_ty_1 = Default::default(); // TODO: What should be set here? - flags.set_update_cmp_on_tep(1); + flags.set_update_cmp_on_tep(0); flags.set_update_cmp_on_tez(1); - flags.set_update_cmp_on_sync(1); + flags.set_update_cmp_on_sync(0); Self { flags } } } diff --git a/src/mcpwm/operator.rs b/src/mcpwm/operator.rs index 6071941d12a..ffd81d529bb 100644 --- a/src/mcpwm/operator.rs +++ b/src/mcpwm/operator.rs @@ -89,13 +89,13 @@ where let mut flags: mcpwm_operator_config_t__bindgen_ty_1 = Default::default(); // TODO: What should these be set to? - flags.set_update_gen_action_on_tez(1); - flags.set_update_gen_action_on_tep(1); - flags.set_update_gen_action_on_sync(1); + flags.set_update_gen_action_on_tez(0); + flags.set_update_gen_action_on_tep(0); + flags.set_update_gen_action_on_sync(0); - flags.set_update_dead_time_on_tez(1); - flags.set_update_dead_time_on_tep(1); - flags.set_update_dead_time_on_sync(1); + flags.set_update_dead_time_on_tez(0); + flags.set_update_dead_time_on_tep(0); + flags.set_update_dead_time_on_sync(0); let config = mcpwm_operator_config_t { group_id: G::ID, @@ -109,6 +109,11 @@ where let mut comparator_x = unsafe { cfg.comparator_x.init(handle) }; let mut comparator_y = unsafe { cfg.comparator_y.init(handle) }; + // Connect operator to timer + unsafe { + esp!(mcpwm_operator_connect_timer(handle, timer_handle)).unwrap(); + } + let generator_a = unsafe { cfg.generator_a.init( handle, @@ -124,11 +129,6 @@ where ) }; - // Connect operator to timer - unsafe { - esp!(mcpwm_operator_connect_timer(handle, timer_handle)).unwrap(); - } - Operator { _instance: instance, _handle: handle, @@ -173,6 +173,8 @@ where /// TODO: what about CountMode::UpDown? /// /// NOTE: The compare value shouldn’t exceed timer’s count peak, otherwise, the compare event will never got triggered. + /// NOTE: This function is safe to from an ISR context + #[inline(always)] pub fn set_compare_value_x(&mut self, value: u16) -> Result<(), EspError> { unsafe { esp!(mcpwm_comparator_set_compare_value( @@ -206,6 +208,8 @@ where /// TODO: what about CountMode::UpDown? /// /// NOTE: The compare value shouldn’t exceed timer’s count peak, otherwise, the compare event will never got triggered. + /// NOTE: This function is safe to from an ISR context + #[inline(always)] pub fn set_compare_value_y(&mut self, value: u16) -> Result<(), EspError> { unsafe { esp!(mcpwm_comparator_set_compare_value( diff --git a/src/mcpwm/timer.rs b/src/mcpwm/timer.rs index 3249a93b5f9..0c5c6eea34f 100644 --- a/src/mcpwm/timer.rs +++ b/src/mcpwm/timer.rs @@ -9,7 +9,9 @@ use esp_idf_sys::{ mcpwm_timer_count_mode_t_MCPWM_TIMER_COUNT_MODE_PAUSE, mcpwm_timer_count_mode_t_MCPWM_TIMER_COUNT_MODE_UP, mcpwm_timer_count_mode_t_MCPWM_TIMER_COUNT_MODE_UP_DOWN, mcpwm_timer_enable, - mcpwm_timer_handle_t, soc_periph_mcpwm_timer_clk_src_t_MCPWM_TIMER_CLK_SRC_DEFAULT, + mcpwm_timer_handle_t, mcpwm_timer_start_stop, + mcpwm_timer_start_stop_cmd_t_MCPWM_TIMER_START_NO_STOP, + soc_periph_mcpwm_timer_clk_src_t_MCPWM_TIMER_CLK_SRC_DEFAULT, }; use crate::mcpwm::Group; @@ -32,8 +34,8 @@ pub struct TimerConfig { impl Default for TimerConfig { fn default() -> Self { Self { - resolution: 160.MHz().into(), - period_ticks: 16_000, // 10kHz + resolution: 80.MHz().into(), + period_ticks: 8_000, // 10kHz count_mode: CountMode::Up, } } @@ -54,8 +56,8 @@ impl TimerConfig { /// You can calculate the frequency as /// `frequency = resolution / period_ticks` /// - /// For example a resolution of 160MHz and a period_ticks of 16_000: - /// `10kHz = 160MHz / 16_000` + /// For example a resolution of 80MHz and a period_ticks of 8_000: + /// `10kHz = 80MHz / 8_000` /// /// NOTE: This will be the same as `Self::get_period_peak` for all `CounterMode` except for /// `CounterMode::UpDown` where the period will be twice as large as the peak value since @@ -106,12 +108,18 @@ impl Timer { }; let mut handle: mcpwm_timer_handle_t = ptr::null_mut(); unsafe { + println!("cfg: {cfg:?}"); esp!(mcpwm_new_timer(&cfg, &mut handle)).unwrap(); } // TODO: note that this has to be called before mcpwm_timer_enable // mcpwm_timer_register_event_callbacks() unsafe { esp!(mcpwm_timer_enable(handle)).unwrap(); + esp!(mcpwm_timer_start_stop( + handle, + mcpwm_timer_start_stop_cmd_t_MCPWM_TIMER_START_NO_STOP + )) + .unwrap(); } let period_peak = if config.count_mode == CountMode::UpDown { From 1bd2dfd5c608a9ccb4a4144a9afcb1cabccb75d4 Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Sat, 10 Dec 2022 18:48:52 +0100 Subject: [PATCH 55/62] Remove leftover debug code --- examples/mcpwm-simple.rs | 1 - src/mcpwm/timer.rs | 1 - 2 files changed, 2 deletions(-) diff --git a/examples/mcpwm-simple.rs b/examples/mcpwm-simple.rs index 6b719600f3e..a8255969b1e 100644 --- a/examples/mcpwm-simple.rs +++ b/examples/mcpwm-simple.rs @@ -83,7 +83,6 @@ fn main() -> anyhow::Result<()> { println!("Starting duty-cycle loop"); let period_ticks = timer.get_period_peak(); - println!("period_ticks: {period_ticks}"); // TODO: Will this work as expected in UP_DOWN counter mode? for duty in (0..period_ticks).step_by(10).cycle() { diff --git a/src/mcpwm/timer.rs b/src/mcpwm/timer.rs index 0c5c6eea34f..ead0a93e93c 100644 --- a/src/mcpwm/timer.rs +++ b/src/mcpwm/timer.rs @@ -108,7 +108,6 @@ impl Timer { }; let mut handle: mcpwm_timer_handle_t = ptr::null_mut(); unsafe { - println!("cfg: {cfg:?}"); esp!(mcpwm_new_timer(&cfg, &mut handle)).unwrap(); } // TODO: note that this has to be called before mcpwm_timer_enable From d786f05b73ea25062c170a2827fc2d2d5dcdaa94 Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Wed, 21 Dec 2022 08:03:07 +0100 Subject: [PATCH 56/62] Remove patch --- Cargo.toml | 3 --- 1 file changed, 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 8f0820f0cca..0ce5171d30f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,9 +13,6 @@ readme = "README.md" documentation = "https://esp-rs.github.io/esp-idf-hal/" rust-version = "1.61" -[patch.crates-io] -esp-idf-sys = { git = "https://github.com/esp-rs/esp-idf-sys", branch = "master" } - [features] default = ["std", "alloc", "esp-idf-sys"] std = ["alloc", "esp-idf-sys/std", "edge-executor?/std"] From 4acbca7a35a6bbae089ccce03c791c4981654037 Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Wed, 25 Jan 2023 18:00:32 +0100 Subject: [PATCH 57/62] Resolve some of the comments * Rename struct Timer to TimerDriver * Fix typo in fn attach_timerX * Remove OptionalOutputPin --- examples/mcpwm-simple.rs | 6 +++--- src/mcpwm/mod.rs | 2 +- src/mcpwm/timer.rs | 6 +++--- src/mcpwm/timer_connection.rs | 19 +++++++------------ src/peripherals.rs | 12 ++++++++++-- 5 files changed, 24 insertions(+), 21 deletions(-) diff --git a/examples/mcpwm-simple.rs b/examples/mcpwm-simple.rs index a8255969b1e..1f292d57657 100644 --- a/examples/mcpwm-simple.rs +++ b/examples/mcpwm-simple.rs @@ -61,7 +61,7 @@ fn main() -> anyhow::Result<()> { use embedded_hal::delay::DelayUs; use esp_idf_hal::delay::FreeRtos; - use esp_idf_hal::mcpwm::{OperatorConfig, Timer, TimerConfig}; + use esp_idf_hal::mcpwm::{OperatorConfig, TimerConfig, TimerDriver}; use esp_idf_hal::prelude::Peripherals; esp_idf_sys::link_patches(); @@ -71,11 +71,11 @@ fn main() -> anyhow::Result<()> { let peripherals = Peripherals::take().unwrap(); let timer_config = TimerConfig::default().period_ticks(8_000); // 10kHz let operator_config = OperatorConfig::default(peripherals.pins.gpio4, peripherals.pins.gpio5); - let timer = Timer::new(peripherals.mcpwm0.timer0, timer_config); + let timer = TimerDriver::new(peripherals.mcpwm0.timer0, timer_config); let mut timer = timer .into_connection() - .attatch_operator0(peripherals.mcpwm0.operator0, operator_config); + .attach_operator0(peripherals.mcpwm0.operator0, operator_config); // Borrow references to the contained timer and operator let (timer, operator, _, _) = timer.split(); diff --git a/src/mcpwm/mod.rs b/src/mcpwm/mod.rs index 9760b470a0b..ee44e0ff199 100644 --- a/src/mcpwm/mod.rs +++ b/src/mcpwm/mod.rs @@ -89,7 +89,7 @@ pub use self::{ generator::{GeneratorAction, GeneratorConfig}, operator::{Operator, OPERATOR}, operator_config::OperatorConfig, - timer::{Timer, TimerConfig, TIMER}, + timer::{TimerConfig, TimerDriver, TIMER}, timer_connection::TimerConnection, }; diff --git a/src/mcpwm/timer.rs b/src/mcpwm/timer.rs index ead0a93e93c..46311008ba6 100644 --- a/src/mcpwm/timer.rs +++ b/src/mcpwm/timer.rs @@ -75,7 +75,7 @@ impl TimerConfig { } } -pub struct Timer { +pub struct TimerDriver { _group: G, handle: mcpwm_timer_handle_t, _timer: TIMER, @@ -90,7 +90,7 @@ pub struct Timer { period_peak: u16, } -impl Timer { +impl TimerDriver { pub fn new(timer: TIMER, config: TimerConfig) -> Self { let mut flags: mcpwm_timer_config_t__bindgen_ty_1 = Default::default(); @@ -186,7 +186,7 @@ impl Timer { // TODO: Should this be done in TimerConnection instead to ensure everything is taken down // in the correct order? -impl Drop for Timer { +impl Drop for TimerDriver { fn drop(&mut self) { unsafe { esp!(mcpwm_del_timer(self.handle)).unwrap(); diff --git a/src/mcpwm/timer_connection.rs b/src/mcpwm/timer_connection.rs index 15047ea7370..b4c8c9a1902 100644 --- a/src/mcpwm/timer_connection.rs +++ b/src/mcpwm/timer_connection.rs @@ -4,7 +4,7 @@ use super::{ comparator::OptionalCmpCfg, generator::OptionalGenCfg, operator::{self, NoOperator, OptionalOperator, OPERATOR}, - timer::Timer, + timer::TimerDriver, Operator, OperatorConfig, }; @@ -16,14 +16,14 @@ where O1: OptionalOperator<1, G>, O2: OptionalOperator<2, G>, { - timer: Timer, + timer: TimerDriver, operator0: O0, operator1: O1, operator2: O2, } impl TimerConnection { - pub(crate) fn new(timer: Timer) -> Self { + pub(crate) fn new(timer: TimerDriver) -> Self { Self { timer, operator0: NoOperator, @@ -46,7 +46,7 @@ impl< O2: OptionalOperator<2, G>, > TimerConnection { - pub fn split(&mut self) -> (&mut Timer, &mut O0, &mut O1, &mut O2) { + pub fn split(&mut self) -> (&mut TimerDriver, &mut O0, &mut O1, &mut O2) { ( &mut self.timer, &mut self.operator0, @@ -63,7 +63,7 @@ where O2: OptionalOperator<2, G>, { #[allow(clippy::type_complexity)] - pub fn attatch_operator0( + pub fn attach_operator0( self, operator_handle: OPERATOR<0, G>, operator_cfg: OperatorConfig, @@ -91,7 +91,7 @@ where O2: OptionalOperator<2, G>, { #[allow(clippy::type_complexity)] - pub fn attatch_operator1( + pub fn attach_operator1( self, operator_handle: OPERATOR<1, G>, operator_cfg: OperatorConfig, @@ -119,7 +119,7 @@ where O1: OptionalOperator<1, G>, { #[allow(clippy::type_complexity)] - pub fn attatch_operator2( + pub fn attach_operator2( self, operator_handle: OPERATOR<2, G>, operator_cfg: OperatorConfig, @@ -154,8 +154,3 @@ impl Drop for TimerConnection } } */ - -// TODO: Should this be moved somewhere else? -pub trait OptionalOutputPin {} - -impl OptionalOutputPin for P {} diff --git a/src/peripherals.rs b/src/peripherals.rs index 497206c1bfb..4c95293ce2f 100644 --- a/src/peripherals.rs +++ b/src/peripherals.rs @@ -180,9 +180,17 @@ impl Peripherals { can: can::CAN::new(), #[cfg(not(feature = "riscv-ulp-hal"))] ledc: ledc::LEDC::new(), - #[cfg(all(any(esp32, esp32s3), esp_idf_version_major = "5"))] + #[cfg(all( + any(esp32, esp32s3), + not(feature = "riscv-ulp-hal"), + esp_idf_version_major = "5" + ))] mcpwm0: mcpwm::MCPWM::::new(), - #[cfg(all(any(esp32, esp32s3), esp_idf_version_major = "5"))] + #[cfg(all( + any(esp32, esp32s3), + not(feature = "riscv-ulp-hal"), + esp_idf_version_major = "5" + ))] mcpwm1: mcpwm::MCPWM::::new(), #[cfg(not(feature = "riscv-ulp-hal"))] rmt: rmt::RMT::new(), From 691337462ddfb4539197e4a7e3d37b200091d472 Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Sat, 28 Jan 2023 12:32:45 +0100 Subject: [PATCH 58/62] Make both comparators obligatory --- src/mcpwm/comparator.rs | 73 +++++------------------- src/mcpwm/generator.rs | 101 +++++++--------------------------- src/mcpwm/operator.rs | 48 +++++----------- src/mcpwm/operator_config.rs | 66 ++++++---------------- src/mcpwm/timer_connection.rs | 25 +++------ 5 files changed, 75 insertions(+), 238 deletions(-) diff --git a/src/mcpwm/comparator.rs b/src/mcpwm/comparator.rs index 5dcb6f5a761..c30ad876577 100644 --- a/src/mcpwm/comparator.rs +++ b/src/mcpwm/comparator.rs @@ -7,7 +7,7 @@ use esp_idf_sys::{ mcpwm_timer_direction_t_MCPWM_TIMER_DIRECTION_UP, }; -use super::generator::{CountingDirection, NoCmpMatchConfig, OnMatchCfg}; +use super::generator::CountingDirection; trait ComparatorChannel {} @@ -17,32 +17,6 @@ impl ComparatorChannel for CmpX {} pub struct CmpY; impl ComparatorChannel for CmpY {} -pub trait OptionalCmp { - type OnMatchCfg: OnMatchCfg; - type Cfg: OptionalCmpCfg; - - fn get_comparator_mut(&mut self) -> Option<&mut Comparator>; -} - -impl OptionalCmp for Comparator { - type OnMatchCfg = super::generator::CountingDirection; - type Cfg = ComparatorConfig; - - fn get_comparator_mut(&mut self) -> Option<&mut Comparator> { - Some(self) - } -} - -pub struct NoCmp; -impl OptionalCmp for NoCmp { - type OnMatchCfg = super::generator::NoCmpMatchConfig; - type Cfg = NoCmpCfg; - - fn get_comparator_mut(&mut self) -> Option<&mut Comparator> { - None - } -} - pub struct Comparator(pub(crate) mcpwm_cmpr_handle_t); impl Comparator { @@ -83,38 +57,8 @@ pub struct ComparatorConfig { flags: mcpwm_comparator_config_t__bindgen_ty_1, } -impl Default for ComparatorConfig { - fn default() -> Self { - let mut flags: mcpwm_comparator_config_t__bindgen_ty_1 = Default::default(); - // TODO: What should be set here? - flags.set_update_cmp_on_tep(0); - flags.set_update_cmp_on_tez(1); - flags.set_update_cmp_on_sync(0); - Self { flags } - } -} - -pub struct NoCmpCfg; - -pub trait OptionalCmpCfg { - type OnMatchCfg: OnMatchCfg; - type Cmp: OptionalCmp; - unsafe fn init(self, operator_handle: mcpwm_oper_handle_t) -> Self::Cmp; -} - -impl OptionalCmpCfg for NoCmpCfg { - type OnMatchCfg = NoCmpMatchConfig; - type Cmp = NoCmp; - - unsafe fn init(self, _operator_handle: mcpwm_oper_handle_t) -> NoCmp { - NoCmp - } -} -impl OptionalCmpCfg for ComparatorConfig { - type OnMatchCfg = CountingDirection; - type Cmp = Comparator; - - unsafe fn init(self, operator_handle: mcpwm_oper_handle_t) -> Comparator { +impl ComparatorConfig { + pub(crate) unsafe fn init(self, operator_handle: mcpwm_oper_handle_t) -> Comparator { let cfg = mcpwm_comparator_config_t { flags: self.flags }; let mut cmp = ptr::null_mut(); @@ -125,3 +69,14 @@ impl OptionalCmpCfg for ComparatorConfig { Comparator(cmp) } } + +impl Default for ComparatorConfig { + fn default() -> Self { + let mut flags: mcpwm_comparator_config_t__bindgen_ty_1 = Default::default(); + // TODO: What should be set here? + flags.set_update_cmp_on_tep(0); + flags.set_update_cmp_on_tez(1); + flags.set_update_cmp_on_sync(0); + Self { flags } + } +} diff --git a/src/mcpwm/generator.rs b/src/mcpwm/generator.rs index 05b8b405574..81fcf3f8dea 100644 --- a/src/mcpwm/generator.rs +++ b/src/mcpwm/generator.rs @@ -13,18 +13,16 @@ use esp_idf_sys::{ use crate::gpio::OutputPin; -use super::comparator::{Comparator, OptionalCmp}; +use super::comparator::Comparator; pub struct NoGen; impl OptionalGen for NoGen {} pub trait OptionalGen {} -impl OptionalGen for Generator +impl OptionalGen for Generator where G: GeneratorChannel, - CMPX: OnMatchCfg, - CMPY: OnMatchCfg, P: OutputPin, { } @@ -44,19 +42,17 @@ impl GeneratorChannel for GenB { } // TODO: Allow OptionalOutputPin? -pub struct Generator { +pub struct Generator { channel: PhantomData, - cmp_x: PhantomData, - cmp_y: PhantomData, pub(crate) _handle: mcpwm_gen_handle_t, pub(crate) _pin: P, } -pub struct GeneratorConfig { +pub struct GeneratorConfig { _channel: PhantomData, pub(crate) flags: mcpwm_generator_config_t__bindgen_ty_1, - pub(crate) on_matches_cmp_x: CMPX, - pub(crate) on_matches_cmp_y: CMPY, + pub(crate) on_matches_cmp_x: CountingDirection, + pub(crate) on_matches_cmp_y: CountingDirection, pub(crate) on_is_empty: CountingDirection, pub(crate) on_is_full: CountingDirection, pub(crate) pin: P, @@ -71,8 +67,8 @@ pub trait OptionalGenCfg { unsafe fn init( self, operator_handle: mcpwm_oper_handle_t, - cmp_x: Option<&mut Comparator>, - cmp_y: Option<&mut Comparator>, + cmp_x: &mut Comparator, + cmp_y: &mut Comparator, ) -> Self::Gen; } @@ -82,23 +78,21 @@ impl OptionalGenCfg for NoGenCfg { unsafe fn init( self, _operator_handle: mcpwm_oper_handle_t, - _cmp_x: Option<&mut Comparator>, - _cmp_y: Option<&mut Comparator>, + _cmp_x: &mut Comparator, + _cmp_y: &mut Comparator, ) -> NoGen { NoGen } } -impl OptionalGenCfg - for GeneratorConfig -{ - type Gen = Generator; +impl OptionalGenCfg for GeneratorConfig { + type Gen = Generator; unsafe fn init( self, operator_handle: mcpwm_oper_handle_t, - cmp_x: Option<&mut Comparator>, - cmp_y: Option<&mut Comparator>, + cmp_x: &mut Comparator, + cmp_y: &mut Comparator, ) -> Self::Gen { let cfg = mcpwm_generator_config_t { gen_gpio_num: self.pin.pin(), @@ -151,45 +145,19 @@ impl Opti )) .unwrap(); - if let Some(cmp_x) = cmp_x { - cmp_x.configure(&mut *gen, self.on_matches_cmp_x.to_counting_direction()); - } - - if let Some(cmp_y) = cmp_y { - cmp_y.configure(&mut *gen, self.on_matches_cmp_y.to_counting_direction()); - } + cmp_x.configure(&mut *gen, self.on_matches_cmp_x); + cmp_y.configure(&mut *gen, self.on_matches_cmp_y); } Generator { channel: PhantomData, - cmp_x: PhantomData, - cmp_y: PhantomData, _handle: gen, _pin: self.pin, } } } -pub trait GenInit { - type Gen: OptionalGen; - - /// This is only to be used internally by esp-idf-hal - unsafe fn init(self, operator_handle: mcpwm_oper_handle_t) -> Self::Gen; -} - -impl GenInit for (&mut CMPX, &mut CMPY, NoGenCfg) -where - CMPX: OptionalCmp, - CMPY: OptionalCmp, -{ - type Gen = NoGen; - - unsafe fn init(self, _operator_handle: mcpwm_oper_handle_t) -> Self::Gen { - NoGen - } -} - -impl GeneratorConfig { +impl GeneratorConfig { pub fn active_high(pin: P) -> Self { let mut result: Self = GeneratorConfig::empty(pin); @@ -215,11 +183,9 @@ impl GeneratorConfig GeneratorConfig +impl GeneratorConfig where G: GeneratorChannel, - CMPX: OnMatchCfg, - CMPY: OnMatchCfg, { fn empty(pin: P) -> Self { let mut flags: mcpwm_generator_config_t__bindgen_ty_1 = Default::default(); @@ -229,8 +195,8 @@ where GeneratorConfig { _channel: PhantomData, flags, - on_matches_cmp_x: OnMatchCfg::empty(), - on_matches_cmp_y: OnMatchCfg::empty(), + on_matches_cmp_x: CountingDirection::empty(), + on_matches_cmp_y: CountingDirection::empty(), on_is_empty: CountingDirection { counting_up: GeneratorAction::Nothing, counting_down: GeneratorAction::Nothing, @@ -244,18 +210,6 @@ where } } -pub struct NoCmpMatchConfig; - -impl OnMatchCfg for NoCmpMatchConfig { - fn empty() -> Self { - NoCmpMatchConfig - } - - fn to_counting_direction(self) -> CountingDirection { - CountingDirection::empty() - } -} - // TODO: Come up with better name #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct CountingDirection { @@ -272,21 +226,6 @@ impl CountingDirection { } } -impl OnMatchCfg for CountingDirection { - fn empty() -> Self { - CountingDirection::empty() - } - - fn to_counting_direction(self) -> CountingDirection { - self - } -} - -pub trait OnMatchCfg { - fn empty() -> Self; - fn to_counting_direction(self) -> CountingDirection; -} - impl From for mcpwm_generator_action_t { fn from(val: GeneratorAction) -> Self { match val { diff --git a/src/mcpwm/operator.rs b/src/mcpwm/operator.rs index ffd81d529bb..af13ee01795 100644 --- a/src/mcpwm/operator.rs +++ b/src/mcpwm/operator.rs @@ -7,7 +7,7 @@ use esp_idf_sys::{ use crate::mcpwm::Group; use super::{ - comparator::{Comparator, OptionalCmp, OptionalCmpCfg}, + comparator::Comparator, generator::{OptionalGen, OptionalGenCfg}, OperatorConfig, }; @@ -53,34 +53,25 @@ impl crate::peripheral::Peripheral for OPERATOR { /// /// Every Motor Control module has three operators. Every operator can generate two output signals called A and B. /// A and B share the same timer and thus frequency and phase but can have induvidual duty set. -pub struct Operator< - const N: u8, - G: Group, - CMPX: OptionalCmp, - CMPY: OptionalCmp, - GENA: OptionalGen, - GENB: OptionalGen, -> { +pub struct Operator { _instance: OPERATOR, _handle: mcpwm_oper_handle_t, - comparator_x: CMPX, // SOC_MCPWM_COMPARATORS_PER_OPERATOR is 2 for ESP32 and ESP32-S3 - comparator_y: CMPY, + comparator_x: Comparator, // SOC_MCPWM_COMPARATORS_PER_OPERATOR is 2 for ESP32 and ESP32-S3 + comparator_y: Comparator, _generator_a: GENA, // One generator per pin, with a maximum of two generators per Operator _generator_b: GENB, //deadtime: D } -pub(crate) unsafe fn new( +pub(crate) unsafe fn new( instance: OPERATOR, timer_handle: mcpwm_timer_handle_t, - cfg: OperatorConfig, -) -> Operator + cfg: OperatorConfig, +) -> Operator where G: Group, - CMPX: OptionalCmpCfg, - CMPY: OptionalCmpCfg, GENA: OptionalGenCfg, GENB: OptionalGenCfg, @@ -115,18 +106,12 @@ where } let generator_a = unsafe { - cfg.generator_a.init( - handle, - comparator_x.get_comparator_mut(), - comparator_y.get_comparator_mut(), - ) + cfg.generator_a + .init(handle, &mut comparator_x, &mut comparator_y) }; let generator_b = unsafe { - cfg.generator_b.init( - handle, - comparator_x.get_comparator_mut(), - comparator_y.get_comparator_mut(), - ) + cfg.generator_b + .init(handle, &mut comparator_x, &mut comparator_y) }; Operator { @@ -140,10 +125,9 @@ where } } -impl Operator +impl Operator where G: Group, - CMPY: OptionalCmp, GENA: OptionalGen, GENB: OptionalGen, { @@ -185,10 +169,9 @@ where } } -impl Operator +impl Operator where G: Group, - CMPX: OptionalCmp, GENA: OptionalGen, GENB: OptionalGen, { @@ -221,12 +204,9 @@ where } pub trait OptionalOperator {} -impl OptionalOperator - for Operator +impl OptionalOperator for Operator where G: Group, - CMPX: OptionalCmp, - CMPY: OptionalCmp, GENA: OptionalGen, GENB: OptionalGen, { diff --git a/src/mcpwm/operator_config.rs b/src/mcpwm/operator_config.rs index 7a75e06c31b..c74d708e2ea 100644 --- a/src/mcpwm/operator_config.rs +++ b/src/mcpwm/operator_config.rs @@ -3,25 +3,22 @@ use esp_idf_sys::mcpwm_operator_config_t__bindgen_ty_1; use crate::gpio::OutputPin; use super::{ - comparator::{ComparatorConfig, NoCmpCfg, OptionalCmpCfg}, - generator::{CountingDirection, GenA, GenB, GeneratorConfig, NoGenCfg, OptionalGenCfg}, + comparator::ComparatorConfig, + generator::{GenA, GenB, GeneratorConfig, NoGenCfg, OptionalGenCfg}, }; -type DefaultGeneratorConfigA = GeneratorConfig; -type DefaultGeneratorConfigB = GeneratorConfig; - #[derive(Default)] -pub struct OperatorConfig { +pub struct OperatorConfig { // TODO: When, how and who should set the flags? // Should that be done automagically, manually or does some specific setting cover all cases? /// Flags for Operator pub(crate) flags: mcpwm_operator_config_t__bindgen_ty_1, /// Configuration for Comparator X - pub(crate) comparator_x: CMPX, + pub(crate) comparator_x: ComparatorConfig, /// Configuration for Comparator Y - pub(crate) comparator_y: CMPY, + pub(crate) comparator_y: ComparatorConfig, /// Configuration for Generator A pub(crate) generator_a: GENA, @@ -30,16 +27,11 @@ pub struct OperatorConfig { pub(crate) generator_b: GENB, } -impl OperatorConfig { +impl OperatorConfig { pub fn default( pin_a: PA, pin_b: PB, - ) -> OperatorConfig< - ComparatorConfig, - ComparatorConfig, - DefaultGeneratorConfigA, - DefaultGeneratorConfigB, - > { + ) -> OperatorConfig, GeneratorConfig> { OperatorConfig::empty() .cmp_x(Default::default()) .cmp_y(Default::default()) @@ -61,8 +53,8 @@ impl OperatorConfig { OperatorConfig { flags, - comparator_x: NoCmpCfg, // SOC_MCPWM_COMPARATORS_PER_OPERATOR is 2 for ESP32 and ESP32-S3 - comparator_y: NoCmpCfg, + comparator_x: Default::default(), // SOC_MCPWM_COMPARATORS_PER_OPERATOR is 2 for ESP32 and ESP32-S3 + comparator_y: Default::default(), generator_a: NoGenCfg, // One generator per pin, with a maximum of two generators per Operator generator_b: NoGenCfg, @@ -70,11 +62,8 @@ impl OperatorConfig { } } -impl OperatorConfig { - fn cmp_x( - self, - config: ComparatorConfig, - ) -> OperatorConfig { +impl OperatorConfig { + fn cmp_x(self, config: ComparatorConfig) -> OperatorConfig { OperatorConfig { flags: self.flags, comparator_x: config, @@ -84,13 +73,8 @@ impl OperatorConfig { generator_b: self.generator_b, } } -} -impl OperatorConfig { - fn cmp_y( - self, - config: ComparatorConfig, - ) -> OperatorConfig { + fn cmp_y(self, config: ComparatorConfig) -> OperatorConfig { OperatorConfig { flags: self.flags, comparator_x: self.comparator_x, @@ -102,19 +86,12 @@ impl OperatorConfig { } } -impl - OperatorConfig -{ +impl OperatorConfig { #[allow(clippy::type_complexity)] fn gen_a( self, - config: GeneratorConfig, - ) -> OperatorConfig< - CMPX, - CMPY, - GeneratorConfig, - GENB, - > { + config: GeneratorConfig, + ) -> OperatorConfig, GENB> { OperatorConfig { flags: self.flags, comparator_x: self.comparator_x, @@ -126,19 +103,12 @@ impl } } -impl - OperatorConfig -{ +impl OperatorConfig { #[allow(clippy::type_complexity)] fn gen_b( self, - config: GeneratorConfig, - ) -> OperatorConfig< - CMPX, - CMPY, - GENA, - GeneratorConfig, - > { + config: GeneratorConfig, + ) -> OperatorConfig> { OperatorConfig { flags: self.flags, comparator_x: self.comparator_x, diff --git a/src/mcpwm/timer_connection.rs b/src/mcpwm/timer_connection.rs index b4c8c9a1902..fd801bb0441 100644 --- a/src/mcpwm/timer_connection.rs +++ b/src/mcpwm/timer_connection.rs @@ -1,7 +1,6 @@ use crate::mcpwm::Group; use super::{ - comparator::OptionalCmpCfg, generator::OptionalGenCfg, operator::{self, NoOperator, OptionalOperator, OPERATOR}, timer::TimerDriver, @@ -63,14 +62,12 @@ where O2: OptionalOperator<2, G>, { #[allow(clippy::type_complexity)] - pub fn attach_operator0( + pub fn attach_operator0( self, operator_handle: OPERATOR<0, G>, - operator_cfg: OperatorConfig, - ) -> TimerConnection, O1, O2> + operator_cfg: OperatorConfig, + ) -> TimerConnection, O1, O2> where - CMPX: OptionalCmpCfg, - CMPY: OptionalCmpCfg, GENA: OptionalGenCfg, GENB: OptionalGenCfg, { @@ -91,14 +88,12 @@ where O2: OptionalOperator<2, G>, { #[allow(clippy::type_complexity)] - pub fn attach_operator1( + pub fn attach_operator1( self, operator_handle: OPERATOR<1, G>, - operator_cfg: OperatorConfig, - ) -> TimerConnection, O2> + operator_cfg: OperatorConfig, + ) -> TimerConnection, O2> where - CMPX: OptionalCmpCfg, - CMPY: OptionalCmpCfg, GENA: OptionalGenCfg, GENB: OptionalGenCfg, { @@ -119,14 +114,12 @@ where O1: OptionalOperator<1, G>, { #[allow(clippy::type_complexity)] - pub fn attach_operator2( + pub fn attach_operator2( self, operator_handle: OPERATOR<2, G>, - operator_cfg: OperatorConfig, - ) -> TimerConnection> + operator_cfg: OperatorConfig, + ) -> TimerConnection> where - CMPX: OptionalCmpCfg, - CMPY: OptionalCmpCfg, GENA: OptionalGenCfg, GENB: OptionalGenCfg, { From cc76cb556bad184ee878ccb8592b69c56e06b102 Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Mon, 6 Feb 2023 19:17:18 +0100 Subject: [PATCH 59/62] Make Generators mandatory --- src/mcpwm/comparator.rs | 26 +++--- src/mcpwm/generator.rs | 148 +++++++++++++--------------------- src/mcpwm/operator.rs | 55 +++++-------- src/mcpwm/operator_config.rs | 92 +++++++-------------- src/mcpwm/timer_connection.rs | 51 +++++------- 5 files changed, 134 insertions(+), 238 deletions(-) diff --git a/src/mcpwm/comparator.rs b/src/mcpwm/comparator.rs index c30ad876577..e70e22bd90c 100644 --- a/src/mcpwm/comparator.rs +++ b/src/mcpwm/comparator.rs @@ -4,23 +4,19 @@ use esp_idf_sys::{ esp, mcpwm_cmpr_handle_t, mcpwm_comparator_config_t, mcpwm_comparator_config_t__bindgen_ty_1, mcpwm_gen_compare_event_action_t, mcpwm_gen_handle_t, mcpwm_gen_t, mcpwm_new_comparator, mcpwm_oper_handle_t, mcpwm_timer_direction_t_MCPWM_TIMER_DIRECTION_DOWN, - mcpwm_timer_direction_t_MCPWM_TIMER_DIRECTION_UP, + mcpwm_timer_direction_t_MCPWM_TIMER_DIRECTION_UP, EspError, }; use super::generator::CountingDirection; -trait ComparatorChannel {} - -pub struct CmpX; -impl ComparatorChannel for CmpX {} - -pub struct CmpY; -impl ComparatorChannel for CmpY {} - pub struct Comparator(pub(crate) mcpwm_cmpr_handle_t); impl Comparator { - pub(crate) unsafe fn configure(&mut self, gen: &mut mcpwm_gen_t, cfg: CountingDirection) { + pub(crate) unsafe fn configure( + &mut self, + gen: &mut mcpwm_gen_t, + cfg: CountingDirection, + ) -> Result<(), EspError> { extern "C" { fn mcpwm_generator_set_actions_on_compare_event( gen: mcpwm_gen_handle_t, @@ -48,7 +44,6 @@ impl Comparator { ..Default::default() } )) - .unwrap(); } } @@ -58,15 +53,18 @@ pub struct ComparatorConfig { } impl ComparatorConfig { - pub(crate) unsafe fn init(self, operator_handle: mcpwm_oper_handle_t) -> Comparator { + pub(crate) unsafe fn init( + self, + operator_handle: mcpwm_oper_handle_t, + ) -> Result { let cfg = mcpwm_comparator_config_t { flags: self.flags }; let mut cmp = ptr::null_mut(); unsafe { - esp!(mcpwm_new_comparator(operator_handle, &cfg, &mut cmp)).unwrap(); + esp!(mcpwm_new_comparator(operator_handle, &cfg, &mut cmp))?; } - Comparator(cmp) + Ok(Comparator(cmp)) } } diff --git a/src/mcpwm/generator.rs b/src/mcpwm/generator.rs index 81fcf3f8dea..824726da0d2 100644 --- a/src/mcpwm/generator.rs +++ b/src/mcpwm/generator.rs @@ -9,93 +9,43 @@ use esp_idf_sys::{ mcpwm_timer_direction_t_MCPWM_TIMER_DIRECTION_DOWN, mcpwm_timer_direction_t_MCPWM_TIMER_DIRECTION_UP, mcpwm_timer_event_t_MCPWM_TIMER_EVENT_EMPTY, mcpwm_timer_event_t_MCPWM_TIMER_EVENT_FULL, mcpwm_timer_event_t_MCPWM_TIMER_EVENT_INVALID, + EspError, }; -use crate::gpio::OutputPin; +use crate::{gpio::OutputPin, peripheral::Peripheral}; -use super::comparator::Comparator; +use super::{comparator::Comparator, Group}; -pub struct NoGen; - -impl OptionalGen for NoGen {} -pub trait OptionalGen {} - -impl OptionalGen for Generator -where - G: GeneratorChannel, - P: OutputPin, -{ -} - -pub trait GeneratorChannel { - const IS_A: bool; +pub enum ComparatorChannel { + CmpX, + CmpY, } -pub struct GenA; -impl GeneratorChannel for GenA { - const IS_A: bool = true; -} - -pub struct GenB; -impl GeneratorChannel for GenB { - const IS_A: bool = false; -} - -// TODO: Allow OptionalOutputPin? -pub struct Generator { - channel: PhantomData, +pub struct Generator<'d, G> { pub(crate) _handle: mcpwm_gen_handle_t, - pub(crate) _pin: P, + _p: PhantomData<&'d mut ()>, + _group: PhantomData, } -pub struct GeneratorConfig { - _channel: PhantomData, +pub struct GeneratorConfig<'d> { pub(crate) flags: mcpwm_generator_config_t__bindgen_ty_1, pub(crate) on_matches_cmp_x: CountingDirection, pub(crate) on_matches_cmp_y: CountingDirection, pub(crate) on_is_empty: CountingDirection, pub(crate) on_is_full: CountingDirection, - pub(crate) pin: P, -} - -pub struct NoGenCfg; - -pub trait OptionalGenCfg { - type Gen: OptionalGen; - - /// This is only to be used internally by esp-idf-hal - unsafe fn init( - self, - operator_handle: mcpwm_oper_handle_t, - cmp_x: &mut Comparator, - cmp_y: &mut Comparator, - ) -> Self::Gen; + pub(crate) pin: i32, + _p: PhantomData<&'d mut ()>, } -impl OptionalGenCfg for NoGenCfg { - type Gen = NoGen; - - unsafe fn init( - self, - _operator_handle: mcpwm_oper_handle_t, - _cmp_x: &mut Comparator, - _cmp_y: &mut Comparator, - ) -> NoGen { - NoGen - } -} - -impl OptionalGenCfg for GeneratorConfig { - type Gen = Generator; - - unsafe fn init( +impl<'d> GeneratorConfig<'d> { + pub(crate) unsafe fn init( self, operator_handle: mcpwm_oper_handle_t, cmp_x: &mut Comparator, cmp_y: &mut Comparator, - ) -> Self::Gen { + ) -> Result, EspError> { let cfg = mcpwm_generator_config_t { - gen_gpio_num: self.pin.pin(), + gen_gpio_num: self.pin, flags: self.flags, }; let mut gen = ptr::null_mut(); @@ -113,7 +63,7 @@ impl OptionalGenCfg for GeneratorConfig } unsafe { - esp!(mcpwm_new_generator(operator_handle, &cfg, &mut gen)).unwrap(); + esp!(mcpwm_new_generator(operator_handle, &cfg, &mut gen))?; esp!(mcpwm_generator_set_actions_on_timer_event( gen, @@ -142,58 +92,67 @@ impl OptionalGenCfg for GeneratorConfig event: mcpwm_timer_event_t_MCPWM_TIMER_EVENT_INVALID, ..Default::default() } - )) - .unwrap(); + ))?; - cmp_x.configure(&mut *gen, self.on_matches_cmp_x); - cmp_y.configure(&mut *gen, self.on_matches_cmp_y); + cmp_x.configure(&mut *gen, self.on_matches_cmp_x)?; + cmp_y.configure(&mut *gen, self.on_matches_cmp_y)?; } - Generator { - channel: PhantomData, + // + Ok(Generator { _handle: gen, - _pin: self.pin, - } + _p: PhantomData, + _group: PhantomData, + }) } -} -impl GeneratorConfig { - pub fn active_high(pin: P) -> Self { + pub fn active_high( + pin: impl Peripheral

+ 'd, + cmp_channel: ComparatorChannel, + ) -> Self { let mut result: Self = GeneratorConfig::empty(pin); result.on_is_empty.counting_up = GeneratorAction::SetHigh; - if G::IS_A { - result.on_matches_cmp_x.counting_up = GeneratorAction::SetLow; - } else { - result.on_matches_cmp_y.counting_up = GeneratorAction::SetLow; + match cmp_channel { + ComparatorChannel::CmpX => { + result.on_matches_cmp_x.counting_up = GeneratorAction::SetLow + } + ComparatorChannel::CmpY => { + result.on_matches_cmp_y.counting_up = GeneratorAction::SetLow + } } result } - pub fn active_low(pin: P) -> Self { + pub fn active_low( + pin: impl Peripheral

+ 'd, + cmp_channel: ComparatorChannel, + ) -> Self { let mut result: Self = GeneratorConfig::empty(pin); + result.on_is_empty.counting_up = GeneratorAction::SetLow; - if G::IS_A { - result.on_matches_cmp_x.counting_up = GeneratorAction::SetHigh; - } else { - result.on_matches_cmp_y.counting_up = GeneratorAction::SetHigh; + match cmp_channel { + ComparatorChannel::CmpX => { + result.on_matches_cmp_x.counting_up = GeneratorAction::SetHigh + } + ComparatorChannel::CmpY => { + result.on_matches_cmp_y.counting_up = GeneratorAction::SetHigh + } } result } } // TODO: Do we have any use for this? -impl GeneratorConfig -where - G: GeneratorChannel, -{ - fn empty(pin: P) -> Self { +impl<'d> GeneratorConfig<'d> { + fn empty(pin: impl Peripheral

+ 'd) -> Self { + crate::into_ref!(pin); + let mut flags: mcpwm_generator_config_t__bindgen_ty_1 = Default::default(); flags.set_invert_pwm(0); flags.set_io_loop_back(0); GeneratorConfig { - _channel: PhantomData, flags, on_matches_cmp_x: CountingDirection::empty(), on_matches_cmp_y: CountingDirection::empty(), @@ -205,7 +164,8 @@ where counting_up: GeneratorAction::Nothing, counting_down: GeneratorAction::Nothing, }, - pin, + pin: pin.pin(), + _p: PhantomData, } } } diff --git a/src/mcpwm/operator.rs b/src/mcpwm/operator.rs index af13ee01795..c834c1d5594 100644 --- a/src/mcpwm/operator.rs +++ b/src/mcpwm/operator.rs @@ -6,11 +6,7 @@ use esp_idf_sys::{ use crate::mcpwm::Group; -use super::{ - comparator::Comparator, - generator::{OptionalGen, OptionalGenCfg}, - OperatorConfig, -}; +use super::{comparator::Comparator, generator::Generator, OperatorConfig}; use core::{marker::PhantomData, ptr}; @@ -53,28 +49,25 @@ impl crate::peripheral::Peripheral for OPERATOR { /// /// Every Motor Control module has three operators. Every operator can generate two output signals called A and B. /// A and B share the same timer and thus frequency and phase but can have induvidual duty set. -pub struct Operator { +pub struct Operator<'d, const N: u8, G: Group> { _instance: OPERATOR, _handle: mcpwm_oper_handle_t, comparator_x: Comparator, // SOC_MCPWM_COMPARATORS_PER_OPERATOR is 2 for ESP32 and ESP32-S3 comparator_y: Comparator, - _generator_a: GENA, // One generator per pin, with a maximum of two generators per Operator - _generator_b: GENB, + _generator_a: Option>, // One generator per pin, with a maximum of two generators per Operator + _generator_b: Option>, //deadtime: D } -pub(crate) unsafe fn new( +pub(crate) unsafe fn new( instance: OPERATOR, timer_handle: mcpwm_timer_handle_t, - cfg: OperatorConfig, -) -> Operator + cfg: OperatorConfig, +) -> Result, EspError> where G: Group, - - GENA: OptionalGenCfg, - GENB: OptionalGenCfg, { let mut handle = ptr::null_mut(); let mut flags: mcpwm_operator_config_t__bindgen_ty_1 = Default::default(); @@ -94,27 +87,29 @@ where }; unsafe { - esp!(esp_idf_sys::mcpwm_new_operator(&config, &mut handle,)).unwrap(); + esp!(esp_idf_sys::mcpwm_new_operator(&config, &mut handle))?; } - let mut comparator_x = unsafe { cfg.comparator_x.init(handle) }; - let mut comparator_y = unsafe { cfg.comparator_y.init(handle) }; + let mut comparator_x = unsafe { cfg.comparator_x.init(handle)? }; + let mut comparator_y = unsafe { cfg.comparator_y.init(handle)? }; // Connect operator to timer unsafe { - esp!(mcpwm_operator_connect_timer(handle, timer_handle)).unwrap(); + esp!(mcpwm_operator_connect_timer(handle, timer_handle))?; } let generator_a = unsafe { cfg.generator_a - .init(handle, &mut comparator_x, &mut comparator_y) + .map(|g| g.init(handle, &mut comparator_x, &mut comparator_y)) + .transpose()? }; let generator_b = unsafe { cfg.generator_b - .init(handle, &mut comparator_x, &mut comparator_y) + .map(|g| g.init(handle, &mut comparator_x, &mut comparator_y)) + .transpose()? }; - Operator { + Ok(Operator { _instance: instance, _handle: handle, comparator_x, @@ -122,14 +117,12 @@ where _generator_a: generator_a, _generator_b: generator_b, - } + }) } -impl Operator +impl<'d, const N: u8, G> Operator<'d, N, G> where G: Group, - GENA: OptionalGen, - GENB: OptionalGen, { // TODO: Note that this is the comparator we are affecting, not the generator. Generator A may not necessarily have // anything to do with comparator A. How do we best convay that? Should we call them Generator X/Y and Comparator A/B? @@ -169,11 +162,9 @@ where } } -impl Operator +impl<'d, const N: u8, G> Operator<'d, N, G> where G: Group, - GENA: OptionalGen, - GENB: OptionalGen, { /// Get compare value, often times same as the duty for output B. /// @@ -204,13 +195,7 @@ where } pub trait OptionalOperator {} -impl OptionalOperator for Operator -where - G: Group, - GENA: OptionalGen, - GENB: OptionalGen, -{ -} +impl<'d, const N: u8, G> OptionalOperator for Operator<'d, N, G> where G: Group {} pub struct NoOperator; impl OptionalOperator for NoOperator {} diff --git a/src/mcpwm/operator_config.rs b/src/mcpwm/operator_config.rs index c74d708e2ea..60506108239 100644 --- a/src/mcpwm/operator_config.rs +++ b/src/mcpwm/operator_config.rs @@ -4,16 +4,11 @@ use crate::gpio::OutputPin; use super::{ comparator::ComparatorConfig, - generator::{GenA, GenB, GeneratorConfig, NoGenCfg, OptionalGenCfg}, + generator::{ComparatorChannel, GeneratorConfig}, }; #[derive(Default)] -pub struct OperatorConfig { - // TODO: When, how and who should set the flags? - // Should that be done automagically, manually or does some specific setting cover all cases? - /// Flags for Operator - pub(crate) flags: mcpwm_operator_config_t__bindgen_ty_1, - +pub struct OperatorConfig<'d> { /// Configuration for Comparator X pub(crate) comparator_x: ComparatorConfig, @@ -21,28 +16,28 @@ pub struct OperatorConfig { pub(crate) comparator_y: ComparatorConfig, /// Configuration for Generator A - pub(crate) generator_a: GENA, + pub(crate) generator_a: Option>, /// Configuration for Generator B - pub(crate) generator_b: GENB, + pub(crate) generator_b: Option>, } -impl OperatorConfig { - pub fn default( - pin_a: PA, - pin_b: PB, - ) -> OperatorConfig, GeneratorConfig> { +impl<'d> OperatorConfig<'d> { + pub fn default(pin_a: PA, pin_b: PB) -> OperatorConfig<'d> { OperatorConfig::empty() .cmp_x(Default::default()) .cmp_y(Default::default()) - .gen_a(GeneratorConfig::active_high(pin_a)) - .gen_b(GeneratorConfig::active_high(pin_b)) + .gen_a(GeneratorConfig::active_high(pin_a, ComparatorChannel::CmpX)) + .gen_b(GeneratorConfig::active_high(pin_b, ComparatorChannel::CmpY)) } pub fn empty() -> Self { let mut flags: mcpwm_operator_config_t__bindgen_ty_1 = Default::default(); // TODO: What value makes most sense as default here? + // TODO: When, how and who should set the flags? + // Should that be done automagically, manually or does some specific setting cover all cases? + // Flags for Operator flags.set_update_gen_action_on_tez(1); flags.set_update_gen_action_on_tep(1); flags.set_update_gen_action_on_sync(1); @@ -52,70 +47,39 @@ impl OperatorConfig { flags.set_update_dead_time_on_sync(1); OperatorConfig { - flags, comparator_x: Default::default(), // SOC_MCPWM_COMPARATORS_PER_OPERATOR is 2 for ESP32 and ESP32-S3 comparator_y: Default::default(), - generator_a: NoGenCfg, // One generator per pin, with a maximum of two generators per Operator - generator_b: NoGenCfg, + generator_a: None, // One generator per pin, with a maximum of two generators per Operator + generator_b: None, } } } -impl OperatorConfig { - fn cmp_x(self, config: ComparatorConfig) -> OperatorConfig { - OperatorConfig { - flags: self.flags, - comparator_x: config, - comparator_y: self.comparator_y, - - generator_a: self.generator_a, - generator_b: self.generator_b, - } +impl<'d> OperatorConfig<'d> { + fn cmp_x(mut self, config: ComparatorConfig) -> Self { + self.comparator_x = config; + self } - fn cmp_y(self, config: ComparatorConfig) -> OperatorConfig { - OperatorConfig { - flags: self.flags, - comparator_x: self.comparator_x, - comparator_y: config, - - generator_a: self.generator_a, - generator_b: self.generator_b, - } + fn cmp_y(mut self, config: ComparatorConfig) -> Self { + self.comparator_y = config; + self } } -impl OperatorConfig { +impl<'d> OperatorConfig<'d> { #[allow(clippy::type_complexity)] - fn gen_a( - self, - config: GeneratorConfig, - ) -> OperatorConfig, GENB> { - OperatorConfig { - flags: self.flags, - comparator_x: self.comparator_x, - comparator_y: self.comparator_y, - - generator_a: config, - generator_b: self.generator_b, - } + fn gen_a(mut self, config: GeneratorConfig<'d>) -> Self { + self.generator_a = Some(config); + self } } -impl OperatorConfig { +impl<'d> OperatorConfig<'d> { #[allow(clippy::type_complexity)] - fn gen_b( - self, - config: GeneratorConfig, - ) -> OperatorConfig> { - OperatorConfig { - flags: self.flags, - comparator_x: self.comparator_x, - comparator_y: self.comparator_y, - - generator_a: self.generator_a, - generator_b: config, - } + fn gen_b(mut self, config: GeneratorConfig<'d>) -> Self { + self.generator_b = Some(config); + self } } diff --git a/src/mcpwm/timer_connection.rs b/src/mcpwm/timer_connection.rs index fd801bb0441..02562494a50 100644 --- a/src/mcpwm/timer_connection.rs +++ b/src/mcpwm/timer_connection.rs @@ -1,7 +1,8 @@ +use esp_idf_sys::EspError; + use crate::mcpwm::Group; use super::{ - generator::OptionalGenCfg, operator::{self, NoOperator, OptionalOperator, OPERATOR}, timer::TimerDriver, Operator, OperatorConfig, @@ -62,22 +63,18 @@ where O2: OptionalOperator<2, G>, { #[allow(clippy::type_complexity)] - pub fn attach_operator0( + pub fn attach_operator0( self, operator_handle: OPERATOR<0, G>, - operator_cfg: OperatorConfig, - ) -> TimerConnection, O1, O2> - where - GENA: OptionalGenCfg, - GENB: OptionalGenCfg, - { - let operator = unsafe { operator::new(operator_handle, self.timer.timer(), operator_cfg) }; - TimerConnection { + operator_cfg: OperatorConfig<'_>, + ) -> Result, O1, O2>, EspError> { + let operator = unsafe { operator::new(operator_handle, self.timer.timer(), operator_cfg) }?; + Ok(TimerConnection { timer: self.timer, operator0: operator, operator1: self.operator1, operator2: self.operator2, - } + }) } } @@ -88,22 +85,18 @@ where O2: OptionalOperator<2, G>, { #[allow(clippy::type_complexity)] - pub fn attach_operator1( + pub fn attach_operator1( self, operator_handle: OPERATOR<1, G>, - operator_cfg: OperatorConfig, - ) -> TimerConnection, O2> - where - GENA: OptionalGenCfg, - GENB: OptionalGenCfg, - { - let operator = unsafe { operator::new(operator_handle, self.timer.timer(), operator_cfg) }; - TimerConnection { + operator_cfg: OperatorConfig<'_>, + ) -> Result, O2>, EspError> { + let operator = unsafe { operator::new(operator_handle, self.timer.timer(), operator_cfg) }?; + Ok(TimerConnection { timer: self.timer, operator0: self.operator0, operator1: operator, operator2: self.operator2, - } + }) } } @@ -114,22 +107,18 @@ where O1: OptionalOperator<1, G>, { #[allow(clippy::type_complexity)] - pub fn attach_operator2( + pub fn attach_operator2( self, operator_handle: OPERATOR<2, G>, - operator_cfg: OperatorConfig, - ) -> TimerConnection> - where - GENA: OptionalGenCfg, - GENB: OptionalGenCfg, - { - let operator = unsafe { operator::new(operator_handle, self.timer.timer(), operator_cfg) }; - TimerConnection { + operator_cfg: OperatorConfig<'_>, + ) -> Result>, EspError> { + let operator = unsafe { operator::new(operator_handle, self.timer.timer(), operator_cfg) }?; + Ok(TimerConnection { timer: self.timer, operator0: self.operator0, operator1: self.operator1, operator2: operator, - } + }) } } From bdcb5e4db4f9c2ec24091d7ad50e44807e26c7ae Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Mon, 6 Feb 2023 20:23:44 +0100 Subject: [PATCH 60/62] Update example --- examples/mcpwm-simple.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/mcpwm-simple.rs b/examples/mcpwm-simple.rs index 1f292d57657..b10a567101d 100644 --- a/examples/mcpwm-simple.rs +++ b/examples/mcpwm-simple.rs @@ -75,7 +75,8 @@ fn main() -> anyhow::Result<()> { let mut timer = timer .into_connection() - .attach_operator0(peripherals.mcpwm0.operator0, operator_config); + .attach_operator0(peripherals.mcpwm0.operator0, operator_config) + .expect("Failed to set up timer connection"); // Borrow references to the contained timer and operator let (timer, operator, _, _) = timer.split(); From 316f6859e85206d86b7bb824dd7077c9b7ea4f2d Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Mon, 6 Feb 2023 22:23:55 +0100 Subject: [PATCH 61/62] Remove some unwraps --- examples/mcpwm-simple.rs | 5 +++-- src/mcpwm/timer.rs | 31 +++++++------------------------ 2 files changed, 10 insertions(+), 26 deletions(-) diff --git a/examples/mcpwm-simple.rs b/examples/mcpwm-simple.rs index b10a567101d..0fe63ca4776 100644 --- a/examples/mcpwm-simple.rs +++ b/examples/mcpwm-simple.rs @@ -71,12 +71,13 @@ fn main() -> anyhow::Result<()> { let peripherals = Peripherals::take().unwrap(); let timer_config = TimerConfig::default().period_ticks(8_000); // 10kHz let operator_config = OperatorConfig::default(peripherals.pins.gpio4, peripherals.pins.gpio5); - let timer = TimerDriver::new(peripherals.mcpwm0.timer0, timer_config); + let timer = TimerDriver::new(peripherals.mcpwm0.timer0, timer_config) + .expect("Failed to set up timer"); let mut timer = timer .into_connection() .attach_operator0(peripherals.mcpwm0.operator0, operator_config) - .expect("Failed to set up timer connection"); + .expect("Failed to set up operator and timer connection"); // Borrow references to the contained timer and operator let (timer, operator, _, _) = timer.split(); diff --git a/src/mcpwm/timer.rs b/src/mcpwm/timer.rs index 46311008ba6..52822ae55ae 100644 --- a/src/mcpwm/timer.rs +++ b/src/mcpwm/timer.rs @@ -11,7 +11,7 @@ use esp_idf_sys::{ mcpwm_timer_count_mode_t_MCPWM_TIMER_COUNT_MODE_UP_DOWN, mcpwm_timer_enable, mcpwm_timer_handle_t, mcpwm_timer_start_stop, mcpwm_timer_start_stop_cmd_t_MCPWM_TIMER_START_NO_STOP, - soc_periph_mcpwm_timer_clk_src_t_MCPWM_TIMER_CLK_SRC_DEFAULT, + soc_periph_mcpwm_timer_clk_src_t_MCPWM_TIMER_CLK_SRC_DEFAULT, EspError, }; use crate::mcpwm::Group; @@ -91,7 +91,7 @@ pub struct TimerDriver { } impl TimerDriver { - pub fn new(timer: TIMER, config: TimerConfig) -> Self { + pub fn new(timer: TIMER, config: TimerConfig) -> Result { let mut flags: mcpwm_timer_config_t__bindgen_ty_1 = Default::default(); // TODO: What should these be set to? @@ -108,17 +108,16 @@ impl TimerDriver { }; let mut handle: mcpwm_timer_handle_t = ptr::null_mut(); unsafe { - esp!(mcpwm_new_timer(&cfg, &mut handle)).unwrap(); + esp!(mcpwm_new_timer(&cfg, &mut handle))?; } // TODO: note that this has to be called before mcpwm_timer_enable // mcpwm_timer_register_event_callbacks() unsafe { - esp!(mcpwm_timer_enable(handle)).unwrap(); + esp!(mcpwm_timer_enable(handle))?; esp!(mcpwm_timer_start_stop( handle, mcpwm_timer_start_stop_cmd_t_MCPWM_TIMER_START_NO_STOP - )) - .unwrap(); + ))?; } let period_peak = if config.count_mode == CountMode::UpDown { @@ -127,13 +126,13 @@ impl TimerDriver { cfg.period_ticks.try_into().unwrap() }; - Self { + Ok(Self { _group: G::default(), handle, _timer: timer, period_ticks: cfg.period_ticks, period_peak, - } + }) } // TODO: make sure this description is accurate @@ -163,22 +162,6 @@ impl TimerDriver { self.handle } - // TODO: It seems that we can't have both at the same time: - // a method for releasing its hardware resources - // and implementing Drop. - /* - pub fn release(self) -> TIMER { - let Self { - _group, - _timer, - handle - } = self; - unsafe { - esp!(mcpwm_del_timer(handle)).unwrap(); - } - _timer - }*/ - pub fn into_connection(self) -> TimerConnection { TimerConnection::new(self) } From 66d0dde4f1883b08c493b9e6d30409070a709dda Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Mon, 6 Feb 2023 22:24:28 +0100 Subject: [PATCH 62/62] fmt --- examples/mcpwm-simple.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/mcpwm-simple.rs b/examples/mcpwm-simple.rs index 0fe63ca4776..5921d82d59d 100644 --- a/examples/mcpwm-simple.rs +++ b/examples/mcpwm-simple.rs @@ -71,8 +71,8 @@ fn main() -> anyhow::Result<()> { let peripherals = Peripherals::take().unwrap(); let timer_config = TimerConfig::default().period_ticks(8_000); // 10kHz let operator_config = OperatorConfig::default(peripherals.pins.gpio4, peripherals.pins.gpio5); - let timer = TimerDriver::new(peripherals.mcpwm0.timer0, timer_config) - .expect("Failed to set up timer"); + let timer = + TimerDriver::new(peripherals.mcpwm0.timer0, timer_config).expect("Failed to set up timer"); let mut timer = timer .into_connection()