esp_hal/ledc/
mod.rs

1//! # LED Controller (LEDC)
2//!
3//! ## Overview
4//!
5//! The LEDC peripheral is primarily designed to control the intensity of LEDs,
6//! although it can also be used to generate PWM signals for other purposes. It
7//! has multiple channels which can generate independent waveforms that can be
8//! used, for example, to drive RGB LED devices.
9//!
10//! The PWM controller can automatically increase or decrease the duty cycle
11//! gradually, allowing for fades without any processor interference.
12//!
13//! ## Configuration
14//! Currently only supports fixed-frequency output. High Speed channels are
15//! available for the ESP32 only, while Low Speed channels are available for all
16//! supported chips.
17//!
18//! ## Examples
19//!
20//! ### Low Speed Channel
21//!
22//! The following example will configure the Low Speed Channel0 to 24kHz output
23//! with 10% duty using the ABPClock and turn on LED with the option to change
24//! LED intensity depending on `duty` value. Possible values (`u32`) are in
25//! range 0..100.
26//!
27//! ```rust, no_run
28#![doc = crate::before_snippet!()]
29//! # use esp_hal::ledc::Ledc;
30//! # use esp_hal::ledc::LSGlobalClkSource;
31//! # use esp_hal::ledc::timer::{self, TimerIFace};
32//! # use esp_hal::ledc::LowSpeed;
33//! # use esp_hal::ledc::channel::{self, ChannelIFace};
34//! # let led = peripherals.GPIO0;
35//!
36//! let mut ledc = Ledc::new(peripherals.LEDC);
37//! ledc.set_global_slow_clock(LSGlobalClkSource::APBClk);
38//!
39//! let mut lstimer0 = ledc.timer::<LowSpeed>(timer::Number::Timer0);
40//! lstimer0
41//!     .configure(timer::config::Config {
42//!         duty: timer::config::Duty::Duty5Bit,
43//!         clock_source: timer::LSClockSource::APBClk,
44//!         frequency: Rate::from_khz(24),
45//!     })?;
46//!
47//! let mut channel0 = ledc.channel(channel::Number::Channel0, led);
48//! channel0
49//!     .configure(channel::config::Config {
50//!         timer: &lstimer0,
51//!         duty_pct: 10,
52//!         pin_config: channel::config::PinConfig::PushPull,
53//!     })?;
54//!
55//! loop {
56//!     // Set up a breathing LED: fade from off to on over a second, then
57//!     // from on back off over the next second.  Then loop.
58//!     channel0.start_duty_fade(0, 100, 1000)?;
59//!     while channel0.is_duty_fade_running() {}
60//!     channel0.start_duty_fade(100, 0, 1000)?;
61//!     while channel0.is_duty_fade_running() {}
62//! }
63//! # }
64//! ```
65//! 
66//! ## Implementation State
67//! - Source clock selection is not supported
68//! - Interrupts are not supported
69
70use self::{
71    channel::Channel,
72    timer::{Timer, TimerSpeed},
73};
74use crate::{
75    gpio::interconnect::PeripheralOutput,
76    pac,
77    peripheral::{Peripheral, PeripheralRef},
78    peripherals::LEDC,
79    system::{Peripheral as PeripheralEnable, PeripheralClockControl},
80};
81
82pub mod channel;
83pub mod timer;
84
85/// Global slow clock source
86#[derive(PartialEq, Eq, Copy, Clone, Debug)]
87pub enum LSGlobalClkSource {
88    /// APB clock.
89    APBClk,
90}
91
92/// LEDC (LED PWM Controller)
93pub struct Ledc<'d> {
94    _instance: PeripheralRef<'d, LEDC>,
95    ledc: &'d pac::ledc::RegisterBlock,
96}
97
98#[cfg(esp32)]
99#[derive(Clone, Copy)]
100/// Used to specify HighSpeed Timer/Channel
101pub struct HighSpeed {}
102
103#[derive(Clone, Copy)]
104/// Used to specify LowSpeed Timer/Channel
105pub struct LowSpeed {}
106
107/// Trait representing the speed mode of a clock or peripheral.
108pub trait Speed {
109    /// Boolean constant indicating whether the speed is high-speed.
110    const IS_HS: bool;
111}
112
113#[cfg(esp32)]
114impl Speed for HighSpeed {
115    const IS_HS: bool = true;
116}
117
118impl Speed for LowSpeed {
119    const IS_HS: bool = false;
120}
121
122impl<'d> Ledc<'d> {
123    /// Return a new LEDC
124    pub fn new(_instance: impl Peripheral<P = LEDC> + 'd) -> Self {
125        crate::into_ref!(_instance);
126
127        if PeripheralClockControl::enable(PeripheralEnable::Ledc) {
128            PeripheralClockControl::reset(PeripheralEnable::Ledc);
129        }
130
131        let ledc = LEDC::regs();
132        Ledc { _instance, ledc }
133    }
134
135    /// Set global slow clock source
136    #[cfg(esp32)]
137    pub fn set_global_slow_clock(&mut self, _clock_source: LSGlobalClkSource) {
138        self.ledc.conf().write(|w| w.apb_clk_sel().set_bit());
139        self.ledc
140            .lstimer(0)
141            .conf()
142            .modify(|_, w| w.para_up().set_bit());
143    }
144
145    #[cfg(not(esp32))]
146    /// Set global slow clock source
147    pub fn set_global_slow_clock(&mut self, clock_source: LSGlobalClkSource) {
148        #[cfg(any(esp32c6, esp32h2))]
149        let pcr = unsafe { &*crate::peripherals::PCR::ptr() };
150
151        #[cfg(any(esp32c6, esp32h2))]
152        pcr.ledc_sclk_conf().write(|w| w.ledc_sclk_en().set_bit());
153
154        match clock_source {
155            LSGlobalClkSource::APBClk => {
156                #[cfg(not(any(esp32c6, esp32h2)))]
157                self.ledc
158                    .conf()
159                    .write(|w| unsafe { w.apb_clk_sel().bits(1) });
160                #[cfg(esp32c6)]
161                pcr.ledc_sclk_conf()
162                    .write(|w| unsafe { w.ledc_sclk_sel().bits(1) });
163                #[cfg(esp32h2)]
164                pcr.ledc_sclk_conf()
165                    .write(|w| unsafe { w.ledc_sclk_sel().bits(0) });
166            }
167        }
168        self.ledc
169            .timer(0)
170            .conf()
171            .modify(|_, w| w.para_up().set_bit());
172    }
173
174    /// Return a new timer
175    pub fn timer<S: TimerSpeed>(&self, number: timer::Number) -> Timer<'d, S> {
176        Timer::new(self.ledc, number)
177    }
178
179    /// Return a new channel
180    pub fn channel<S: TimerSpeed>(
181        &self,
182        number: channel::Number,
183        output_pin: impl Peripheral<P = impl PeripheralOutput> + 'd,
184    ) -> Channel<'d, S> {
185        Channel::new(number, output_pin)
186    }
187}