diff --git a/esp-hal/src/ledc/channel.rs b/esp-hal/src/ledc/channel.rs index dd3bd18cd24..be613ea7587 100644 --- a/esp-hal/src/ledc/channel.rs +++ b/esp-hal/src/ledc/channel.rs @@ -13,7 +13,6 @@ use super::timer::{TimerIFace, TimerSpeed}; use crate::{ gpio::{OutputSignal, PeripheralOutput}, peripheral::{Peripheral, PeripheralRef}, - peripherals::ledc::RegisterBlock, }; /// Fade parameter sub-errors @@ -141,11 +140,43 @@ pub trait ChannelHW { /// Check whether a duty-cycle fade is running HW fn is_duty_fade_running_hw(&self) -> bool; + + /// Enable signal output on this channel by setting LEDC_SIG_OUT_EN_CHn. + /// The LEDC_SIG_OUT_EN_CHn register should also set when calling + /// [`ChannelHW::configure_hw`] and + /// [`ChannelHW::configure_hw_with_pin_config`]. + fn enable_signal_output(&self); + + /// Disable signal output on this channel by clearing LEDC_SIG_OUT_EN_CHn. + fn disable_signal_output(&self); + + cfg_if::cfg_if! { + if #[cfg(feature = "esp32s3")] { + /// Enable the overflow counter and set the number of counter overflows needed to generate an interrupt. + fn enable_counter_with_overflow(&self, overflow_num: u16); + + /// Disable the overflow counter by clearing the appropriate register. + fn disable_counter(&self); + + /// Enables the overflow counting interrupt for this channel. + fn enable_overflow_interrupt(&self); + + /// Disables the overflow counting interrupt for this channel. + fn disable_overflow_interrupt(&self); + + /// Returns true if the overflow counting interrupt is active for this channel. + /// Note that this function will return from the raw interrupt status register + /// instead of the masked interrupt status register. + fn is_overflow_interrupt_active(&self) -> bool; + + /// Clears the overflow counting interrupt flag for this channel. + fn clear_overflow_interrupt(&self); + } + } } /// Channel struct pub struct Channel<'a, S: TimerSpeed, O: PeripheralOutput> { - ledc: &'a RegisterBlock, timer: Option<&'a dyn TimerIFace>, number: Number, output_pin: PeripheralRef<'a, O>, @@ -155,9 +186,7 @@ impl<'a, S: TimerSpeed, O: PeripheralOutput> Channel<'a, S, O> { /// Return a new channel pub fn new(number: Number, output_pin: impl Peripheral

+ 'a) -> Self { crate::into_ref!(output_pin); - let ledc = unsafe { &*crate::peripherals::LEDC::ptr() }; Channel { - ledc, timer: None, number, output_pin, @@ -341,12 +370,12 @@ impl<'a, O: PeripheralOutput, S: crate::ledc::timer::TimerSpeed> Channel<'a, S, #[cfg(esp32)] fn set_channel(&mut self, timer_number: u8) { if S::IS_HS { - let ch = self.ledc.hsch(self.number as usize); + let ch = unsafe { &*crate::peripherals::LEDC::ptr() }.hsch(self.number as usize); ch.hpoint().write(|w| unsafe { w.hpoint().bits(0x0) }); ch.conf0() .modify(|_, w| unsafe { w.sig_out_en().set_bit().timer_sel().bits(timer_number) }); } else { - let ch = self.ledc.lsch(self.number as usize); + let ch = unsafe { &*crate::peripherals::LEDC::ptr() }.lsch(self.number as usize); ch.hpoint().write(|w| unsafe { w.hpoint().bits(0x0) }); ch.conf0() .modify(|_, w| unsafe { w.sig_out_en().set_bit().timer_sel().bits(timer_number) }); @@ -356,7 +385,7 @@ impl<'a, O: PeripheralOutput, S: crate::ledc::timer::TimerSpeed> Channel<'a, S, #[cfg(not(esp32))] fn set_channel(&mut self, timer_number: u8) { { - let ch = self.ledc.ch(self.number as usize); + let ch = unsafe { &*crate::peripherals::LEDC::ptr() }.ch(self.number as usize); ch.hpoint().write(|w| unsafe { w.hpoint().bits(0x0) }); ch.conf0().modify(|_, w| { w.sig_out_en().set_bit(); @@ -369,7 +398,7 @@ impl<'a, O: PeripheralOutput, S: crate::ledc::timer::TimerSpeed> Channel<'a, S, #[cfg(esp32)] fn start_duty_without_fading(&self) { if S::IS_HS { - self.ledc + unsafe { &*crate::peripherals::LEDC::ptr() } .hsch(self.number as usize) .conf1() .write(|w| unsafe { @@ -380,7 +409,7 @@ impl<'a, O: PeripheralOutput, S: crate::ledc::timer::TimerSpeed> Channel<'a, S, w.duty_scale().bits(0x0) }); } else { - self.ledc + unsafe { &*crate::peripherals::LEDC::ptr() } .lsch(self.number as usize) .conf1() .write(|w| unsafe { @@ -395,30 +424,35 @@ impl<'a, O: PeripheralOutput, S: crate::ledc::timer::TimerSpeed> Channel<'a, S, #[cfg(any(esp32c6, esp32h2))] fn start_duty_without_fading(&self) { let cnum = self.number as usize; - self.ledc + unsafe { &*crate::peripherals::LEDC::ptr() } .ch(cnum) .conf1() .write(|w| w.duty_start().set_bit()); - self.ledc.ch_gamma_wr(cnum).write(|w| { - w.ch_gamma_duty_inc().set_bit(); - unsafe { - w.ch_gamma_duty_num().bits(0x1); - w.ch_gamma_duty_cycle().bits(0x1); - w.ch_gamma_scale().bits(0x0) - } - }); + unsafe { &*crate::peripherals::LEDC::ptr() } + .ch_gamma_wr(cnum) + .write(|w| { + w.ch_gamma_duty_inc().set_bit(); + unsafe { + w.ch_gamma_duty_num().bits(0x1); + w.ch_gamma_duty_cycle().bits(0x1); + w.ch_gamma_scale().bits(0x0) + } + }); } #[cfg(not(any(esp32, esp32c6, esp32h2)))] fn start_duty_without_fading(&self) { - self.ledc.ch(self.number as usize).conf1().write(|w| { - w.duty_start().set_bit(); - w.duty_inc().set_bit(); - unsafe { - w.duty_num().bits(0x1); - w.duty_cycle().bits(0x1); - w.duty_scale().bits(0x0) - } - }); + unsafe { &*crate::peripherals::LEDC::ptr() } + .ch(self.number as usize) + .conf1() + .write(|w| { + w.duty_start().set_bit(); + w.duty_inc().set_bit(); + unsafe { + w.duty_num().bits(0x1); + w.duty_cycle().bits(0x1); + w.duty_scale().bits(0x0) + } + }); } #[cfg(esp32)] @@ -430,7 +464,7 @@ impl<'a, O: PeripheralOutput, S: crate::ledc::timer::TimerSpeed> Channel<'a, S, duty_per_cycle: u16, ) { if S::IS_HS { - self.ledc + unsafe { &*crate::peripherals::LEDC::ptr() } .hsch(self.number as usize) .conf1() .write(|w| unsafe { @@ -446,7 +480,7 @@ impl<'a, O: PeripheralOutput, S: crate::ledc::timer::TimerSpeed> Channel<'a, S, .bits(duty_per_cycle) }); } else { - self.ledc + unsafe { &*crate::peripherals::LEDC::ptr() } .lsch(self.number as usize) .conf1() .write(|w| unsafe { @@ -473,24 +507,26 @@ impl<'a, O: PeripheralOutput, S: crate::ledc::timer::TimerSpeed> Channel<'a, S, duty_per_cycle: u16, ) { let cnum = self.number as usize; - self.ledc + unsafe { &*crate::peripherals::LEDC::ptr() } .ch(cnum) .conf1() .write(|w| w.duty_start().set_bit()); - self.ledc.ch_gamma_wr(cnum).write(|w| unsafe { - w.ch_gamma_duty_inc() - .variant(duty_inc) - .ch_gamma_duty_num() // count of incs before stopping - .bits(duty_steps) - .ch_gamma_duty_cycle() // overflows between incs - .bits(cycles_per_step) - .ch_gamma_scale() - .bits(duty_per_cycle) - }); - self.ledc + unsafe { &*crate::peripherals::LEDC::ptr() } + .ch_gamma_wr(cnum) + .write(|w| unsafe { + w.ch_gamma_duty_inc() + .variant(duty_inc) + .ch_gamma_duty_num() // count of incs before stopping + .bits(duty_steps) + .ch_gamma_duty_cycle() // overflows between incs + .bits(cycles_per_step) + .ch_gamma_scale() + .bits(duty_per_cycle) + }); + unsafe { &*crate::peripherals::LEDC::ptr() } .ch_gamma_wr_addr(cnum) .write(|w| unsafe { w.ch_gamma_wr_addr().bits(0) }); - self.ledc + unsafe { &*crate::peripherals::LEDC::ptr() } .ch_gamma_conf(cnum) .write(|w| unsafe { w.ch_gamma_entry_num().bits(0x1) }); } @@ -503,7 +539,7 @@ impl<'a, O: PeripheralOutput, S: crate::ledc::timer::TimerSpeed> Channel<'a, S, cycles_per_step: u16, duty_per_cycle: u16, ) { - self.ledc + unsafe { &*crate::peripherals::LEDC::ptr() } .ch(self.number as usize) .conf1() .write(|w| unsafe { @@ -520,7 +556,7 @@ impl<'a, O: PeripheralOutput, S: crate::ledc::timer::TimerSpeed> Channel<'a, S, #[cfg(esp32)] fn update_channel(&self) { if !S::IS_HS { - self.ledc + unsafe { &*crate::peripherals::LEDC::ptr() } .lsch(self.number as usize) .conf0() .modify(|_, w| w.para_up().set_bit()); @@ -528,7 +564,7 @@ impl<'a, O: PeripheralOutput, S: crate::ledc::timer::TimerSpeed> Channel<'a, S, } #[cfg(not(esp32))] fn update_channel(&self) { - self.ledc + unsafe { &*crate::peripherals::LEDC::ptr() } .ch(self.number as usize) .conf0() .modify(|_, w| w.para_up().set_bit()); @@ -618,12 +654,12 @@ where #[cfg(esp32)] fn set_duty_hw(&self, duty: u32) { if S::IS_HS { - self.ledc + unsafe { &*crate::peripherals::LEDC::ptr() } .hsch(self.number as usize) .duty() .write(|w| unsafe { w.duty().bits(duty << 4) }); } else { - self.ledc + unsafe { &*crate::peripherals::LEDC::ptr() } .lsch(self.number as usize) .duty() .write(|w| unsafe { w.duty().bits(duty << 4) }); @@ -635,7 +671,7 @@ where /// Set duty in channel HW #[cfg(not(esp32))] fn set_duty_hw(&self, duty: u32) { - self.ledc + unsafe { &*crate::peripherals::LEDC::ptr() } .ch(self.number as usize) .duty() .write(|w| unsafe { w.duty().bits(duty << 4) }); @@ -654,19 +690,19 @@ where duty_per_cycle: u16, ) { if S::IS_HS { - self.ledc + unsafe { &*crate::peripherals::LEDC::ptr() } .hsch(self.number as usize) .duty() .write(|w| unsafe { w.duty().bits(start_duty << 4) }); - self.ledc + unsafe { &*crate::peripherals::LEDC::ptr() } .int_clr() .write(|w| w.duty_chng_end_hsch(self.number as u8).clear_bit_by_one()); } else { - self.ledc + unsafe { &*crate::peripherals::LEDC::ptr() } .lsch(self.number as usize) .duty() .write(|w| unsafe { w.duty().bits(start_duty << 4) }); - self.ledc + unsafe { &*crate::peripherals::LEDC::ptr() } .int_clr() .write(|w| w.duty_chng_end_lsch(self.number as u8).clear_bit_by_one()); } @@ -684,11 +720,11 @@ where cycles_per_step: u16, duty_per_cycle: u16, ) { - self.ledc + unsafe { &*crate::peripherals::LEDC::ptr() } .ch(self.number as usize) .duty() .write(|w| unsafe { w.duty().bits(start_duty << 4) }); - self.ledc + unsafe { &*crate::peripherals::LEDC::ptr() } .int_clr() .write(|w| w.duty_chng_end_ch(self.number as u8).clear_bit_by_one()); self.start_duty_fade_inner(duty_inc, duty_steps, cycles_per_step, duty_per_cycle); @@ -697,7 +733,9 @@ where #[cfg(esp32)] fn is_duty_fade_running_hw(&self) -> bool { - let reg = self.ledc.int_raw().read(); + let reg = unsafe { &*crate::peripherals::LEDC::ptr() } + .int_raw() + .read(); if S::IS_HS { reg.duty_chng_end_hsch(self.number as u8).bit_is_clear() } else { @@ -707,10 +745,110 @@ where #[cfg(not(esp32))] fn is_duty_fade_running_hw(&self) -> bool { - self.ledc + unsafe { &*crate::peripherals::LEDC::ptr() } .int_raw() .read() .duty_chng_end_ch(self.number as u8) .bit_is_clear() } + + /// Enable signal output on this channel by setting LEDC_SIG_OUT_EN_CHn. + /// The LEDC_SIG_OUT_EN_CHn register should also set when calling + /// [`ChannelHW::configure_hw`] and + /// [`ChannelHW::configure_hw_with_pin_config`]. + #[cfg(esp32)] + fn enable_signal_output(&self) { + if S::IS_HS { + let ch = unsafe { &*crate::peripherals::LEDC::ptr() }.hsch(self.number as usize); + ch.conf0().modify(|_, w| w.sig_out_en().set_bit()); + } else { + let ch = unsafe { &*crate::peripherals::LEDC::ptr() }.lsch(self.number as usize); + ch.conf0() + .modify(|_, w| w.sig_out_en().set_bit().para_up().set_bit()); + } + } + + /// Enable signal output on this channel by setting LEDC_SIG_OUT_EN_CHn. + /// The LEDC_SIG_OUT_EN_CHn register should also set when calling + /// [`ChannelHW::configure_hw`] and + /// [`ChannelHW::configure_hw_with_pin_config`]. + #[cfg(not(esp32))] + fn enable_signal_output(&self) { + unsafe { &*crate::peripherals::LEDC::ptr() } + .ch(self.number as usize) + .conf0() + .modify(|_, w| w.sig_out_en().set_bit().para_up().set_bit()); + } + + /// Disable signal output on this channel by clearing LEDC_SIG_OUT_EN_CHn. + #[cfg(esp32)] + fn disable_signal_output(&self) { + if S::IS_HS { + let ch = unsafe { &*crate::peripherals::LEDC::ptr() }.hsch(self.number as usize); + ch.conf0().modify(|_, w| w.sig_out_en().clear_bit()); + } else { + let ch = unsafe { &*crate::peripherals::LEDC::ptr() }.lsch(self.number as usize); + ch.conf0() + .modify(|_, w| w.sig_out_en().clear_bit().para_up().set_bit()); + } + } + + /// Disable signal output on this channel by clearing LEDC_SIG_OUT_EN_CHn. + #[cfg(not(esp32))] + fn disable_signal_output(&self) { + unsafe { &*crate::peripherals::LEDC::ptr() } + .ch(self.number as usize) + .conf0() + .modify(|_, w| w.sig_out_en().clear_bit().para_up().set_bit()); + } + + cfg_if::cfg_if! { + if #[cfg(feature = "esp32s3")] { + /// Enables the overflow counting interrupt for this channel. + fn enable_overflow_interrupt(&self) { + critical_section::with(|_cs| { + unsafe { &*crate::peripherals::LEDC::ptr() }.int_ena().modify(|_, w| w.ovf_cnt_ch(self.number as u8).set_bit()) + }) + } + + /// Disables the overflow counting interrupt for this channel. + fn disable_overflow_interrupt(&self) { + critical_section::with(|_cs| { + unsafe { &*crate::peripherals::LEDC::ptr() }.int_ena().modify(|_, w| w.ovf_cnt_ch(self.number as u8).clear_bit()) + }) + } + + /// Enable the overflow counter and set the number of counter overflows needed to generate an interrupt. + /// + /// # Arguments + /// + /// * `overflow_num` - The number of counter overflows before generating an interrupt. + /// This is a 10-bit number, so it should be in the range of 1 to 1024. + fn enable_counter_with_overflow(&self, overflow_num: u16) { + assert!(overflow_num > 0 && overflow_num <= 1024, "Overflow number must be in the range of 1 to 1024"); + unsafe { &*crate::peripherals::LEDC::ptr() }.ch(self.number as usize).conf0().modify(|_, w| unsafe { + w.ovf_cnt_en().set_bit().ovf_num().bits(overflow_num - 1).para_up().set_bit() + }); + } + + /// Disable the overflow counter by clearing the appropriate register. + fn disable_counter(&self){ + unsafe { &*crate::peripherals::LEDC::ptr() }.ch(self.number as usize).conf0().modify(|_, w| { + w.ovf_cnt_en().clear_bit().para_up().set_bit() + }); + } + + /// Returns true if the overflow counting interrupt is active for this channel. + /// Note that this function will return from the raw interrupt status register + /// instead of the masked interrupt status register. + fn is_overflow_interrupt_active(&self) -> bool { + unsafe { &*crate::peripherals::LEDC::ptr() }.int_raw().read().ovf_cnt_ch(self.number as u8).bit_is_set() + } + + /// Clears the overflow counting interrupt flag for this channel. + fn clear_overflow_interrupt(&self) { + unsafe { &*crate::peripherals::LEDC::ptr() }.int_clr().write(|w| w.ovf_cnt_ch(self.number as u8).clear_bit_by_one()) + } + } + } } diff --git a/esp-hal/src/ledc/mod.rs b/esp-hal/src/ledc/mod.rs index dc862b11045..fb215a7cbce 100644 --- a/esp-hal/src/ledc/mod.rs +++ b/esp-hal/src/ledc/mod.rs @@ -66,8 +66,11 @@ use self::{ }; use crate::{ gpio::OutputPin, + interrupt::{self, InterruptHandler}, peripheral::{Peripheral, PeripheralRef}, + peripherals::Interrupt, system::{Peripheral as PeripheralEnable, PeripheralClockControl}, + InterruptConfigurable, }; pub mod channel; @@ -83,7 +86,6 @@ pub enum LSGlobalClkSource { /// LEDC (LED PWM Controller) pub struct Ledc<'d> { _instance: PeripheralRef<'d, crate::peripherals::LEDC>, - ledc: &'d crate::peripherals::ledc::RegisterBlock, } #[cfg(esp32)] @@ -116,15 +118,16 @@ impl<'d> Ledc<'d> { PeripheralClockControl::reset(PeripheralEnable::Ledc); PeripheralClockControl::enable(PeripheralEnable::Ledc); - let ledc = unsafe { &*crate::peripherals::LEDC::ptr() }; - Ledc { _instance, ledc } + Ledc { _instance } } /// Set global slow clock source #[cfg(esp32)] pub fn set_global_slow_clock(&mut self, _clock_source: LSGlobalClkSource) { - self.ledc.conf().write(|w| w.apb_clk_sel().set_bit()); - self.ledc + unsafe { &*crate::peripherals::LEDC::ptr() } + .conf() + .write(|w| w.apb_clk_sel().set_bit()); + unsafe { &*crate::peripherals::LEDC::ptr() } .lstimer(0) .conf() .modify(|_, w| w.para_up().set_bit()); @@ -142,7 +145,7 @@ impl<'d> Ledc<'d> { match clock_source { LSGlobalClkSource::APBClk => { #[cfg(not(any(esp32c6, esp32h2)))] - self.ledc + unsafe { &*crate::peripherals::LEDC::ptr() } .conf() .write(|w| unsafe { w.apb_clk_sel().bits(1) }); #[cfg(esp32c6)] @@ -153,15 +156,15 @@ impl<'d> Ledc<'d> { .write(|w| unsafe { w.ledc_sclk_sel().bits(0) }); } } - self.ledc + unsafe { &*crate::peripherals::LEDC::ptr() } .timer(0) .conf() .modify(|_, w| w.para_up().set_bit()); } /// Return a new timer - pub fn get_timer(&self, number: timer::Number) -> Timer<'d, S> { - Timer::new(self.ledc, number) + pub fn get_timer(&self, number: timer::Number) -> Timer { + Timer::new(number) } /// Return a new channel @@ -173,3 +176,14 @@ impl<'d> Ledc<'d> { Channel::new(number, output_pin) } } + +impl<'d> crate::private::Sealed for Ledc<'d> {} + +impl<'d> InterruptConfigurable for Ledc<'d> { + fn set_interrupt_handler(&mut self, handler: InterruptHandler) { + unsafe { + interrupt::bind_interrupt(Interrupt::LEDC, handler.handler()); + interrupt::enable(Interrupt::LEDC, handler.priority()).unwrap(); + } + } +} diff --git a/esp-hal/src/ledc/timer.rs b/esp-hal/src/ledc/timer.rs index ef3d7b7b630..56398fd3ff6 100644 --- a/esp-hal/src/ledc/timer.rs +++ b/esp-hal/src/ledc/timer.rs @@ -16,7 +16,7 @@ use fugit::HertzU32; #[cfg(esp32)] use super::HighSpeed; use super::{LowSpeed, Speed}; -use crate::{clock::Clocks, peripherals::ledc}; +use crate::clock::Clocks; const LEDC_TIMER_DIV_NUM_MAX: u64 = 0x3FFFF; @@ -132,7 +132,7 @@ pub mod config { /// Trait defining the type of timer source pub trait TimerSpeed: Speed { /// The type of clock source used by the timer in this speed mode. - type ClockSourceType; + type ClockSourceType: Sync; } /// Timer source type for LowSpeed timers @@ -149,7 +149,7 @@ impl TimerSpeed for HighSpeed { } /// Interface for Timers -pub trait TimerIFace { +pub trait TimerIFace: Sync { /// Return the frequency of the timer fn get_freq(&self) -> Option; @@ -179,11 +179,19 @@ pub trait TimerHW { /// Update the timer in HW fn update_hw(&self); + + /// Pause the timer + fn pause(&self); + + /// Resume the timer + fn resume(&self); + + /// Reset the timer + fn reset(&self); } /// Timer struct -pub struct Timer<'a, S: TimerSpeed> { - ledc: &'a crate::peripherals::ledc::RegisterBlock, +pub struct Timer { number: Number, duty: Option, frequency: u32, @@ -192,9 +200,9 @@ pub struct Timer<'a, S: TimerSpeed> { clock_source: Option, } -impl<'a, S: TimerSpeed> TimerIFace for Timer<'a, S> +impl TimerIFace for Timer where - Timer<'a, S>: TimerHW, + Timer: TimerHW, { /// Return the frequency of the timer fn get_freq(&self) -> Option { @@ -254,11 +262,10 @@ where } } -impl<'a, S: TimerSpeed> Timer<'a, S> { +impl Timer { /// Create a new instance of a timer - pub fn new(ledc: &'a ledc::RegisterBlock, number: Number) -> Self { + pub fn new(number: Number) -> Self { Timer { - ledc, number, duty: None, frequency: 0u32, @@ -270,7 +277,7 @@ impl<'a, S: TimerSpeed> Timer<'a, S> { } /// Timer HW implementation for LowSpeed timers -impl<'a> TimerHW for Timer<'a, LowSpeed> { +impl TimerHW for Timer { /// Get the current source timer frequency from the HW fn get_freq_hw(&self) -> Option { self.clock_source.map(|source| match source { @@ -287,7 +294,7 @@ impl<'a> TimerHW for Timer<'a, LowSpeed> { let duty = unwrap!(self.duty) as u8; let use_apb = !self.use_ref_tick; - self.ledc + unsafe { &*crate::peripherals::LEDC::ptr() } .lstimer(self.number as usize) .conf() .modify(|_, w| unsafe { @@ -305,7 +312,7 @@ impl<'a> TimerHW for Timer<'a, LowSpeed> { let duty = unwrap!(self.duty) as u8; let use_ref_tick = self.use_ref_tick; - self.ledc + unsafe { &*crate::peripherals::LEDC::ptr() } .timer(self.number as usize) .conf() .modify(|_, w| unsafe { @@ -321,19 +328,54 @@ impl<'a> TimerHW for Timer<'a, LowSpeed> { fn update_hw(&self) { cfg_if::cfg_if! { if #[cfg(esp32)] { - let tmr = self.ledc.lstimer(self.number as usize); + let tmr = unsafe { &*crate::peripherals::LEDC::ptr() }.lstimer(self.number as usize); } else { - let tmr = self.ledc.timer(self.number as usize); + let tmr = unsafe { &*crate::peripherals::LEDC::ptr() }.timer(self.number as usize); } } tmr.conf().modify(|_, w| w.para_up().set_bit()); } + + /// Pause the timer + fn pause(&self) { + cfg_if::cfg_if! { + if #[cfg(esp32)] { + unsafe { &*crate::peripherals::LEDC::ptr() }.lstimer(self.number as usize).conf().modify(|_, w| w.pause().set_bit()); + } else { + unsafe { &*crate::peripherals::LEDC::ptr() }.timer(self.number as usize).conf().modify(|_, w| w.pause().set_bit()); + } + } + } + + /// Resume the timer + fn resume(&self) { + cfg_if::cfg_if! { + if #[cfg(esp32)] { + unsafe { &*crate::peripherals::LEDC::ptr() }.lstimer(self.number as usize).conf().modify(|_, w| w.pause().clear_bit()); + } else { + unsafe { &*crate::peripherals::LEDC::ptr() }.timer(self.number as usize).conf().modify(|_, w| w.pause().clear_bit()); + } + } + } + + /// Reset the timer + fn reset(&self) { + cfg_if::cfg_if! { + if #[cfg(esp32)] { + unsafe { &*crate::peripherals::LEDC::ptr() }.lstimer(self.number as usize).conf().modify(|_, w| w.rst().set_bit()); + unsafe { &*crate::peripherals::LEDC::ptr() }.lstimer(self.number as usize).conf().modify(|_, w| w.rst().clear_bit()); + } else { + unsafe { &*crate::peripherals::LEDC::ptr() }.timer(self.number as usize).conf().modify(|_, w| w.rst().set_bit()); + unsafe { &*crate::peripherals::LEDC::ptr() }.timer(self.number as usize).conf().modify(|_, w| w.rst().clear_bit()); + } + } + } } #[cfg(esp32)] /// Timer HW implementation for HighSpeed timers -impl<'a> TimerHW for Timer<'a, HighSpeed> { +impl TimerHW for Timer { /// Get the current source timer frequency from the HW fn get_freq_hw(&self) -> Option { self.clock_source.map(|source| match source { @@ -349,7 +391,7 @@ impl<'a> TimerHW for Timer<'a, HighSpeed> { let duty = unwrap!(self.duty) as u8; let sel_hstimer = self.clock_source == Some(HSClockSource::APBClk); - self.ledc + unsafe { &*crate::peripherals::LEDC::ptr() } .hstimer(self.number as usize) .conf() .modify(|_, w| unsafe { @@ -365,4 +407,32 @@ impl<'a> TimerHW for Timer<'a, HighSpeed> { fn update_hw(&self) { // Nothing to do for HS timers } + + /// Pause the timer + fn pause(&self) { + unsafe { &*crate::peripherals::LEDC::ptr() } + .hstimer(self.number as usize) + .conf() + .modify(|_, w| w.pause().set_bit()); + } + + /// Resume the timer + fn resume(&self) { + unsafe { &*crate::peripherals::LEDC::ptr() } + .hstimer(self.number as usize) + .conf() + .modify(|_, w| w.pause().clear_bit()); + } + + /// Reset the timer + fn reset(&self) { + unsafe { &*crate::peripherals::LEDC::ptr() } + .hstimer(self.number as usize) + .conf() + .modify(|_, w| w.rst().set_bit()); + unsafe { &*crate::peripherals::LEDC::ptr() } + .hstimer(self.number as usize) + .conf() + .modify(|_, w| w.rst().clear_bit()); + } } diff --git a/examples/src/bin/ledc_pulse_counting.rs b/examples/src/bin/ledc_pulse_counting.rs new file mode 100644 index 00000000000..0a8fb9ed00f --- /dev/null +++ b/examples/src/bin/ledc_pulse_counting.rs @@ -0,0 +1,98 @@ +//! Generates a PWM signal on LEDC channel 0 with a duty cycle of 50% and a +//! frequency of 24 kHz. The PWM signal will be generated for 100 cycles and +//! then the signal will be disabled. +//! +//! The following wiring is assumed: +//! - LED => GPIO0 + +//% CHIPS: esp32s3 + +#![no_std] +#![no_main] + +use core::cell::RefCell; + +use critical_section::Mutex; +use esp_backtrace as _; +use esp_hal::{ + gpio::{GpioPin, Io}, + interrupt::Priority, + ledc::{ + channel::{self, Channel, ChannelIFace}, + timer::{self, Timer, TimerIFace}, + LSGlobalClkSource, + Ledc, + LowSpeed, + }, + prelude::*, +}; +use esp_println as _; +use static_cell::StaticCell; + +const DESIRED_PULSE_COUNT: u16 = 100; + +static CHANNEL0: Mutex>>>> = + Mutex::new(RefCell::new(None)); + +#[entry] +fn main() -> ! { + let peripherals = esp_hal::init(esp_hal::Config::default()); + + let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); + let led = io.pins.gpio0; + + let mut ledc = Ledc::new(peripherals.LEDC); + + ledc.set_global_slow_clock(LSGlobalClkSource::APBClk); + ledc.set_interrupt_handler(interrupt_handler); + + let lstimer0 = { + static LSTIMER0: StaticCell> = StaticCell::new(); + LSTIMER0.init_with(|| { + let mut lstimer0 = ledc.get_timer::(timer::Number::Timer0); + lstimer0 + .configure(timer::config::Config { + duty: timer::config::Duty::Duty1Bit, + clock_source: timer::LSClockSource::APBClk, + frequency: 24.kHz(), + }) + .unwrap(); + lstimer0 + }) + }; + + let channel0 = ledc.get_channel(channel::Number::Channel0, led); + + channel0.enable_counter_with_overflow(DESIRED_PULSE_COUNT); + critical_section::with(|cs| { + CHANNEL0.borrow_ref_mut(cs).replace(channel0); + let mut channel0 = CHANNEL0.borrow(cs).borrow_mut(); + let channel0 = channel0.as_mut().unwrap(); + channel0.enable_overflow_interrupt(); + channel0 + .configure(channel::config::Config { + timer: lstimer0, + duty_pct: 50, + pin_config: channel::config::PinConfig::PushPull, + }) + .unwrap(); + }); + + loop {} +} + +#[handler(priority = Priority::Priority1)] +#[ram] +fn interrupt_handler() { + critical_section::with(|cs| { + let channel0 = CHANNEL0.borrow(cs).borrow(); + if let Some(channel0) = channel0.as_ref() { + if channel0.is_overflow_interrupt_active() { + // Disable the signal output and clear the overflow interrupt + // Note that the timer is not stopped + channel0.disable_signal_output(); + channel0.clear_overflow_interrupt(); + } + } + }) +}