Skip to main content

pic32_hal/
oc.rs

1//! Output Compare
2//!
3
4use crate::pac::{OCMP1, OCMP2, OCMP3, OCMP4, OCMP5};
5use crate::pac::{TMR2, TMR3};
6use core::convert::Infallible;
7use embedded_hal::pwm::{ErrorType, SetDutyCycle};
8use embedded_hal_0_2::PwmPin;
9
10use core::marker::PhantomData;
11
12/// Marker for incomplete configuration.
13pub struct TimebaseUninit;
14
15/// Marker for configurations where the even numbered 16-bit timer (e.g. Timer 2) is used as a time base
16pub struct Timebase16even;
17
18/// Marker for configurations where the odd numbered 16-bit timer (e.g. Timer 3) is used as a time base
19pub struct Timebase16odd;
20
21/// Marker for configurations where two 16-bits timer form a 32-bit time base
22pub struct Timebase32;
23
24/// Output compare configuration (excluding PWM modes)
25pub enum OcConfig {
26    /// No operation
27    Off,
28    /// Rising slope when the timer is equal to the specified value
29    RisingSlope(u32),
30
31    /// Falling slope when the timer is equal to the specified value
32    FallingSlope(u32),
33
34    /// Toggle when the timer is equal to the specified value
35    Toggle(u32),
36
37    /// Single pulse with values defining the rising edge and falling edge, respectively
38    SinglePulse(u32, u32),
39
40    /// Continuous pulses with values defining the rising edges and falling edges, respectively
41    ContinuousPulses(u32, u32),
42}
43
44impl OcConfig {
45    const fn ocm_bits(&self) -> u8 {
46        match self {
47            Self::Off => 0b000,
48            Self::RisingSlope(_) => 0b001,
49            Self::FallingSlope(_) => 0b010,
50            Self::Toggle(_) => 0b011,
51            Self::SinglePulse(_, _) => 0b100,
52            Self::ContinuousPulses(_, _) => 0b101,
53        }
54    }
55}
56
57/// Output compare modules configured for non-PWM output compare operations
58pub struct Oc<OCMP, TIMEBASE> {
59    ocmp: OCMP,
60    _timebase: PhantomData<TIMEBASE>,
61}
62
63macro_rules! oc_impl {
64    ($constructor: ident, $ocmp: ty) => {
65        impl Oc<$ocmp, TimebaseUninit> {
66            pub fn $constructor(ocmp: $ocmp, stop_in_idle_mode: bool) -> Self {
67                ocmp.cont
68                    .write(|w| w.on().clear_bit().sidl().bit(stop_in_idle_mode));
69                Self {
70                    ocmp,
71                    _timebase: PhantomData,
72                }
73            }
74        }
75
76        impl<TIMEBASE> Oc<$ocmp, TIMEBASE> {
77            /// Select the even numbered 16-bit timer as the time base.
78            pub fn timebase16even(self) -> Oc<$ocmp, Timebase16even> {
79                self.ocmp
80                    .cont
81                    .modify(|_, w| w.octsel().bit(false).oc32().bit(false));
82                Oc {
83                    ocmp: self.ocmp,
84                    _timebase: PhantomData,
85                }
86            }
87
88            /// Select the odd numbered 16-bit timer as the time base.
89            pub fn timebase16odd(self) -> Oc<$ocmp, Timebase16odd> {
90                self.ocmp
91                    .cont
92                    .modify(|_, w| w.octsel().bit(true).oc32().bit(false));
93                self.ocmp.contset.write(|w| w.on().set_bit());
94                Oc {
95                    ocmp: self.ocmp,
96                    _timebase: PhantomData,
97                }
98            }
99
100            /// Select both 16-bit timers as a 32-bit time base.
101            pub fn timebase32(self) -> Oc<$ocmp, Timebase32> {
102                self.ocmp
103                    .cont
104                    .modify(|_, w| w.octsel().bit(false).oc32().bit(true));
105                self.ocmp.contset.write(|w| w.on().set_bit());
106                Oc {
107                    ocmp: self.ocmp,
108                    _timebase: PhantomData,
109                }
110            }
111
112            /// Deactivate the output compare module and return the PAC object
113            pub fn free(self) -> $ocmp {
114                self.ocmp.cont.write(|w| w.on().clear_bit());
115                self.ocmp
116            }
117
118            /// Turn output compare module off
119            #[inline(never)] // forces some cycles after OCMP access (as required by data sheet)
120            pub fn turn_off(&mut self) {
121                self.ocmp.contclr.write(|w| w.on().set_bit());
122            }
123        }
124
125        impl Oc<$ocmp, Timebase16even> {
126            pub fn turn_on(&mut self, config: OcConfig) {
127                self.ocmp
128                    .cont
129                    .modify(|_, w| unsafe { w.ocm().bits(config.ocm_bits()) });
130                let (r, rs) = match config {
131                    OcConfig::RisingSlope(r) | OcConfig::FallingSlope(r) | OcConfig::Toggle(r) => {
132                        (r, 0)
133                    }
134                    OcConfig::SinglePulse(r, rs) | OcConfig::ContinuousPulses(r, rs) => (r, rs),
135                    OcConfig::Off => (0, 0),
136                };
137                self.ocmp.r.write(|w| unsafe { w.r().bits(r) });
138                self.ocmp.rs.write(|w| unsafe { w.rs().bits(rs) });
139                self.ocmp.contset.write(|w| w.on().set_bit());
140            }
141        }
142
143        impl Oc<$ocmp, Timebase16odd> {
144            pub fn turn_on(&mut self, config: OcConfig) {
145                self.ocmp
146                    .cont
147                    .modify(|_, w| unsafe { w.ocm().bits(config.ocm_bits()) });
148                let (r, rs) = match config {
149                    OcConfig::RisingSlope(r) | OcConfig::FallingSlope(r) | OcConfig::Toggle(r) => {
150                        (r, 0)
151                    }
152                    OcConfig::SinglePulse(r, rs) | OcConfig::ContinuousPulses(r, rs) => (r, rs),
153                    OcConfig::Off => (0, 0),
154                };
155                self.ocmp.r.write(|w| unsafe { w.r().bits(r) });
156                self.ocmp.rs.write(|w| unsafe { w.rs().bits(rs) });
157                self.ocmp.contset.write(|w| w.on().set_bit());
158            }
159        }
160        impl Oc<$ocmp, Timebase32> {
161            pub fn turn_on(&mut self, config: OcConfig) {
162                self.ocmp
163                    .cont
164                    .modify(|_, w| unsafe { w.ocm().bits(config.ocm_bits()) });
165                let (r, rs) = match config {
166                    OcConfig::RisingSlope(r) | OcConfig::FallingSlope(r) | OcConfig::Toggle(r) => {
167                        (r, 0)
168                    }
169                    OcConfig::SinglePulse(r, rs) | OcConfig::ContinuousPulses(r, rs) => (r, rs),
170                    OcConfig::Off => (0, 0),
171                };
172                self.ocmp.r.write(|w| unsafe { w.r().bits(r) });
173                self.ocmp.rs.write(|w| unsafe { w.rs().bits(rs) });
174                self.ocmp.contset.write(|w| w.on().set_bit());
175            }
176        }
177    };
178}
179
180oc_impl!(oc1, OCMP1);
181oc_impl!(oc2, OCMP2);
182oc_impl!(oc3, OCMP3);
183oc_impl!(oc4, OCMP4);
184oc_impl!(oc5, OCMP5);
185
186/// Output compare modules configured for PWM
187pub struct Pwm<OCMP, TIMEBASE> {
188    ocmp: OCMP,
189    _timebase: PhantomData<TIMEBASE>,
190}
191
192macro_rules! pwm_impl {
193    ($constructor: ident, $ocmp: ty, $timer_even: ty, $timer_odd: ty) => {
194        impl Pwm<$ocmp, TimebaseUninit> {
195            /// Initialize the output compare module for PWM.
196            ///
197            /// The respective timer must be set up previously. The HAL code for PWM
198            /// reads the respective timer period register to get the maximum duty cycle
199            /// value.
200            pub fn $constructor(
201                ocmp: $ocmp,
202                enable_fault_pin: bool,
203                stop_in_idle_mode: bool,
204            ) -> Self {
205                let ocm = match enable_fault_pin {
206                    false => 0b110,
207                    true => 0b111,
208                };
209                ocmp.cont
210                    .write(|w| unsafe { w.sidl().bit(stop_in_idle_mode).ocm().bits(ocm) });
211                Pwm {
212                    ocmp,
213                    _timebase: PhantomData,
214                }
215            }
216        }
217
218        impl<TIMEBASE> Pwm<$ocmp, TIMEBASE> {
219            /// Select the even numbered 16-bit timer as the time base.
220            pub fn timebase16even(self) -> Pwm<$ocmp, Timebase16even> {
221                self.ocmp
222                    .cont
223                    .modify(|_, w| w.octsel().bit(false).oc32().bit(false).on().bit(true));
224                Pwm {
225                    ocmp: self.ocmp,
226                    _timebase: PhantomData,
227                }
228            }
229
230            /// Select the odd numbered 16-bit timer as the time base.
231            pub fn timebase16odd(self) -> Pwm<$ocmp, Timebase16odd> {
232                self.ocmp
233                    .cont
234                    .modify(|_, w| w.octsel().bit(true).oc32().bit(false).on().bit(true));
235                self.ocmp.contset.write(|w| w.on().set_bit());
236                Pwm {
237                    ocmp: self.ocmp,
238                    _timebase: PhantomData,
239                }
240            }
241
242            /// Select both 16-bit timers as a 32-bit time base.
243            pub fn timebase32(self) -> Pwm<$ocmp, Timebase32> {
244                self.ocmp
245                    .cont
246                    .modify(|_, w| w.octsel().bit(false).oc32().bit(true).on().bit(true));
247                self.ocmp.contset.write(|w| w.on().set_bit());
248                Pwm {
249                    ocmp: self.ocmp,
250                    _timebase: PhantomData,
251                }
252            }
253
254            /// Get fault state
255            ///
256            /// Returns false if fault pin is not enabled.
257            pub fn fault(&self) -> bool {
258                self.ocmp.cont.read().ocflt().bit()
259            }
260
261            /// Turn PWM on.
262            ///
263            /// Can be used to re-enable PWM after a fault has been detected.
264            pub fn on(&mut self) {
265                self.ocmp.contset.write(|w| w.on().set_bit());
266            }
267
268            /// Turn PWM off.
269            pub fn off(&mut self) {
270                self.ocmp.contclr.write(|w| w.on().set_bit());
271            }
272
273            /// Deactivate the output compare module and return the PAC object
274            pub fn free(self) -> $ocmp {
275                self.ocmp.cont.write(|w| w.on().clear_bit());
276                self.ocmp
277            }
278        }
279
280        impl PwmPin for Pwm<$ocmp, Timebase16even> {
281            type Duty = u32;
282
283            fn enable(&mut self) {
284                self.ocmp.contset.write(|w| w.on().set_bit());
285            }
286
287            fn disable(&mut self) {
288                self.ocmp.contclr.write(|w| w.on().set_bit());
289            }
290
291            fn get_duty(&self) -> Self::Duty {
292                self.ocmp.rs.read().rs().bits() as Self::Duty
293            }
294
295            fn set_duty(&mut self, duty: Self::Duty) {
296                self.ocmp.rs.write(|w| unsafe { w.rs().bits(duty) });
297            }
298
299            fn get_max_duty(&self) -> Self::Duty {
300                unsafe { (*<$timer_even>::ptr()).pr.read().pr().bits() + 1 }
301            }
302        }
303
304        impl PwmPin for Pwm<$ocmp, Timebase16odd> {
305            type Duty = u32;
306
307            fn enable(&mut self) {
308                self.ocmp.contset.write(|w| w.on().set_bit());
309            }
310
311            fn disable(&mut self) {
312                self.ocmp.contclr.write(|w| w.on().set_bit());
313            }
314
315            fn get_duty(&self) -> Self::Duty {
316                self.ocmp.rs.read().rs().bits() as Self::Duty
317            }
318
319            fn set_duty(&mut self, duty: Self::Duty) {
320                self.ocmp.rs.write(|w| unsafe { w.rs().bits(duty) });
321            }
322
323            fn get_max_duty(&self) -> Self::Duty {
324                unsafe { (*<$timer_odd>::ptr()).pr.read().pr().bits() + 1 }
325            }
326        }
327
328        impl PwmPin for Pwm<$ocmp, Timebase32> {
329            type Duty = u32;
330
331            fn enable(&mut self) {
332                self.ocmp.contset.write(|w| w.on().set_bit());
333            }
334
335            fn disable(&mut self) {
336                self.ocmp.contclr.write(|w| w.on().set_bit());
337            }
338
339            fn get_duty(&self) -> Self::Duty {
340                self.ocmp.rs.read().rs().bits() as Self::Duty
341            }
342
343            fn set_duty(&mut self, duty: Self::Duty) {
344                self.ocmp.rs.write(|w| unsafe { w.rs().bits(duty) });
345            }
346
347            fn get_max_duty(&self) -> Self::Duty {
348                let pr = unsafe { (*<$timer_even>::ptr()).pr.read().pr().bits() };
349                pr.saturating_add(1)
350            }
351        }
352
353        impl<TIMEBASE> ErrorType for Pwm<$ocmp, TIMEBASE> {
354            type Error = Infallible;
355        }
356
357        impl SetDutyCycle for Pwm<$ocmp, Timebase16even> {
358            fn max_duty_cycle(&self) -> u16 {
359                unsafe { (*<$timer_even>::ptr()).pr.read().pr().bits() as u16 + 1 }
360            }
361
362            fn set_duty_cycle(&mut self, duty: u16) -> Result<(), Self::Error> {
363                self.ocmp.rs.write(|w| unsafe { w.rs().bits(duty as u32) });
364                Ok(())
365            }
366        }
367
368        impl SetDutyCycle for Pwm<$ocmp, Timebase16odd> {
369            fn max_duty_cycle(&self) -> u16 {
370                unsafe { (*<$timer_odd>::ptr()).pr.read().pr().bits() as u16 + 1 }
371            }
372
373            fn set_duty_cycle(&mut self, duty: u16) -> Result<(), Self::Error> {
374                self.ocmp.rs.write(|w| unsafe { w.rs().bits(duty as u32) });
375                Ok(())
376            }
377        }
378    };
379}
380
381pwm_impl!(oc1, OCMP1, TMR2, TMR3);
382pwm_impl!(oc2, OCMP2, TMR2, TMR3);
383pwm_impl!(oc3, OCMP3, TMR2, TMR3);
384pwm_impl!(oc4, OCMP4, TMR2, TMR3);
385pwm_impl!(oc5, OCMP5, TMR2, TMR3);