Skip to main content

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 esp_sync::RawMutex;
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
87static MUTEX: RawMutex = RawMutex::new();
88
89impl<const NUM: usize> Unit<'_, NUM> {
90    /// return a new Unit
91    pub(super) fn new() -> Self {
92        Self {
93            counter: Counter::new(),
94            channel0: Channel::new(),
95            channel1: Channel::new(),
96        }
97    }
98
99    /// Configures a lower limit to the count value.
100    ///
101    /// When the count drops to this value:
102    /// - A low limit interrupt is triggered.
103    /// - The count is reset to 0.
104    ///
105    /// If None is specified, then no interrupt is triggered and
106    /// the count wraps around after [i16::MIN].
107    ///
108    /// Note: The specified value must be negative.
109    pub fn set_low_limit(&self, value: Option<i16>) -> Result<(), InvalidLowLimit> {
110        let pcnt = PCNT::regs();
111        let unit = pcnt.unit(NUM);
112
113        if let Some(value) = value {
114            // low limit must be >= or the limit is -32768 and when that's
115            // hit the event status claims it was the high limit.
116            // tested on an esp32s3
117            if !value.is_negative() {
118                return Err(InvalidLowLimit);
119            } else {
120                unit.conf2()
121                    .modify(|_, w| unsafe { w.cnt_l_lim().bits(value as u16) });
122                unit.conf0().modify(|_, w| w.thr_l_lim_en().set_bit());
123            }
124        } else {
125            unit.conf0().modify(|_, w| w.thr_l_lim_en().clear_bit());
126        }
127        Ok(())
128    }
129
130    /// Configures a high limit to the count value.
131    ///
132    /// When the count rises to this value:
133    /// - A high limit interrupt is triggered.
134    /// - The count is reset to 0.
135    ///
136    /// If None is specified, then no interrupt is triggered and
137    /// the count wraps around after [i16::MAX].
138    ///
139    /// Note: The specified value must be positive.
140    pub fn set_high_limit(&self, value: Option<i16>) -> Result<(), InvalidHighLimit> {
141        let pcnt = PCNT::regs();
142        let unit = pcnt.unit(NUM);
143
144        if let Some(value) = value {
145            if !value.is_positive() {
146                return Err(InvalidHighLimit);
147            } else {
148                unit.conf2()
149                    .modify(|_, w| unsafe { w.cnt_h_lim().bits(value as u16) });
150                unit.conf0().modify(|_, w| w.thr_h_lim_en().set_bit());
151            }
152        } else {
153            unit.conf0().modify(|_, w| w.thr_h_lim_en().clear_bit());
154        }
155        Ok(())
156    }
157
158    /// Configures a threshold value to trigger an interrupt.
159    ///
160    /// When the count equals this value a threshold0 interrupt is triggered.
161    /// If None is specified, then no interrupt is triggered.
162    pub fn set_threshold0(&self, value: Option<i16>) {
163        let pcnt = PCNT::regs();
164        let unit = pcnt.unit(NUM);
165
166        if let Some(value) = value {
167            unit.conf1()
168                .modify(|_, w| unsafe { w.cnt_thres0().bits(value as u16) });
169            unit.conf0().modify(|_, w| w.thr_thres0_en().set_bit());
170        } else {
171            unit.conf0().modify(|_, w| w.thr_thres0_en().clear_bit());
172        }
173    }
174
175    /// Configures a threshold value to trigger an interrupt.
176    ///
177    /// When the count equals this value a threshold1 interrupt is triggered.
178    /// If None is specified, then no interrupt is triggered.
179    pub fn set_threshold1(&self, value: Option<i16>) {
180        let pcnt = PCNT::regs();
181        let unit = pcnt.unit(NUM);
182
183        if let Some(value) = value {
184            unit.conf1()
185                .modify(|_, w| unsafe { w.cnt_thres1().bits(value as u16) });
186            unit.conf0().modify(|_, w| w.thr_thres1_en().set_bit());
187        } else {
188            unit.conf0().modify(|_, w| w.thr_thres1_en().clear_bit());
189        }
190    }
191
192    /// Configures the glitch filter hardware of the unit.
193    ///
194    /// `threshold` is the minimum number of APB_CLK cycles for a pulse to be
195    /// considered valid. If it is None, the filter is disabled.
196    ///
197    /// Note: This maximum possible threshold is 1023.
198    pub fn set_filter(&self, threshold: Option<u16>) -> Result<(), InvalidFilterThreshold> {
199        let pcnt = PCNT::regs();
200        let unit = pcnt.unit(NUM);
201
202        match threshold {
203            None => {
204                unit.conf0().modify(|_, w| w.filter_en().clear_bit());
205            }
206            Some(threshold) => {
207                if threshold > 1023 {
208                    return Err(InvalidFilterThreshold);
209                }
210                unit.conf0().modify(|_, w| unsafe {
211                    w.filter_thres().bits(threshold);
212                    w.filter_en().set_bit()
213                });
214            }
215        }
216        Ok(())
217    }
218
219    /// Resets the counter value to zero.
220    pub fn clear(&self) {
221        MUTEX.lock(|| {
222            let bits = PCNT::regs().ctrl().read().bits();
223            PCNT::regs().ctrl().write(|w| {
224                unsafe { w.bits(bits) };
225                w.cnt_rst_u(NUM as u8).set_bit()
226            });
227            PCNT::regs().ctrl().write(|w| {
228                unsafe { w.bits(bits) };
229                w.cnt_rst_u(NUM as u8).clear_bit()
230            });
231        });
232    }
233
234    /// Pause the counter
235    pub fn pause(&self) {
236        MUTEX.lock(|| {
237            PCNT::regs()
238                .ctrl()
239                .modify(|_, w| w.cnt_pause_u(NUM as u8).set_bit());
240        });
241    }
242
243    /// Resume the counter
244    pub fn resume(&self) {
245        MUTEX.lock(|| {
246            PCNT::regs()
247                .ctrl()
248                .modify(|_, w| w.cnt_pause_u(NUM as u8).clear_bit());
249        });
250    }
251
252    /// Get the latest events for this unit.
253    pub fn events(&self) -> Events {
254        let status = PCNT::regs().u_status(NUM).read();
255
256        Events {
257            low_limit: status.l_lim().bit(),
258            high_limit: status.h_lim().bit(),
259            threshold0: status.thres0().bit(),
260            threshold1: status.thres1().bit(),
261            zero: status.zero().bit(),
262        }
263    }
264
265    /// Get the mode of the last zero crossing
266    pub fn zero_mode(&self) -> ZeroMode {
267        PCNT::regs().u_status(NUM).read().zero_mode().bits().into()
268    }
269
270    /// Enable interrupts for this unit.
271    pub fn listen(&self) {
272        MUTEX.lock(|| {
273            PCNT::regs()
274                .int_ena()
275                .modify(|_, w| w.cnt_thr_event_u(NUM as u8).set_bit());
276        });
277    }
278
279    /// Disable interrupts for this unit.
280    pub fn unlisten(&self) {
281        MUTEX.lock(|| {
282            PCNT::regs()
283                .int_ena()
284                .modify(|_, w| w.cnt_thr_event_u(NUM as u8).clear_bit());
285        });
286    }
287
288    /// Returns true if an interrupt is active for this unit.
289    pub fn interrupt_is_set(&self) -> bool {
290        PCNT::regs()
291            .int_raw()
292            .read()
293            .cnt_thr_event_u(NUM as u8)
294            .bit()
295    }
296
297    /// Clear the interrupt bit for this unit.
298    pub fn reset_interrupt(&self) {
299        PCNT::regs()
300            .int_clr()
301            .write(|w| w.cnt_thr_event_u(NUM as u8).set_bit());
302    }
303
304    /// Get the current counter value.
305    pub fn value(&self) -> i16 {
306        self.counter.get()
307    }
308}
309
310impl<const NUM: usize> Drop for Unit<'_, NUM> {
311    fn drop(&mut self) {
312        // This is here to prevent the destructuring of Unit.
313    }
314}
315
316// The entire Unit is Send but the individual channels are not.
317unsafe impl<const NUM: usize> Send for Unit<'_, NUM> {}
318
319/// Represents the counter within a pulse counter unit.
320#[derive(Clone)]
321pub struct Counter<'d, const NUM: usize> {
322    _phantom: PhantomData<&'d ()>,
323
324    _guard: GenericPeripheralGuard<{ crate::system::Peripheral::Pcnt as u8 }>,
325}
326
327impl<const NUM: usize> Counter<'_, NUM> {
328    fn new() -> Self {
329        let guard = GenericPeripheralGuard::new();
330
331        Self {
332            _phantom: PhantomData,
333            _guard: guard,
334        }
335    }
336
337    /// Get the current counter value.
338    pub fn get(&self) -> i16 {
339        let pcnt = PCNT::regs();
340        pcnt.u_cnt(NUM).read().cnt().bits() as i16
341    }
342}