esp_hal/pcnt/
unit.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
//! # PCNT - Unit Module
//!
//! ## Overview
//! The `unit` module is responsible for configuring and handling individual
//! units of the `PCNT` peripheral. Each unit represents a separate instance of
//! the `PCNT` module, identified by unit numbers like `Unit0`, `Unit1`, and so
//! on. Users can interact with these units to configure settings such as low
//! and high limits, thresholds, and optional filtering. The unit module also
//! enables users to pause, resume, and clear the counter, as well as enable or
//! disable interrupts for specific events associated with the unit.

use core::marker::PhantomData;

use critical_section::CriticalSection;

use crate::{pcnt::channel::Channel, peripherals::PCNT, system::GenericPeripheralGuard};

/// Invalid filter threshold value
#[derive(Debug, Clone, Copy, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct InvalidFilterThreshold;

/// Invalid low limit - must be < 0
#[derive(Debug, Clone, Copy, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct InvalidLowLimit;

/// Invalid high limit - must be > 0
#[derive(Debug, Clone, Copy, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct InvalidHighLimit;

/// the current status of the counter.
#[derive(Copy, Clone, Debug, Default, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum ZeroMode {
    /// pulse counter decreases from positive to 0.
    #[default]
    PosZero  = 0,
    /// pulse counter increases from negative to 0
    NegZero  = 1,
    /// pulse counter is negative (not implemented?)
    Negative = 2,
    /// pulse counter is positive (not implemented?)
    Positive = 3,
}

impl From<u8> for ZeroMode {
    fn from(value: u8) -> Self {
        match value {
            0 => Self::PosZero,
            1 => Self::NegZero,
            2 => Self::Negative,
            3 => Self::Positive,
            _ => unreachable!(), // TODO: is this good enough?  should we use some default?
        }
    }
}

/// Events that can occur in a pulse counter unit.
#[derive(Copy, Clone, Debug, Default)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Events {
    /// Set when the pulse counter reaches the low limit.
    pub low_limit: bool,
    /// Set when the pulse counter reaches the high limit.
    pub high_limit: bool,
    /// Set when the pulse counter crosses threshold 0.
    pub threshold0: bool,
    /// Set when the pulse counter crosses threshold 1.
    pub threshold1: bool,
    /// Set when the pulse counter reaches zero.
    pub zero: bool,
}

/// Represents a pulse counter unit.
#[non_exhaustive]
pub struct Unit<'d, const NUM: usize> {
    /// The counter for PCNT unit.
    pub counter: Counter<'d, NUM>,
    /// The first channel in PCNT unit.
    pub channel0: Channel<'d, NUM, 0>,
    /// The second channel in PCNT unit.
    pub channel1: Channel<'d, NUM, 1>,

    _guard: GenericPeripheralGuard<{ crate::system::Peripheral::Pcnt as u8 }>,
}

impl<const NUM: usize> Unit<'_, NUM> {
    /// return a new Unit
    pub(super) fn new() -> Self {
        let guard = GenericPeripheralGuard::new();

        Self {
            counter: Counter::new(),
            channel0: Channel::new(),
            channel1: Channel::new(),
            _guard: guard,
        }
    }

    /// Configures a lower limit to the count value.
    ///
    /// When the count drops to this value:
    /// - A low limit interrupt is triggered.
    /// - The count is reset to 0.
    ///
    /// If None is specified, then no interrupt is triggered and
    /// the count wraps around after [i16::MIN].
    ///
    /// Note: The specified value must be negative.
    pub fn set_low_limit(&self, value: Option<i16>) -> Result<(), InvalidLowLimit> {
        let pcnt = PCNT::regs();
        let unit = pcnt.unit(NUM);

        if let Some(value) = value {
            // low limit must be >= or the limit is -32768 and when that's
            // hit the event status claims it was the high limit.
            // tested on an esp32s3
            if !value.is_negative() {
                return Err(InvalidLowLimit);
            } else {
                unit.conf2()
                    .modify(|_, w| unsafe { w.cnt_l_lim().bits(value as u16) });
                unit.conf0().modify(|_, w| w.thr_l_lim_en().set_bit());
            }
        } else {
            unit.conf0().modify(|_, w| w.thr_l_lim_en().clear_bit());
        }
        Ok(())
    }

    /// Configures a high limit to the count value.
    ///
    /// When the count rises to this value:
    /// - A high limit interrupt is triggered.
    /// - The count is reset to 0.
    ///
    /// If None is specified, then no interrupt is triggered and
    /// the count wraps around after [i16::MAX].
    ///
    /// Note: The specified value must be positive.
    pub fn set_high_limit(&self, value: Option<i16>) -> Result<(), InvalidHighLimit> {
        let pcnt = PCNT::regs();
        let unit = pcnt.unit(NUM);

        if let Some(value) = value {
            if !value.is_positive() {
                return Err(InvalidHighLimit);
            } else {
                unit.conf2()
                    .modify(|_, w| unsafe { w.cnt_h_lim().bits(value as u16) });
                unit.conf0().modify(|_, w| w.thr_h_lim_en().set_bit());
            }
        } else {
            unit.conf0().modify(|_, w| w.thr_h_lim_en().clear_bit());
        }
        Ok(())
    }

    /// Configures a threshold value to trigger an interrupt.
    ///
    /// When the count equals this value a threshold0 interrupt is triggered.
    /// If None is specified, then no interrupt is triggered.
    pub fn set_threshold0(&self, value: Option<i16>) {
        let pcnt = PCNT::regs();
        let unit = pcnt.unit(NUM);

        if let Some(value) = value {
            unit.conf1()
                .modify(|_, w| unsafe { w.cnt_thres0().bits(value as u16) });
            unit.conf0().modify(|_, w| w.thr_thres0_en().set_bit());
        } else {
            unit.conf0().modify(|_, w| w.thr_thres0_en().clear_bit());
        }
    }

    /// Configures a threshold value to trigger an interrupt.
    ///
    /// When the count equals this value a threshold1 interrupt is triggered.
    /// If None is specified, then no interrupt is triggered.
    pub fn set_threshold1(&self, value: Option<i16>) {
        let pcnt = PCNT::regs();
        let unit = pcnt.unit(NUM);

        if let Some(value) = value {
            unit.conf1()
                .modify(|_, w| unsafe { w.cnt_thres1().bits(value as u16) });
            unit.conf0().modify(|_, w| w.thr_thres1_en().set_bit());
        } else {
            unit.conf0().modify(|_, w| w.thr_thres1_en().clear_bit());
        }
    }

    /// Configures the glitch filter hardware of the unit.
    ///
    /// `threshold` is the minimum number of APB_CLK cycles for a pulse to be
    /// considered valid. If it is None, the filter is disabled.
    ///
    /// Note: This maximum possible threshold is 1023.
    pub fn set_filter(&self, threshold: Option<u16>) -> Result<(), InvalidFilterThreshold> {
        let pcnt = PCNT::regs();
        let unit = pcnt.unit(NUM);

        match threshold {
            None => {
                unit.conf0().modify(|_, w| w.filter_en().clear_bit());
            }
            Some(threshold) => {
                if threshold > 1023 {
                    return Err(InvalidFilterThreshold);
                }
                unit.conf0().modify(|_, w| unsafe {
                    w.filter_thres().bits(threshold).filter_en().set_bit()
                });
            }
        }
        Ok(())
    }

    /// Resets the counter value to zero.
    pub fn clear(&self) {
        let pcnt = PCNT::regs();
        critical_section::with(|_cs| {
            pcnt.ctrl().modify(|_, w| w.cnt_rst_u(NUM as u8).set_bit());
            // TODO: does this need a delay? (liebman / Jan 2 2023)
            pcnt.ctrl()
                .modify(|_, w| w.cnt_rst_u(NUM as u8).clear_bit());
        });
    }

    /// Pause the counter
    pub fn pause(&self) {
        let pcnt = PCNT::regs();
        critical_section::with(|_cs| {
            pcnt.ctrl()
                .modify(|_, w| w.cnt_pause_u(NUM as u8).set_bit());
        });
    }

    /// Resume the counter
    pub fn resume(&self) {
        let pcnt = PCNT::regs();
        critical_section::with(|_cs| {
            pcnt.ctrl()
                .modify(|_, w| w.cnt_pause_u(NUM as u8).clear_bit());
        });
    }

    /// Get the latest events for this unit.
    pub fn events(&self) -> Events {
        let pcnt = PCNT::regs();
        let status = pcnt.u_status(NUM).read();

        Events {
            low_limit: status.l_lim().bit(),
            high_limit: status.h_lim().bit(),
            threshold0: status.thres0().bit(),
            threshold1: status.thres1().bit(),
            zero: status.zero().bit(),
        }
    }

    /// Get the mode of the last zero crossing
    pub fn zero_mode(&self) -> ZeroMode {
        let pcnt = PCNT::regs();
        pcnt.u_status(NUM).read().zero_mode().bits().into()
    }

    /// Enable interrupts for this unit.
    pub fn listen(&self) {
        let pcnt = PCNT::regs();
        critical_section::with(|_cs| {
            pcnt.int_ena()
                .modify(|_, w| w.cnt_thr_event_u(NUM as u8).set_bit());
        });
    }

    /// Disable interrupts for this unit.
    pub fn unlisten(&self, _cs: CriticalSection<'_>) {
        let pcnt = PCNT::regs();
        critical_section::with(|_cs| {
            pcnt.int_ena()
                .modify(|_, w| w.cnt_thr_event_u(NUM as u8).clear_bit());
        });
    }

    /// Returns true if an interrupt is active for this unit.
    pub fn interrupt_is_set(&self) -> bool {
        let pcnt = PCNT::regs();
        pcnt.int_raw().read().cnt_thr_event_u(NUM as u8).bit()
    }

    /// Clear the interrupt bit for this unit.
    pub fn reset_interrupt(&self) {
        let pcnt = PCNT::regs();
        critical_section::with(|_cs| {
            pcnt.int_clr()
                .write(|w| w.cnt_thr_event_u(NUM as u8).set_bit());
        });
    }

    /// Get the current counter value.
    pub fn value(&self) -> i16 {
        self.counter.get()
    }
}

impl<const NUM: usize> Drop for Unit<'_, NUM> {
    fn drop(&mut self) {
        // This is here to prevent the destructuring of Unit.
    }
}

// The entire Unit is Send but the individual channels are not.
unsafe impl<const NUM: usize> Send for Unit<'_, NUM> {}

/// Represents the counter within a pulse counter unit.
#[derive(Clone)]
pub struct Counter<'d, const NUM: usize> {
    _phantom: PhantomData<&'d ()>,
}

impl<const NUM: usize> Counter<'_, NUM> {
    fn new() -> Self {
        Self {
            _phantom: PhantomData,
        }
    }

    /// Get the current counter value.
    pub fn get(&self) -> i16 {
        let pcnt = PCNT::regs();
        pcnt.u_cnt(NUM).read().cnt().bits() as i16
    }
}