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}