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    peripherals::LEDC,
78    system::{Peripheral as PeripheralEnable, PeripheralClockControl},
79};
80
81pub mod channel;
82pub mod timer;
83
84/// Global slow clock source
85#[derive(PartialEq, Eq, Copy, Clone, Debug)]
86pub enum LSGlobalClkSource {
87    /// APB clock.
88    APBClk,
89}
90
91/// LEDC (LED PWM Controller)
92pub struct Ledc<'d> {
93    _instance: LEDC<'d>,
94    ledc: &'d pac::ledc::RegisterBlock,
95}
96
97#[cfg(esp32)]
98#[derive(Clone, Copy)]
99/// Used to specify HighSpeed Timer/Channel
100pub struct HighSpeed {}
101
102#[derive(Clone, Copy)]
103/// Used to specify LowSpeed Timer/Channel
104pub struct LowSpeed {}
105
106/// Trait representing the speed mode of a clock or peripheral.
107pub trait Speed {
108    /// Boolean constant indicating whether the speed is high-speed.
109    const IS_HS: bool;
110}
111
112#[cfg(esp32)]
113impl Speed for HighSpeed {
114    const IS_HS: bool = true;
115}
116
117impl Speed for LowSpeed {
118    const IS_HS: bool = false;
119}
120
121impl<'d> Ledc<'d> {
122    /// Return a new LEDC
123    pub fn new(_instance: LEDC<'d>) -> Self {
124        if PeripheralClockControl::enable(PeripheralEnable::Ledc) {
125            PeripheralClockControl::reset(PeripheralEnable::Ledc);
126        }
127
128        let ledc = LEDC::regs();
129        Ledc { _instance, ledc }
130    }
131
132    /// Set global slow clock source
133    #[cfg(esp32)]
134    pub fn set_global_slow_clock(&mut self, _clock_source: LSGlobalClkSource) {
135        self.ledc.conf().write(|w| w.apb_clk_sel().set_bit());
136        self.ledc
137            .lstimer(0)
138            .conf()
139            .modify(|_, w| w.para_up().set_bit());
140    }
141
142    #[cfg(not(esp32))]
143    /// Set global slow clock source
144    pub fn set_global_slow_clock(&mut self, clock_source: LSGlobalClkSource) {
145        #[cfg(any(esp32c6, esp32h2))]
146        let pcr = unsafe { &*crate::peripherals::PCR::ptr() };
147
148        #[cfg(any(esp32c6, esp32h2))]
149        pcr.ledc_sclk_conf().write(|w| w.ledc_sclk_en().set_bit());
150
151        match clock_source {
152            LSGlobalClkSource::APBClk => {
153                #[cfg(not(any(esp32c6, esp32h2)))]
154                self.ledc
155                    .conf()
156                    .write(|w| unsafe { w.apb_clk_sel().bits(1) });
157                #[cfg(esp32c6)]
158                pcr.ledc_sclk_conf()
159                    .write(|w| unsafe { w.ledc_sclk_sel().bits(1) });
160                #[cfg(esp32h2)]
161                pcr.ledc_sclk_conf()
162                    .write(|w| unsafe { w.ledc_sclk_sel().bits(0) });
163            }
164        }
165        self.ledc
166            .timer(0)
167            .conf()
168            .modify(|_, w| w.para_up().set_bit());
169    }
170
171    /// Return a new timer
172    pub fn timer<S: TimerSpeed>(&self, number: timer::Number) -> Timer<'d, S> {
173        Timer::new(self.ledc, number)
174    }
175
176    /// Return a new channel
177    pub fn channel<S: TimerSpeed>(
178        &self,
179        number: channel::Number,
180        output_pin: impl PeripheralOutput<'d>,
181    ) -> Channel<'d, S> {
182        Channel::new(number, output_pin)
183    }
184}