Skip to content
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

[RSDK-10192] [RSDK-10199] Update PWM logic for ESP-IDF 5 #440

Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 51 additions & 24 deletions micro-rdk/src/esp32/pwm.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use crate::esp32::esp_idf_svc::hal::gpio::AnyIOPin;
use crate::esp32::esp_idf_svc::hal::gpio::Pin;
use crate::esp32::esp_idf_svc::hal::ledc::{
config::TimerConfig, LedcDriver, LedcTimerDriver, SpeedMode, CHANNEL0, CHANNEL1, CHANNEL2,
CHANNEL3, CHANNEL4, CHANNEL5, CHANNEL6, CHANNEL7, TIMER0, TIMER1, TIMER2, TIMER3,
config::TimerConfig, LedcDriver, LedcTimer, LedcTimerDriver, LowSpeed, CHANNEL0, CHANNEL1,
CHANNEL2, CHANNEL3, CHANNEL4, CHANNEL5, CHANNEL6, CHANNEL7, TIMER0, TIMER1, TIMER2, TIMER3,
};
use crate::esp32::esp_idf_svc::hal::peripheral::Peripheral;
use crate::esp32::esp_idf_svc::hal::prelude::FromValueType;
Expand Down Expand Up @@ -109,8 +109,7 @@ impl<'a> PwmDriver<'a> {

pub fn get_timer_frequency(&self) -> u32 {
let timer: ledc_timer_t = (self.timer_number as u8).into();
// TODO(RSDK-10199): SpeedMode is now a trait in ESP-IDF 5
unsafe { ledc_get_freq(SpeedMode::LowSpeed.into(), timer) }
unsafe { ledc_get_freq(LowSpeed::SPEED_MODE, timer) }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i don't think LowSpeed is an enum anymore

Copy link
Member Author

@gvaradarajan gvaradarajan Mar 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not, it's a struct implementing the SpeedMode trait, but the trait contains a variable called SPEED_MODE

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think they are private

Copy link
Member Author

@gvaradarajan gvaradarajan Mar 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can trait variables be private? I didn't think they could

EDIT: I've confirmed that public/private visibility does not exist for things inside traits (although you can make the trait itself private by not exporting it)

}

pub fn set_timer_frequency(&mut self, frequency_hz: u32) -> Result<(), Esp32PwmError> {
Expand Down Expand Up @@ -166,6 +165,49 @@ impl From<PwmChannel> for usize {
}
}

pub enum LedcTimerOption<'a> {
Timer0(LedcTimerDriver<'a, TIMER0>),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a couple of place where LedcTimerDriver is returned or used would need to add the new generic param

Timer1(LedcTimerDriver<'a, TIMER1>),
Timer2(LedcTimerDriver<'a, TIMER2>),
Timer3(LedcTimerDriver<'a, TIMER3>),
}

impl LedcTimerOption<'_> {
pub fn new(timer: u8, conf: &TimerConfig) -> Result<Self, Esp32PwmError> {
match timer {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

need to cover the default case

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah, you're right, I missed that. My bad

0 => {
let driver = LedcTimerDriver::new(unsafe { TIMER0::new() }, conf)
.map_err(Esp32PwmError::EspError)?;
Ok(Self::Timer0(driver))
}
1 => {
let driver = LedcTimerDriver::new(unsafe { TIMER1::new() }, conf)
.map_err(Esp32PwmError::EspError)?;
Ok(Self::Timer1(driver))
}
2 => {
let driver = LedcTimerDriver::new(unsafe { TIMER2::new() }, conf)
.map_err(Esp32PwmError::EspError)?;
Ok(Self::Timer2(driver))
}
3 => {
let driver = LedcTimerDriver::new(unsafe { TIMER3::new() }, conf)
.map_err(Esp32PwmError::EspError)?;
Ok(Self::Timer3(driver))
}
}
}

pub fn timer(&self) -> ledc_timer_t {
match self {
Timer0(_) => TIMER0::timer(),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

where are these symbols coming from

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

imported above from esp_idf_svc::hal::ledc

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see Timer0 is it defined elsewhere?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

L. 169?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh i see then the syntax should be Self::Timer0

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yep, this PR's a real mess huh?

Timer1(_) => TIMER1::timer(),
Timer2(_) => TIMER2::timer(),
Timer3(_) => TIMER3::timer(),
}
}
}

#[derive(Debug)]
struct LedcManager<'a> {
used_channel: PwmChannelInUse,
Expand All @@ -176,8 +218,7 @@ struct LedcManager<'a> {
struct LedcTimerWrapper<'a> {
frequency: u32,
count: u8,
// TODO(RSDK-10192): LedcTimerDriver expects an additional template argument in ESP-IDF 5
timer: OnceCell<LedcTimerDriver<'a>>,
timer: OnceCell<LedcTimerOption<'a>>,
}

impl Debug for LedcTimerWrapper<'_> {
Expand All @@ -190,24 +231,11 @@ impl Debug for LedcTimerWrapper<'_> {
}
}

fn create_timer_driver<'a>(
timer: u8,
conf: &TimerConfig,
) -> Result<LedcTimerDriver<'a>, Esp32PwmError> {
match timer {
0 => LedcTimerDriver::new(unsafe { TIMER0::new() }, conf).map_err(Esp32PwmError::EspError),
1 => LedcTimerDriver::new(unsafe { TIMER1::new() }, conf).map_err(Esp32PwmError::EspError),
2 => LedcTimerDriver::new(unsafe { TIMER2::new() }, conf).map_err(Esp32PwmError::EspError),
3 => LedcTimerDriver::new(unsafe { TIMER3::new() }, conf).map_err(Esp32PwmError::EspError),
_ => unreachable!(),
}
}

impl LedcTimerWrapper<'_> {
fn new(id: u8, frequency_hz: u32) -> Result<Self, Esp32PwmError> {
let timer_config = TimerConfig::default().frequency(frequency_hz.Hz());
let timer = OnceCell::new();
let _ = timer.set(create_timer_driver(id, &timer_config)?);
let _ = timer.set(LedcTimerOption::new(timer, &timer_config)?);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you change the signature of the function it doesn't take a oncecell anymore

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think I did? Which function do you mean?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LedcTimerOption::new takes a u8 and not a OnceCell

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry I'm still confused, create_timer_driver didn't take a OnceCell either, what function are we talking about again?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LedcTimerOption::new(timer: u8, conf: &TimerConfig) takes an u8 but you pass timer as first arg. I think you meant id :P

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ohhhh, thank you.

Ok(Self {
count: 0,
frequency: frequency_hz,
Expand All @@ -232,7 +260,7 @@ impl LedcTimerWrapper<'_> {
// We have to reconfigure the timer so the appropriate clock source for that frequency may be
// selected. If no appropriate clock source exists the previous timer frequency
// will be retained
match create_timer_driver(id, &timer_config) {
match LedcTimerOption::new(id, &timer_config) {
Ok(driver) => {
let _ = self.timer.set(driver);
self.frequency = frequency_hz;
Expand All @@ -242,7 +270,7 @@ impl LedcTimerWrapper<'_> {
let timer_config = TimerConfig::default().frequency(self.frequency.Hz());
let _ =
self.timer
.set(create_timer_driver(id, &timer_config).unwrap_or_else(|_| {
.set(LedcTimerOption::new(id, &timer_config).unwrap_or_else(|_| {
panic!("bad frequency previously set on timer {:?}", id)
}));
Err(err)
Expand Down Expand Up @@ -352,8 +380,7 @@ impl<'a> LedcManager<'a> {
.ok_or(Esp32PwmError::NoTimersAvailable)?;
unsafe {
ledc_bind_channel_timer(
// TODO(RSDK-10199): SpeedMode is now a trait in ESP-IDF 5
SpeedMode::LowSpeed.into(),
LowSpeed::SPEED_MODE,
Into::<usize>::into(channel) as u32,
new_timer as u32,
)
Expand Down