embassy_nrf/
pwm.rs

1//! Pulse Width Modulation (PWM) driver.
2
3#![macro_use]
4
5use core::sync::atomic::{compiler_fence, Ordering};
6
7use embassy_hal_internal::{Peri, PeripheralType};
8
9use crate::gpio::{convert_drive, AnyPin, OutputDrive, Pin as GpioPin, PselBits, SealedPin as _, DISCONNECTED};
10use crate::pac::gpio::vals as gpiovals;
11use crate::pac::pwm::vals;
12use crate::ppi::{Event, Task};
13use crate::util::slice_in_ram_or;
14use crate::{interrupt, pac};
15
16/// SimplePwm is the traditional pwm interface you're probably used to, allowing
17/// to simply set a duty cycle across up to four channels.
18pub struct SimplePwm<'d> {
19    r: pac::pwm::Pwm,
20    duty: [u16; 4],
21    ch0: Option<Peri<'d, AnyPin>>,
22    ch1: Option<Peri<'d, AnyPin>>,
23    ch2: Option<Peri<'d, AnyPin>>,
24    ch3: Option<Peri<'d, AnyPin>>,
25}
26
27/// SequencePwm allows you to offload the updating of a sequence of duty cycles
28/// to up to four channels, as well as repeat that sequence n times.
29pub struct SequencePwm<'d> {
30    r: pac::pwm::Pwm,
31    ch0: Option<Peri<'d, AnyPin>>,
32    ch1: Option<Peri<'d, AnyPin>>,
33    ch2: Option<Peri<'d, AnyPin>>,
34    ch3: Option<Peri<'d, AnyPin>>,
35}
36
37/// PWM error
38#[derive(Debug, Clone, Copy, PartialEq, Eq)]
39#[cfg_attr(feature = "defmt", derive(defmt::Format))]
40#[non_exhaustive]
41pub enum Error {
42    /// Max Sequence size is 32767
43    SequenceTooLong,
44    /// Min Sequence count is 1
45    SequenceTimesAtLeastOne,
46    /// EasyDMA can only read from data memory, read only buffers in flash will fail.
47    BufferNotInRAM,
48}
49
50const MAX_SEQUENCE_LEN: usize = 32767;
51/// The used pwm clock frequency
52pub const PWM_CLK_HZ: u32 = 16_000_000;
53
54impl<'d> SequencePwm<'d> {
55    /// Create a new 1-channel PWM
56    #[allow(unused_unsafe)]
57    pub fn new_1ch<T: Instance>(pwm: Peri<'d, T>, ch0: Peri<'d, impl GpioPin>, config: Config) -> Result<Self, Error> {
58        Self::new_inner(pwm, Some(ch0.into()), None, None, None, config)
59    }
60
61    /// Create a new 2-channel PWM
62    #[allow(unused_unsafe)]
63    pub fn new_2ch<T: Instance>(
64        pwm: Peri<'d, T>,
65        ch0: Peri<'d, impl GpioPin>,
66        ch1: Peri<'d, impl GpioPin>,
67        config: Config,
68    ) -> Result<Self, Error> {
69        Self::new_inner(pwm, Some(ch0.into()), Some(ch1.into()), None, None, config)
70    }
71
72    /// Create a new 3-channel PWM
73    #[allow(unused_unsafe)]
74    pub fn new_3ch<T: Instance>(
75        pwm: Peri<'d, T>,
76        ch0: Peri<'d, impl GpioPin>,
77        ch1: Peri<'d, impl GpioPin>,
78        ch2: Peri<'d, impl GpioPin>,
79        config: Config,
80    ) -> Result<Self, Error> {
81        Self::new_inner(pwm, Some(ch0.into()), Some(ch1.into()), Some(ch2.into()), None, config)
82    }
83
84    /// Create a new 4-channel PWM
85    #[allow(unused_unsafe)]
86    pub fn new_4ch<T: Instance>(
87        pwm: Peri<'d, T>,
88        ch0: Peri<'d, impl GpioPin>,
89        ch1: Peri<'d, impl GpioPin>,
90        ch2: Peri<'d, impl GpioPin>,
91        ch3: Peri<'d, impl GpioPin>,
92        config: Config,
93    ) -> Result<Self, Error> {
94        Self::new_inner(
95            pwm,
96            Some(ch0.into()),
97            Some(ch1.into()),
98            Some(ch2.into()),
99            Some(ch3.into()),
100            config,
101        )
102    }
103
104    fn new_inner<T: Instance>(
105        _pwm: Peri<'d, T>,
106        ch0: Option<Peri<'d, AnyPin>>,
107        ch1: Option<Peri<'d, AnyPin>>,
108        ch2: Option<Peri<'d, AnyPin>>,
109        ch3: Option<Peri<'d, AnyPin>>,
110        config: Config,
111    ) -> Result<Self, Error> {
112        let r = T::regs();
113
114        if let Some(pin) = &ch0 {
115            pin.set_low();
116            pin.conf().write(|w| {
117                w.set_dir(gpiovals::Dir::OUTPUT);
118                w.set_input(gpiovals::Input::DISCONNECT);
119                convert_drive(w, config.ch0_drive);
120            });
121        }
122        if let Some(pin) = &ch1 {
123            pin.set_low();
124            pin.conf().write(|w| {
125                w.set_dir(gpiovals::Dir::OUTPUT);
126                w.set_input(gpiovals::Input::DISCONNECT);
127                convert_drive(w, config.ch1_drive);
128            });
129        }
130        if let Some(pin) = &ch2 {
131            pin.set_low();
132            pin.conf().write(|w| {
133                w.set_dir(gpiovals::Dir::OUTPUT);
134                w.set_input(gpiovals::Input::DISCONNECT);
135                convert_drive(w, config.ch2_drive);
136            });
137        }
138        if let Some(pin) = &ch3 {
139            pin.set_low();
140            pin.conf().write(|w| {
141                w.set_dir(gpiovals::Dir::OUTPUT);
142                w.set_input(gpiovals::Input::DISCONNECT);
143                convert_drive(w, config.ch3_drive);
144            });
145        }
146
147        r.psel().out(0).write_value(ch0.psel_bits());
148        r.psel().out(1).write_value(ch1.psel_bits());
149        r.psel().out(2).write_value(ch2.psel_bits());
150        r.psel().out(3).write_value(ch3.psel_bits());
151
152        // Disable all interrupts
153        r.intenclr().write(|w| w.0 = 0xFFFF_FFFF);
154        r.shorts().write(|_| ());
155        r.events_stopped().write_value(0);
156        r.events_loopsdone().write_value(0);
157        r.events_seqend(0).write_value(0);
158        r.events_seqend(1).write_value(0);
159        r.events_pwmperiodend().write_value(0);
160        r.events_seqstarted(0).write_value(0);
161        r.events_seqstarted(1).write_value(0);
162
163        r.decoder().write(|w| {
164            w.set_load(vals::Load::from_bits(config.sequence_load as u8));
165            w.set_mode(vals::Mode::REFRESH_COUNT);
166        });
167
168        r.mode().write(|w| match config.counter_mode {
169            CounterMode::UpAndDown => w.set_updown(vals::Updown::UP_AND_DOWN),
170            CounterMode::Up => w.set_updown(vals::Updown::UP),
171        });
172        r.prescaler()
173            .write(|w| w.set_prescaler(vals::Prescaler::from_bits(config.prescaler as u8)));
174        r.countertop().write(|w| w.set_countertop(config.max_duty));
175
176        Ok(Self {
177            r: T::regs(),
178            ch0,
179            ch1,
180            ch2,
181            ch3,
182        })
183    }
184
185    /// Returns reference to `Stopped` event endpoint for PPI.
186    #[inline(always)]
187    pub fn event_stopped(&self) -> Event<'d> {
188        Event::from_reg(self.r.events_stopped())
189    }
190
191    /// Returns reference to `LoopsDone` event endpoint for PPI.
192    #[inline(always)]
193    pub fn event_loops_done(&self) -> Event<'d> {
194        Event::from_reg(self.r.events_loopsdone())
195    }
196
197    /// Returns reference to `PwmPeriodEnd` event endpoint for PPI.
198    #[inline(always)]
199    pub fn event_pwm_period_end(&self) -> Event<'d> {
200        Event::from_reg(self.r.events_pwmperiodend())
201    }
202
203    /// Returns reference to `Seq0 End` event endpoint for PPI.
204    #[inline(always)]
205    pub fn event_seq_end(&self) -> Event<'d> {
206        Event::from_reg(self.r.events_seqend(0))
207    }
208
209    /// Returns reference to `Seq1 End` event endpoint for PPI.
210    #[inline(always)]
211    pub fn event_seq1_end(&self) -> Event<'d> {
212        Event::from_reg(self.r.events_seqend(1))
213    }
214
215    /// Returns reference to `Seq0 Started` event endpoint for PPI.
216    #[inline(always)]
217    pub fn event_seq0_started(&self) -> Event<'d> {
218        Event::from_reg(self.r.events_seqstarted(0))
219    }
220
221    /// Returns reference to `Seq1 Started` event endpoint for PPI.
222    #[inline(always)]
223    pub fn event_seq1_started(&self) -> Event<'d> {
224        Event::from_reg(self.r.events_seqstarted(1))
225    }
226
227    /// Returns reference to `Seq0 Start` task endpoint for PPI.
228    /// # Safety
229    ///
230    /// Interacting with the sequence while it runs puts it in an unknown state
231    #[inline(always)]
232    pub unsafe fn task_start_seq0(&self) -> Task<'d> {
233        Task::from_reg(self.r.tasks_seqstart(0))
234    }
235
236    /// Returns reference to `Seq1 Started` task endpoint for PPI.
237    /// # Safety
238    ///
239    /// Interacting with the sequence while it runs puts it in an unknown state
240    #[inline(always)]
241    pub unsafe fn task_start_seq1(&self) -> Task<'d> {
242        Task::from_reg(self.r.tasks_seqstart(1))
243    }
244
245    /// Returns reference to `NextStep` task endpoint for PPI.
246    /// # Safety
247    ///
248    /// Interacting with the sequence while it runs puts it in an unknown state
249    #[inline(always)]
250    pub unsafe fn task_next_step(&self) -> Task<'d> {
251        Task::from_reg(self.r.tasks_nextstep())
252    }
253
254    /// Returns reference to `Stop` task endpoint for PPI.
255    /// # Safety
256    ///
257    /// Interacting with the sequence while it runs puts it in an unknown state
258    #[inline(always)]
259    pub unsafe fn task_stop(&self) -> Task<'d> {
260        Task::from_reg(self.r.tasks_stop())
261    }
262}
263
264impl<'a> Drop for SequencePwm<'a> {
265    fn drop(&mut self) {
266        if let Some(pin) = &self.ch0 {
267            pin.set_low();
268            pin.conf().write(|_| ());
269            self.r.psel().out(0).write_value(DISCONNECTED);
270        }
271        if let Some(pin) = &self.ch1 {
272            pin.set_low();
273            pin.conf().write(|_| ());
274            self.r.psel().out(1).write_value(DISCONNECTED);
275        }
276        if let Some(pin) = &self.ch2 {
277            pin.set_low();
278            pin.conf().write(|_| ());
279            self.r.psel().out(2).write_value(DISCONNECTED);
280        }
281        if let Some(pin) = &self.ch3 {
282            pin.set_low();
283            pin.conf().write(|_| ());
284            self.r.psel().out(3).write_value(DISCONNECTED);
285        }
286
287        self.r.enable().write(|w| w.set_enable(false));
288    }
289}
290
291/// Configuration for the PWM as a whole.
292#[non_exhaustive]
293pub struct Config {
294    /// Selects up mode or up-and-down mode for the counter
295    pub counter_mode: CounterMode,
296    /// Top value to be compared against buffer values
297    pub max_duty: u16,
298    /// Configuration for PWM_CLK
299    pub prescaler: Prescaler,
300    /// How a sequence is read from RAM and is spread to the compare register
301    pub sequence_load: SequenceLoad,
302    /// Drive strength for the channel 0 line.
303    pub ch0_drive: OutputDrive,
304    /// Drive strength for the channel 1 line.
305    pub ch1_drive: OutputDrive,
306    /// Drive strength for the channel 2 line.
307    pub ch2_drive: OutputDrive,
308    /// Drive strength for the channel 3 line.
309    pub ch3_drive: OutputDrive,
310}
311
312impl Default for Config {
313    fn default() -> Config {
314        Config {
315            counter_mode: CounterMode::Up,
316            max_duty: 1000,
317            prescaler: Prescaler::Div16,
318            sequence_load: SequenceLoad::Common,
319            ch0_drive: OutputDrive::Standard,
320            ch1_drive: OutputDrive::Standard,
321            ch2_drive: OutputDrive::Standard,
322            ch3_drive: OutputDrive::Standard,
323        }
324    }
325}
326
327/// Configuration per sequence
328#[non_exhaustive]
329#[derive(Clone)]
330pub struct SequenceConfig {
331    /// Number of PWM periods to delay between each sequence sample
332    pub refresh: u32,
333    /// Number of PWM periods after the sequence ends before starting the next sequence
334    pub end_delay: u32,
335}
336
337impl Default for SequenceConfig {
338    fn default() -> SequenceConfig {
339        SequenceConfig {
340            refresh: 0,
341            end_delay: 0,
342        }
343    }
344}
345
346/// A composition of a sequence buffer and its configuration.
347#[non_exhaustive]
348pub struct Sequence<'s> {
349    /// The words comprising the sequence. Must not exceed 32767 words.
350    pub words: &'s [u16],
351    /// Configuration associated with the sequence.
352    pub config: SequenceConfig,
353}
354
355impl<'s> Sequence<'s> {
356    /// Create a new `Sequence`
357    pub fn new(words: &'s [u16], config: SequenceConfig) -> Self {
358        Self { words, config }
359    }
360}
361
362/// A single sequence that can be started and stopped.
363/// Takes one sequence along with its configuration.
364#[non_exhaustive]
365pub struct SingleSequencer<'d, 's> {
366    sequencer: Sequencer<'d, 's>,
367}
368
369impl<'d, 's> SingleSequencer<'d, 's> {
370    /// Create a new sequencer
371    pub fn new(pwm: &'s mut SequencePwm<'d>, words: &'s [u16], config: SequenceConfig) -> Self {
372        Self {
373            sequencer: Sequencer::new(pwm, Sequence::new(words, config), None),
374        }
375    }
376
377    /// Start or restart playback.
378    #[inline(always)]
379    pub fn start(&self, times: SingleSequenceMode) -> Result<(), Error> {
380        let (start_seq, times) = match times {
381            SingleSequenceMode::Times(n) if n == 1 => (StartSequence::One, SequenceMode::Loop(1)),
382            SingleSequenceMode::Times(n) if n & 1 == 1 => (StartSequence::One, SequenceMode::Loop((n / 2) + 1)),
383            SingleSequenceMode::Times(n) => (StartSequence::Zero, SequenceMode::Loop(n / 2)),
384            SingleSequenceMode::Infinite => (StartSequence::Zero, SequenceMode::Infinite),
385        };
386        self.sequencer.start(start_seq, times)
387    }
388
389    /// Stop playback. Disables the peripheral. Does NOT clear the last duty
390    /// cycle from the pin. Returns any sequences previously provided to
391    /// `start` so that they may be further mutated.
392    #[inline(always)]
393    pub fn stop(&self) {
394        self.sequencer.stop();
395    }
396}
397
398/// A composition of sequences that can be started and stopped.
399/// Takes at least one sequence along with its configuration.
400/// Optionally takes a second sequence and its configuration.
401/// In the case where no second sequence is provided then the first sequence
402/// is used.
403#[non_exhaustive]
404pub struct Sequencer<'d, 's> {
405    _pwm: &'s mut SequencePwm<'d>,
406    sequence0: Sequence<'s>,
407    sequence1: Option<Sequence<'s>>,
408}
409
410impl<'d, 's> Sequencer<'d, 's> {
411    /// Create a new double sequence. In the absence of sequence 1, sequence 0
412    /// will be used twice in the one loop.
413    pub fn new(pwm: &'s mut SequencePwm<'d>, sequence0: Sequence<'s>, sequence1: Option<Sequence<'s>>) -> Self {
414        Sequencer {
415            _pwm: pwm,
416            sequence0,
417            sequence1,
418        }
419    }
420
421    /// Start or restart playback. The sequence mode applies to both sequences combined as one.
422    #[inline(always)]
423    pub fn start(&self, start_seq: StartSequence, times: SequenceMode) -> Result<(), Error> {
424        let sequence0 = &self.sequence0;
425        let alt_sequence = self.sequence1.as_ref().unwrap_or(&self.sequence0);
426
427        slice_in_ram_or(sequence0.words, Error::BufferNotInRAM)?;
428        slice_in_ram_or(alt_sequence.words, Error::BufferNotInRAM)?;
429
430        if sequence0.words.len() > MAX_SEQUENCE_LEN || alt_sequence.words.len() > MAX_SEQUENCE_LEN {
431            return Err(Error::SequenceTooLong);
432        }
433
434        if let SequenceMode::Loop(0) = times {
435            return Err(Error::SequenceTimesAtLeastOne);
436        }
437
438        self.stop();
439
440        let r = self._pwm.r;
441
442        r.seq(0).refresh().write(|w| w.0 = sequence0.config.refresh);
443        r.seq(0).enddelay().write(|w| w.0 = sequence0.config.end_delay);
444        r.seq(0).ptr().write_value(sequence0.words.as_ptr() as u32);
445        r.seq(0).cnt().write(|w| w.0 = sequence0.words.len() as u32);
446
447        r.seq(1).refresh().write(|w| w.0 = alt_sequence.config.refresh);
448        r.seq(1).enddelay().write(|w| w.0 = alt_sequence.config.end_delay);
449        r.seq(1).ptr().write_value(alt_sequence.words.as_ptr() as u32);
450        r.seq(1).cnt().write(|w| w.0 = alt_sequence.words.len() as u32);
451
452        r.enable().write(|w| w.set_enable(true));
453
454        // defensive before seqstart
455        compiler_fence(Ordering::SeqCst);
456
457        let seqstart_index = if start_seq == StartSequence::One { 1 } else { 0 };
458
459        match times {
460            SequenceMode::Loop(n) => {
461                r.loop_().write(|w| w.set_cnt(vals::LoopCnt::from_bits(n)));
462            }
463            // to play infinitely, repeat the sequence one time, then have loops done self trigger seq0 again
464            SequenceMode::Infinite => {
465                r.loop_().write(|w| w.set_cnt(vals::LoopCnt::from_bits(1)));
466                r.shorts().write(|w| w.set_loopsdone_seqstart0(true));
467            }
468        }
469
470        r.tasks_seqstart(seqstart_index).write_value(1);
471
472        Ok(())
473    }
474
475    /// Stop playback. Disables the peripheral. Does NOT clear the last duty
476    /// cycle from the pin. Returns any sequences previously provided to
477    /// `start` so that they may be further mutated.
478    #[inline(always)]
479    pub fn stop(&self) {
480        let r = self._pwm.r;
481
482        r.shorts().write(|_| ());
483
484        compiler_fence(Ordering::SeqCst);
485
486        r.tasks_stop().write_value(1);
487        r.enable().write(|w| w.set_enable(false));
488    }
489}
490
491impl<'d, 's> Drop for Sequencer<'d, 's> {
492    fn drop(&mut self) {
493        self.stop();
494    }
495}
496
497/// How many times to run a single sequence
498#[derive(Debug, Eq, PartialEq, Clone, Copy)]
499pub enum SingleSequenceMode {
500    /// Run a single sequence n Times total.
501    Times(u16),
502    /// Repeat until `stop` is called.
503    Infinite,
504}
505
506/// Which sequence to start a loop with
507#[derive(Debug, Eq, PartialEq, Clone, Copy)]
508pub enum StartSequence {
509    /// Start with Sequence 0
510    Zero,
511    /// Start with Sequence 1
512    One,
513}
514
515/// How many loops to run two sequences
516#[derive(Debug, Eq, PartialEq, Clone, Copy)]
517pub enum SequenceMode {
518    /// Run two sequences n loops i.e. (n * (seq0 + seq1.unwrap_or(seq0)))
519    Loop(u16),
520    /// Repeat until `stop` is called.
521    Infinite,
522}
523
524/// PWM Base clock is system clock (16MHz) divided by prescaler
525#[derive(Debug, Eq, PartialEq, Clone, Copy)]
526pub enum Prescaler {
527    /// Divide by 1
528    Div1,
529    /// Divide by 2
530    Div2,
531    /// Divide by 4
532    Div4,
533    /// Divide by 8
534    Div8,
535    /// Divide by 16
536    Div16,
537    /// Divide by 32
538    Div32,
539    /// Divide by 64
540    Div64,
541    /// Divide by 128
542    Div128,
543}
544
545/// How the sequence values are distributed across the channels
546#[derive(Debug, Eq, PartialEq, Clone, Copy)]
547pub enum SequenceLoad {
548    /// Provided sequence will be used across all channels
549    Common,
550    /// Provided sequence contains grouped values for each channel ex:
551    /// [ch0_0_and_ch1_0, ch2_0_and_ch3_0, ... ch0_n_and_ch1_n, ch2_n_and_ch3_n]
552    Grouped,
553    /// Provided sequence contains individual values for each channel ex:
554    /// [ch0_0, ch1_0, ch2_0, ch3_0... ch0_n, ch1_n, ch2_n, ch3_n]
555    Individual,
556    /// Similar to Individual mode, but only three channels are used. The fourth
557    /// value is loaded into the pulse generator counter as its top value.
558    Waveform,
559}
560
561/// Selects up mode or up-and-down mode for the counter
562#[derive(Debug, Eq, PartialEq, Clone, Copy)]
563pub enum CounterMode {
564    /// Up counter (edge-aligned PWM duty cycle)
565    Up,
566    /// Up and down counter (center-aligned PWM duty cycle)
567    UpAndDown,
568}
569
570impl<'d> SimplePwm<'d> {
571    /// Create a new 1-channel PWM
572    #[allow(unused_unsafe)]
573    pub fn new_1ch<T: Instance>(pwm: Peri<'d, T>, ch0: Peri<'d, impl GpioPin>) -> Self {
574        unsafe { Self::new_inner(pwm, Some(ch0.into()), None, None, None) }
575    }
576
577    /// Create a new 2-channel PWM
578    #[allow(unused_unsafe)]
579    pub fn new_2ch<T: Instance>(pwm: Peri<'d, T>, ch0: Peri<'d, impl GpioPin>, ch1: Peri<'d, impl GpioPin>) -> Self {
580        Self::new_inner(pwm, Some(ch0.into()), Some(ch1.into()), None, None)
581    }
582
583    /// Create a new 3-channel PWM
584    #[allow(unused_unsafe)]
585    pub fn new_3ch<T: Instance>(
586        pwm: Peri<'d, T>,
587        ch0: Peri<'d, impl GpioPin>,
588        ch1: Peri<'d, impl GpioPin>,
589        ch2: Peri<'d, impl GpioPin>,
590    ) -> Self {
591        unsafe { Self::new_inner(pwm, Some(ch0.into()), Some(ch1.into()), Some(ch2.into()), None) }
592    }
593
594    /// Create a new 4-channel PWM
595    #[allow(unused_unsafe)]
596    pub fn new_4ch<T: Instance>(
597        pwm: Peri<'d, T>,
598        ch0: Peri<'d, impl GpioPin>,
599        ch1: Peri<'d, impl GpioPin>,
600        ch2: Peri<'d, impl GpioPin>,
601        ch3: Peri<'d, impl GpioPin>,
602    ) -> Self {
603        unsafe {
604            Self::new_inner(
605                pwm,
606                Some(ch0.into()),
607                Some(ch1.into()),
608                Some(ch2.into()),
609                Some(ch3.into()),
610            )
611        }
612    }
613
614    fn new_inner<T: Instance>(
615        _pwm: Peri<'d, T>,
616        ch0: Option<Peri<'d, AnyPin>>,
617        ch1: Option<Peri<'d, AnyPin>>,
618        ch2: Option<Peri<'d, AnyPin>>,
619        ch3: Option<Peri<'d, AnyPin>>,
620    ) -> Self {
621        let r = T::regs();
622
623        for (i, ch) in [&ch0, &ch1, &ch2, &ch3].into_iter().enumerate() {
624            if let Some(pin) = ch {
625                pin.set_low();
626
627                pin.conf().write(|w| {
628                    w.set_dir(gpiovals::Dir::OUTPUT);
629                    w.set_input(gpiovals::Input::DISCONNECT);
630                    w.set_drive(gpiovals::Drive::S0S1);
631                });
632            }
633            r.psel().out(i).write_value(ch.psel_bits());
634        }
635
636        let pwm = Self {
637            r: T::regs(),
638            ch0,
639            ch1,
640            ch2,
641            ch3,
642            duty: [0; 4],
643        };
644
645        // Disable all interrupts
646        r.intenclr().write(|w| w.0 = 0xFFFF_FFFF);
647        r.shorts().write(|_| ());
648
649        // Enable
650        r.enable().write(|w| w.set_enable(true));
651
652        r.seq(0).ptr().write_value((pwm.duty).as_ptr() as u32);
653        r.seq(0).cnt().write(|w| w.0 = 4);
654        r.seq(0).refresh().write(|w| w.0 = 0);
655        r.seq(0).enddelay().write(|w| w.0 = 0);
656
657        r.decoder().write(|w| {
658            w.set_load(vals::Load::INDIVIDUAL);
659            w.set_mode(vals::Mode::REFRESH_COUNT);
660        });
661        r.mode().write(|w| w.set_updown(vals::Updown::UP));
662        r.prescaler().write(|w| w.set_prescaler(vals::Prescaler::DIV_16));
663        r.countertop().write(|w| w.set_countertop(1000));
664        r.loop_().write(|w| w.set_cnt(vals::LoopCnt::DISABLED));
665
666        pwm
667    }
668
669    /// Returns the enable state of the pwm counter
670    #[inline(always)]
671    pub fn is_enabled(&self) -> bool {
672        self.r.enable().read().enable()
673    }
674
675    /// Enables the PWM generator.
676    #[inline(always)]
677    pub fn enable(&self) {
678        self.r.enable().write(|w| w.set_enable(true));
679    }
680
681    /// Disables the PWM generator. Does NOT clear the last duty cycle from the pin.
682    #[inline(always)]
683    pub fn disable(&self) {
684        self.r.enable().write(|w| w.set_enable(false));
685    }
686
687    /// Returns the current duty of the channel
688    pub fn duty(&self, channel: usize) -> u16 {
689        self.duty[channel]
690    }
691
692    /// Sets duty cycle (15 bit) for a PWM channel.
693    pub fn set_duty(&mut self, channel: usize, duty: u16) {
694        self.duty[channel] = duty & 0x7FFF;
695
696        // reload ptr in case self was moved
697        self.r.seq(0).ptr().write_value((self.duty).as_ptr() as u32);
698
699        // defensive before seqstart
700        compiler_fence(Ordering::SeqCst);
701
702        self.r.events_seqend(0).write_value(0);
703
704        // tasks_seqstart() doesn't exist in all svds so write its bit instead
705        self.r.tasks_seqstart(0).write_value(1);
706
707        // defensive wait until waveform is loaded after seqstart so set_duty
708        // can't be called again while dma is still reading
709        if self.is_enabled() {
710            while self.r.events_seqend(0).read() == 0 {}
711        }
712    }
713
714    /// Sets the PWM clock prescaler.
715    #[inline(always)]
716    pub fn set_prescaler(&self, div: Prescaler) {
717        self.r
718            .prescaler()
719            .write(|w| w.set_prescaler(vals::Prescaler::from_bits(div as u8)));
720    }
721
722    /// Gets the PWM clock prescaler.
723    #[inline(always)]
724    pub fn prescaler(&self) -> Prescaler {
725        match self.r.prescaler().read().prescaler().to_bits() {
726            0 => Prescaler::Div1,
727            1 => Prescaler::Div2,
728            2 => Prescaler::Div4,
729            3 => Prescaler::Div8,
730            4 => Prescaler::Div16,
731            5 => Prescaler::Div32,
732            6 => Prescaler::Div64,
733            7 => Prescaler::Div128,
734            _ => unreachable!(),
735        }
736    }
737
738    /// Sets the maximum duty cycle value.
739    #[inline(always)]
740    pub fn set_max_duty(&self, duty: u16) {
741        self.r.countertop().write(|w| w.set_countertop(duty.min(32767u16)));
742    }
743
744    /// Returns the maximum duty cycle value.
745    #[inline(always)]
746    pub fn max_duty(&self) -> u16 {
747        self.r.countertop().read().countertop()
748    }
749
750    /// Sets the PWM output frequency.
751    #[inline(always)]
752    pub fn set_period(&self, freq: u32) {
753        let clk = PWM_CLK_HZ >> (self.prescaler() as u8);
754        let duty = clk / freq;
755        self.set_max_duty(duty.min(32767) as u16);
756    }
757
758    /// Returns the PWM output frequency.
759    #[inline(always)]
760    pub fn period(&self) -> u32 {
761        let clk = PWM_CLK_HZ >> (self.prescaler() as u8);
762        let max_duty = self.max_duty() as u32;
763        clk / max_duty
764    }
765
766    /// Sets the PWM-Channel0 output drive strength
767    #[inline(always)]
768    pub fn set_ch0_drive(&self, drive: OutputDrive) {
769        if let Some(pin) = &self.ch0 {
770            pin.conf().modify(|w| convert_drive(w, drive));
771        }
772    }
773
774    /// Sets the PWM-Channel1 output drive strength
775    #[inline(always)]
776    pub fn set_ch1_drive(&self, drive: OutputDrive) {
777        if let Some(pin) = &self.ch1 {
778            pin.conf().modify(|w| convert_drive(w, drive));
779        }
780    }
781
782    /// Sets the PWM-Channel2 output drive strength
783    #[inline(always)]
784    pub fn set_ch2_drive(&self, drive: OutputDrive) {
785        if let Some(pin) = &self.ch2 {
786            pin.conf().modify(|w| convert_drive(w, drive));
787        }
788    }
789
790    /// Sets the PWM-Channel3 output drive strength
791    #[inline(always)]
792    pub fn set_ch3_drive(&self, drive: OutputDrive) {
793        if let Some(pin) = &self.ch3 {
794            pin.conf().modify(|w| convert_drive(w, drive));
795        }
796    }
797}
798
799impl<'a> Drop for SimplePwm<'a> {
800    fn drop(&mut self) {
801        let r = &self.r;
802
803        self.disable();
804
805        if let Some(pin) = &self.ch0 {
806            pin.set_low();
807            pin.conf().write(|_| ());
808            r.psel().out(0).write_value(DISCONNECTED);
809        }
810        if let Some(pin) = &self.ch1 {
811            pin.set_low();
812            pin.conf().write(|_| ());
813            r.psel().out(1).write_value(DISCONNECTED);
814        }
815        if let Some(pin) = &self.ch2 {
816            pin.set_low();
817            pin.conf().write(|_| ());
818            r.psel().out(2).write_value(DISCONNECTED);
819        }
820        if let Some(pin) = &self.ch3 {
821            pin.set_low();
822            pin.conf().write(|_| ());
823            r.psel().out(3).write_value(DISCONNECTED);
824        }
825    }
826}
827
828pub(crate) trait SealedInstance {
829    fn regs() -> pac::pwm::Pwm;
830}
831
832/// PWM peripheral instance.
833#[allow(private_bounds)]
834pub trait Instance: SealedInstance + PeripheralType + 'static {
835    /// Interrupt for this peripheral.
836    type Interrupt: interrupt::typelevel::Interrupt;
837}
838
839macro_rules! impl_pwm {
840    ($type:ident, $pac_type:ident, $irq:ident) => {
841        impl crate::pwm::SealedInstance for peripherals::$type {
842            fn regs() -> pac::pwm::Pwm {
843                pac::$pac_type
844            }
845        }
846        impl crate::pwm::Instance for peripherals::$type {
847            type Interrupt = crate::interrupt::typelevel::$irq;
848        }
849    };
850}