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}