esp_hal/pcnt/mod.rs
1//! # Pulse Counter (PCNT)
2//!
3//! ## Overview
4//! The PCNT module is designed to count the number of rising
5//! and/or falling edges of input signals. They may contain multiple pulse
6//! counter units in the module. Each unit is in effect an independent counter
7//! with multiple channels, where each channel can increment/decrement the
8//! counter on a rising/falling edge. Furthermore, each channel can be
9//! configured separately.
10//!
11//! It consists of two main modules:
12//! * [channel]
13//! * [unit]
14//!
15//! ## Examples
16//! ### Decoding a quadrature encoder
17//!
18//! ```rust, no_run
19#![doc = crate::before_snippet!()]
20//! # use esp_hal::gpio::{Input, InputConfig, Pull};
21//! # use esp_hal::interrupt::Priority;
22//! # use esp_hal::pcnt::{channel, unit, Pcnt};
23//! # use core::{sync::atomic::Ordering, cell::RefCell, cmp::min};
24//! # use critical_section::Mutex;
25//! # use portable_atomic::AtomicI32;
26//!
27//! static UNIT0: Mutex<RefCell<Option<unit::Unit<'static, 1>>>> =
28//! Mutex::new(RefCell::new(None)); static VALUE: AtomicI32 = AtomicI32::new(0);
29//!
30//! // Initialize Pulse Counter (PCNT) unit with limits and filter settings
31//! let mut pcnt = Pcnt::new(peripherals.PCNT);
32//! pcnt.set_interrupt_handler(interrupt_handler);
33//! let u0 = pcnt.unit1;
34//! u0.set_low_limit(Some(-100))?;
35//! u0.set_high_limit(Some(100))?;
36//! u0.set_filter(Some(min(10u16 * 80, 1023u16)))?;
37//! u0.clear();
38//!
39//! // Set up channels with control and edge signals
40//! let ch0 = &u0.channel0;
41//! let config = InputConfig::default().with_pull(Pull::Up);
42//! let pin_a = Input::new(peripherals.GPIO4, config);
43//! let pin_b = Input::new(peripherals.GPIO5, config);
44//! let (input_a, _) = pin_a.split();
45//! let (input_b, _) = pin_b.split();
46//! ch0.set_ctrl_signal(input_a.clone());
47//! ch0.set_edge_signal(input_b.clone());
48//! ch0.set_ctrl_mode(channel::CtrlMode::Reverse, channel::CtrlMode::Keep);
49//! ch0.set_input_mode(channel::EdgeMode::Increment,
50//! channel::EdgeMode::Decrement);
51//!
52//! let ch1 = &u0.channel1;
53//! ch1.set_ctrl_signal(input_b);
54//! ch1.set_edge_signal(input_a);
55//! ch1.set_ctrl_mode(channel::CtrlMode::Reverse, channel::CtrlMode::Keep);
56//! ch1.set_input_mode(channel::EdgeMode::Decrement,
57//! channel::EdgeMode::Increment);
58//!
59//! // Enable interrupts and resume pulse counter unit
60//! u0.listen();
61//! u0.resume();
62//! let counter = u0.counter.clone();
63//!
64//! critical_section::with(|cs| UNIT0.borrow_ref_mut(cs).replace(u0));
65//!
66//! // Monitor counter value and print updates
67//! let mut last_value: i32 = 0;
68//! loop {
69//! let value: i32 = counter.get() as i32 + VALUE.load(Ordering::SeqCst);
70//! if value != last_value {
71//! last_value = value;
72//! }
73//! }
74//!
75//! #[handler(priority = Priority::Priority2)]
76//! fn interrupt_handler() {
77//! critical_section::with(|cs| {
78//! let mut u0 = UNIT0.borrow_ref_mut(cs);
79//! if let Some(u0) = u0.as_mut() {
80//! if u0.interrupt_is_set() {
81//! let events = u0.events();
82//! if events.high_limit {
83//! VALUE.fetch_add(100, Ordering::SeqCst);
84//! } else if events.low_limit {
85//! VALUE.fetch_add(-100, Ordering::SeqCst);
86//! }
87//! u0.reset_interrupt();
88//! }
89//! }
90//! });
91//! }
92//! # }
93//! ```
94//!
95//! [channel]: channel/index.html
96//! [unit]: unit/index.html
97
98use self::unit::Unit;
99use crate::{
100 interrupt::{self, InterruptHandler},
101 peripheral::{Peripheral, PeripheralRef},
102 peripherals::{Interrupt, PCNT},
103 system::GenericPeripheralGuard,
104};
105
106pub mod channel;
107pub mod unit;
108
109/// Pulse Counter (PCNT) peripheral driver.
110pub struct Pcnt<'d> {
111 _instance: PeripheralRef<'d, PCNT>,
112
113 /// Unit 0
114 pub unit0: Unit<'d, 0>,
115 /// Unit 1
116 pub unit1: Unit<'d, 1>,
117 /// Unit 2
118 pub unit2: Unit<'d, 2>,
119 /// Unit 3
120 pub unit3: Unit<'d, 3>,
121 #[cfg(esp32)]
122 /// Unit 4
123 pub unit4: Unit<'d, 4>,
124 #[cfg(esp32)]
125 /// Unit 5
126 pub unit5: Unit<'d, 5>,
127 #[cfg(esp32)]
128 /// Unit 6
129 pub unit6: Unit<'d, 6>,
130 #[cfg(esp32)]
131 /// Unit 7
132 pub unit7: Unit<'d, 7>,
133
134 _guard: GenericPeripheralGuard<{ crate::system::Peripheral::Pcnt as u8 }>,
135}
136
137impl<'d> Pcnt<'d> {
138 /// Return a new PCNT
139 pub fn new(_instance: impl Peripheral<P = PCNT> + 'd) -> Self {
140 crate::into_ref!(_instance);
141
142 let guard = GenericPeripheralGuard::new();
143 let pcnt = PCNT::regs();
144
145 // disable filter, all events, and channel settings
146 for unit in pcnt.unit_iter() {
147 unit.conf0().write(|w| unsafe {
148 // All bits are accounted for in the TRM.
149 w.bits(0)
150 });
151 }
152
153 // Remove reset bit from units.
154 pcnt.ctrl().modify(|_, w| {
155 #[cfg(not(esp32))]
156 let unit_count = 4;
157 #[cfg(esp32)]
158 let unit_count = 8;
159
160 for i in 0..unit_count {
161 w.cnt_rst_u(i).clear_bit();
162 }
163
164 w.clk_en().set_bit()
165 });
166
167 Pcnt {
168 _instance,
169 unit0: Unit::new(),
170 unit1: Unit::new(),
171 unit2: Unit::new(),
172 unit3: Unit::new(),
173 #[cfg(esp32)]
174 unit4: Unit::new(),
175 #[cfg(esp32)]
176 unit5: Unit::new(),
177 #[cfg(esp32)]
178 unit6: Unit::new(),
179 #[cfg(esp32)]
180 unit7: Unit::new(),
181 _guard: guard,
182 }
183 }
184
185 /// Set the interrupt handler for the PCNT peripheral.
186 ///
187 /// Note that this will replace any previously registered interrupt
188 /// handlers.
189 #[instability::unstable]
190 pub fn set_interrupt_handler(&mut self, handler: InterruptHandler) {
191 for core in crate::system::Cpu::other() {
192 crate::interrupt::disable(core, Interrupt::PCNT);
193 }
194 unsafe { interrupt::bind_interrupt(Interrupt::PCNT, handler.handler()) };
195 unwrap!(interrupt::enable(Interrupt::PCNT, handler.priority()));
196 }
197}
198
199impl crate::private::Sealed for Pcnt<'_> {}
200
201#[instability::unstable]
202impl crate::interrupt::InterruptConfigurable for Pcnt<'_> {
203 fn set_interrupt_handler(&mut self, handler: InterruptHandler) {
204 self.set_interrupt_handler(handler);
205 }
206}