esp_hal/pcnt/
unit.rs

1//! # PCNT - Unit Module
2//!
3//! ## Overview
4//! The `unit` module is responsible for configuring and handling individual
5//! units of the `PCNT` peripheral. Each unit represents a separate instance of
6//! the `PCNT` module, identified by unit numbers like `Unit0`, `Unit1`, and so
7//! on. Users can interact with these units to configure settings such as low
8//! and high limits, thresholds, and optional filtering. The unit module also
9//! enables users to pause, resume, and clear the counter, as well as enable or
10//! disable interrupts for specific events associated with the unit.
11
12use core::marker::PhantomData;
13
14use critical_section::CriticalSection;
15
16use crate::{pcnt::channel::Channel, peripherals::PCNT, system::GenericPeripheralGuard};
17
18/// Invalid filter threshold value
19#[derive(Debug, Clone, Copy, PartialEq)]
20#[cfg_attr(feature = "defmt", derive(defmt::Format))]
21pub struct InvalidFilterThreshold;
22
23/// Invalid low limit - must be < 0
24#[derive(Debug, Clone, Copy, PartialEq)]
25#[cfg_attr(feature = "defmt", derive(defmt::Format))]
26pub struct InvalidLowLimit;
27
28/// Invalid high limit - must be > 0
29#[derive(Debug, Clone, Copy, PartialEq)]
30#[cfg_attr(feature = "defmt", derive(defmt::Format))]
31pub struct InvalidHighLimit;
32
33/// the current status of the counter.
34#[derive(Copy, Clone, Debug, Default, PartialEq)]
35#[cfg_attr(feature = "defmt", derive(defmt::Format))]
36pub enum ZeroMode {
37    /// pulse counter decreases from positive to 0.
38    #[default]
39    PosZero  = 0,
40    /// pulse counter increases from negative to 0
41    NegZero  = 1,
42    /// pulse counter is negative (not implemented?)
43    Negative = 2,
44    /// pulse counter is positive (not implemented?)
45    Positive = 3,
46}
47
48impl From<u8> for ZeroMode {
49    fn from(value: u8) -> Self {
50        match value {
51            0 => Self::PosZero,
52            1 => Self::NegZero,
53            2 => Self::Negative,
54            3 => Self::Positive,
55            _ => unreachable!(), // TODO: is this good enough?  should we use some default?
56        }
57    }
58}
59
60/// Events that can occur in a pulse counter unit.
61#[derive(Copy, Clone, Debug, Default)]
62#[cfg_attr(feature = "defmt", derive(defmt::Format))]
63pub struct Events {
64    /// Set when the pulse counter reaches the low limit.
65    pub low_limit: bool,
66    /// Set when the pulse counter reaches the high limit.
67    pub high_limit: bool,
68    /// Set when the pulse counter crosses threshold 0.
69    pub threshold0: bool,
70    /// Set when the pulse counter crosses threshold 1.
71    pub threshold1: bool,
72    /// Set when the pulse counter reaches zero.
73    pub zero: bool,
74}
75
76/// Represents a pulse counter unit.
77#[non_exhaustive]
78pub struct Unit<'d, const NUM: usize> {
79    /// The counter for PCNT unit.
80    pub counter: Counter<'d, NUM>,
81    /// The first channel in PCNT unit.
82    pub channel0: Channel<'d, NUM, 0>,
83    /// The second channel in PCNT unit.
84    pub channel1: Channel<'d, NUM, 1>,
85}
86
87impl<const NUM: usize> Unit<'_, NUM> {
88    /// return a new Unit
89    pub(super) fn new() -> Self {
90        Self {
91            counter: Counter::new(),
92            channel0: Channel::new(),
93            channel1: Channel::new(),
94        }
95    }
96
97    /// Configures a lower limit to the count value.
98    ///
99    /// When the count drops to this value:
100    /// - A low limit interrupt is triggered.
101    /// - The count is reset to 0.
102    ///
103    /// If None is specified, then no interrupt is triggered and
104    /// the count wraps around after [i16::MIN].
105    ///
106    /// Note: The specified value must be negative.
107    pub fn set_low_limit(&self, value: Option<i16>) -> Result<(), InvalidLowLimit> {
108        let pcnt = PCNT::regs();
109        let unit = pcnt.unit(NUM);
110
111        if let Some(value) = value {
112            // low limit must be >= or the limit is -32768 and when that's
113            // hit the event status claims it was the high limit.
114            // tested on an esp32s3
115            if !value.is_negative() {
116                return Err(InvalidLowLimit);
117            } else {
118                unit.conf2()
119                    .modify(|_, w| unsafe { w.cnt_l_lim().bits(value as u16) });
120                unit.conf0().modify(|_, w| w.thr_l_lim_en().set_bit());
121            }
122        } else {
123            unit.conf0().modify(|_, w| w.thr_l_lim_en().clear_bit());
124        }
125        Ok(())
126    }
127
128    /// Configures a high limit to the count value.
129    ///
130    /// When the count rises to this value:
131    /// - A high limit interrupt is triggered.
132    /// - The count is reset to 0.
133    ///
134    /// If None is specified, then no interrupt is triggered and
135    /// the count wraps around after [i16::MAX].
136    ///
137    /// Note: The specified value must be positive.
138    pub fn set_high_limit(&self, value: Option<i16>) -> Result<(), InvalidHighLimit> {
139        let pcnt = PCNT::regs();
140        let unit = pcnt.unit(NUM);
141
142        if let Some(value) = value {
143            if !value.is_positive() {
144                return Err(InvalidHighLimit);
145            } else {
146                unit.conf2()
147                    .modify(|_, w| unsafe { w.cnt_h_lim().bits(value as u16) });
148                unit.conf0().modify(|_, w| w.thr_h_lim_en().set_bit());
149            }
150        } else {
151            unit.conf0().modify(|_, w| w.thr_h_lim_en().clear_bit());
152        }
153        Ok(())
154    }
155
156    /// Configures a threshold value to trigger an interrupt.
157    ///
158    /// When the count equals this value a threshold0 interrupt is triggered.
159    /// If None is specified, then no interrupt is triggered.
160    pub fn set_threshold0(&self, value: Option<i16>) {
161        let pcnt = PCNT::regs();
162        let unit = pcnt.unit(NUM);
163
164        if let Some(value) = value {
165            unit.conf1()
166                .modify(|_, w| unsafe { w.cnt_thres0().bits(value as u16) });
167            unit.conf0().modify(|_, w| w.thr_thres0_en().set_bit());
168        } else {
169            unit.conf0().modify(|_, w| w.thr_thres0_en().clear_bit());
170        }
171    }
172
173    /// Configures a threshold value to trigger an interrupt.
174    ///
175    /// When the count equals this value a threshold1 interrupt is triggered.
176    /// If None is specified, then no interrupt is triggered.
177    pub fn set_threshold1(&self, value: Option<i16>) {
178        let pcnt = PCNT::regs();
179        let unit = pcnt.unit(NUM);
180
181        if let Some(value) = value {
182            unit.conf1()
183                .modify(|_, w| unsafe { w.cnt_thres1().bits(value as u16) });
184            unit.conf0().modify(|_, w| w.thr_thres1_en().set_bit());
185        } else {
186            unit.conf0().modify(|_, w| w.thr_thres1_en().clear_bit());
187        }
188    }
189
190    /// Configures the glitch filter hardware of the unit.
191    ///
192    /// `threshold` is the minimum number of APB_CLK cycles for a pulse to be
193    /// considered valid. If it is None, the filter is disabled.
194    ///
195    /// Note: This maximum possible threshold is 1023.
196    pub fn set_filter(&self, threshold: Option<u16>) -> Result<(), InvalidFilterThreshold> {
197        let pcnt = PCNT::regs();
198        let unit = pcnt.unit(NUM);
199
200        match threshold {
201            None => {
202                unit.conf0().modify(|_, w| w.filter_en().clear_bit());
203            }
204            Some(threshold) => {
205                if threshold > 1023 {
206                    return Err(InvalidFilterThreshold);
207                }
208                unit.conf0().modify(|_, w| unsafe {
209                    w.filter_thres().bits(threshold).filter_en().set_bit()
210                });
211            }
212        }
213        Ok(())
214    }
215
216    /// Resets the counter value to zero.
217    pub fn clear(&self) {
218        let pcnt = PCNT::regs();
219        critical_section::with(|_cs| {
220            pcnt.ctrl().modify(|_, w| w.cnt_rst_u(NUM as u8).set_bit());
221            // TODO: does this need a delay? (liebman / Jan 2 2023)
222            pcnt.ctrl()
223                .modify(|_, w| w.cnt_rst_u(NUM as u8).clear_bit());
224        });
225    }
226
227    /// Pause the counter
228    pub fn pause(&self) {
229        let pcnt = PCNT::regs();
230        critical_section::with(|_cs| {
231            pcnt.ctrl()
232                .modify(|_, w| w.cnt_pause_u(NUM as u8).set_bit());
233        });
234    }
235
236    /// Resume the counter
237    pub fn resume(&self) {
238        let pcnt = PCNT::regs();
239        critical_section::with(|_cs| {
240            pcnt.ctrl()
241                .modify(|_, w| w.cnt_pause_u(NUM as u8).clear_bit());
242        });
243    }
244
245    /// Get the latest events for this unit.
246    pub fn events(&self) -> Events {
247        let pcnt = PCNT::regs();
248        let status = pcnt.u_status(NUM).read();
249
250        Events {
251            low_limit: status.l_lim().bit(),
252            high_limit: status.h_lim().bit(),
253            threshold0: status.thres0().bit(),
254            threshold1: status.thres1().bit(),
255            zero: status.zero().bit(),
256        }
257    }
258
259    /// Get the mode of the last zero crossing
260    pub fn zero_mode(&self) -> ZeroMode {
261        let pcnt = PCNT::regs();
262        pcnt.u_status(NUM).read().zero_mode().bits().into()
263    }
264
265    /// Enable interrupts for this unit.
266    pub fn listen(&self) {
267        let pcnt = PCNT::regs();
268        critical_section::with(|_cs| {
269            pcnt.int_ena()
270                .modify(|_, w| w.cnt_thr_event_u(NUM as u8).set_bit());
271        });
272    }
273
274    /// Disable interrupts for this unit.
275    pub fn unlisten(&self, _cs: CriticalSection<'_>) {
276        let pcnt = PCNT::regs();
277        critical_section::with(|_cs| {
278            pcnt.int_ena()
279                .modify(|_, w| w.cnt_thr_event_u(NUM as u8).clear_bit());
280        });
281    }
282
283    /// Returns true if an interrupt is active for this unit.
284    pub fn interrupt_is_set(&self) -> bool {
285        let pcnt = PCNT::regs();
286        pcnt.int_raw().read().cnt_thr_event_u(NUM as u8).bit()
287    }
288
289    /// Clear the interrupt bit for this unit.
290    pub fn reset_interrupt(&self) {
291        let pcnt = PCNT::regs();
292        critical_section::with(|_cs| {
293            pcnt.int_clr()
294                .write(|w| w.cnt_thr_event_u(NUM as u8).set_bit());
295        });
296    }
297
298    /// Get the current counter value.
299    pub fn value(&self) -> i16 {
300        self.counter.get()
301    }
302}
303
304impl<const NUM: usize> Drop for Unit<'_, NUM> {
305    fn drop(&mut self) {
306        // This is here to prevent the destructuring of Unit.
307    }
308}
309
310// The entire Unit is Send but the individual channels are not.
311unsafe impl<const NUM: usize> Send for Unit<'_, NUM> {}
312
313/// Represents the counter within a pulse counter unit.
314#[derive(Clone)]
315pub struct Counter<'d, const NUM: usize> {
316    _phantom: PhantomData<&'d ()>,
317
318    _guard: GenericPeripheralGuard<{ crate::system::Peripheral::Pcnt as u8 }>,
319}
320
321impl<const NUM: usize> Counter<'_, NUM> {
322    fn new() -> Self {
323        let guard = GenericPeripheralGuard::new();
324
325        Self {
326            _phantom: PhantomData,
327            _guard: guard,
328        }
329    }
330
331    /// Get the current counter value.
332    pub fn get(&self) -> i16 {
333        let pcnt = PCNT::regs();
334        pcnt.u_cnt(NUM).read().cnt().bits() as i16
335    }
336}