Skip to main content

esp_hal/ledc/
channel.rs

1//! # LEDC channel
2//!
3//! ## Overview
4//! The LEDC Channel module  provides a high-level interface to
5//! configure and control individual PWM channels of the LEDC peripheral.
6//!
7//! ## Configuration
8//! The module allows precise and flexible control over LED lighting and other
9//! `Pulse-Width Modulation (PWM)` applications by offering configurable duty
10//! cycles and frequencies.
11
12use super::{
13    low_level,
14    timer::{TimerIFace, TimerSpeed},
15};
16use crate::{
17    gpio::{
18        DriveMode,
19        OutputConfig,
20        interconnect::{self, PeripheralOutput},
21    },
22    pac::ledc::RegisterBlock,
23    peripherals::LEDC,
24};
25
26/// Fade parameter sub-errors
27#[derive(Debug, Clone, Copy, PartialEq)]
28#[cfg_attr(feature = "defmt", derive(defmt::Format))]
29pub enum FadeError {
30    /// Starts duty % out of range.
31    StartDuty,
32    /// Ends duty % out of range.
33    EndDuty,
34    /// Duty % change from start to end is out of range.
35    DutyRange,
36    /// Duration too long for timer frequency and duty resolution
37    Duration,
38}
39
40/// Channel errors
41#[derive(Debug, Clone, Copy, PartialEq)]
42#[cfg_attr(feature = "defmt", derive(defmt::Format))]
43pub enum Error {
44    /// Invalid duty % value
45    Duty,
46    /// Timer not configured
47    Timer,
48    /// Channel not configured
49    Channel,
50    /// Fade parameters invalid
51    Fade(FadeError),
52}
53
54/// Channel number
55#[derive(PartialEq, Eq, Copy, Clone, Debug)]
56#[cfg_attr(feature = "defmt", derive(defmt::Format))]
57pub enum Number {
58    /// Channel 0
59    Channel0 = 0,
60    /// Channel 1
61    Channel1 = 1,
62    /// Channel 2
63    Channel2 = 2,
64    /// Channel 3
65    Channel3 = 3,
66    /// Channel 4
67    Channel4 = 4,
68    /// Channel 5
69    Channel5 = 5,
70    #[cfg(ledc_channel_count = "8")]
71    /// Channel 6
72    Channel6 = 6,
73    #[cfg(ledc_channel_count = "8")]
74    /// Channel 7
75    Channel7 = 7,
76}
77
78/// Channel configuration
79pub mod config {
80    use crate::{
81        gpio::DriveMode,
82        ledc::timer::{TimerIFace, TimerSpeed},
83    };
84
85    /// Channel configuration
86    #[derive(Copy, Clone)]
87    pub struct Config<'a, S: TimerSpeed> {
88        /// A reference to the timer associated with this channel.
89        pub timer: &'a dyn TimerIFace<S>,
90        /// The duty cycle percentage (0-100).
91        pub duty_pct: u8,
92        /// The pin configuration (PushPull or OpenDrain).
93        pub drive_mode: DriveMode,
94    }
95}
96
97/// Channel interface
98pub trait ChannelIFace<'a, S: TimerSpeed + 'a>
99where
100    Channel<'a, S>: ChannelHW,
101{
102    /// Configures channel.
103    fn configure(&mut self, config: config::Config<'a, S>) -> Result<(), Error>;
104
105    /// Sets channel duty HW.
106    fn set_duty(&self, duty_pct: u8) -> Result<(), Error>;
107
108    /// Starts a duty-cycle fade.
109    fn start_duty_fade(
110        &self,
111        start_duty_pct: u8,
112        end_duty_pct: u8,
113        duration_ms: u16,
114    ) -> Result<(), Error>;
115
116    /// Returns whether a duty-cycle fade is running.
117    fn is_duty_fade_running(&self) -> bool;
118}
119
120/// Channel HW interface
121pub trait ChannelHW {
122    /// Configures Channel HW except for the duty which is set via
123    /// [`Self::set_duty_hw`].
124    fn configure_hw(&mut self) -> Result<(), Error>;
125    /// Configures the hardware for the channel with a specific pin
126    /// configuration.
127    fn configure_hw_with_drive_mode(&mut self, cfg: DriveMode) -> Result<(), Error>;
128
129    /// Sets channel duty HW.
130    fn set_duty_hw(&self, duty: u32);
131
132    /// Starts a duty-cycle fade HW.
133    fn start_duty_fade_hw(
134        &self,
135        start_duty: u32,
136        duty_inc: bool,
137        duty_steps: u16,
138        cycles_per_step: u16,
139        duty_per_cycle: u16,
140    );
141
142    /// Returns whether a duty-cycle fade is running HW.
143    fn is_duty_fade_running_hw(&self) -> bool;
144}
145
146/// Channel struct
147pub struct Channel<'a, S: TimerSpeed> {
148    ledc: &'a RegisterBlock,
149    timer: Option<&'a dyn TimerIFace<S>>,
150    number: Number,
151    output_pin: interconnect::OutputSignal<'a>,
152}
153
154impl<'a, S: TimerSpeed> Channel<'a, S> {
155    /// Returns a new channel.
156    pub fn new(number: Number, output_pin: impl PeripheralOutput<'a>) -> Self {
157        let ledc = LEDC::regs();
158        Channel {
159            ledc,
160            timer: None,
161            number,
162            output_pin: output_pin.into(),
163        }
164    }
165}
166
167impl<'a, S: TimerSpeed> ChannelIFace<'a, S> for Channel<'a, S>
168where
169    Channel<'a, S>: ChannelHW,
170{
171    /// Configures channel.
172    fn configure(&mut self, config: config::Config<'a, S>) -> Result<(), Error> {
173        self.timer = Some(config.timer);
174
175        self.set_duty(config.duty_pct)?;
176        self.configure_hw_with_drive_mode(config.drive_mode)?;
177
178        Ok(())
179    }
180
181    /// Sets duty % of channel.
182    fn set_duty(&self, duty_pct: u8) -> Result<(), Error> {
183        let duty_exp;
184        if let Some(timer) = self.timer {
185            if let Some(timer_duty) = timer.duty() {
186                duty_exp = timer_duty as u32;
187            } else {
188                return Err(Error::Timer);
189            }
190        } else {
191            return Err(Error::Channel);
192        }
193
194        let duty_range = 2u32.pow(duty_exp);
195        let duty_value = (duty_range * duty_pct as u32) / 100;
196
197        if duty_pct > 100u8 {
198            // duty_pct greater than 100%
199            return Err(Error::Duty);
200        }
201
202        self.set_duty_hw(duty_value);
203
204        Ok(())
205    }
206
207    /// Starts a duty fade from one % to another.
208    ///
209    /// There is a constraint on the combination of timer frequency, timer PWM
210    /// duty resolution (the bit count), the fade "range" (abs(start-end)), and
211    /// the duration:
212    ///
213    /// frequency * duration / ((1<<bit_count) * abs(start-end)) < 1024
214    ///
215    /// Small percentage changes, long durations, coarse PWM resolutions (that
216    /// is, low bit counts), and high timer frequencies will all be more likely
217    /// to fail this requirement. If it does fail, returns an error.
218    fn start_duty_fade(
219        &self,
220        start_duty_pct: u8,
221        end_duty_pct: u8,
222        duration_ms: u16,
223    ) -> Result<(), Error> {
224        let duty_exp;
225        let frequency;
226        if start_duty_pct > 100u8 {
227            return Err(Error::Fade(FadeError::StartDuty));
228        }
229        if end_duty_pct > 100u8 {
230            return Err(Error::Fade(FadeError::EndDuty));
231        }
232        if let Some(timer) = self.timer {
233            if let Some(timer_duty) = timer.duty() {
234                if timer.frequency() > 0 {
235                    duty_exp = timer_duty as u32;
236                    frequency = timer.frequency();
237                } else {
238                    return Err(Error::Timer);
239                }
240            } else {
241                return Err(Error::Timer);
242            }
243        } else {
244            return Err(Error::Channel);
245        }
246
247        let duty_range = (1u32 << duty_exp) - 1;
248        let start_duty_value = (duty_range * start_duty_pct as u32) / 100;
249        let end_duty_value = (duty_range * end_duty_pct as u32) / 100;
250
251        // NB: since we do the multiplication first here, there's no loss of
252        // precision from using milliseconds instead of (e.g.) nanoseconds.
253        let pwm_cycles = (duration_ms as u32) * frequency / 1000;
254
255        let abs_duty_diff = end_duty_value.abs_diff(start_duty_value);
256        let duty_steps: u32 = u16::try_from(abs_duty_diff).unwrap_or(65535).into();
257        // This conversion may fail if duration_ms is too big, and if either
258        // duty_steps gets truncated, or the fade is over a short range of duty
259        // percentages, so it's too small.  Returning an Err in either case is
260        // fine: shortening the duration_ms will sort things out.
261        let cycles_per_step: u16 = (pwm_cycles / duty_steps)
262            .try_into()
263            .map_err(|_| Error::Fade(FadeError::Duration))
264            .and_then(|res| {
265                if res > 1023 {
266                    Err(Error::Fade(FadeError::Duration))
267                } else {
268                    Ok(res)
269                }
270            })?;
271        // This can't fail unless abs_duty_diff is bigger than 65536*65535-1,
272        // and so duty_steps gets truncated.  But that requires duty_exp to be
273        // at least 32, and the hardware only supports up to 20.  Still, handle
274        // it in case something changes in the future.
275        let duty_per_cycle: u16 = (abs_duty_diff / duty_steps)
276            .try_into()
277            .map_err(|_| Error::Fade(FadeError::DutyRange))?;
278
279        self.start_duty_fade_hw(
280            start_duty_value,
281            end_duty_value > start_duty_value,
282            duty_steps.try_into().unwrap(),
283            cycles_per_step,
284            duty_per_cycle,
285        );
286
287        Ok(())
288    }
289
290    fn is_duty_fade_running(&self) -> bool {
291        self.is_duty_fade_running_hw()
292    }
293}
294
295mod ehal1 {
296    use embedded_hal::pwm::{self, ErrorKind, ErrorType, SetDutyCycle};
297
298    use super::{Channel, ChannelHW, Error};
299    use crate::ledc::timer::TimerSpeed;
300
301    impl pwm::Error for Error {
302        fn kind(&self) -> pwm::ErrorKind {
303            ErrorKind::Other
304        }
305    }
306
307    impl<S: TimerSpeed> ErrorType for Channel<'_, S> {
308        type Error = Error;
309    }
310
311    impl<'a, S: TimerSpeed> SetDutyCycle for Channel<'a, S>
312    where
313        Channel<'a, S>: ChannelHW,
314    {
315        fn max_duty_cycle(&self) -> u16 {
316            let duty_exp;
317
318            if let Some(timer_duty) = self.timer.and_then(|timer| timer.duty()) {
319                duty_exp = timer_duty as u32;
320            } else {
321                return 0;
322            }
323
324            let duty_range = 2u32.pow(duty_exp);
325
326            duty_range as u16
327        }
328
329        fn set_duty_cycle(&mut self, mut duty: u16) -> Result<(), Self::Error> {
330            let max = self.max_duty_cycle();
331            duty = if duty > max { max } else { duty };
332            self.set_duty_hw(duty.into());
333            Ok(())
334        }
335    }
336}
337
338impl<S: crate::ledc::timer::TimerSpeed> Channel<'_, S> {
339    fn set_channel(&mut self, timer_number: u8) {
340        low_level::set_channel(self.ledc, self.number, timer_number, S::IS_HS);
341        low_level::start_duty_without_fading(self.ledc, self.number, S::IS_HS);
342    }
343
344    fn start_duty_without_fading(&self) {
345        low_level::start_duty_without_fading(self.ledc, self.number, S::IS_HS);
346    }
347
348    fn update_channel(&self) {
349        low_level::update_channel(self.ledc, self.number, S::IS_HS);
350    }
351}
352
353impl<S> ChannelHW for Channel<'_, S>
354where
355    S: crate::ledc::timer::TimerSpeed,
356{
357    /// Configures Channel HW.
358    fn configure_hw(&mut self) -> Result<(), Error> {
359        self.configure_hw_with_drive_mode(DriveMode::PushPull)
360    }
361
362    fn configure_hw_with_drive_mode(&mut self, cfg: DriveMode) -> Result<(), Error> {
363        if let Some(timer) = self.timer {
364            if !timer.is_configured() {
365                return Err(Error::Timer);
366            }
367
368            self.output_pin
369                .apply_output_config(&OutputConfig::default().with_drive_mode(cfg));
370            self.output_pin.set_output_enable(true);
371
372            let timer_number = timer.number() as u8;
373
374            self.set_channel(timer_number);
375            self.update_channel();
376
377            let signal = low_level::output_signal(self.number, S::IS_HS);
378            signal.connect_to(&self.output_pin);
379        } else {
380            return Err(Error::Timer);
381        }
382
383        Ok(())
384    }
385
386    /// Sets duty in channel HW.
387    fn set_duty_hw(&self, duty: u32) {
388        low_level::set_duty_hw(self.ledc, self.number, S::IS_HS, duty);
389        self.start_duty_without_fading();
390        self.update_channel();
391    }
392
393    /// Starts a duty-cycle fade HW.
394    fn start_duty_fade_hw(
395        &self,
396        start_duty: u32,
397        duty_inc: bool,
398        duty_steps: u16,
399        cycles_per_step: u16,
400        duty_per_cycle: u16,
401    ) {
402        low_level::start_duty_fade_hw(
403            self.ledc,
404            self.number,
405            S::IS_HS,
406            start_duty,
407            duty_inc,
408            duty_steps,
409            cycles_per_step,
410            duty_per_cycle,
411        );
412        self.update_channel();
413    }
414
415    fn is_duty_fade_running_hw(&self) -> bool {
416        low_level::is_duty_fade_running_hw(self.ledc, self.number, S::IS_HS)
417    }
418}