(ESP32 only) Support for the high-speed channels

This commit is contained in:
ivmarkov 2024-05-23 19:02:42 +00:00
parent 2ae14905c6
commit fefb4cfa6a
2 changed files with 160 additions and 77 deletions

View File

@ -31,7 +31,7 @@ use core::sync::atomic::{AtomicBool, Ordering};
use esp_idf_sys::*; use esp_idf_sys::*;
use crate::gpio::OutputPin; use crate::gpio::OutputPin;
use crate::peripheral::Peripheral; use crate::peripheral::{Peripheral, PeripheralRef};
use crate::task::CriticalSection; use crate::task::CriticalSection;
use crate::units::*; use crate::units::*;
@ -61,12 +61,14 @@ pub mod config {
pub struct TimerConfig { pub struct TimerConfig {
pub frequency: Hertz, pub frequency: Hertz,
pub resolution: Resolution, pub resolution: Resolution,
pub speed_mode: SpeedMode,
} }
impl TimerConfig { impl TimerConfig {
pub fn new() -> Self { pub const fn new() -> Self {
Default::default() Self {
frequency: Hertz(1000),
resolution: Resolution::Bits8,
}
} }
#[must_use] #[must_use]
@ -80,41 +82,38 @@ pub mod config {
self.resolution = r; self.resolution = r;
self self
} }
#[must_use]
pub fn speed_mode(mut self, mode: SpeedMode) -> Self {
self.speed_mode = mode;
self
}
} }
impl Default for TimerConfig { impl Default for TimerConfig {
fn default() -> Self { fn default() -> Self {
TimerConfig { Self::new()
frequency: 1000.Hz(),
resolution: Resolution::Bits8,
speed_mode: SpeedMode::LowSpeed,
}
} }
} }
} }
/// LED Control timer driver /// LED Control timer driver
pub struct LedcTimerDriver<'d> { pub struct LedcTimerDriver<'d, T>
timer: u8, where
speed_mode: SpeedMode, T: LedcTimer,
{
_timer: PeripheralRef<'d, T>,
max_duty: Duty, max_duty: Duty,
_p: PhantomData<&'d mut ()>, _p: PhantomData<&'d mut ()>,
} }
impl<'d> LedcTimerDriver<'d> { impl<'d, T> LedcTimerDriver<'d, T>
pub fn new<T: LedcTimer>( where
_timer: impl Peripheral<P = T> + 'd, T: LedcTimer,
{
pub fn new(
timer: impl Peripheral<P = T> + 'd,
config: &config::TimerConfig, config: &config::TimerConfig,
) -> Result<Self, EspError> { ) -> Result<Self, EspError> {
crate::into_ref!(timer);
let timer_config = ledc_timer_config_t { let timer_config = ledc_timer_config_t {
speed_mode: config.speed_mode.into(), speed_mode: T::SpeedMode::SPEED_MODE,
timer_num: T::timer(), timer_num: T::timer() as _,
#[cfg(esp_idf_version_major = "4")] #[cfg(esp_idf_version_major = "4")]
__bindgen_anon_1: ledc_timer_config_t__bindgen_ty_1 { __bindgen_anon_1: ledc_timer_config_t__bindgen_ty_1 {
duty_resolution: config.resolution.timer_bits(), duty_resolution: config.resolution.timer_bits(),
@ -138,8 +137,7 @@ impl<'d> LedcTimerDriver<'d> {
esp!(unsafe { ledc_timer_config(&timer_config) })?; esp!(unsafe { ledc_timer_config(&timer_config) })?;
Ok(Self { Ok(Self {
timer: T::timer() as _, _timer: timer,
speed_mode: config.speed_mode,
max_duty: config.resolution.max_duty(), max_duty: config.resolution.max_duty(),
_p: PhantomData, _p: PhantomData,
}) })
@ -147,41 +145,42 @@ impl<'d> LedcTimerDriver<'d> {
/// Pauses the timer. Operation can be resumed with [`resume_timer()`]. /// Pauses the timer. Operation can be resumed with [`resume_timer()`].
pub fn pause(&mut self) -> Result<(), EspError> { pub fn pause(&mut self) -> Result<(), EspError> {
esp!(unsafe { ledc_timer_pause(self.speed_mode.into(), self.timer()) })?; esp!(unsafe { ledc_timer_pause(T::SpeedMode::SPEED_MODE, self.timer()) })?;
Ok(()) Ok(())
} }
/// Resumes the operation of the previously paused timer /// Resumes the operation of the previously paused timer
pub fn resume(&mut self) -> Result<(), EspError> { pub fn resume(&mut self) -> Result<(), EspError> {
esp!(unsafe { ledc_timer_resume(self.speed_mode.into(), self.timer()) })?; esp!(unsafe { ledc_timer_resume(T::SpeedMode::SPEED_MODE, self.timer()) })?;
Ok(()) Ok(())
} }
/// Set the frequency of the timer. /// Set the frequency of the timer.
pub fn set_frequency(&mut self, frequency: Hertz) -> Result<(), EspError> { pub fn set_frequency(&mut self, frequency: Hertz) -> Result<(), EspError> {
esp!(unsafe { esp!(unsafe { ledc_set_freq(T::SpeedMode::SPEED_MODE, T::timer(), frequency.into()) })?;
ledc_set_freq(self.speed_mode.into(), self.timer.into(), frequency.into())
})?;
Ok(()) Ok(())
} }
fn reset(&mut self) -> Result<(), EspError> { fn reset(&mut self) -> Result<(), EspError> {
esp!(unsafe { ledc_timer_rst(self.speed_mode.into(), self.timer()) })?; esp!(unsafe { ledc_timer_rst(T::SpeedMode::SPEED_MODE, T::timer()) })?;
Ok(()) Ok(())
} }
pub fn timer(&self) -> ledc_timer_t { pub fn timer(&self) -> ledc_timer_t {
self.timer as _ T::timer()
} }
} }
impl<'d> Drop for LedcTimerDriver<'d> { impl<'d, T> Drop for LedcTimerDriver<'d, T>
where
T: LedcTimer,
{
fn drop(&mut self) { fn drop(&mut self) {
self.reset().unwrap(); self.reset().unwrap();
} }
} }
unsafe impl<'d> Send for LedcTimerDriver<'d> {} unsafe impl<'d, T> Send for LedcTimerDriver<'d, T> where T: LedcTimer {}
/// LED Control driver /// LED Control driver
pub struct LedcDriver<'d> { pub struct LedcDriver<'d> {
@ -189,7 +188,7 @@ pub struct LedcDriver<'d> {
timer: u8, timer: u8,
duty: Duty, duty: Duty,
hpoint: HPoint, hpoint: HPoint,
speed_mode: SpeedMode, speed_mode: ledc_mode_t,
max_duty: Duty, max_duty: Duty,
_p: PhantomData<&'d mut ()>, _p: PhantomData<&'d mut ()>,
} }
@ -199,11 +198,16 @@ pub struct LedcDriver<'d> {
// and implementing Drop. // and implementing Drop.
impl<'d> LedcDriver<'d> { impl<'d> LedcDriver<'d> {
/// Creates a new LED Control driver /// Creates a new LED Control driver
pub fn new<C: LedcChannel, B: Borrow<LedcTimerDriver<'d>>>( pub fn new<C, T, B>(
_channel: impl Peripheral<P = C> + 'd, _channel: impl Peripheral<P = C> + 'd,
timer_driver: B, timer_driver: B,
pin: impl Peripheral<P = impl OutputPin> + 'd, pin: impl Peripheral<P = impl OutputPin> + 'd,
) -> Result<Self, EspError> { ) -> Result<Self, EspError>
where
C: LedcChannel<SpeedMode = <T as LedcTimer>::SpeedMode>,
T: LedcTimer + 'd,
B: Borrow<LedcTimerDriver<'d, T>>,
{
if !FADE_FUNC_INSTALLED.load(Ordering::SeqCst) { if !FADE_FUNC_INSTALLED.load(Ordering::SeqCst) {
let _guard = FADE_FUNC_INSTALLED_CS.enter(); let _guard = FADE_FUNC_INSTALLED_CS.enter();
@ -223,7 +227,7 @@ impl<'d> LedcDriver<'d> {
let mut driver = LedcDriver { let mut driver = LedcDriver {
duty: 0, duty: 0,
hpoint: 0, hpoint: 0,
speed_mode: timer_driver.borrow().speed_mode, speed_mode: T::SpeedMode::SPEED_MODE,
max_duty: timer_driver.borrow().max_duty, max_duty: timer_driver.borrow().max_duty,
timer: timer_driver.borrow().timer() as _, timer: timer_driver.borrow().timer() as _,
channel: C::channel() as _, channel: C::channel() as _,
@ -244,7 +248,7 @@ impl<'d> LedcDriver<'d> {
crate::into_ref!(pin); crate::into_ref!(pin);
let channel_config = ledc_channel_config_t { let channel_config = ledc_channel_config_t {
speed_mode: self.speed_mode.into(), speed_mode: self.speed_mode,
channel: self.channel as u32, channel: self.channel as u32,
timer_sel: self.timer as u32, timer_sel: self.timer as u32,
intr_type: ledc_intr_type_t_LEDC_INTR_DISABLE, intr_type: ledc_intr_type_t_LEDC_INTR_DISABLE,
@ -303,14 +307,12 @@ impl<'d> LedcDriver<'d> {
} }
fn stop(&mut self) -> Result<(), EspError> { fn stop(&mut self) -> Result<(), EspError> {
esp!(unsafe { ledc_stop(self.speed_mode.into(), self.channel(), IDLE_LEVEL,) })?; esp!(unsafe { ledc_stop(self.speed_mode, self.channel(), IDLE_LEVEL,) })?;
Ok(()) Ok(())
} }
fn update_duty(&mut self, duty: Duty, hpoint: HPoint) -> Result<(), EspError> { fn update_duty(&mut self, duty: Duty, hpoint: HPoint) -> Result<(), EspError> {
esp!(unsafe { esp!(unsafe { ledc_set_duty_and_update(self.speed_mode, self.channel(), duty, hpoint) })?;
ledc_set_duty_and_update(self.speed_mode.into(), self.channel(), duty, hpoint)
})?;
Ok(()) Ok(())
} }
@ -503,47 +505,50 @@ mod chip {
} }
} }
/// Ledc Speed Mode /// Speed mode for the LED Control peripheral
#[derive(PartialEq, Eq, Copy, Clone, Debug)] /// The ESP32 supports two speed modes: low and high speed
pub enum SpeedMode { /// All others support only low speed mode.
#[cfg(esp_idf_soc_ledc_support_hs_mode)] pub trait SpeedMode: Send + Sync + 'static {
/// High Speed Mode. Currently only supported on the ESP32. const SPEED_MODE: ledc_mode_t;
HighSpeed,
/// Low Speed Mode. The only configuration supported on ESP32S2, ESP32S3, ESP32C2 and ESP32C3.
LowSpeed,
} }
impl Default for SpeedMode { /// Low speed mode for the LED Control peripheral
fn default() -> Self { pub struct LowSpeed;
Self::LowSpeed
} impl SpeedMode for LowSpeed {
const SPEED_MODE: ledc_mode_t = ledc_mode_t_LEDC_LOW_SPEED_MODE;
} }
impl From<SpeedMode> for ledc_mode_t { #[cfg(esp32)]
fn from(speed_mode: SpeedMode) -> Self { /// High speed mode for the LED Control peripheral (ESP32 only)
match speed_mode { pub struct HighSpeed;
#[cfg(esp_idf_soc_ledc_support_hs_mode)]
SpeedMode::HighSpeed => ledc_mode_t_LEDC_HIGH_SPEED_MODE, #[cfg(esp32)]
SpeedMode::LowSpeed => ledc_mode_t_LEDC_LOW_SPEED_MODE, impl SpeedMode for HighSpeed {
} const SPEED_MODE: ledc_mode_t = ledc_mode_t_LEDC_HIGH_SPEED_MODE;
}
} }
/// LED Control peripheral timer /// LED Control peripheral timer
pub trait LedcTimer { pub trait LedcTimer {
type SpeedMode: SpeedMode;
fn timer() -> ledc_timer_t; fn timer() -> ledc_timer_t;
} }
/// LED Control peripheral output channel /// LED Control peripheral output channel
pub trait LedcChannel { pub trait LedcChannel {
type SpeedMode: SpeedMode;
fn channel() -> ledc_channel_t; fn channel() -> ledc_channel_t;
} }
macro_rules! impl_timer { macro_rules! impl_timer {
($instance:ident: $timer:expr) => { ($typ:ty; $instance:ident: $timer:expr) => {
crate::impl_peripheral!($instance); crate::impl_peripheral!($instance);
impl LedcTimer for $instance { impl LedcTimer for $instance {
type SpeedMode = $typ;
fn timer() -> ledc_timer_t { fn timer() -> ledc_timer_t {
$timer $timer
} }
@ -551,16 +556,27 @@ mod chip {
}; };
} }
impl_timer!(TIMER0: ledc_timer_t_LEDC_TIMER_0); impl_timer!(LowSpeed; TIMER0: ledc_timer_t_LEDC_TIMER_0);
impl_timer!(TIMER1: ledc_timer_t_LEDC_TIMER_1); impl_timer!(LowSpeed; TIMER1: ledc_timer_t_LEDC_TIMER_1);
impl_timer!(TIMER2: ledc_timer_t_LEDC_TIMER_2); impl_timer!(LowSpeed; TIMER2: ledc_timer_t_LEDC_TIMER_2);
impl_timer!(TIMER3: ledc_timer_t_LEDC_TIMER_3); impl_timer!(LowSpeed; TIMER3: ledc_timer_t_LEDC_TIMER_3);
#[cfg(esp32)]
impl_timer!(HighSpeed; HTIMER0: ledc_timer_t_LEDC_TIMER_0);
#[cfg(esp32)]
impl_timer!(HighSpeed; HTIMER1: ledc_timer_t_LEDC_TIMER_1);
#[cfg(esp32)]
impl_timer!(HighSpeed; HTIMER2: ledc_timer_t_LEDC_TIMER_2);
#[cfg(esp32)]
impl_timer!(HighSpeed; HTIMER3: ledc_timer_t_LEDC_TIMER_3);
macro_rules! impl_channel { macro_rules! impl_channel {
($instance:ident: $channel:expr) => { ($typ:ty; $instance:ident: $channel:expr) => {
crate::impl_peripheral!($instance); crate::impl_peripheral!($instance);
impl LedcChannel for $instance { impl LedcChannel for $instance {
type SpeedMode = $typ;
fn channel() -> ledc_channel_t { fn channel() -> ledc_channel_t {
$channel $channel
} }
@ -568,16 +584,33 @@ mod chip {
}; };
} }
impl_channel!(CHANNEL0: ledc_channel_t_LEDC_CHANNEL_0); impl_channel!(LowSpeed; CHANNEL0: ledc_channel_t_LEDC_CHANNEL_0);
impl_channel!(CHANNEL1: ledc_channel_t_LEDC_CHANNEL_1); impl_channel!(LowSpeed; CHANNEL1: ledc_channel_t_LEDC_CHANNEL_1);
impl_channel!(CHANNEL2: ledc_channel_t_LEDC_CHANNEL_2); impl_channel!(LowSpeed; CHANNEL2: ledc_channel_t_LEDC_CHANNEL_2);
impl_channel!(CHANNEL3: ledc_channel_t_LEDC_CHANNEL_3); impl_channel!(LowSpeed; CHANNEL3: ledc_channel_t_LEDC_CHANNEL_3);
impl_channel!(CHANNEL4: ledc_channel_t_LEDC_CHANNEL_4); impl_channel!(LowSpeed; CHANNEL4: ledc_channel_t_LEDC_CHANNEL_4);
impl_channel!(CHANNEL5: ledc_channel_t_LEDC_CHANNEL_5); impl_channel!(LowSpeed; CHANNEL5: ledc_channel_t_LEDC_CHANNEL_5);
#[cfg(any(esp32, esp32s2, esp32s3, esp8684))] #[cfg(any(esp32, esp32s2, esp32s3, esp8684))]
impl_channel!(CHANNEL6: ledc_channel_t_LEDC_CHANNEL_6); impl_channel!(LowSpeed; CHANNEL6: ledc_channel_t_LEDC_CHANNEL_6);
#[cfg(any(esp32, esp32s2, esp32s3, esp8684))] #[cfg(any(esp32, esp32s2, esp32s3, esp8684))]
impl_channel!(CHANNEL7: ledc_channel_t_LEDC_CHANNEL_7); impl_channel!(LowSpeed; CHANNEL7: ledc_channel_t_LEDC_CHANNEL_7);
#[cfg(esp32)]
impl_channel!(HighSpeed; HCHANNEL0: ledc_channel_t_LEDC_CHANNEL_0);
#[cfg(esp32)]
impl_channel!(HighSpeed; HCHANNEL1: ledc_channel_t_LEDC_CHANNEL_1);
#[cfg(esp32)]
impl_channel!(HighSpeed; HCHANNEL2: ledc_channel_t_LEDC_CHANNEL_2);
#[cfg(esp32)]
impl_channel!(HighSpeed; HCHANNEL3: ledc_channel_t_LEDC_CHANNEL_3);
#[cfg(esp32)]
impl_channel!(HighSpeed; HCHANNEL4: ledc_channel_t_LEDC_CHANNEL_4);
#[cfg(esp32)]
impl_channel!(HighSpeed; HCHANNEL5: ledc_channel_t_LEDC_CHANNEL_5);
#[cfg(esp32)]
impl_channel!(HighSpeed; HCHANNEL6: ledc_channel_t_LEDC_CHANNEL_6);
#[cfg(esp32)]
impl_channel!(HighSpeed; HCHANNEL7: ledc_channel_t_LEDC_CHANNEL_7);
/// The LED Control device peripheral /// The LED Control device peripheral
pub struct LEDC { pub struct LEDC {
@ -626,4 +659,50 @@ mod chip {
} }
} }
} }
/// The LED Control device peripheral (high speed channels, ESP32 only)
#[cfg(esp32)]
pub struct HLEDC {
pub timer0: HTIMER0,
pub timer1: HTIMER1,
pub timer2: HTIMER2,
pub timer3: HTIMER3,
pub channel0: HCHANNEL0,
pub channel1: HCHANNEL1,
pub channel2: HCHANNEL2,
pub channel3: HCHANNEL3,
pub channel4: HCHANNEL4,
pub channel5: HCHANNEL5,
pub channel6: HCHANNEL6,
pub channel7: HCHANNEL7,
}
#[cfg(esp32)]
impl HLEDC {
/// Creates a new instance of the HLEDC peripheral. Typically one wants
/// to use the instance [`ledc`](crate::peripherals::Peripherals::fledc) from
/// the device peripherals obtained via
/// [`peripherals::Peripherals::take()`](crate::peripherals::Peripherals::take()).
///
/// # Safety
///
/// It is safe to instantiate the HLEDC peripheral exactly one time.
/// Care has to be taken that this has not already been done elsewhere.
pub unsafe fn new() -> Self {
Self {
timer0: HTIMER0::new(),
timer1: HTIMER1::new(),
timer2: HTIMER2::new(),
timer3: HTIMER3::new(),
channel0: HCHANNEL0::new(),
channel1: HCHANNEL1::new(),
channel2: HCHANNEL2::new(),
channel3: HCHANNEL3::new(),
channel4: HCHANNEL4::new(),
channel5: HCHANNEL5::new(),
channel6: HCHANNEL6::new(),
channel7: HCHANNEL7::new(),
}
}
}
} }

View File

@ -69,6 +69,8 @@ pub struct Peripherals {
pub hall_sensor: crate::hall::HallSensor, pub hall_sensor: crate::hall::HallSensor,
pub can: can::CAN, pub can: can::CAN,
pub ledc: ledc::LEDC, pub ledc: ledc::LEDC,
#[cfg(esp32)]
pub hledc: ledc::HLEDC,
pub rmt: rmt::RMT, pub rmt: rmt::RMT,
#[cfg(all( #[cfg(all(
any(esp32, esp32s2, esp32s3, esp32c6, esp32p4), any(esp32, esp32s2, esp32s3, esp32c6, esp32p4),
@ -164,6 +166,8 @@ impl Peripherals {
hall_sensor: crate::hall::HallSensor::new(), hall_sensor: crate::hall::HallSensor::new(),
can: can::CAN::new(), can: can::CAN::new(),
ledc: ledc::LEDC::new(), ledc: ledc::LEDC::new(),
#[cfg(esp32)]
hledc: ledc::HLEDC::new(),
rmt: rmt::RMT::new(), rmt: rmt::RMT::new(),
#[cfg(all( #[cfg(all(
any(esp32, esp32s2, esp32s3, esp32c6, esp32p4), any(esp32, esp32s2, esp32s3, esp32c6, esp32p4),