-
Notifications
You must be signed in to change notification settings - Fork 173
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
MCPWM 5.0 #132
base: master
Are you sure you want to change the base?
MCPWM 5.0 #132
Changes from 56 commits
e47f8af
5a86430
bef1ea4
208bde0
ad91ea1
4e3b09c
8a1b0fd
17a3e63
a428ed6
8f2f559
d64529a
729cb85
15610a2
cc8589d
8ca34ba
4ed2696
490a84a
c874e78
9ebbeee
d05ae44
f5a13ca
6bc129a
1d779db
8384b06
5f01835
5acf5fc
a10f4f3
12e34db
0caf311
451dcd9
952a36f
9ec6458
ca5685e
dc05884
d932af4
8d80760
1907eef
bb8d957
e7e755e
d20ae62
7b2a929
6e8d31e
d3e44c1
c15c69f
f053be1
326299f
44cd1d8
5c4738b
7ab9357
aa19703
55745e6
1c3dae4
8bca808
7063f05
1bd2dfd
d786f05
4acbca7
6913374
cc76cb5
bdcb5e4
316f685
66d0dde
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
/// 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% | ||
/// | ||
/// . . | ||
/// . . | ||
/// .------. .------. | ||
/// | | | | | ||
/// pin4 | | | | | ||
/// | | | | | ||
/// ----- -------------------------- -------------------------- | ||
/// . . | ||
/// . . | ||
/// .------------------------. .------------------------. | ||
/// | | | | | ||
/// pin5 | | | | | ||
/// | | | | | ||
/// ----- -------- -------- | ||
/// . . | ||
/// | ||
/// | ||
/// # duty = 50% | ||
/// . . | ||
/// . . | ||
/// .---------------. .---------------. | ||
/// | | | | | ||
/// pin4 | | | | | ||
/// | | | | | ||
/// ----- ----------------- ----------------- | ||
/// . . | ||
/// . . | ||
/// .---------------. .---------------. | ||
/// | | | | | ||
/// pin5 | | | | | ||
/// | | | | | ||
/// ----- ----------------- ----------------- | ||
/// . . | ||
/// | ||
/// | ||
/// # duty = 90% | ||
/// . . | ||
/// . . | ||
/// .------------------------. .------------------------. | ||
/// | | | | | ||
/// pin4 | | | | | ||
/// | | | | | ||
/// ----- -------- -------- | ||
/// . . | ||
/// . . | ||
/// .------. .------. | ||
/// | | | | | ||
/// pin5 | | | | | ||
/// | | | | | ||
/// ----- -------------------------- -------------------------- | ||
/// . . | ||
|
||
#[cfg(all(any(esp32, esp32s3), esp_idf_version_major = "5"))] | ||
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::prelude::Peripherals; | ||
|
||
esp_idf_sys::link_patches(); | ||
|
||
println!("Configuring MCPWM"); | ||
|
||
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 mut timer = timer | ||
.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"); | ||
|
||
let period_ticks = timer.get_period_peak(); | ||
|
||
// TODO: Will this work as expected in UP_DOWN counter mode? | ||
for duty in (0..period_ticks).step_by(10).cycle() { | ||
if duty % 100 == 0 { | ||
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 | ||
operator.set_compare_value_y(period_ticks - duty)?; // and this controls pin5 | ||
FreeRtos.delay_ms(10)?; | ||
} | ||
|
||
unreachable!() | ||
} | ||
|
||
#[cfg(not(all(any(esp32, esp32s3), esp_idf_version_major = "5")))] | ||
fn main() { | ||
esp_idf_sys::link_patches(); | ||
|
||
println!("Sorry MCPWM is only supported on ESP32 and ESP32-S3"); | ||
} |
Original file line number | Diff line number | Diff line change | ||||||||
---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,127 @@ | ||||||||||
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_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, | ||||||||||
}; | ||||||||||
|
||||||||||
use super::generator::{CountingDirection, NoCmpMatchConfig, OnMatchCfg}; | ||||||||||
|
||||||||||
trait ComparatorChannel {} | ||||||||||
|
||||||||||
pub struct CmpX; | ||||||||||
impl ComparatorChannel for CmpX {} | ||||||||||
|
||||||||||
pub struct CmpY; | ||||||||||
impl ComparatorChannel for CmpY {} | ||||||||||
|
||||||||||
pub trait OptionalCmp { | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am struggling to understand what Struct Would you elaborate? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Again, I am not quite happy with the huge amount of traits either :/ The motivation behind esp-idf-hal/src/mcpwm/operator.rs Line 143 in 4acbca7
I personally do not quite like a method doing that check at runtime. I will hower change it if you would. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I do not have time the time to check right now but I believe, if I am not mistaken, there was also something similar when building the generator config. The generator config should not depend on events from comparators that are not provided. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. On second thought, perhaps both comparators should be obligatory since they can not be used for anything else outside their operator anyways... |
||||||||||
type OnMatchCfg: OnMatchCfg; | ||||||||||
type Cfg: OptionalCmpCfg<Cmp = Self>; | ||||||||||
|
||||||||||
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 { | ||||||||||
pub(crate) unsafe fn configure(&mut self, gen: &mut mcpwm_gen_t, cfg: CountingDirection) { | ||||||||||
extern "C" { | ||||||||||
fn mcpwm_generator_set_actions_on_compare_event( | ||||||||||
gen: mcpwm_gen_handle_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; | ||||||||||
} | ||||||||||
|
||||||||||
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(); | ||||||||||
} | ||||||||||
} | ||||||||||
|
||||||||||
#[derive(Debug, Clone, Copy)] | ||||||||||
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); | ||||||||||
Comment on lines
+75
to
+76
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just remembered espressif/esp-idf#9904 . Do you think There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
flags.set_update_cmp_on_sync(0); | ||||||||||
Self { flags } | ||||||||||
} | ||||||||||
} | ||||||||||
|
||||||||||
pub struct NoCmpCfg; | ||||||||||
|
||||||||||
pub trait OptionalCmpCfg { | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ditto here. Why are we hiding simple |
||||||||||
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) | ||||||||||
} | ||||||||||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The implementation uses lot's of traits which results in a heavy utilization of generics, making the code difficult to comprehend and probably difficult to use.
I don't mind generics and traits, if they cannot be avoided, but I'm questioning the utility of almost all traits in this module. Most of them can be replaced with alternatives: enums or simple options.
I'll comment on each trait below.
For
ComparatorChannel
specifically: why not just