Skip to content

Commit ac206af

Browse files
authoredJan 17, 2023
pulse counter implementation (#328)
* start of pulse counter implementation * implement interrupts implement pcnt for esp32, esp32s2, and esp32s3 * implement pcnt for esp32s2 * fix esp32 PCNT signal names * update PCNT register/fields for cleaned up PAC * implement events/get_events (choosing what events interrupt) * added pcnt example: simple encoder configuration * restrict pcnt::channel::Channel::new() to super * PcntPin -> PcntSignal added range checks for thresholds and limits * PcntSource is a better name I think * handle error for PCNT Unit configure() in example * update pac versions for status register change * cargo fmt * cargo fmt (examples) * PcntSource now only stores the source id. add a critical section to protect the ctrl & isr_en registers * cargo fmt
1 parent a542735 commit ac206af

File tree

15 files changed

+1177
-36
lines changed

15 files changed

+1177
-36
lines changed
 

‎esp-hal-common/Cargo.toml

+4-4
Original file line numberDiff line numberDiff line change
@@ -51,11 +51,11 @@ ufmt-write = { version = "0.1.0", optional = true }
5151
# Each supported device MUST have its PAC included below along with a
5252
# corresponding feature. We rename the PAC packages because we cannot
5353
# have dependencies and features with the same names.
54-
esp32 = { version = "0.18.0", features = ["critical-section"], optional = true }
55-
esp32c2 = { version = "0.5.1", features = ["critical-section"], optional = true }
54+
esp32 = { version = "0.19.0", features = ["critical-section"], optional = true }
55+
esp32c2 = { version = "0.6.0", features = ["critical-section"], optional = true }
5656
esp32c3 = { version = "0.9.0", features = ["critical-section"], optional = true }
57-
esp32s2 = { version = "0.8.0", features = ["critical-section"], optional = true }
58-
esp32s3 = { version = "0.12.0", features = ["critical-section"], optional = true }
57+
esp32s2 = { version = "0.9.0", features = ["critical-section"], optional = true }
58+
esp32s3 = { version = "0.13.0", features = ["critical-section"], optional = true }
5959

6060
[features]
6161
esp32 = ["esp32/rt" , "xtensa", "xtensa-lx/esp32", "xtensa-lx-rt/esp32", "lock_api"]

‎esp-hal-common/src/gpio/esp32.rs

+32-32
Original file line numberDiff line numberDiff line change
@@ -116,26 +116,26 @@ pub enum InputSignal {
116116
PWM0_F2 = 36,
117117
GPIO_BT_ACTIVE = 37,
118118
GPIO_BT_PRIORITY = 38,
119-
PCNT_SIG_CH0_0 = 39,
120-
PCNT_SIG_CH1_0 = 40,
121-
PCNT_CTRL_CH0_0 = 41,
122-
PCNT_CTRL_CH1_0 = 42,
123-
PCNT_SIG_CH0_1 = 43,
124-
PCNT_SIG_CH1_1 = 44,
125-
PCNT_CTRL_CH0_1 = 45,
126-
PCNT_CTRL_CH1_1 = 46,
127-
PCNT_SIG_CH0_2 = 47,
128-
PCNT_SIG_CH1_2 = 48,
129-
PCNT_CTRL_CH0_2 = 49,
130-
PCNT_CTRL_CH1_2 = 50,
131-
PCNT_SIG_CH0_3 = 51,
132-
PCNT_SIG_CH1_3 = 52,
133-
PCNT_CTRL_CH0_3 = 53,
134-
PCNT_CTRL_CH1_3 = 54,
135-
PCNT_SIG_CH0_4 = 55,
136-
PCNT_SIG_CH1_4 = 56,
137-
PCNT_CTRL_CH0_4 = 57,
138-
PCNT_CTRL_CH1_4 = 58,
119+
PCNT0_SIG_CH0 = 39,
120+
PCNT0_SIG_CH1 = 40,
121+
PCNT0_CTRL_CH0 = 41,
122+
PCNT0_CTRL_CH1 = 42,
123+
PCNT1_SIG_CH0 = 43,
124+
PCNT1_SIG_CH1 = 44,
125+
PCNT1_CTRL_CH0 = 45,
126+
PCNT1_CTRL_CH1 = 46,
127+
PCNT2_SIG_CH0 = 47,
128+
PCNT2_SIG_CH1 = 48,
129+
PCNT2_CTRL_CH0 = 49,
130+
PCNT2_CTRL_CH1 = 50,
131+
PCNT3_SIG_CH0 = 51,
132+
PCNT3_SIG_CH1 = 52,
133+
PCNT3_CTRL_CH0 = 53,
134+
PCNT3_CTRL_CH1 = 54,
135+
PCNT4_SIG_CH0 = 55,
136+
PCNT4_SIG_CH1 = 56,
137+
PCNT4_CTRL_CH0 = 57,
138+
PCNT4_CTRL_CH1 = 58,
139139
HSPICS1 = 61,
140140
HSPICS2 = 62,
141141
VSPICLK = 63,
@@ -146,18 +146,18 @@ pub enum InputSignal {
146146
VSPICS0 = 68,
147147
VSPICS1 = 69,
148148
VSPICS2 = 70,
149-
PCNT_SIG_CH0_5 = 71,
150-
PCNT_SIG_CH1_5 = 72,
151-
PCNT_CTRL_CH0_5 = 73,
152-
PCNT_CTRL_CH1_5 = 74,
153-
PCNT_SIG_CH0_6 = 75,
154-
PCNT_SIG_CH1_6 = 76,
155-
PCNT_CTRL_CH0_6 = 77,
156-
PCNT_CTRL_CH1_6 = 78,
157-
PCNT_SIG_CH0_7 = 79,
158-
PCNT_SIG_CH1_7 = 80,
159-
PCNT_CTRL_CH0_7 = 81,
160-
PCNT_CTRL_CH1_7 = 82,
149+
PCNT5_SIG_CH0 = 71,
150+
PCNT5_SIG_CH1 = 72,
151+
PCNT5_CTRL_CH0 = 73,
152+
PCNT5_CTRL_CH1 = 74,
153+
PCNT6_SIG_CH0 = 75,
154+
PCNT6_SIG_CH1 = 76,
155+
PCNT6_CTRL_CH0 = 77,
156+
PCNT6_CTRL_CH1 = 78,
157+
PCNT7_SIG_CH0 = 79,
158+
PCNT7_SIG_CH1 = 80,
159+
PCNT7_CTRL_CH0 = 81,
160+
PCNT7_CTRL_CH1 = 82,
161161
RMT_SIG_0 = 83,
162162
RMT_SIG_1 = 84,
163163
RMT_SIG_2 = 85,

‎esp-hal-common/src/gpio/esp32s2.rs

+16
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,22 @@ pub enum InputSignal {
102102
I2S0I_WS = 28,
103103
I2CEXT0_SCL = 29,
104104
I2CEXT0_SDA = 30,
105+
PCNT0_SIG_CH0 = 39,
106+
PCNT0_SIG_CH1 = 40,
107+
PCNT0_CTRL_CH0 = 41,
108+
PCNT0_CTRL_CH1 = 42,
109+
PCNT1_SIG_CH0 = 43,
110+
PCNT1_SIG_CH1 = 44,
111+
PCNT1_CTRL_CH0 = 45,
112+
PCNT1_CTRL_CH1 = 46,
113+
PCNT2_SIG_CH0 = 47,
114+
PCNT2_SIG_CH1 = 48,
115+
PCNT2_CTRL_CH0 = 49,
116+
PCNT2_CTRL_CH1 = 50,
117+
PCNT3_SIG_CH0 = 51,
118+
PCNT3_SIG_CH1 = 52,
119+
PCNT3_CTRL_CH0 = 53,
120+
PCNT3_CTRL_CH1 = 54,
105121
USB_OTG_IDDIG = 64,
106122
USB_OTG_AVALID = 65,
107123
USB_SRP_BVALID = 66,

‎esp-hal-common/src/gpio/esp32s3.rs

+16
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,22 @@ pub enum InputSignal {
6363
I2S1I_SD = 30,
6464
I2S1I_BCK = 31,
6565
I2S1I_WS = 32,
66+
PCNT0_SIG_CH0 = 33,
67+
PCNT0_SIG_CH1 = 34,
68+
PCNT0_CTRL_CH0 = 35,
69+
PCNT0_CTRL_CH1 = 36,
70+
PCNT1_SIG_CH0 = 37,
71+
PCNT1_SIG_CH1 = 38,
72+
PCNT1_CTRL_CH0 = 39,
73+
PCNT1_CTRL_CH1 = 40,
74+
PCNT2_SIG_CH0 = 41,
75+
PCNT2_SIG_CH1 = 42,
76+
PCNT2_CTRL_CH0 = 43,
77+
PCNT2_CTRL_CH1 = 44,
78+
PCNT3_SIG_CH0 = 45,
79+
PCNT3_SIG_CH1 = 46,
80+
PCNT3_CTRL_CH0 = 47,
81+
PCNT3_CTRL_CH1 = 48,
6682
I2S0I_SD1 = 51,
6783
I2S0I_SD2 = 52,
6884
I2S0I_SD3 = 53,

‎esp-hal-common/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ pub mod ledc;
6060
pub mod mcpwm;
6161
#[cfg(usb_otg)]
6262
pub mod otg_fs;
63+
#[cfg(any(esp32, esp32s2, esp32s3))]
64+
pub mod pcnt;
6365
pub mod peripheral;
6466
pub mod prelude;
6567
#[cfg(rmt)]

‎esp-hal-common/src/pcnt/channel.rs

+243
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,243 @@
1+
use super::unit;
2+
use crate::{
3+
gpio::{
4+
types::{InputSignal, ONE_INPUT, ZERO_INPUT},
5+
InputPin,
6+
},
7+
peripheral::Peripheral,
8+
peripherals::GPIO,
9+
};
10+
11+
/// Channel number
12+
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
13+
pub enum Number {
14+
Channel0,
15+
Channel1,
16+
}
17+
18+
/// PCNT channel action on signal edge
19+
#[derive(Debug, Copy, Clone, Default)]
20+
pub enum EdgeMode {
21+
/// Hold current count value
22+
Hold = 0,
23+
/// Increase count value
24+
#[default]
25+
Increment = 1,
26+
/// Decrease count value
27+
Decrement = 2,
28+
}
29+
30+
/// PCNT channel action on control level
31+
#[derive(Debug, Copy, Clone, Default)]
32+
pub enum CtrlMode {
33+
/// Keep current count mode
34+
Keep = 0,
35+
/// Invert current count mode (increase -> decrease, decrease -> increase)
36+
#[default]
37+
Reverse = 1,
38+
/// Hold current count value
39+
Disable = 2,
40+
}
41+
42+
/// Pulse Counter configuration for a single channel
43+
#[derive(Debug, Copy, Clone, Default)]
44+
pub struct Config {
45+
/// PCNT low control mode
46+
pub lctrl_mode: CtrlMode,
47+
/// PCNT high control mode
48+
pub hctrl_mode: CtrlMode,
49+
/// PCNT signal positive edge count mode
50+
pub pos_edge: EdgeMode,
51+
/// PCNT signal negative edge count mode
52+
pub neg_edge: EdgeMode,
53+
pub invert_ctrl: bool,
54+
pub invert_sig: bool,
55+
}
56+
57+
/// PcntPin can be always high, always low, or an actual pin
58+
#[derive(Clone, Copy)]
59+
pub struct PcntSource {
60+
source: u8,
61+
}
62+
63+
impl PcntSource {
64+
pub fn from_pin<'a, P: InputPin>(pin: impl Peripheral<P = P> + 'a) -> Self {
65+
crate::into_ref!(pin);
66+
Self {
67+
source: pin.number(),
68+
}
69+
}
70+
pub fn always_high() -> Self {
71+
Self { source: ONE_INPUT }
72+
}
73+
pub fn always_low() -> Self {
74+
Self { source: ZERO_INPUT }
75+
}
76+
}
77+
78+
pub struct Channel {
79+
unit: unit::Number,
80+
channel: Number,
81+
}
82+
83+
impl Channel {
84+
/// return a new Channel
85+
pub(super) fn new(unit: unit::Number, channel: Number) -> Self {
86+
Self { unit, channel }
87+
}
88+
89+
/// Configure the channel
90+
pub fn configure(&mut self, ctrl_signal: PcntSource, edge_signal: PcntSource, config: Config) {
91+
let pcnt = unsafe { &*crate::peripherals::PCNT::ptr() };
92+
let conf0 = match self.unit {
93+
unit::Number::Unit0 => &pcnt.u0_conf0,
94+
unit::Number::Unit1 => &pcnt.u1_conf0,
95+
unit::Number::Unit2 => &pcnt.u2_conf0,
96+
unit::Number::Unit3 => &pcnt.u3_conf0,
97+
#[cfg(esp32)]
98+
unit::Number::Unit4 => &pcnt.u4_conf0,
99+
#[cfg(esp32)]
100+
unit::Number::Unit5 => &pcnt.u5_conf0,
101+
#[cfg(esp32)]
102+
unit::Number::Unit6 => &pcnt.u6_conf0,
103+
#[cfg(esp32)]
104+
unit::Number::Unit7 => &pcnt.u7_conf0,
105+
};
106+
match self.channel {
107+
Number::Channel0 => {
108+
conf0.modify(|_, w| unsafe {
109+
w.ch0_hctrl_mode()
110+
.bits(config.hctrl_mode as u8)
111+
.ch0_lctrl_mode()
112+
.bits(config.lctrl_mode as u8)
113+
.ch0_neg_mode()
114+
.bits(config.neg_edge as u8)
115+
.ch0_pos_mode()
116+
.bits(config.pos_edge as u8)
117+
});
118+
}
119+
Number::Channel1 => {
120+
conf0.modify(|_, w| unsafe {
121+
w.ch1_hctrl_mode()
122+
.bits(config.hctrl_mode as u8)
123+
.ch1_lctrl_mode()
124+
.bits(config.lctrl_mode as u8)
125+
.ch1_neg_mode()
126+
.bits(config.neg_edge as u8)
127+
.ch1_pos_mode()
128+
.bits(config.pos_edge as u8)
129+
});
130+
}
131+
}
132+
self.set_ctrl_signal(ctrl_signal, config.invert_ctrl);
133+
self.set_edge_signal(edge_signal, config.invert_sig);
134+
}
135+
136+
/// Set the control signal (pin/high/low) for this channel
137+
pub fn set_ctrl_signal(&self, source: PcntSource, invert: bool) -> &Self {
138+
let signal = match self.unit {
139+
unit::Number::Unit0 => match self.channel {
140+
Number::Channel0 => InputSignal::PCNT0_CTRL_CH0,
141+
Number::Channel1 => InputSignal::PCNT0_CTRL_CH1,
142+
},
143+
unit::Number::Unit1 => match self.channel {
144+
Number::Channel0 => InputSignal::PCNT1_CTRL_CH0,
145+
Number::Channel1 => InputSignal::PCNT1_CTRL_CH1,
146+
},
147+
unit::Number::Unit2 => match self.channel {
148+
Number::Channel0 => InputSignal::PCNT2_CTRL_CH0,
149+
Number::Channel1 => InputSignal::PCNT2_CTRL_CH1,
150+
},
151+
unit::Number::Unit3 => match self.channel {
152+
Number::Channel0 => InputSignal::PCNT3_CTRL_CH0,
153+
Number::Channel1 => InputSignal::PCNT3_CTRL_CH1,
154+
},
155+
#[cfg(esp32)]
156+
unit::Number::Unit4 => match self.channel {
157+
Number::Channel0 => InputSignal::PCNT4_CTRL_CH0,
158+
Number::Channel1 => InputSignal::PCNT4_CTRL_CH1,
159+
},
160+
#[cfg(esp32)]
161+
unit::Number::Unit5 => match self.channel {
162+
Number::Channel0 => InputSignal::PCNT5_CTRL_CH0,
163+
Number::Channel1 => InputSignal::PCNT5_CTRL_CH1,
164+
},
165+
#[cfg(esp32)]
166+
unit::Number::Unit6 => match self.channel {
167+
Number::Channel0 => InputSignal::PCNT6_CTRL_CH0,
168+
Number::Channel1 => InputSignal::PCNT6_CTRL_CH1,
169+
},
170+
#[cfg(esp32)]
171+
unit::Number::Unit7 => match self.channel {
172+
Number::Channel0 => InputSignal::PCNT7_CTRL_CH0,
173+
Number::Channel1 => InputSignal::PCNT7_CTRL_CH1,
174+
},
175+
};
176+
177+
if (signal as usize) <= crate::types::INPUT_SIGNAL_MAX as usize {
178+
unsafe { &*GPIO::PTR }.func_in_sel_cfg[signal as usize].modify(|_, w| unsafe {
179+
w.sel()
180+
.set_bit()
181+
.in_inv_sel()
182+
.bit(invert)
183+
.in_sel()
184+
.bits(source.source)
185+
});
186+
}
187+
self
188+
}
189+
190+
/// Set the edge signal (pin/high/low) for this channel
191+
pub fn set_edge_signal(&self, source: PcntSource, invert: bool) -> &Self {
192+
let signal = match self.unit {
193+
unit::Number::Unit0 => match self.channel {
194+
Number::Channel0 => InputSignal::PCNT0_SIG_CH0,
195+
Number::Channel1 => InputSignal::PCNT0_SIG_CH1,
196+
},
197+
unit::Number::Unit1 => match self.channel {
198+
Number::Channel0 => InputSignal::PCNT1_SIG_CH0,
199+
Number::Channel1 => InputSignal::PCNT1_SIG_CH1,
200+
},
201+
unit::Number::Unit2 => match self.channel {
202+
Number::Channel0 => InputSignal::PCNT2_SIG_CH0,
203+
Number::Channel1 => InputSignal::PCNT2_SIG_CH1,
204+
},
205+
unit::Number::Unit3 => match self.channel {
206+
Number::Channel0 => InputSignal::PCNT3_SIG_CH0,
207+
Number::Channel1 => InputSignal::PCNT3_SIG_CH1,
208+
},
209+
#[cfg(esp32)]
210+
unit::Number::Unit4 => match self.channel {
211+
Number::Channel0 => InputSignal::PCNT4_SIG_CH0,
212+
Number::Channel1 => InputSignal::PCNT4_SIG_CH1,
213+
},
214+
#[cfg(esp32)]
215+
unit::Number::Unit5 => match self.channel {
216+
Number::Channel0 => InputSignal::PCNT5_SIG_CH0,
217+
Number::Channel1 => InputSignal::PCNT5_SIG_CH1,
218+
},
219+
#[cfg(esp32)]
220+
unit::Number::Unit6 => match self.channel {
221+
Number::Channel0 => InputSignal::PCNT6_SIG_CH0,
222+
Number::Channel1 => InputSignal::PCNT6_SIG_CH1,
223+
},
224+
#[cfg(esp32)]
225+
unit::Number::Unit7 => match self.channel {
226+
Number::Channel0 => InputSignal::PCNT7_SIG_CH0,
227+
Number::Channel1 => InputSignal::PCNT7_SIG_CH1,
228+
},
229+
};
230+
231+
if (signal as usize) <= crate::types::INPUT_SIGNAL_MAX as usize {
232+
unsafe { &*GPIO::PTR }.func_in_sel_cfg[signal as usize].modify(|_, w| unsafe {
233+
w.sel()
234+
.set_bit()
235+
.in_inv_sel()
236+
.bit(invert)
237+
.in_sel()
238+
.bits(source.source)
239+
});
240+
}
241+
self
242+
}
243+
}

‎esp-hal-common/src/pcnt/mod.rs

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
use self::unit::Unit;
2+
use crate::{
3+
peripheral::{Peripheral, PeripheralRef},
4+
system::PeripheralClockControl,
5+
};
6+
7+
pub mod channel;
8+
pub mod unit;
9+
10+
pub struct PCNT<'d> {
11+
_instance: PeripheralRef<'d, crate::peripherals::PCNT>,
12+
}
13+
14+
impl<'d> PCNT<'d> {
15+
/// Return a new PCNT
16+
pub fn new(
17+
_instance: impl Peripheral<P = crate::peripherals::PCNT> + 'd,
18+
peripheral_clock_control: &mut PeripheralClockControl,
19+
) -> Self {
20+
crate::into_ref!(_instance);
21+
// Enable the PCNT peripherals clock in the system peripheral
22+
peripheral_clock_control.enable(crate::system::Peripheral::Pcnt);
23+
PCNT { _instance }
24+
}
25+
26+
/// Return a unit
27+
pub fn get_unit(&self, number: unit::Number) -> Unit {
28+
Unit::new(number)
29+
}
30+
}

‎esp-hal-common/src/pcnt/unit.rs

+392
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,392 @@
1+
use critical_section::CriticalSection;
2+
3+
use super::channel;
4+
5+
/// Unit number
6+
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
7+
pub enum Number {
8+
Unit0,
9+
Unit1,
10+
Unit2,
11+
Unit3,
12+
#[cfg(esp32)]
13+
Unit4,
14+
#[cfg(esp32)]
15+
Unit5,
16+
#[cfg(esp32)]
17+
Unit6,
18+
#[cfg(esp32)]
19+
Unit7,
20+
}
21+
22+
/// Unit errors
23+
#[derive(Debug)]
24+
pub enum Error {
25+
/// Invalid filter threshold value
26+
InvalidFilterThresh,
27+
/// Invalid low limit - must be < 0
28+
InvalidLowLimit,
29+
/// Invalid high limit - must be > 0
30+
InvalidHighLimit,
31+
}
32+
33+
/// the current status of the counter.
34+
#[derive(Copy, Clone, Debug, Default)]
35+
pub enum ZeroMode {
36+
/// pulse counter decreases from positive to 0.
37+
#[default]
38+
PosZero = 0,
39+
/// pulse counter increases from negative to 0
40+
NegZero = 1,
41+
/// pulse counter is negative (not implemented?)
42+
Negitive = 2,
43+
/// pulse counter is positive (not implemented?)
44+
Positive = 3,
45+
}
46+
47+
impl From<u8> for ZeroMode {
48+
fn from(value: u8) -> Self {
49+
match value {
50+
0 => Self::PosZero,
51+
1 => Self::NegZero,
52+
2 => Self::Negitive,
53+
3 => Self::Positive,
54+
_ => unreachable!(), // TODO: is this good enoough? should we use some default?
55+
}
56+
}
57+
}
58+
59+
// Events
60+
#[derive(Copy, Clone, Debug, Default)]
61+
pub struct Events {
62+
pub low_limit: bool,
63+
pub high_limit: bool,
64+
pub thresh0: bool,
65+
pub thresh1: bool,
66+
pub zero: bool,
67+
}
68+
69+
/// Unit configuration
70+
#[derive(Copy, Clone, Default)]
71+
pub struct Config {
72+
pub low_limit: i16,
73+
pub high_limit: i16,
74+
pub thresh0: i16,
75+
pub thresh1: i16,
76+
pub filter: Option<u16>,
77+
}
78+
79+
pub struct Unit {
80+
number: Number,
81+
}
82+
83+
impl Unit {
84+
/// return a new Unit
85+
pub(super) fn new(number: Number) -> Self {
86+
let pcnt = unsafe { &*crate::peripherals::PCNT::ptr() };
87+
let conf0 = match number {
88+
Number::Unit0 => &pcnt.u0_conf0,
89+
Number::Unit1 => &pcnt.u1_conf0,
90+
Number::Unit2 => &pcnt.u2_conf0,
91+
Number::Unit3 => &pcnt.u3_conf0,
92+
#[cfg(esp32)]
93+
Number::Unit4 => &pcnt.u4_conf0,
94+
#[cfg(esp32)]
95+
Number::Unit5 => &pcnt.u5_conf0,
96+
#[cfg(esp32)]
97+
Number::Unit6 => &pcnt.u6_conf0,
98+
#[cfg(esp32)]
99+
Number::Unit7 => &pcnt.u7_conf0,
100+
};
101+
// disable filter and all events
102+
conf0.modify(|_, w| unsafe {
103+
w.filter_en()
104+
.clear_bit()
105+
.filter_thres()
106+
.bits(0)
107+
.thr_l_lim_en()
108+
.clear_bit()
109+
.thr_h_lim_en()
110+
.clear_bit()
111+
.thr_thres0_en()
112+
.clear_bit()
113+
.thr_thres1_en()
114+
.clear_bit()
115+
.thr_zero_en()
116+
.clear_bit()
117+
});
118+
Self { number }
119+
}
120+
121+
pub fn configure(&mut self, config: Config) -> Result<(), Error> {
122+
// low limit must be >= or the limit is -32768 and when thats
123+
// hit the event status claims it was the high limit.
124+
// tested on an esp32s3
125+
if config.low_limit >= 0 {
126+
return Err(Error::InvalidLowLimit);
127+
}
128+
if config.high_limit <= 0 {
129+
return Err(Error::InvalidHighLimit);
130+
}
131+
let (filter_en, filter) = match config.filter {
132+
Some(filter) => (true, filter),
133+
None => (false, 0),
134+
};
135+
// filter must be less than 1024
136+
if filter > 1023 {
137+
return Err(Error::InvalidFilterThresh);
138+
}
139+
140+
let pcnt = unsafe { &*crate::peripherals::PCNT::ptr() };
141+
let (conf0, conf1, conf2) = match self.number {
142+
Number::Unit0 => (&pcnt.u0_conf0, &pcnt.u0_conf1, &pcnt.u0_conf2),
143+
Number::Unit1 => (&pcnt.u1_conf0, &pcnt.u1_conf1, &pcnt.u1_conf2),
144+
Number::Unit2 => (&pcnt.u2_conf0, &pcnt.u2_conf1, &pcnt.u2_conf2),
145+
Number::Unit3 => (&pcnt.u3_conf0, &pcnt.u3_conf1, &pcnt.u3_conf2),
146+
#[cfg(esp32)]
147+
Number::Unit4 => (&pcnt.u4_conf0, &pcnt.u4_conf1, &pcnt.u4_conf2),
148+
#[cfg(esp32)]
149+
Number::Unit5 => (&pcnt.u5_conf0, &pcnt.u5_conf1, &pcnt.u5_conf2),
150+
#[cfg(esp32)]
151+
Number::Unit6 => (&pcnt.u6_conf0, &pcnt.u6_conf1, &pcnt.u6_conf2),
152+
#[cfg(esp32)]
153+
Number::Unit7 => (&pcnt.u7_conf0, &pcnt.u7_conf1, &pcnt.u7_conf2),
154+
};
155+
conf2.write(|w| unsafe {
156+
w.cnt_l_lim()
157+
.bits(config.low_limit as u16)
158+
.cnt_h_lim()
159+
.bits(config.high_limit as u16)
160+
});
161+
conf1.write(|w| unsafe {
162+
w.cnt_thres0()
163+
.bits(config.thresh0 as u16)
164+
.cnt_thres1()
165+
.bits(config.thresh1 as u16)
166+
});
167+
conf0.modify(|_, w| unsafe { w.filter_thres().bits(filter).filter_en().bit(filter_en) });
168+
self.pause();
169+
self.clear();
170+
Ok(())
171+
}
172+
173+
pub fn get_channel(&self, number: channel::Number) -> super::channel::Channel {
174+
super::channel::Channel::new(self.number, number)
175+
}
176+
177+
pub fn clear(&self) {
178+
let pcnt = unsafe { &*crate::peripherals::PCNT::ptr() };
179+
critical_section::with(|_cs| {
180+
match self.number {
181+
Number::Unit0 => pcnt.ctrl.modify(|_, w| w.cnt_rst_u0().set_bit()),
182+
Number::Unit1 => pcnt.ctrl.modify(|_, w| w.cnt_rst_u1().set_bit()),
183+
Number::Unit2 => pcnt.ctrl.modify(|_, w| w.cnt_rst_u2().set_bit()),
184+
Number::Unit3 => pcnt.ctrl.modify(|_, w| w.cnt_rst_u3().set_bit()),
185+
#[cfg(esp32)]
186+
Number::Unit4 => pcnt.ctrl.modify(|_, w| w.cnt_rst_u4().set_bit()),
187+
#[cfg(esp32)]
188+
Number::Unit5 => pcnt.ctrl.modify(|_, w| w.cnt_rst_u5().set_bit()),
189+
#[cfg(esp32)]
190+
Number::Unit6 => pcnt.ctrl.modify(|_, w| w.cnt_rst_u6().set_bit()),
191+
#[cfg(esp32)]
192+
Number::Unit7 => pcnt.ctrl.modify(|_, w| w.cnt_rst_u7().set_bit()),
193+
}
194+
// TODO: does this need a delay? (liebman / Jan 2 2023)
195+
match self.number {
196+
Number::Unit0 => pcnt.ctrl.modify(|_, w| w.cnt_rst_u0().clear_bit()),
197+
Number::Unit1 => pcnt.ctrl.modify(|_, w| w.cnt_rst_u1().clear_bit()),
198+
Number::Unit2 => pcnt.ctrl.modify(|_, w| w.cnt_rst_u2().clear_bit()),
199+
Number::Unit3 => pcnt.ctrl.modify(|_, w| w.cnt_rst_u3().clear_bit()),
200+
#[cfg(esp32)]
201+
Number::Unit4 => pcnt.ctrl.modify(|_, w| w.cnt_rst_u4().clear_bit()),
202+
#[cfg(esp32)]
203+
Number::Unit5 => pcnt.ctrl.modify(|_, w| w.cnt_rst_u5().clear_bit()),
204+
#[cfg(esp32)]
205+
Number::Unit6 => pcnt.ctrl.modify(|_, w| w.cnt_rst_u6().clear_bit()),
206+
#[cfg(esp32)]
207+
Number::Unit7 => pcnt.ctrl.modify(|_, w| w.cnt_rst_u7().clear_bit()),
208+
}
209+
});
210+
}
211+
212+
/// Pause the counter
213+
pub fn pause(&self) {
214+
let pcnt = unsafe { &*crate::peripherals::PCNT::ptr() };
215+
critical_section::with(|_cs| match self.number {
216+
Number::Unit0 => pcnt.ctrl.modify(|_, w| w.cnt_pause_u0().set_bit()),
217+
Number::Unit1 => pcnt.ctrl.modify(|_, w| w.cnt_pause_u1().set_bit()),
218+
Number::Unit2 => pcnt.ctrl.modify(|_, w| w.cnt_pause_u2().set_bit()),
219+
Number::Unit3 => pcnt.ctrl.modify(|_, w| w.cnt_pause_u3().set_bit()),
220+
#[cfg(esp32)]
221+
Number::Unit4 => pcnt.ctrl.modify(|_, w| w.cnt_pause_u4().set_bit()),
222+
#[cfg(esp32)]
223+
Number::Unit5 => pcnt.ctrl.modify(|_, w| w.cnt_pause_u5().set_bit()),
224+
#[cfg(esp32)]
225+
Number::Unit6 => pcnt.ctrl.modify(|_, w| w.cnt_pause_u6().set_bit()),
226+
#[cfg(esp32)]
227+
Number::Unit7 => pcnt.ctrl.modify(|_, w| w.cnt_pause_u7().set_bit()),
228+
});
229+
}
230+
231+
/// Resume the counter
232+
pub fn resume(&self) {
233+
let pcnt = unsafe { &*crate::peripherals::PCNT::ptr() };
234+
critical_section::with(|_cs| match self.number {
235+
Number::Unit0 => pcnt.ctrl.modify(|_, w| w.cnt_pause_u0().clear_bit()),
236+
Number::Unit1 => pcnt.ctrl.modify(|_, w| w.cnt_pause_u1().clear_bit()),
237+
Number::Unit2 => pcnt.ctrl.modify(|_, w| w.cnt_pause_u2().clear_bit()),
238+
Number::Unit3 => pcnt.ctrl.modify(|_, w| w.cnt_pause_u3().clear_bit()),
239+
#[cfg(esp32)]
240+
Number::Unit4 => pcnt.ctrl.modify(|_, w| w.cnt_pause_u4().clear_bit()),
241+
#[cfg(esp32)]
242+
Number::Unit5 => pcnt.ctrl.modify(|_, w| w.cnt_pause_u5().clear_bit()),
243+
#[cfg(esp32)]
244+
Number::Unit6 => pcnt.ctrl.modify(|_, w| w.cnt_pause_u6().clear_bit()),
245+
#[cfg(esp32)]
246+
Number::Unit7 => pcnt.ctrl.modify(|_, w| w.cnt_pause_u7().clear_bit()),
247+
});
248+
}
249+
250+
/// Enable which events generate interrupts on this unit.
251+
pub fn events(&self, events: Events) {
252+
let pcnt = unsafe { &*crate::peripherals::PCNT::ptr() };
253+
let conf0 = match self.number {
254+
Number::Unit0 => &pcnt.u0_conf0,
255+
Number::Unit1 => &pcnt.u1_conf0,
256+
Number::Unit2 => &pcnt.u2_conf0,
257+
Number::Unit3 => &pcnt.u3_conf0,
258+
#[cfg(esp32)]
259+
Number::Unit4 => &pcnt.u4_conf0,
260+
#[cfg(esp32)]
261+
Number::Unit5 => &pcnt.u5_conf0,
262+
#[cfg(esp32)]
263+
Number::Unit6 => &pcnt.u6_conf0,
264+
#[cfg(esp32)]
265+
Number::Unit7 => &pcnt.u7_conf0,
266+
};
267+
conf0.modify(|_, w| {
268+
w.thr_l_lim_en()
269+
.bit(events.low_limit)
270+
.thr_h_lim_en()
271+
.bit(events.high_limit)
272+
.thr_thres0_en()
273+
.bit(events.thresh0)
274+
.thr_thres1_en()
275+
.bit(events.thresh1)
276+
.thr_zero_en()
277+
.bit(events.zero)
278+
});
279+
}
280+
281+
/// Get the latest events for this unit.
282+
pub fn get_events(&self) -> Events {
283+
let pcnt = unsafe { &*crate::peripherals::PCNT::ptr() };
284+
let status = pcnt.u_status[self.number as usize].read();
285+
286+
Events {
287+
low_limit: status.l_lim().bit(),
288+
high_limit: status.h_lim().bit(),
289+
thresh0: status.thres0().bit(),
290+
thresh1: status.thres1().bit(),
291+
zero: status.zero().bit(),
292+
}
293+
}
294+
295+
/// Get the mode of the last zero crossing
296+
pub fn get_zero_mode(&self) -> ZeroMode {
297+
let pcnt = unsafe { &*crate::peripherals::PCNT::ptr() };
298+
pcnt.u_status[self.number as usize]
299+
.read()
300+
.zero_mode()
301+
.bits()
302+
.into()
303+
}
304+
305+
/// Enable interrupts for this unit.
306+
pub fn listen(&self) {
307+
let pcnt = unsafe { &*crate::peripherals::PCNT::ptr() };
308+
critical_section::with(|_cs| {
309+
pcnt.int_ena.modify(|_, w| match self.number {
310+
Number::Unit0 => w.cnt_thr_event_u0().set_bit(),
311+
Number::Unit1 => w.cnt_thr_event_u1().set_bit(),
312+
Number::Unit2 => w.cnt_thr_event_u2().set_bit(),
313+
Number::Unit3 => w.cnt_thr_event_u3().set_bit(),
314+
#[cfg(esp32)]
315+
Number::Unit4 => w.cnt_thr_event_u4().set_bit(),
316+
#[cfg(esp32)]
317+
Number::Unit5 => w.cnt_thr_event_u5().set_bit(),
318+
#[cfg(esp32)]
319+
Number::Unit6 => w.cnt_thr_event_u6().set_bit(),
320+
#[cfg(esp32)]
321+
Number::Unit7 => w.cnt_thr_event_u7().set_bit(),
322+
})
323+
});
324+
}
325+
326+
/// Disable interrupts for this unit.
327+
pub fn unlisten(&self, _cs: CriticalSection) {
328+
let pcnt = unsafe { &*crate::peripherals::PCNT::ptr() };
329+
critical_section::with(|_cs| {
330+
pcnt.int_ena.write(|w| match self.number {
331+
Number::Unit0 => w.cnt_thr_event_u0().clear_bit(),
332+
Number::Unit1 => w.cnt_thr_event_u1().clear_bit(),
333+
Number::Unit2 => w.cnt_thr_event_u2().clear_bit(),
334+
Number::Unit3 => w.cnt_thr_event_u3().clear_bit(),
335+
#[cfg(esp32)]
336+
Number::Unit4 => w.cnt_thr_event_u4().clear_bit(),
337+
#[cfg(esp32)]
338+
Number::Unit5 => w.cnt_thr_event_u5().clear_bit(),
339+
#[cfg(esp32)]
340+
Number::Unit6 => w.cnt_thr_event_u6().clear_bit(),
341+
#[cfg(esp32)]
342+
Number::Unit7 => w.cnt_thr_event_u7().clear_bit(),
343+
})
344+
});
345+
}
346+
347+
/// Returns true if an interrupt is active for this unit.
348+
pub fn interrupt_set(&self) -> bool {
349+
let pcnt = unsafe { &*crate::peripherals::PCNT::ptr() };
350+
match self.number {
351+
Number::Unit0 => pcnt.int_st.read().cnt_thr_event_u0().bit(),
352+
Number::Unit1 => pcnt.int_st.read().cnt_thr_event_u1().bit(),
353+
Number::Unit2 => pcnt.int_st.read().cnt_thr_event_u2().bit(),
354+
Number::Unit3 => pcnt.int_st.read().cnt_thr_event_u3().bit(),
355+
#[cfg(esp32)]
356+
Number::Unit4 => pcnt.int_st.read().cnt_thr_event_u4().bit(),
357+
#[cfg(esp32)]
358+
Number::Unit5 => pcnt.int_st.read().cnt_thr_event_u5().bit(),
359+
#[cfg(esp32)]
360+
Number::Unit6 => pcnt.int_st.read().cnt_thr_event_u6().bit(),
361+
#[cfg(esp32)]
362+
Number::Unit7 => pcnt.int_st.read().cnt_thr_event_u7().bit(),
363+
}
364+
}
365+
366+
/// Clear the interrupt bit for this unit.
367+
pub fn reset_interrupt(&self) {
368+
let pcnt = unsafe { &*crate::peripherals::PCNT::ptr() };
369+
critical_section::with(|_cs| {
370+
pcnt.int_clr.write(|w| match self.number {
371+
Number::Unit0 => w.cnt_thr_event_u0().set_bit(),
372+
Number::Unit1 => w.cnt_thr_event_u1().set_bit(),
373+
Number::Unit2 => w.cnt_thr_event_u2().set_bit(),
374+
Number::Unit3 => w.cnt_thr_event_u3().set_bit(),
375+
#[cfg(esp32)]
376+
Number::Unit4 => w.cnt_thr_event_u4().set_bit(),
377+
#[cfg(esp32)]
378+
Number::Unit5 => w.cnt_thr_event_u5().set_bit(),
379+
#[cfg(esp32)]
380+
Number::Unit6 => w.cnt_thr_event_u6().set_bit(),
381+
#[cfg(esp32)]
382+
Number::Unit7 => w.cnt_thr_event_u7().set_bit(),
383+
})
384+
});
385+
}
386+
387+
/// Get the current counter value.
388+
pub fn get_value(&self) -> i16 {
389+
let pcnt = unsafe { &*crate::peripherals::PCNT::ptr() };
390+
pcnt.u_cnt[self.number as usize].read().cnt().bits() as i16
391+
}
392+
}

‎esp-hal-common/src/system.rs

+7
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ pub enum Peripheral {
3030
Mcpwm0,
3131
#[cfg(any(esp32, esp32s3))]
3232
Mcpwm1,
33+
#[cfg(any(esp32, esp32s2, esp32s3))]
34+
Pcnt,
3335
#[cfg(any(esp32c2, esp32c3))]
3436
ApbSarAdc,
3537
#[cfg(gdma)]
@@ -108,6 +110,11 @@ impl PeripheralClockControl {
108110
perip_clk_en0.modify(|_, w| w.pwm1_clk_en().set_bit());
109111
perip_rst_en0.modify(|_, w| w.pwm1_rst().clear_bit());
110112
}
113+
#[cfg(any(esp32, esp32s2, esp32s3))]
114+
Peripheral::Pcnt => {
115+
perip_clk_en0.modify(|_, w| w.pcnt_clk_en().set_bit());
116+
perip_rst_en0.modify(|_, w| w.pcnt_rst().clear_bit());
117+
}
111118
#[cfg(any(esp32c2, esp32c3))]
112119
Peripheral::ApbSarAdc => {
113120
perip_clk_en0.modify(|_, w| w.apb_saradc_clk_en().set_bit());

‎esp32-hal/examples/pcnt_encoder.rs

+144
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
//! PCNT Encoder Demo
2+
//!
3+
//! This example decodes a quadrature encoder
4+
//!
5+
//! Since the PCNT units reset to zero when they reach their limits
6+
//! we enable an interrupt on the upper and lower limits and
7+
//! track the overflow in an AtomicI32
8+
9+
#![no_std]
10+
#![no_main]
11+
use core::{
12+
cell::RefCell,
13+
cmp::min,
14+
sync::atomic::{AtomicI32, Ordering},
15+
};
16+
17+
use critical_section::Mutex;
18+
use esp32_hal as esp_hal;
19+
use esp_backtrace as _;
20+
use esp_hal::{
21+
clock::ClockControl,
22+
interrupt,
23+
pcnt::{channel, channel::PcntSource, unit, PCNT},
24+
peripherals::{self, Peripherals},
25+
prelude::*,
26+
timer::TimerGroup,
27+
Rtc,
28+
IO,
29+
};
30+
use esp_println::println;
31+
use xtensa_lx_rt::entry;
32+
33+
static UNIT0: Mutex<RefCell<Option<unit::Unit>>> = Mutex::new(RefCell::new(None));
34+
static VALUE: AtomicI32 = AtomicI32::new(0);
35+
36+
#[entry]
37+
fn main() -> ! {
38+
let peripherals = Peripherals::take();
39+
let mut system = peripherals.DPORT.split();
40+
let clocks = ClockControl::boot_defaults(system.clock_control).freeze();
41+
42+
let mut rtc = Rtc::new(peripherals.RTC_CNTL);
43+
let timer_group0 = TimerGroup::new(peripherals.TIMG0, &clocks);
44+
let mut wdt = timer_group0.wdt;
45+
let io = IO::new(peripherals.GPIO, peripherals.IO_MUX);
46+
47+
// Disable MWDT and RWDT (Watchdog) flash boot protection
48+
wdt.disable();
49+
rtc.rwdt.disable();
50+
51+
let unit_number = unit::Number::Unit1;
52+
53+
// setup a pulse couter
54+
println!("setup pulse counter unit 0");
55+
let pcnt = PCNT::new(peripherals.PCNT, &mut system.peripheral_clock_control);
56+
let mut u0 = pcnt.get_unit(unit_number);
57+
u0.configure(unit::Config {
58+
low_limit: -100,
59+
high_limit: 100,
60+
filter: Some(min(10u16 * 80, 1023u16)),
61+
..Default::default()
62+
})
63+
.unwrap();
64+
65+
println!("setup channel 0");
66+
let mut ch0 = u0.get_channel(channel::Number::Channel0);
67+
let mut pin_a = io.pins.gpio22.into_pull_up_input();
68+
let mut pin_b = io.pins.gpio23.into_pull_up_input();
69+
70+
ch0.configure(
71+
PcntSource::from_pin(&mut pin_a),
72+
PcntSource::from_pin(&mut pin_b),
73+
channel::Config {
74+
lctrl_mode: channel::CtrlMode::Reverse,
75+
hctrl_mode: channel::CtrlMode::Keep,
76+
pos_edge: channel::EdgeMode::Decrement,
77+
neg_edge: channel::EdgeMode::Increment,
78+
invert_ctrl: false,
79+
invert_sig: false,
80+
},
81+
);
82+
83+
println!("setup channel 1");
84+
let mut ch1 = u0.get_channel(channel::Number::Channel1);
85+
ch1.configure(
86+
PcntSource::from_pin(&mut pin_b),
87+
PcntSource::from_pin(&mut pin_a),
88+
channel::Config {
89+
lctrl_mode: channel::CtrlMode::Reverse,
90+
hctrl_mode: channel::CtrlMode::Keep,
91+
pos_edge: channel::EdgeMode::Increment,
92+
neg_edge: channel::EdgeMode::Decrement,
93+
invert_ctrl: false,
94+
invert_sig: false,
95+
},
96+
);
97+
println!("subscribing to events");
98+
u0.events(unit::Events {
99+
low_limit: true,
100+
high_limit: true,
101+
thresh0: false,
102+
thresh1: false,
103+
zero: false,
104+
});
105+
106+
println!("enabling interrupts");
107+
u0.listen();
108+
println!("resume pulse counter unit 0");
109+
u0.resume();
110+
111+
critical_section::with(|cs| UNIT0.borrow_ref_mut(cs).replace(u0));
112+
113+
interrupt::enable(peripherals::Interrupt::PCNT, interrupt::Priority::Priority2).unwrap();
114+
115+
let mut last_value: i32 = 0;
116+
loop {
117+
critical_section::with(|cs| {
118+
let mut u0 = UNIT0.borrow_ref_mut(cs);
119+
let u0 = u0.as_mut().unwrap();
120+
let value: i32 = u0.get_value() as i32 + VALUE.load(Ordering::SeqCst);
121+
if value != last_value {
122+
println!("value: {value}");
123+
last_value = value;
124+
}
125+
});
126+
}
127+
}
128+
129+
#[interrupt]
130+
fn PCNT() {
131+
critical_section::with(|cs| {
132+
let mut u0 = UNIT0.borrow_ref_mut(cs);
133+
let u0 = u0.as_mut().unwrap();
134+
if u0.interrupt_set() {
135+
let events = u0.get_events();
136+
if events.high_limit {
137+
VALUE.fetch_add(100, Ordering::SeqCst);
138+
} else if events.low_limit {
139+
VALUE.fetch_add(-100, Ordering::SeqCst);
140+
}
141+
u0.reset_interrupt();
142+
}
143+
});
144+
}

‎esp32-hal/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ pub use esp_hal_common::{
1919
ledc,
2020
macros,
2121
mcpwm,
22+
pcnt,
2223
peripherals,
2324
prelude,
2425
pulse_control,

‎esp32s2-hal/examples/pcnt_encoder.rs

+144
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
//! PCNT Encoder Demo
2+
//!
3+
//! This example decodes a quadrature encoder
4+
//!
5+
//! Since the PCNT units reset to zero when they reach their limits
6+
//! we enable an interrupt on the upper and lower limits and
7+
//! track the overflow in an AtomicI32
8+
9+
#![no_std]
10+
#![no_main]
11+
use core::{
12+
cell::RefCell,
13+
cmp::min,
14+
sync::atomic::{AtomicI32, Ordering},
15+
};
16+
17+
use critical_section::Mutex;
18+
use esp32s2_hal as esp_hal;
19+
use esp_backtrace as _;
20+
use esp_hal::{
21+
clock::ClockControl,
22+
interrupt,
23+
pcnt::{channel, channel::PcntSource, unit, PCNT},
24+
peripherals::{self, Peripherals},
25+
prelude::*,
26+
timer::TimerGroup,
27+
Rtc,
28+
IO,
29+
};
30+
use esp_println::println;
31+
use xtensa_lx_rt::entry;
32+
33+
static UNIT0: Mutex<RefCell<Option<unit::Unit>>> = Mutex::new(RefCell::new(None));
34+
static VALUE: AtomicI32 = AtomicI32::new(0);
35+
36+
#[entry]
37+
fn main() -> ! {
38+
let peripherals = Peripherals::take();
39+
let mut system = peripherals.SYSTEM.split();
40+
let clocks = ClockControl::boot_defaults(system.clock_control).freeze();
41+
42+
let mut rtc = Rtc::new(peripherals.RTC_CNTL);
43+
let timer_group0 = TimerGroup::new(peripherals.TIMG0, &clocks);
44+
let mut wdt = timer_group0.wdt;
45+
let io = IO::new(peripherals.GPIO, peripherals.IO_MUX);
46+
47+
// Disable MWDT and RWDT (Watchdog) flash boot protection
48+
wdt.disable();
49+
rtc.rwdt.disable();
50+
51+
let unit_number = unit::Number::Unit1;
52+
53+
// setup a pulse couter
54+
println!("setup pulse counter unit 0");
55+
let pcnt = PCNT::new(peripherals.PCNT, &mut system.peripheral_clock_control);
56+
let mut u0 = pcnt.get_unit(unit_number);
57+
u0.configure(unit::Config {
58+
low_limit: -100,
59+
high_limit: 100,
60+
filter: Some(min(10u16 * 80, 1023u16)),
61+
..Default::default()
62+
})
63+
.unwrap();
64+
65+
println!("setup channel 0");
66+
let mut ch0 = u0.get_channel(channel::Number::Channel0);
67+
let mut pin_a = io.pins.gpio5.into_pull_up_input();
68+
let mut pin_b = io.pins.gpio6.into_pull_up_input();
69+
70+
ch0.configure(
71+
PcntSource::from_pin(&mut pin_a),
72+
PcntSource::from_pin(&mut pin_b),
73+
channel::Config {
74+
lctrl_mode: channel::CtrlMode::Reverse,
75+
hctrl_mode: channel::CtrlMode::Keep,
76+
pos_edge: channel::EdgeMode::Decrement,
77+
neg_edge: channel::EdgeMode::Increment,
78+
invert_ctrl: false,
79+
invert_sig: false,
80+
},
81+
);
82+
83+
println!("setup channel 1");
84+
let mut ch1 = u0.get_channel(channel::Number::Channel1);
85+
ch1.configure(
86+
PcntSource::from_pin(&mut pin_b),
87+
PcntSource::from_pin(&mut pin_a),
88+
channel::Config {
89+
lctrl_mode: channel::CtrlMode::Reverse,
90+
hctrl_mode: channel::CtrlMode::Keep,
91+
pos_edge: channel::EdgeMode::Increment,
92+
neg_edge: channel::EdgeMode::Decrement,
93+
invert_ctrl: false,
94+
invert_sig: false,
95+
},
96+
);
97+
println!("subscribing to events");
98+
u0.events(unit::Events {
99+
low_limit: true,
100+
high_limit: true,
101+
thresh0: false,
102+
thresh1: false,
103+
zero: false,
104+
});
105+
106+
println!("enabling interrupts");
107+
u0.listen();
108+
println!("resume pulse counter unit 0");
109+
u0.resume();
110+
111+
critical_section::with(|cs| UNIT0.borrow_ref_mut(cs).replace(u0));
112+
113+
interrupt::enable(peripherals::Interrupt::PCNT, interrupt::Priority::Priority2).unwrap();
114+
115+
let mut last_value: i32 = 0;
116+
loop {
117+
critical_section::with(|cs| {
118+
let mut u0 = UNIT0.borrow_ref_mut(cs);
119+
let u0 = u0.as_mut().unwrap();
120+
let value: i32 = u0.get_value() as i32 + VALUE.load(Ordering::SeqCst);
121+
if value != last_value {
122+
println!("value: {value}");
123+
last_value = value;
124+
}
125+
});
126+
}
127+
}
128+
129+
#[interrupt]
130+
fn PCNT() {
131+
critical_section::with(|cs| {
132+
let mut u0 = UNIT0.borrow_ref_mut(cs);
133+
let u0 = u0.as_mut().unwrap();
134+
if u0.interrupt_set() {
135+
let events = u0.get_events();
136+
if events.high_limit {
137+
VALUE.store(VALUE.load(Ordering::SeqCst) + 100, Ordering::SeqCst);
138+
} else if events.low_limit {
139+
VALUE.store(VALUE.load(Ordering::SeqCst) - 100, Ordering::SeqCst);
140+
}
141+
u0.reset_interrupt();
142+
}
143+
});
144+
}

‎esp32s2-hal/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ pub use esp_hal_common::{
1818
ledc,
1919
macros,
2020
otg_fs,
21+
pcnt,
2122
peripherals,
2223
prelude,
2324
pulse_control,

‎esp32s3-hal/examples/pcnt_encoder.rs

+144
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
//! PCNT Encoder Demo
2+
//!
3+
//! This example decodes a quadrature encoder
4+
//!
5+
//! Since the PCNT units reset to zero when they reach their limits
6+
//! we enable an interrupt on the upper and lower limits and
7+
//! track the overflow in an AtomicI32
8+
9+
#![no_std]
10+
#![no_main]
11+
use core::{
12+
cell::RefCell,
13+
cmp::min,
14+
sync::atomic::{AtomicI32, Ordering},
15+
};
16+
17+
use critical_section::Mutex;
18+
use esp32s3_hal as esp_hal;
19+
use esp_backtrace as _;
20+
use esp_hal::{
21+
clock::ClockControl,
22+
interrupt,
23+
pcnt::{channel, channel::PcntSource, unit, PCNT},
24+
peripherals::{self, Peripherals},
25+
prelude::*,
26+
timer::TimerGroup,
27+
Rtc,
28+
IO,
29+
};
30+
use esp_println::println;
31+
use xtensa_lx_rt::entry;
32+
33+
static UNIT0: Mutex<RefCell<Option<unit::Unit>>> = Mutex::new(RefCell::new(None));
34+
static VALUE: AtomicI32 = AtomicI32::new(0);
35+
36+
#[entry]
37+
fn main() -> ! {
38+
let peripherals = Peripherals::take();
39+
let mut system = peripherals.SYSTEM.split();
40+
let clocks = ClockControl::boot_defaults(system.clock_control).freeze();
41+
42+
let mut rtc = Rtc::new(peripherals.RTC_CNTL);
43+
let timer_group0 = TimerGroup::new(peripherals.TIMG0, &clocks);
44+
let mut wdt = timer_group0.wdt;
45+
let io = IO::new(peripherals.GPIO, peripherals.IO_MUX);
46+
47+
// Disable MWDT and RWDT (Watchdog) flash boot protection
48+
wdt.disable();
49+
rtc.rwdt.disable();
50+
51+
let unit_number = unit::Number::Unit1;
52+
53+
// setup a pulse couter
54+
println!("setup pulse counter unit 0");
55+
let pcnt = PCNT::new(peripherals.PCNT, &mut system.peripheral_clock_control);
56+
let mut u0 = pcnt.get_unit(unit_number);
57+
u0.configure(unit::Config {
58+
low_limit: -100,
59+
high_limit: 100,
60+
filter: Some(min(10u16 * 80, 1023u16)),
61+
..Default::default()
62+
})
63+
.unwrap();
64+
65+
println!("setup channel 0");
66+
let mut ch0 = u0.get_channel(channel::Number::Channel0);
67+
let mut pin_a = io.pins.gpio5.into_pull_up_input();
68+
let mut pin_b = io.pins.gpio6.into_pull_up_input();
69+
70+
ch0.configure(
71+
PcntSource::from_pin(&mut pin_a),
72+
PcntSource::from_pin(&mut pin_b),
73+
channel::Config {
74+
lctrl_mode: channel::CtrlMode::Reverse,
75+
hctrl_mode: channel::CtrlMode::Keep,
76+
pos_edge: channel::EdgeMode::Decrement,
77+
neg_edge: channel::EdgeMode::Increment,
78+
invert_ctrl: false,
79+
invert_sig: false,
80+
},
81+
);
82+
83+
println!("setup channel 1");
84+
let mut ch1 = u0.get_channel(channel::Number::Channel1);
85+
ch1.configure(
86+
PcntSource::from_pin(&mut pin_b),
87+
PcntSource::from_pin(&mut pin_a),
88+
channel::Config {
89+
lctrl_mode: channel::CtrlMode::Reverse,
90+
hctrl_mode: channel::CtrlMode::Keep,
91+
pos_edge: channel::EdgeMode::Increment,
92+
neg_edge: channel::EdgeMode::Decrement,
93+
invert_ctrl: false,
94+
invert_sig: false,
95+
},
96+
);
97+
println!("subscribing to events");
98+
u0.events(unit::Events {
99+
low_limit: true,
100+
high_limit: true,
101+
thresh0: false,
102+
thresh1: false,
103+
zero: false,
104+
});
105+
106+
println!("enabling interrupts");
107+
u0.listen();
108+
println!("resume pulse counter unit 0");
109+
u0.resume();
110+
111+
critical_section::with(|cs| UNIT0.borrow_ref_mut(cs).replace(u0));
112+
113+
interrupt::enable(peripherals::Interrupt::PCNT, interrupt::Priority::Priority2).unwrap();
114+
115+
let mut last_value: i32 = 0;
116+
loop {
117+
critical_section::with(|cs| {
118+
let mut u0 = UNIT0.borrow_ref_mut(cs);
119+
let u0 = u0.as_mut().unwrap();
120+
let value: i32 = u0.get_value() as i32 + VALUE.load(Ordering::SeqCst);
121+
if value != last_value {
122+
println!("value: {value}");
123+
last_value = value;
124+
}
125+
});
126+
}
127+
}
128+
129+
#[interrupt]
130+
fn PCNT() {
131+
critical_section::with(|cs| {
132+
let mut u0 = UNIT0.borrow_ref_mut(cs);
133+
let u0 = u0.as_mut().unwrap();
134+
if u0.interrupt_set() {
135+
let events = u0.get_events();
136+
if events.high_limit {
137+
VALUE.fetch_add(100, Ordering::SeqCst);
138+
} else if events.low_limit {
139+
VALUE.fetch_add(-100, Ordering::SeqCst);
140+
}
141+
u0.reset_interrupt();
142+
}
143+
});
144+
}

‎esp32s3-hal/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ pub use esp_hal_common::{
2020
macros,
2121
mcpwm,
2222
otg_fs,
23+
pcnt,
2324
peripherals,
2425
prelude,
2526
pulse_control,

0 commit comments

Comments
 (0)
Please sign in to comment.