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    /// Start duty % out of range
31    StartDuty,
32    /// End 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    /// Configure channel
103    fn configure(&mut self, config: config::Config<'a, S>) -> Result<(), Error>;
104
105    /// Set channel duty HW
106    fn set_duty(&self, duty_pct: u8) -> Result<(), Error>;
107
108    /// Start 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    /// Check 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    /// Configure Channel HW except for the duty which is set via
123    /// [`Self::set_duty_hw`].
124    fn configure_hw(&mut self) -> Result<(), Error>;
125    /// Configure 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    /// Set channel duty HW
130    fn set_duty_hw(&self, duty: u32);
131
132    /// Start 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    /// Check 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    /// Return 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    /// Configure 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    /// Set 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    /// Start a duty fade from one % to another.
208    ///
209    /// There's 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, this function will return
218    /// an error Result.
219    fn start_duty_fade(
220        &self,
221        start_duty_pct: u8,
222        end_duty_pct: u8,
223        duration_ms: u16,
224    ) -> Result<(), Error> {
225        let duty_exp;
226        let frequency;
227        if start_duty_pct > 100u8 {
228            return Err(Error::Fade(FadeError::StartDuty));
229        }
230        if end_duty_pct > 100u8 {
231            return Err(Error::Fade(FadeError::EndDuty));
232        }
233        if let Some(timer) = self.timer {
234            if let Some(timer_duty) = timer.duty() {
235                if timer.frequency() > 0 {
236                    duty_exp = timer_duty as u32;
237                    frequency = timer.frequency();
238                } else {
239                    return Err(Error::Timer);
240                }
241            } else {
242                return Err(Error::Timer);
243            }
244        } else {
245            return Err(Error::Channel);
246        }
247
248        let duty_range = (1u32 << duty_exp) - 1;
249        let start_duty_value = (duty_range * start_duty_pct as u32) / 100;
250        let end_duty_value = (duty_range * end_duty_pct as u32) / 100;
251
252        // NB: since we do the multiplication first here, there's no loss of
253        // precision from using milliseconds instead of (e.g.) nanoseconds.
254        let pwm_cycles = (duration_ms as u32) * frequency / 1000;
255
256        let abs_duty_diff = end_duty_value.abs_diff(start_duty_value);
257        let duty_steps: u32 = u16::try_from(abs_duty_diff).unwrap_or(65535).into();
258        // This conversion may fail if duration_ms is too big, and if either
259        // duty_steps gets truncated, or the fade is over a short range of duty
260        // percentages, so it's too small.  Returning an Err in either case is
261        // fine: shortening the duration_ms will sort things out.
262        let cycles_per_step: u16 = (pwm_cycles / duty_steps)
263            .try_into()
264            .map_err(|_| Error::Fade(FadeError::Duration))
265            .and_then(|res| {
266                if res > 1023 {
267                    Err(Error::Fade(FadeError::Duration))
268                } else {
269                    Ok(res)
270                }
271            })?;
272        // This can't fail unless abs_duty_diff is bigger than 65536*65535-1,
273        // and so duty_steps gets truncated.  But that requires duty_exp to be
274        // at least 32, and the hardware only supports up to 20.  Still, handle
275        // it in case something changes in the future.
276        let duty_per_cycle: u16 = (abs_duty_diff / duty_steps)
277            .try_into()
278            .map_err(|_| Error::Fade(FadeError::DutyRange))?;
279
280        self.start_duty_fade_hw(
281            start_duty_value,
282            end_duty_value > start_duty_value,
283            duty_steps.try_into().unwrap(),
284            cycles_per_step,
285            duty_per_cycle,
286        );
287
288        Ok(())
289    }
290
291    fn is_duty_fade_running(&self) -> bool {
292        self.is_duty_fade_running_hw()
293    }
294}
295
296mod ehal1 {
297    use embedded_hal::pwm::{self, ErrorKind, ErrorType, SetDutyCycle};
298
299    use super::{Channel, ChannelHW, Error};
300    use crate::ledc::timer::TimerSpeed;
301
302    impl pwm::Error for Error {
303        fn kind(&self) -> pwm::ErrorKind {
304            ErrorKind::Other
305        }
306    }
307
308    impl<S: TimerSpeed> ErrorType for Channel<'_, S> {
309        type Error = Error;
310    }
311
312    impl<'a, S: TimerSpeed> SetDutyCycle for Channel<'a, S>
313    where
314        Channel<'a, S>: ChannelHW,
315    {
316        fn max_duty_cycle(&self) -> u16 {
317            let duty_exp;
318
319            if let Some(timer_duty) = self.timer.and_then(|timer| timer.duty()) {
320                duty_exp = timer_duty as u32;
321            } else {
322                return 0;
323            }
324
325            let duty_range = 2u32.pow(duty_exp);
326
327            duty_range as u16
328        }
329
330        fn set_duty_cycle(&mut self, mut duty: u16) -> Result<(), Self::Error> {
331            let max = self.max_duty_cycle();
332            duty = if duty > max { max } else { duty };
333            self.set_duty_hw(duty.into());
334            Ok(())
335        }
336    }
337}
338
339impl<S: crate::ledc::timer::TimerSpeed> Channel<'_, S> {
340    fn set_channel(&mut self, timer_number: u8) {
341        low_level::set_channel(self.ledc, self.number, timer_number, S::IS_HS);
342        low_level::start_duty_without_fading(self.ledc, self.number, S::IS_HS);
343    }
344
345    fn start_duty_without_fading(&self) {
346        low_level::start_duty_without_fading(self.ledc, self.number, S::IS_HS);
347    }
348
349    fn update_channel(&self) {
350        low_level::update_channel(self.ledc, self.number, S::IS_HS);
351    }
352}
353
354impl<S> ChannelHW for Channel<'_, S>
355where
356    S: crate::ledc::timer::TimerSpeed,
357{
358    /// Configure Channel HW
359    fn configure_hw(&mut self) -> Result<(), Error> {
360        self.configure_hw_with_drive_mode(DriveMode::PushPull)
361    }
362
363    fn configure_hw_with_drive_mode(&mut self, cfg: DriveMode) -> Result<(), Error> {
364        if let Some(timer) = self.timer {
365            if !timer.is_configured() {
366                return Err(Error::Timer);
367            }
368
369            self.output_pin
370                .apply_output_config(&OutputConfig::default().with_drive_mode(cfg));
371            self.output_pin.set_output_enable(true);
372
373            let timer_number = timer.number() as u8;
374
375            self.set_channel(timer_number);
376            self.update_channel();
377
378            let signal = low_level::output_signal(self.number, S::IS_HS);
379            signal.connect_to(&self.output_pin);
380        } else {
381            return Err(Error::Timer);
382        }
383
384        Ok(())
385    }
386
387    /// Set duty in channel HW
388    fn set_duty_hw(&self, duty: u32) {
389        low_level::set_duty_hw(self.ledc, self.number, S::IS_HS, duty);
390        self.start_duty_without_fading();
391        self.update_channel();
392    }
393
394    /// Start a duty-cycle fade HW
395    fn start_duty_fade_hw(
396        &self,
397        start_duty: u32,
398        duty_inc: bool,
399        duty_steps: u16,
400        cycles_per_step: u16,
401        duty_per_cycle: u16,
402    ) {
403        low_level::start_duty_fade_hw(
404            self.ledc,
405            self.number,
406            S::IS_HS,
407            start_duty,
408            duty_inc,
409            duty_steps,
410            cycles_per_step,
411            duty_per_cycle,
412        );
413        self.update_channel();
414    }
415
416    fn is_duty_fade_running_hw(&self) -> bool {
417        low_level::is_duty_fade_running_hw(self.ledc, self.number, S::IS_HS)
418    }
419}