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
This commit is contained in:
parent
a542735847
commit
ac206af656
@ -51,11 +51,11 @@ ufmt-write = { version = "0.1.0", optional = true }
|
||||
# Each supported device MUST have its PAC included below along with a
|
||||
# corresponding feature. We rename the PAC packages because we cannot
|
||||
# have dependencies and features with the same names.
|
||||
esp32 = { version = "0.18.0", features = ["critical-section"], optional = true }
|
||||
esp32c2 = { version = "0.5.1", features = ["critical-section"], optional = true }
|
||||
esp32 = { version = "0.19.0", features = ["critical-section"], optional = true }
|
||||
esp32c2 = { version = "0.6.0", features = ["critical-section"], optional = true }
|
||||
esp32c3 = { version = "0.9.0", features = ["critical-section"], optional = true }
|
||||
esp32s2 = { version = "0.8.0", features = ["critical-section"], optional = true }
|
||||
esp32s3 = { version = "0.12.0", features = ["critical-section"], optional = true }
|
||||
esp32s2 = { version = "0.9.0", features = ["critical-section"], optional = true }
|
||||
esp32s3 = { version = "0.13.0", features = ["critical-section"], optional = true }
|
||||
|
||||
[features]
|
||||
esp32 = ["esp32/rt" , "xtensa", "xtensa-lx/esp32", "xtensa-lx-rt/esp32", "lock_api"]
|
||||
|
||||
@ -116,26 +116,26 @@ pub enum InputSignal {
|
||||
PWM0_F2 = 36,
|
||||
GPIO_BT_ACTIVE = 37,
|
||||
GPIO_BT_PRIORITY = 38,
|
||||
PCNT_SIG_CH0_0 = 39,
|
||||
PCNT_SIG_CH1_0 = 40,
|
||||
PCNT_CTRL_CH0_0 = 41,
|
||||
PCNT_CTRL_CH1_0 = 42,
|
||||
PCNT_SIG_CH0_1 = 43,
|
||||
PCNT_SIG_CH1_1 = 44,
|
||||
PCNT_CTRL_CH0_1 = 45,
|
||||
PCNT_CTRL_CH1_1 = 46,
|
||||
PCNT_SIG_CH0_2 = 47,
|
||||
PCNT_SIG_CH1_2 = 48,
|
||||
PCNT_CTRL_CH0_2 = 49,
|
||||
PCNT_CTRL_CH1_2 = 50,
|
||||
PCNT_SIG_CH0_3 = 51,
|
||||
PCNT_SIG_CH1_3 = 52,
|
||||
PCNT_CTRL_CH0_3 = 53,
|
||||
PCNT_CTRL_CH1_3 = 54,
|
||||
PCNT_SIG_CH0_4 = 55,
|
||||
PCNT_SIG_CH1_4 = 56,
|
||||
PCNT_CTRL_CH0_4 = 57,
|
||||
PCNT_CTRL_CH1_4 = 58,
|
||||
PCNT0_SIG_CH0 = 39,
|
||||
PCNT0_SIG_CH1 = 40,
|
||||
PCNT0_CTRL_CH0 = 41,
|
||||
PCNT0_CTRL_CH1 = 42,
|
||||
PCNT1_SIG_CH0 = 43,
|
||||
PCNT1_SIG_CH1 = 44,
|
||||
PCNT1_CTRL_CH0 = 45,
|
||||
PCNT1_CTRL_CH1 = 46,
|
||||
PCNT2_SIG_CH0 = 47,
|
||||
PCNT2_SIG_CH1 = 48,
|
||||
PCNT2_CTRL_CH0 = 49,
|
||||
PCNT2_CTRL_CH1 = 50,
|
||||
PCNT3_SIG_CH0 = 51,
|
||||
PCNT3_SIG_CH1 = 52,
|
||||
PCNT3_CTRL_CH0 = 53,
|
||||
PCNT3_CTRL_CH1 = 54,
|
||||
PCNT4_SIG_CH0 = 55,
|
||||
PCNT4_SIG_CH1 = 56,
|
||||
PCNT4_CTRL_CH0 = 57,
|
||||
PCNT4_CTRL_CH1 = 58,
|
||||
HSPICS1 = 61,
|
||||
HSPICS2 = 62,
|
||||
VSPICLK = 63,
|
||||
@ -146,18 +146,18 @@ pub enum InputSignal {
|
||||
VSPICS0 = 68,
|
||||
VSPICS1 = 69,
|
||||
VSPICS2 = 70,
|
||||
PCNT_SIG_CH0_5 = 71,
|
||||
PCNT_SIG_CH1_5 = 72,
|
||||
PCNT_CTRL_CH0_5 = 73,
|
||||
PCNT_CTRL_CH1_5 = 74,
|
||||
PCNT_SIG_CH0_6 = 75,
|
||||
PCNT_SIG_CH1_6 = 76,
|
||||
PCNT_CTRL_CH0_6 = 77,
|
||||
PCNT_CTRL_CH1_6 = 78,
|
||||
PCNT_SIG_CH0_7 = 79,
|
||||
PCNT_SIG_CH1_7 = 80,
|
||||
PCNT_CTRL_CH0_7 = 81,
|
||||
PCNT_CTRL_CH1_7 = 82,
|
||||
PCNT5_SIG_CH0 = 71,
|
||||
PCNT5_SIG_CH1 = 72,
|
||||
PCNT5_CTRL_CH0 = 73,
|
||||
PCNT5_CTRL_CH1 = 74,
|
||||
PCNT6_SIG_CH0 = 75,
|
||||
PCNT6_SIG_CH1 = 76,
|
||||
PCNT6_CTRL_CH0 = 77,
|
||||
PCNT6_CTRL_CH1 = 78,
|
||||
PCNT7_SIG_CH0 = 79,
|
||||
PCNT7_SIG_CH1 = 80,
|
||||
PCNT7_CTRL_CH0 = 81,
|
||||
PCNT7_CTRL_CH1 = 82,
|
||||
RMT_SIG_0 = 83,
|
||||
RMT_SIG_1 = 84,
|
||||
RMT_SIG_2 = 85,
|
||||
|
||||
@ -102,6 +102,22 @@ pub enum InputSignal {
|
||||
I2S0I_WS = 28,
|
||||
I2CEXT0_SCL = 29,
|
||||
I2CEXT0_SDA = 30,
|
||||
PCNT0_SIG_CH0 = 39,
|
||||
PCNT0_SIG_CH1 = 40,
|
||||
PCNT0_CTRL_CH0 = 41,
|
||||
PCNT0_CTRL_CH1 = 42,
|
||||
PCNT1_SIG_CH0 = 43,
|
||||
PCNT1_SIG_CH1 = 44,
|
||||
PCNT1_CTRL_CH0 = 45,
|
||||
PCNT1_CTRL_CH1 = 46,
|
||||
PCNT2_SIG_CH0 = 47,
|
||||
PCNT2_SIG_CH1 = 48,
|
||||
PCNT2_CTRL_CH0 = 49,
|
||||
PCNT2_CTRL_CH1 = 50,
|
||||
PCNT3_SIG_CH0 = 51,
|
||||
PCNT3_SIG_CH1 = 52,
|
||||
PCNT3_CTRL_CH0 = 53,
|
||||
PCNT3_CTRL_CH1 = 54,
|
||||
USB_OTG_IDDIG = 64,
|
||||
USB_OTG_AVALID = 65,
|
||||
USB_SRP_BVALID = 66,
|
||||
|
||||
@ -63,6 +63,22 @@ pub enum InputSignal {
|
||||
I2S1I_SD = 30,
|
||||
I2S1I_BCK = 31,
|
||||
I2S1I_WS = 32,
|
||||
PCNT0_SIG_CH0 = 33,
|
||||
PCNT0_SIG_CH1 = 34,
|
||||
PCNT0_CTRL_CH0 = 35,
|
||||
PCNT0_CTRL_CH1 = 36,
|
||||
PCNT1_SIG_CH0 = 37,
|
||||
PCNT1_SIG_CH1 = 38,
|
||||
PCNT1_CTRL_CH0 = 39,
|
||||
PCNT1_CTRL_CH1 = 40,
|
||||
PCNT2_SIG_CH0 = 41,
|
||||
PCNT2_SIG_CH1 = 42,
|
||||
PCNT2_CTRL_CH0 = 43,
|
||||
PCNT2_CTRL_CH1 = 44,
|
||||
PCNT3_SIG_CH0 = 45,
|
||||
PCNT3_SIG_CH1 = 46,
|
||||
PCNT3_CTRL_CH0 = 47,
|
||||
PCNT3_CTRL_CH1 = 48,
|
||||
I2S0I_SD1 = 51,
|
||||
I2S0I_SD2 = 52,
|
||||
I2S0I_SD3 = 53,
|
||||
|
||||
@ -60,6 +60,8 @@ pub mod ledc;
|
||||
pub mod mcpwm;
|
||||
#[cfg(usb_otg)]
|
||||
pub mod otg_fs;
|
||||
#[cfg(any(esp32, esp32s2, esp32s3))]
|
||||
pub mod pcnt;
|
||||
pub mod peripheral;
|
||||
pub mod prelude;
|
||||
#[cfg(rmt)]
|
||||
|
||||
243
esp-hal-common/src/pcnt/channel.rs
Normal file
243
esp-hal-common/src/pcnt/channel.rs
Normal file
@ -0,0 +1,243 @@
|
||||
use super::unit;
|
||||
use crate::{
|
||||
gpio::{
|
||||
types::{InputSignal, ONE_INPUT, ZERO_INPUT},
|
||||
InputPin,
|
||||
},
|
||||
peripheral::Peripheral,
|
||||
peripherals::GPIO,
|
||||
};
|
||||
|
||||
/// Channel number
|
||||
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
|
||||
pub enum Number {
|
||||
Channel0,
|
||||
Channel1,
|
||||
}
|
||||
|
||||
/// PCNT channel action on signal edge
|
||||
#[derive(Debug, Copy, Clone, Default)]
|
||||
pub enum EdgeMode {
|
||||
/// Hold current count value
|
||||
Hold = 0,
|
||||
/// Increase count value
|
||||
#[default]
|
||||
Increment = 1,
|
||||
/// Decrease count value
|
||||
Decrement = 2,
|
||||
}
|
||||
|
||||
/// PCNT channel action on control level
|
||||
#[derive(Debug, Copy, Clone, Default)]
|
||||
pub enum CtrlMode {
|
||||
/// Keep current count mode
|
||||
Keep = 0,
|
||||
/// Invert current count mode (increase -> decrease, decrease -> increase)
|
||||
#[default]
|
||||
Reverse = 1,
|
||||
/// Hold current count value
|
||||
Disable = 2,
|
||||
}
|
||||
|
||||
/// Pulse Counter configuration for a single channel
|
||||
#[derive(Debug, Copy, Clone, Default)]
|
||||
pub struct Config {
|
||||
/// PCNT low control mode
|
||||
pub lctrl_mode: CtrlMode,
|
||||
/// PCNT high control mode
|
||||
pub hctrl_mode: CtrlMode,
|
||||
/// PCNT signal positive edge count mode
|
||||
pub pos_edge: EdgeMode,
|
||||
/// PCNT signal negative edge count mode
|
||||
pub neg_edge: EdgeMode,
|
||||
pub invert_ctrl: bool,
|
||||
pub invert_sig: bool,
|
||||
}
|
||||
|
||||
/// PcntPin can be always high, always low, or an actual pin
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct PcntSource {
|
||||
source: u8,
|
||||
}
|
||||
|
||||
impl PcntSource {
|
||||
pub fn from_pin<'a, P: InputPin>(pin: impl Peripheral<P = P> + 'a) -> Self {
|
||||
crate::into_ref!(pin);
|
||||
Self {
|
||||
source: pin.number(),
|
||||
}
|
||||
}
|
||||
pub fn always_high() -> Self {
|
||||
Self { source: ONE_INPUT }
|
||||
}
|
||||
pub fn always_low() -> Self {
|
||||
Self { source: ZERO_INPUT }
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Channel {
|
||||
unit: unit::Number,
|
||||
channel: Number,
|
||||
}
|
||||
|
||||
impl Channel {
|
||||
/// return a new Channel
|
||||
pub(super) fn new(unit: unit::Number, channel: Number) -> Self {
|
||||
Self { unit, channel }
|
||||
}
|
||||
|
||||
/// Configure the channel
|
||||
pub fn configure(&mut self, ctrl_signal: PcntSource, edge_signal: PcntSource, config: Config) {
|
||||
let pcnt = unsafe { &*crate::peripherals::PCNT::ptr() };
|
||||
let conf0 = match self.unit {
|
||||
unit::Number::Unit0 => &pcnt.u0_conf0,
|
||||
unit::Number::Unit1 => &pcnt.u1_conf0,
|
||||
unit::Number::Unit2 => &pcnt.u2_conf0,
|
||||
unit::Number::Unit3 => &pcnt.u3_conf0,
|
||||
#[cfg(esp32)]
|
||||
unit::Number::Unit4 => &pcnt.u4_conf0,
|
||||
#[cfg(esp32)]
|
||||
unit::Number::Unit5 => &pcnt.u5_conf0,
|
||||
#[cfg(esp32)]
|
||||
unit::Number::Unit6 => &pcnt.u6_conf0,
|
||||
#[cfg(esp32)]
|
||||
unit::Number::Unit7 => &pcnt.u7_conf0,
|
||||
};
|
||||
match self.channel {
|
||||
Number::Channel0 => {
|
||||
conf0.modify(|_, w| unsafe {
|
||||
w.ch0_hctrl_mode()
|
||||
.bits(config.hctrl_mode as u8)
|
||||
.ch0_lctrl_mode()
|
||||
.bits(config.lctrl_mode as u8)
|
||||
.ch0_neg_mode()
|
||||
.bits(config.neg_edge as u8)
|
||||
.ch0_pos_mode()
|
||||
.bits(config.pos_edge as u8)
|
||||
});
|
||||
}
|
||||
Number::Channel1 => {
|
||||
conf0.modify(|_, w| unsafe {
|
||||
w.ch1_hctrl_mode()
|
||||
.bits(config.hctrl_mode as u8)
|
||||
.ch1_lctrl_mode()
|
||||
.bits(config.lctrl_mode as u8)
|
||||
.ch1_neg_mode()
|
||||
.bits(config.neg_edge as u8)
|
||||
.ch1_pos_mode()
|
||||
.bits(config.pos_edge as u8)
|
||||
});
|
||||
}
|
||||
}
|
||||
self.set_ctrl_signal(ctrl_signal, config.invert_ctrl);
|
||||
self.set_edge_signal(edge_signal, config.invert_sig);
|
||||
}
|
||||
|
||||
/// Set the control signal (pin/high/low) for this channel
|
||||
pub fn set_ctrl_signal(&self, source: PcntSource, invert: bool) -> &Self {
|
||||
let signal = match self.unit {
|
||||
unit::Number::Unit0 => match self.channel {
|
||||
Number::Channel0 => InputSignal::PCNT0_CTRL_CH0,
|
||||
Number::Channel1 => InputSignal::PCNT0_CTRL_CH1,
|
||||
},
|
||||
unit::Number::Unit1 => match self.channel {
|
||||
Number::Channel0 => InputSignal::PCNT1_CTRL_CH0,
|
||||
Number::Channel1 => InputSignal::PCNT1_CTRL_CH1,
|
||||
},
|
||||
unit::Number::Unit2 => match self.channel {
|
||||
Number::Channel0 => InputSignal::PCNT2_CTRL_CH0,
|
||||
Number::Channel1 => InputSignal::PCNT2_CTRL_CH1,
|
||||
},
|
||||
unit::Number::Unit3 => match self.channel {
|
||||
Number::Channel0 => InputSignal::PCNT3_CTRL_CH0,
|
||||
Number::Channel1 => InputSignal::PCNT3_CTRL_CH1,
|
||||
},
|
||||
#[cfg(esp32)]
|
||||
unit::Number::Unit4 => match self.channel {
|
||||
Number::Channel0 => InputSignal::PCNT4_CTRL_CH0,
|
||||
Number::Channel1 => InputSignal::PCNT4_CTRL_CH1,
|
||||
},
|
||||
#[cfg(esp32)]
|
||||
unit::Number::Unit5 => match self.channel {
|
||||
Number::Channel0 => InputSignal::PCNT5_CTRL_CH0,
|
||||
Number::Channel1 => InputSignal::PCNT5_CTRL_CH1,
|
||||
},
|
||||
#[cfg(esp32)]
|
||||
unit::Number::Unit6 => match self.channel {
|
||||
Number::Channel0 => InputSignal::PCNT6_CTRL_CH0,
|
||||
Number::Channel1 => InputSignal::PCNT6_CTRL_CH1,
|
||||
},
|
||||
#[cfg(esp32)]
|
||||
unit::Number::Unit7 => match self.channel {
|
||||
Number::Channel0 => InputSignal::PCNT7_CTRL_CH0,
|
||||
Number::Channel1 => InputSignal::PCNT7_CTRL_CH1,
|
||||
},
|
||||
};
|
||||
|
||||
if (signal as usize) <= crate::types::INPUT_SIGNAL_MAX as usize {
|
||||
unsafe { &*GPIO::PTR }.func_in_sel_cfg[signal as usize].modify(|_, w| unsafe {
|
||||
w.sel()
|
||||
.set_bit()
|
||||
.in_inv_sel()
|
||||
.bit(invert)
|
||||
.in_sel()
|
||||
.bits(source.source)
|
||||
});
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the edge signal (pin/high/low) for this channel
|
||||
pub fn set_edge_signal(&self, source: PcntSource, invert: bool) -> &Self {
|
||||
let signal = match self.unit {
|
||||
unit::Number::Unit0 => match self.channel {
|
||||
Number::Channel0 => InputSignal::PCNT0_SIG_CH0,
|
||||
Number::Channel1 => InputSignal::PCNT0_SIG_CH1,
|
||||
},
|
||||
unit::Number::Unit1 => match self.channel {
|
||||
Number::Channel0 => InputSignal::PCNT1_SIG_CH0,
|
||||
Number::Channel1 => InputSignal::PCNT1_SIG_CH1,
|
||||
},
|
||||
unit::Number::Unit2 => match self.channel {
|
||||
Number::Channel0 => InputSignal::PCNT2_SIG_CH0,
|
||||
Number::Channel1 => InputSignal::PCNT2_SIG_CH1,
|
||||
},
|
||||
unit::Number::Unit3 => match self.channel {
|
||||
Number::Channel0 => InputSignal::PCNT3_SIG_CH0,
|
||||
Number::Channel1 => InputSignal::PCNT3_SIG_CH1,
|
||||
},
|
||||
#[cfg(esp32)]
|
||||
unit::Number::Unit4 => match self.channel {
|
||||
Number::Channel0 => InputSignal::PCNT4_SIG_CH0,
|
||||
Number::Channel1 => InputSignal::PCNT4_SIG_CH1,
|
||||
},
|
||||
#[cfg(esp32)]
|
||||
unit::Number::Unit5 => match self.channel {
|
||||
Number::Channel0 => InputSignal::PCNT5_SIG_CH0,
|
||||
Number::Channel1 => InputSignal::PCNT5_SIG_CH1,
|
||||
},
|
||||
#[cfg(esp32)]
|
||||
unit::Number::Unit6 => match self.channel {
|
||||
Number::Channel0 => InputSignal::PCNT6_SIG_CH0,
|
||||
Number::Channel1 => InputSignal::PCNT6_SIG_CH1,
|
||||
},
|
||||
#[cfg(esp32)]
|
||||
unit::Number::Unit7 => match self.channel {
|
||||
Number::Channel0 => InputSignal::PCNT7_SIG_CH0,
|
||||
Number::Channel1 => InputSignal::PCNT7_SIG_CH1,
|
||||
},
|
||||
};
|
||||
|
||||
if (signal as usize) <= crate::types::INPUT_SIGNAL_MAX as usize {
|
||||
unsafe { &*GPIO::PTR }.func_in_sel_cfg[signal as usize].modify(|_, w| unsafe {
|
||||
w.sel()
|
||||
.set_bit()
|
||||
.in_inv_sel()
|
||||
.bit(invert)
|
||||
.in_sel()
|
||||
.bits(source.source)
|
||||
});
|
||||
}
|
||||
self
|
||||
}
|
||||
}
|
||||
30
esp-hal-common/src/pcnt/mod.rs
Normal file
30
esp-hal-common/src/pcnt/mod.rs
Normal file
@ -0,0 +1,30 @@
|
||||
use self::unit::Unit;
|
||||
use crate::{
|
||||
peripheral::{Peripheral, PeripheralRef},
|
||||
system::PeripheralClockControl,
|
||||
};
|
||||
|
||||
pub mod channel;
|
||||
pub mod unit;
|
||||
|
||||
pub struct PCNT<'d> {
|
||||
_instance: PeripheralRef<'d, crate::peripherals::PCNT>,
|
||||
}
|
||||
|
||||
impl<'d> PCNT<'d> {
|
||||
/// Return a new PCNT
|
||||
pub fn new(
|
||||
_instance: impl Peripheral<P = crate::peripherals::PCNT> + 'd,
|
||||
peripheral_clock_control: &mut PeripheralClockControl,
|
||||
) -> Self {
|
||||
crate::into_ref!(_instance);
|
||||
// Enable the PCNT peripherals clock in the system peripheral
|
||||
peripheral_clock_control.enable(crate::system::Peripheral::Pcnt);
|
||||
PCNT { _instance }
|
||||
}
|
||||
|
||||
/// Return a unit
|
||||
pub fn get_unit(&self, number: unit::Number) -> Unit {
|
||||
Unit::new(number)
|
||||
}
|
||||
}
|
||||
392
esp-hal-common/src/pcnt/unit.rs
Normal file
392
esp-hal-common/src/pcnt/unit.rs
Normal file
@ -0,0 +1,392 @@
|
||||
use critical_section::CriticalSection;
|
||||
|
||||
use super::channel;
|
||||
|
||||
/// Unit number
|
||||
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
|
||||
pub enum Number {
|
||||
Unit0,
|
||||
Unit1,
|
||||
Unit2,
|
||||
Unit3,
|
||||
#[cfg(esp32)]
|
||||
Unit4,
|
||||
#[cfg(esp32)]
|
||||
Unit5,
|
||||
#[cfg(esp32)]
|
||||
Unit6,
|
||||
#[cfg(esp32)]
|
||||
Unit7,
|
||||
}
|
||||
|
||||
/// Unit errors
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
/// Invalid filter threshold value
|
||||
InvalidFilterThresh,
|
||||
/// Invalid low limit - must be < 0
|
||||
InvalidLowLimit,
|
||||
/// Invalid high limit - must be > 0
|
||||
InvalidHighLimit,
|
||||
}
|
||||
|
||||
/// the current status of the counter.
|
||||
#[derive(Copy, Clone, Debug, Default)]
|
||||
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?)
|
||||
Negitive = 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::Negitive,
|
||||
3 => Self::Positive,
|
||||
_ => unreachable!(), // TODO: is this good enoough? should we use some default?
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Events
|
||||
#[derive(Copy, Clone, Debug, Default)]
|
||||
pub struct Events {
|
||||
pub low_limit: bool,
|
||||
pub high_limit: bool,
|
||||
pub thresh0: bool,
|
||||
pub thresh1: bool,
|
||||
pub zero: bool,
|
||||
}
|
||||
|
||||
/// Unit configuration
|
||||
#[derive(Copy, Clone, Default)]
|
||||
pub struct Config {
|
||||
pub low_limit: i16,
|
||||
pub high_limit: i16,
|
||||
pub thresh0: i16,
|
||||
pub thresh1: i16,
|
||||
pub filter: Option<u16>,
|
||||
}
|
||||
|
||||
pub struct Unit {
|
||||
number: Number,
|
||||
}
|
||||
|
||||
impl Unit {
|
||||
/// return a new Unit
|
||||
pub(super) fn new(number: Number) -> Self {
|
||||
let pcnt = unsafe { &*crate::peripherals::PCNT::ptr() };
|
||||
let conf0 = match number {
|
||||
Number::Unit0 => &pcnt.u0_conf0,
|
||||
Number::Unit1 => &pcnt.u1_conf0,
|
||||
Number::Unit2 => &pcnt.u2_conf0,
|
||||
Number::Unit3 => &pcnt.u3_conf0,
|
||||
#[cfg(esp32)]
|
||||
Number::Unit4 => &pcnt.u4_conf0,
|
||||
#[cfg(esp32)]
|
||||
Number::Unit5 => &pcnt.u5_conf0,
|
||||
#[cfg(esp32)]
|
||||
Number::Unit6 => &pcnt.u6_conf0,
|
||||
#[cfg(esp32)]
|
||||
Number::Unit7 => &pcnt.u7_conf0,
|
||||
};
|
||||
// disable filter and all events
|
||||
conf0.modify(|_, w| unsafe {
|
||||
w.filter_en()
|
||||
.clear_bit()
|
||||
.filter_thres()
|
||||
.bits(0)
|
||||
.thr_l_lim_en()
|
||||
.clear_bit()
|
||||
.thr_h_lim_en()
|
||||
.clear_bit()
|
||||
.thr_thres0_en()
|
||||
.clear_bit()
|
||||
.thr_thres1_en()
|
||||
.clear_bit()
|
||||
.thr_zero_en()
|
||||
.clear_bit()
|
||||
});
|
||||
Self { number }
|
||||
}
|
||||
|
||||
pub fn configure(&mut self, config: Config) -> Result<(), Error> {
|
||||
// low limit must be >= or the limit is -32768 and when thats
|
||||
// hit the event status claims it was the high limit.
|
||||
// tested on an esp32s3
|
||||
if config.low_limit >= 0 {
|
||||
return Err(Error::InvalidLowLimit);
|
||||
}
|
||||
if config.high_limit <= 0 {
|
||||
return Err(Error::InvalidHighLimit);
|
||||
}
|
||||
let (filter_en, filter) = match config.filter {
|
||||
Some(filter) => (true, filter),
|
||||
None => (false, 0),
|
||||
};
|
||||
// filter must be less than 1024
|
||||
if filter > 1023 {
|
||||
return Err(Error::InvalidFilterThresh);
|
||||
}
|
||||
|
||||
let pcnt = unsafe { &*crate::peripherals::PCNT::ptr() };
|
||||
let (conf0, conf1, conf2) = match self.number {
|
||||
Number::Unit0 => (&pcnt.u0_conf0, &pcnt.u0_conf1, &pcnt.u0_conf2),
|
||||
Number::Unit1 => (&pcnt.u1_conf0, &pcnt.u1_conf1, &pcnt.u1_conf2),
|
||||
Number::Unit2 => (&pcnt.u2_conf0, &pcnt.u2_conf1, &pcnt.u2_conf2),
|
||||
Number::Unit3 => (&pcnt.u3_conf0, &pcnt.u3_conf1, &pcnt.u3_conf2),
|
||||
#[cfg(esp32)]
|
||||
Number::Unit4 => (&pcnt.u4_conf0, &pcnt.u4_conf1, &pcnt.u4_conf2),
|
||||
#[cfg(esp32)]
|
||||
Number::Unit5 => (&pcnt.u5_conf0, &pcnt.u5_conf1, &pcnt.u5_conf2),
|
||||
#[cfg(esp32)]
|
||||
Number::Unit6 => (&pcnt.u6_conf0, &pcnt.u6_conf1, &pcnt.u6_conf2),
|
||||
#[cfg(esp32)]
|
||||
Number::Unit7 => (&pcnt.u7_conf0, &pcnt.u7_conf1, &pcnt.u7_conf2),
|
||||
};
|
||||
conf2.write(|w| unsafe {
|
||||
w.cnt_l_lim()
|
||||
.bits(config.low_limit as u16)
|
||||
.cnt_h_lim()
|
||||
.bits(config.high_limit as u16)
|
||||
});
|
||||
conf1.write(|w| unsafe {
|
||||
w.cnt_thres0()
|
||||
.bits(config.thresh0 as u16)
|
||||
.cnt_thres1()
|
||||
.bits(config.thresh1 as u16)
|
||||
});
|
||||
conf0.modify(|_, w| unsafe { w.filter_thres().bits(filter).filter_en().bit(filter_en) });
|
||||
self.pause();
|
||||
self.clear();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_channel(&self, number: channel::Number) -> super::channel::Channel {
|
||||
super::channel::Channel::new(self.number, number)
|
||||
}
|
||||
|
||||
pub fn clear(&self) {
|
||||
let pcnt = unsafe { &*crate::peripherals::PCNT::ptr() };
|
||||
critical_section::with(|_cs| {
|
||||
match self.number {
|
||||
Number::Unit0 => pcnt.ctrl.modify(|_, w| w.cnt_rst_u0().set_bit()),
|
||||
Number::Unit1 => pcnt.ctrl.modify(|_, w| w.cnt_rst_u1().set_bit()),
|
||||
Number::Unit2 => pcnt.ctrl.modify(|_, w| w.cnt_rst_u2().set_bit()),
|
||||
Number::Unit3 => pcnt.ctrl.modify(|_, w| w.cnt_rst_u3().set_bit()),
|
||||
#[cfg(esp32)]
|
||||
Number::Unit4 => pcnt.ctrl.modify(|_, w| w.cnt_rst_u4().set_bit()),
|
||||
#[cfg(esp32)]
|
||||
Number::Unit5 => pcnt.ctrl.modify(|_, w| w.cnt_rst_u5().set_bit()),
|
||||
#[cfg(esp32)]
|
||||
Number::Unit6 => pcnt.ctrl.modify(|_, w| w.cnt_rst_u6().set_bit()),
|
||||
#[cfg(esp32)]
|
||||
Number::Unit7 => pcnt.ctrl.modify(|_, w| w.cnt_rst_u7().set_bit()),
|
||||
}
|
||||
// TODO: does this need a delay? (liebman / Jan 2 2023)
|
||||
match self.number {
|
||||
Number::Unit0 => pcnt.ctrl.modify(|_, w| w.cnt_rst_u0().clear_bit()),
|
||||
Number::Unit1 => pcnt.ctrl.modify(|_, w| w.cnt_rst_u1().clear_bit()),
|
||||
Number::Unit2 => pcnt.ctrl.modify(|_, w| w.cnt_rst_u2().clear_bit()),
|
||||
Number::Unit3 => pcnt.ctrl.modify(|_, w| w.cnt_rst_u3().clear_bit()),
|
||||
#[cfg(esp32)]
|
||||
Number::Unit4 => pcnt.ctrl.modify(|_, w| w.cnt_rst_u4().clear_bit()),
|
||||
#[cfg(esp32)]
|
||||
Number::Unit5 => pcnt.ctrl.modify(|_, w| w.cnt_rst_u5().clear_bit()),
|
||||
#[cfg(esp32)]
|
||||
Number::Unit6 => pcnt.ctrl.modify(|_, w| w.cnt_rst_u6().clear_bit()),
|
||||
#[cfg(esp32)]
|
||||
Number::Unit7 => pcnt.ctrl.modify(|_, w| w.cnt_rst_u7().clear_bit()),
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// Pause the counter
|
||||
pub fn pause(&self) {
|
||||
let pcnt = unsafe { &*crate::peripherals::PCNT::ptr() };
|
||||
critical_section::with(|_cs| match self.number {
|
||||
Number::Unit0 => pcnt.ctrl.modify(|_, w| w.cnt_pause_u0().set_bit()),
|
||||
Number::Unit1 => pcnt.ctrl.modify(|_, w| w.cnt_pause_u1().set_bit()),
|
||||
Number::Unit2 => pcnt.ctrl.modify(|_, w| w.cnt_pause_u2().set_bit()),
|
||||
Number::Unit3 => pcnt.ctrl.modify(|_, w| w.cnt_pause_u3().set_bit()),
|
||||
#[cfg(esp32)]
|
||||
Number::Unit4 => pcnt.ctrl.modify(|_, w| w.cnt_pause_u4().set_bit()),
|
||||
#[cfg(esp32)]
|
||||
Number::Unit5 => pcnt.ctrl.modify(|_, w| w.cnt_pause_u5().set_bit()),
|
||||
#[cfg(esp32)]
|
||||
Number::Unit6 => pcnt.ctrl.modify(|_, w| w.cnt_pause_u6().set_bit()),
|
||||
#[cfg(esp32)]
|
||||
Number::Unit7 => pcnt.ctrl.modify(|_, w| w.cnt_pause_u7().set_bit()),
|
||||
});
|
||||
}
|
||||
|
||||
/// Resume the counter
|
||||
pub fn resume(&self) {
|
||||
let pcnt = unsafe { &*crate::peripherals::PCNT::ptr() };
|
||||
critical_section::with(|_cs| match self.number {
|
||||
Number::Unit0 => pcnt.ctrl.modify(|_, w| w.cnt_pause_u0().clear_bit()),
|
||||
Number::Unit1 => pcnt.ctrl.modify(|_, w| w.cnt_pause_u1().clear_bit()),
|
||||
Number::Unit2 => pcnt.ctrl.modify(|_, w| w.cnt_pause_u2().clear_bit()),
|
||||
Number::Unit3 => pcnt.ctrl.modify(|_, w| w.cnt_pause_u3().clear_bit()),
|
||||
#[cfg(esp32)]
|
||||
Number::Unit4 => pcnt.ctrl.modify(|_, w| w.cnt_pause_u4().clear_bit()),
|
||||
#[cfg(esp32)]
|
||||
Number::Unit5 => pcnt.ctrl.modify(|_, w| w.cnt_pause_u5().clear_bit()),
|
||||
#[cfg(esp32)]
|
||||
Number::Unit6 => pcnt.ctrl.modify(|_, w| w.cnt_pause_u6().clear_bit()),
|
||||
#[cfg(esp32)]
|
||||
Number::Unit7 => pcnt.ctrl.modify(|_, w| w.cnt_pause_u7().clear_bit()),
|
||||
});
|
||||
}
|
||||
|
||||
/// Enable which events generate interrupts on this unit.
|
||||
pub fn events(&self, events: Events) {
|
||||
let pcnt = unsafe { &*crate::peripherals::PCNT::ptr() };
|
||||
let conf0 = match self.number {
|
||||
Number::Unit0 => &pcnt.u0_conf0,
|
||||
Number::Unit1 => &pcnt.u1_conf0,
|
||||
Number::Unit2 => &pcnt.u2_conf0,
|
||||
Number::Unit3 => &pcnt.u3_conf0,
|
||||
#[cfg(esp32)]
|
||||
Number::Unit4 => &pcnt.u4_conf0,
|
||||
#[cfg(esp32)]
|
||||
Number::Unit5 => &pcnt.u5_conf0,
|
||||
#[cfg(esp32)]
|
||||
Number::Unit6 => &pcnt.u6_conf0,
|
||||
#[cfg(esp32)]
|
||||
Number::Unit7 => &pcnt.u7_conf0,
|
||||
};
|
||||
conf0.modify(|_, w| {
|
||||
w.thr_l_lim_en()
|
||||
.bit(events.low_limit)
|
||||
.thr_h_lim_en()
|
||||
.bit(events.high_limit)
|
||||
.thr_thres0_en()
|
||||
.bit(events.thresh0)
|
||||
.thr_thres1_en()
|
||||
.bit(events.thresh1)
|
||||
.thr_zero_en()
|
||||
.bit(events.zero)
|
||||
});
|
||||
}
|
||||
|
||||
/// Get the latest events for this unit.
|
||||
pub fn get_events(&self) -> Events {
|
||||
let pcnt = unsafe { &*crate::peripherals::PCNT::ptr() };
|
||||
let status = pcnt.u_status[self.number as usize].read();
|
||||
|
||||
Events {
|
||||
low_limit: status.l_lim().bit(),
|
||||
high_limit: status.h_lim().bit(),
|
||||
thresh0: status.thres0().bit(),
|
||||
thresh1: status.thres1().bit(),
|
||||
zero: status.zero().bit(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the mode of the last zero crossing
|
||||
pub fn get_zero_mode(&self) -> ZeroMode {
|
||||
let pcnt = unsafe { &*crate::peripherals::PCNT::ptr() };
|
||||
pcnt.u_status[self.number as usize]
|
||||
.read()
|
||||
.zero_mode()
|
||||
.bits()
|
||||
.into()
|
||||
}
|
||||
|
||||
/// Enable interrupts for this unit.
|
||||
pub fn listen(&self) {
|
||||
let pcnt = unsafe { &*crate::peripherals::PCNT::ptr() };
|
||||
critical_section::with(|_cs| {
|
||||
pcnt.int_ena.modify(|_, w| match self.number {
|
||||
Number::Unit0 => w.cnt_thr_event_u0().set_bit(),
|
||||
Number::Unit1 => w.cnt_thr_event_u1().set_bit(),
|
||||
Number::Unit2 => w.cnt_thr_event_u2().set_bit(),
|
||||
Number::Unit3 => w.cnt_thr_event_u3().set_bit(),
|
||||
#[cfg(esp32)]
|
||||
Number::Unit4 => w.cnt_thr_event_u4().set_bit(),
|
||||
#[cfg(esp32)]
|
||||
Number::Unit5 => w.cnt_thr_event_u5().set_bit(),
|
||||
#[cfg(esp32)]
|
||||
Number::Unit6 => w.cnt_thr_event_u6().set_bit(),
|
||||
#[cfg(esp32)]
|
||||
Number::Unit7 => w.cnt_thr_event_u7().set_bit(),
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
/// Disable interrupts for this unit.
|
||||
pub fn unlisten(&self, _cs: CriticalSection) {
|
||||
let pcnt = unsafe { &*crate::peripherals::PCNT::ptr() };
|
||||
critical_section::with(|_cs| {
|
||||
pcnt.int_ena.write(|w| match self.number {
|
||||
Number::Unit0 => w.cnt_thr_event_u0().clear_bit(),
|
||||
Number::Unit1 => w.cnt_thr_event_u1().clear_bit(),
|
||||
Number::Unit2 => w.cnt_thr_event_u2().clear_bit(),
|
||||
Number::Unit3 => w.cnt_thr_event_u3().clear_bit(),
|
||||
#[cfg(esp32)]
|
||||
Number::Unit4 => w.cnt_thr_event_u4().clear_bit(),
|
||||
#[cfg(esp32)]
|
||||
Number::Unit5 => w.cnt_thr_event_u5().clear_bit(),
|
||||
#[cfg(esp32)]
|
||||
Number::Unit6 => w.cnt_thr_event_u6().clear_bit(),
|
||||
#[cfg(esp32)]
|
||||
Number::Unit7 => w.cnt_thr_event_u7().clear_bit(),
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
/// Returns true if an interrupt is active for this unit.
|
||||
pub fn interrupt_set(&self) -> bool {
|
||||
let pcnt = unsafe { &*crate::peripherals::PCNT::ptr() };
|
||||
match self.number {
|
||||
Number::Unit0 => pcnt.int_st.read().cnt_thr_event_u0().bit(),
|
||||
Number::Unit1 => pcnt.int_st.read().cnt_thr_event_u1().bit(),
|
||||
Number::Unit2 => pcnt.int_st.read().cnt_thr_event_u2().bit(),
|
||||
Number::Unit3 => pcnt.int_st.read().cnt_thr_event_u3().bit(),
|
||||
#[cfg(esp32)]
|
||||
Number::Unit4 => pcnt.int_st.read().cnt_thr_event_u4().bit(),
|
||||
#[cfg(esp32)]
|
||||
Number::Unit5 => pcnt.int_st.read().cnt_thr_event_u5().bit(),
|
||||
#[cfg(esp32)]
|
||||
Number::Unit6 => pcnt.int_st.read().cnt_thr_event_u6().bit(),
|
||||
#[cfg(esp32)]
|
||||
Number::Unit7 => pcnt.int_st.read().cnt_thr_event_u7().bit(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Clear the interrupt bit for this unit.
|
||||
pub fn reset_interrupt(&self) {
|
||||
let pcnt = unsafe { &*crate::peripherals::PCNT::ptr() };
|
||||
critical_section::with(|_cs| {
|
||||
pcnt.int_clr.write(|w| match self.number {
|
||||
Number::Unit0 => w.cnt_thr_event_u0().set_bit(),
|
||||
Number::Unit1 => w.cnt_thr_event_u1().set_bit(),
|
||||
Number::Unit2 => w.cnt_thr_event_u2().set_bit(),
|
||||
Number::Unit3 => w.cnt_thr_event_u3().set_bit(),
|
||||
#[cfg(esp32)]
|
||||
Number::Unit4 => w.cnt_thr_event_u4().set_bit(),
|
||||
#[cfg(esp32)]
|
||||
Number::Unit5 => w.cnt_thr_event_u5().set_bit(),
|
||||
#[cfg(esp32)]
|
||||
Number::Unit6 => w.cnt_thr_event_u6().set_bit(),
|
||||
#[cfg(esp32)]
|
||||
Number::Unit7 => w.cnt_thr_event_u7().set_bit(),
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
/// Get the current counter value.
|
||||
pub fn get_value(&self) -> i16 {
|
||||
let pcnt = unsafe { &*crate::peripherals::PCNT::ptr() };
|
||||
pcnt.u_cnt[self.number as usize].read().cnt().bits() as i16
|
||||
}
|
||||
}
|
||||
@ -30,6 +30,8 @@ pub enum Peripheral {
|
||||
Mcpwm0,
|
||||
#[cfg(any(esp32, esp32s3))]
|
||||
Mcpwm1,
|
||||
#[cfg(any(esp32, esp32s2, esp32s3))]
|
||||
Pcnt,
|
||||
#[cfg(any(esp32c2, esp32c3))]
|
||||
ApbSarAdc,
|
||||
#[cfg(gdma)]
|
||||
@ -108,6 +110,11 @@ impl PeripheralClockControl {
|
||||
perip_clk_en0.modify(|_, w| w.pwm1_clk_en().set_bit());
|
||||
perip_rst_en0.modify(|_, w| w.pwm1_rst().clear_bit());
|
||||
}
|
||||
#[cfg(any(esp32, esp32s2, esp32s3))]
|
||||
Peripheral::Pcnt => {
|
||||
perip_clk_en0.modify(|_, w| w.pcnt_clk_en().set_bit());
|
||||
perip_rst_en0.modify(|_, w| w.pcnt_rst().clear_bit());
|
||||
}
|
||||
#[cfg(any(esp32c2, esp32c3))]
|
||||
Peripheral::ApbSarAdc => {
|
||||
perip_clk_en0.modify(|_, w| w.apb_saradc_clk_en().set_bit());
|
||||
|
||||
144
esp32-hal/examples/pcnt_encoder.rs
Normal file
144
esp32-hal/examples/pcnt_encoder.rs
Normal file
@ -0,0 +1,144 @@
|
||||
//! PCNT Encoder Demo
|
||||
//!
|
||||
//! This example decodes a quadrature encoder
|
||||
//!
|
||||
//! Since the PCNT units reset to zero when they reach their limits
|
||||
//! we enable an interrupt on the upper and lower limits and
|
||||
//! track the overflow in an AtomicI32
|
||||
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
use core::{
|
||||
cell::RefCell,
|
||||
cmp::min,
|
||||
sync::atomic::{AtomicI32, Ordering},
|
||||
};
|
||||
|
||||
use critical_section::Mutex;
|
||||
use esp32_hal as esp_hal;
|
||||
use esp_backtrace as _;
|
||||
use esp_hal::{
|
||||
clock::ClockControl,
|
||||
interrupt,
|
||||
pcnt::{channel, channel::PcntSource, unit, PCNT},
|
||||
peripherals::{self, Peripherals},
|
||||
prelude::*,
|
||||
timer::TimerGroup,
|
||||
Rtc,
|
||||
IO,
|
||||
};
|
||||
use esp_println::println;
|
||||
use xtensa_lx_rt::entry;
|
||||
|
||||
static UNIT0: Mutex<RefCell<Option<unit::Unit>>> = Mutex::new(RefCell::new(None));
|
||||
static VALUE: AtomicI32 = AtomicI32::new(0);
|
||||
|
||||
#[entry]
|
||||
fn main() -> ! {
|
||||
let peripherals = Peripherals::take();
|
||||
let mut system = peripherals.DPORT.split();
|
||||
let clocks = ClockControl::boot_defaults(system.clock_control).freeze();
|
||||
|
||||
let mut rtc = Rtc::new(peripherals.RTC_CNTL);
|
||||
let timer_group0 = TimerGroup::new(peripherals.TIMG0, &clocks);
|
||||
let mut wdt = timer_group0.wdt;
|
||||
let io = IO::new(peripherals.GPIO, peripherals.IO_MUX);
|
||||
|
||||
// Disable MWDT and RWDT (Watchdog) flash boot protection
|
||||
wdt.disable();
|
||||
rtc.rwdt.disable();
|
||||
|
||||
let unit_number = unit::Number::Unit1;
|
||||
|
||||
// setup a pulse couter
|
||||
println!("setup pulse counter unit 0");
|
||||
let pcnt = PCNT::new(peripherals.PCNT, &mut system.peripheral_clock_control);
|
||||
let mut u0 = pcnt.get_unit(unit_number);
|
||||
u0.configure(unit::Config {
|
||||
low_limit: -100,
|
||||
high_limit: 100,
|
||||
filter: Some(min(10u16 * 80, 1023u16)),
|
||||
..Default::default()
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
println!("setup channel 0");
|
||||
let mut ch0 = u0.get_channel(channel::Number::Channel0);
|
||||
let mut pin_a = io.pins.gpio22.into_pull_up_input();
|
||||
let mut pin_b = io.pins.gpio23.into_pull_up_input();
|
||||
|
||||
ch0.configure(
|
||||
PcntSource::from_pin(&mut pin_a),
|
||||
PcntSource::from_pin(&mut pin_b),
|
||||
channel::Config {
|
||||
lctrl_mode: channel::CtrlMode::Reverse,
|
||||
hctrl_mode: channel::CtrlMode::Keep,
|
||||
pos_edge: channel::EdgeMode::Decrement,
|
||||
neg_edge: channel::EdgeMode::Increment,
|
||||
invert_ctrl: false,
|
||||
invert_sig: false,
|
||||
},
|
||||
);
|
||||
|
||||
println!("setup channel 1");
|
||||
let mut ch1 = u0.get_channel(channel::Number::Channel1);
|
||||
ch1.configure(
|
||||
PcntSource::from_pin(&mut pin_b),
|
||||
PcntSource::from_pin(&mut pin_a),
|
||||
channel::Config {
|
||||
lctrl_mode: channel::CtrlMode::Reverse,
|
||||
hctrl_mode: channel::CtrlMode::Keep,
|
||||
pos_edge: channel::EdgeMode::Increment,
|
||||
neg_edge: channel::EdgeMode::Decrement,
|
||||
invert_ctrl: false,
|
||||
invert_sig: false,
|
||||
},
|
||||
);
|
||||
println!("subscribing to events");
|
||||
u0.events(unit::Events {
|
||||
low_limit: true,
|
||||
high_limit: true,
|
||||
thresh0: false,
|
||||
thresh1: false,
|
||||
zero: false,
|
||||
});
|
||||
|
||||
println!("enabling interrupts");
|
||||
u0.listen();
|
||||
println!("resume pulse counter unit 0");
|
||||
u0.resume();
|
||||
|
||||
critical_section::with(|cs| UNIT0.borrow_ref_mut(cs).replace(u0));
|
||||
|
||||
interrupt::enable(peripherals::Interrupt::PCNT, interrupt::Priority::Priority2).unwrap();
|
||||
|
||||
let mut last_value: i32 = 0;
|
||||
loop {
|
||||
critical_section::with(|cs| {
|
||||
let mut u0 = UNIT0.borrow_ref_mut(cs);
|
||||
let u0 = u0.as_mut().unwrap();
|
||||
let value: i32 = u0.get_value() as i32 + VALUE.load(Ordering::SeqCst);
|
||||
if value != last_value {
|
||||
println!("value: {value}");
|
||||
last_value = value;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[interrupt]
|
||||
fn PCNT() {
|
||||
critical_section::with(|cs| {
|
||||
let mut u0 = UNIT0.borrow_ref_mut(cs);
|
||||
let u0 = u0.as_mut().unwrap();
|
||||
if u0.interrupt_set() {
|
||||
let events = u0.get_events();
|
||||
if events.high_limit {
|
||||
VALUE.fetch_add(100, Ordering::SeqCst);
|
||||
} else if events.low_limit {
|
||||
VALUE.fetch_add(-100, Ordering::SeqCst);
|
||||
}
|
||||
u0.reset_interrupt();
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -19,6 +19,7 @@ pub use esp_hal_common::{
|
||||
ledc,
|
||||
macros,
|
||||
mcpwm,
|
||||
pcnt,
|
||||
peripherals,
|
||||
prelude,
|
||||
pulse_control,
|
||||
|
||||
144
esp32s2-hal/examples/pcnt_encoder.rs
Normal file
144
esp32s2-hal/examples/pcnt_encoder.rs
Normal file
@ -0,0 +1,144 @@
|
||||
//! PCNT Encoder Demo
|
||||
//!
|
||||
//! This example decodes a quadrature encoder
|
||||
//!
|
||||
//! Since the PCNT units reset to zero when they reach their limits
|
||||
//! we enable an interrupt on the upper and lower limits and
|
||||
//! track the overflow in an AtomicI32
|
||||
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
use core::{
|
||||
cell::RefCell,
|
||||
cmp::min,
|
||||
sync::atomic::{AtomicI32, Ordering},
|
||||
};
|
||||
|
||||
use critical_section::Mutex;
|
||||
use esp32s2_hal as esp_hal;
|
||||
use esp_backtrace as _;
|
||||
use esp_hal::{
|
||||
clock::ClockControl,
|
||||
interrupt,
|
||||
pcnt::{channel, channel::PcntSource, unit, PCNT},
|
||||
peripherals::{self, Peripherals},
|
||||
prelude::*,
|
||||
timer::TimerGroup,
|
||||
Rtc,
|
||||
IO,
|
||||
};
|
||||
use esp_println::println;
|
||||
use xtensa_lx_rt::entry;
|
||||
|
||||
static UNIT0: Mutex<RefCell<Option<unit::Unit>>> = Mutex::new(RefCell::new(None));
|
||||
static VALUE: AtomicI32 = AtomicI32::new(0);
|
||||
|
||||
#[entry]
|
||||
fn main() -> ! {
|
||||
let peripherals = Peripherals::take();
|
||||
let mut system = peripherals.SYSTEM.split();
|
||||
let clocks = ClockControl::boot_defaults(system.clock_control).freeze();
|
||||
|
||||
let mut rtc = Rtc::new(peripherals.RTC_CNTL);
|
||||
let timer_group0 = TimerGroup::new(peripherals.TIMG0, &clocks);
|
||||
let mut wdt = timer_group0.wdt;
|
||||
let io = IO::new(peripherals.GPIO, peripherals.IO_MUX);
|
||||
|
||||
// Disable MWDT and RWDT (Watchdog) flash boot protection
|
||||
wdt.disable();
|
||||
rtc.rwdt.disable();
|
||||
|
||||
let unit_number = unit::Number::Unit1;
|
||||
|
||||
// setup a pulse couter
|
||||
println!("setup pulse counter unit 0");
|
||||
let pcnt = PCNT::new(peripherals.PCNT, &mut system.peripheral_clock_control);
|
||||
let mut u0 = pcnt.get_unit(unit_number);
|
||||
u0.configure(unit::Config {
|
||||
low_limit: -100,
|
||||
high_limit: 100,
|
||||
filter: Some(min(10u16 * 80, 1023u16)),
|
||||
..Default::default()
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
println!("setup channel 0");
|
||||
let mut ch0 = u0.get_channel(channel::Number::Channel0);
|
||||
let mut pin_a = io.pins.gpio5.into_pull_up_input();
|
||||
let mut pin_b = io.pins.gpio6.into_pull_up_input();
|
||||
|
||||
ch0.configure(
|
||||
PcntSource::from_pin(&mut pin_a),
|
||||
PcntSource::from_pin(&mut pin_b),
|
||||
channel::Config {
|
||||
lctrl_mode: channel::CtrlMode::Reverse,
|
||||
hctrl_mode: channel::CtrlMode::Keep,
|
||||
pos_edge: channel::EdgeMode::Decrement,
|
||||
neg_edge: channel::EdgeMode::Increment,
|
||||
invert_ctrl: false,
|
||||
invert_sig: false,
|
||||
},
|
||||
);
|
||||
|
||||
println!("setup channel 1");
|
||||
let mut ch1 = u0.get_channel(channel::Number::Channel1);
|
||||
ch1.configure(
|
||||
PcntSource::from_pin(&mut pin_b),
|
||||
PcntSource::from_pin(&mut pin_a),
|
||||
channel::Config {
|
||||
lctrl_mode: channel::CtrlMode::Reverse,
|
||||
hctrl_mode: channel::CtrlMode::Keep,
|
||||
pos_edge: channel::EdgeMode::Increment,
|
||||
neg_edge: channel::EdgeMode::Decrement,
|
||||
invert_ctrl: false,
|
||||
invert_sig: false,
|
||||
},
|
||||
);
|
||||
println!("subscribing to events");
|
||||
u0.events(unit::Events {
|
||||
low_limit: true,
|
||||
high_limit: true,
|
||||
thresh0: false,
|
||||
thresh1: false,
|
||||
zero: false,
|
||||
});
|
||||
|
||||
println!("enabling interrupts");
|
||||
u0.listen();
|
||||
println!("resume pulse counter unit 0");
|
||||
u0.resume();
|
||||
|
||||
critical_section::with(|cs| UNIT0.borrow_ref_mut(cs).replace(u0));
|
||||
|
||||
interrupt::enable(peripherals::Interrupt::PCNT, interrupt::Priority::Priority2).unwrap();
|
||||
|
||||
let mut last_value: i32 = 0;
|
||||
loop {
|
||||
critical_section::with(|cs| {
|
||||
let mut u0 = UNIT0.borrow_ref_mut(cs);
|
||||
let u0 = u0.as_mut().unwrap();
|
||||
let value: i32 = u0.get_value() as i32 + VALUE.load(Ordering::SeqCst);
|
||||
if value != last_value {
|
||||
println!("value: {value}");
|
||||
last_value = value;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[interrupt]
|
||||
fn PCNT() {
|
||||
critical_section::with(|cs| {
|
||||
let mut u0 = UNIT0.borrow_ref_mut(cs);
|
||||
let u0 = u0.as_mut().unwrap();
|
||||
if u0.interrupt_set() {
|
||||
let events = u0.get_events();
|
||||
if events.high_limit {
|
||||
VALUE.store(VALUE.load(Ordering::SeqCst) + 100, Ordering::SeqCst);
|
||||
} else if events.low_limit {
|
||||
VALUE.store(VALUE.load(Ordering::SeqCst) - 100, Ordering::SeqCst);
|
||||
}
|
||||
u0.reset_interrupt();
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -18,6 +18,7 @@ pub use esp_hal_common::{
|
||||
ledc,
|
||||
macros,
|
||||
otg_fs,
|
||||
pcnt,
|
||||
peripherals,
|
||||
prelude,
|
||||
pulse_control,
|
||||
|
||||
144
esp32s3-hal/examples/pcnt_encoder.rs
Normal file
144
esp32s3-hal/examples/pcnt_encoder.rs
Normal file
@ -0,0 +1,144 @@
|
||||
//! PCNT Encoder Demo
|
||||
//!
|
||||
//! This example decodes a quadrature encoder
|
||||
//!
|
||||
//! Since the PCNT units reset to zero when they reach their limits
|
||||
//! we enable an interrupt on the upper and lower limits and
|
||||
//! track the overflow in an AtomicI32
|
||||
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
use core::{
|
||||
cell::RefCell,
|
||||
cmp::min,
|
||||
sync::atomic::{AtomicI32, Ordering},
|
||||
};
|
||||
|
||||
use critical_section::Mutex;
|
||||
use esp32s3_hal as esp_hal;
|
||||
use esp_backtrace as _;
|
||||
use esp_hal::{
|
||||
clock::ClockControl,
|
||||
interrupt,
|
||||
pcnt::{channel, channel::PcntSource, unit, PCNT},
|
||||
peripherals::{self, Peripherals},
|
||||
prelude::*,
|
||||
timer::TimerGroup,
|
||||
Rtc,
|
||||
IO,
|
||||
};
|
||||
use esp_println::println;
|
||||
use xtensa_lx_rt::entry;
|
||||
|
||||
static UNIT0: Mutex<RefCell<Option<unit::Unit>>> = Mutex::new(RefCell::new(None));
|
||||
static VALUE: AtomicI32 = AtomicI32::new(0);
|
||||
|
||||
#[entry]
|
||||
fn main() -> ! {
|
||||
let peripherals = Peripherals::take();
|
||||
let mut system = peripherals.SYSTEM.split();
|
||||
let clocks = ClockControl::boot_defaults(system.clock_control).freeze();
|
||||
|
||||
let mut rtc = Rtc::new(peripherals.RTC_CNTL);
|
||||
let timer_group0 = TimerGroup::new(peripherals.TIMG0, &clocks);
|
||||
let mut wdt = timer_group0.wdt;
|
||||
let io = IO::new(peripherals.GPIO, peripherals.IO_MUX);
|
||||
|
||||
// Disable MWDT and RWDT (Watchdog) flash boot protection
|
||||
wdt.disable();
|
||||
rtc.rwdt.disable();
|
||||
|
||||
let unit_number = unit::Number::Unit1;
|
||||
|
||||
// setup a pulse couter
|
||||
println!("setup pulse counter unit 0");
|
||||
let pcnt = PCNT::new(peripherals.PCNT, &mut system.peripheral_clock_control);
|
||||
let mut u0 = pcnt.get_unit(unit_number);
|
||||
u0.configure(unit::Config {
|
||||
low_limit: -100,
|
||||
high_limit: 100,
|
||||
filter: Some(min(10u16 * 80, 1023u16)),
|
||||
..Default::default()
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
println!("setup channel 0");
|
||||
let mut ch0 = u0.get_channel(channel::Number::Channel0);
|
||||
let mut pin_a = io.pins.gpio5.into_pull_up_input();
|
||||
let mut pin_b = io.pins.gpio6.into_pull_up_input();
|
||||
|
||||
ch0.configure(
|
||||
PcntSource::from_pin(&mut pin_a),
|
||||
PcntSource::from_pin(&mut pin_b),
|
||||
channel::Config {
|
||||
lctrl_mode: channel::CtrlMode::Reverse,
|
||||
hctrl_mode: channel::CtrlMode::Keep,
|
||||
pos_edge: channel::EdgeMode::Decrement,
|
||||
neg_edge: channel::EdgeMode::Increment,
|
||||
invert_ctrl: false,
|
||||
invert_sig: false,
|
||||
},
|
||||
);
|
||||
|
||||
println!("setup channel 1");
|
||||
let mut ch1 = u0.get_channel(channel::Number::Channel1);
|
||||
ch1.configure(
|
||||
PcntSource::from_pin(&mut pin_b),
|
||||
PcntSource::from_pin(&mut pin_a),
|
||||
channel::Config {
|
||||
lctrl_mode: channel::CtrlMode::Reverse,
|
||||
hctrl_mode: channel::CtrlMode::Keep,
|
||||
pos_edge: channel::EdgeMode::Increment,
|
||||
neg_edge: channel::EdgeMode::Decrement,
|
||||
invert_ctrl: false,
|
||||
invert_sig: false,
|
||||
},
|
||||
);
|
||||
println!("subscribing to events");
|
||||
u0.events(unit::Events {
|
||||
low_limit: true,
|
||||
high_limit: true,
|
||||
thresh0: false,
|
||||
thresh1: false,
|
||||
zero: false,
|
||||
});
|
||||
|
||||
println!("enabling interrupts");
|
||||
u0.listen();
|
||||
println!("resume pulse counter unit 0");
|
||||
u0.resume();
|
||||
|
||||
critical_section::with(|cs| UNIT0.borrow_ref_mut(cs).replace(u0));
|
||||
|
||||
interrupt::enable(peripherals::Interrupt::PCNT, interrupt::Priority::Priority2).unwrap();
|
||||
|
||||
let mut last_value: i32 = 0;
|
||||
loop {
|
||||
critical_section::with(|cs| {
|
||||
let mut u0 = UNIT0.borrow_ref_mut(cs);
|
||||
let u0 = u0.as_mut().unwrap();
|
||||
let value: i32 = u0.get_value() as i32 + VALUE.load(Ordering::SeqCst);
|
||||
if value != last_value {
|
||||
println!("value: {value}");
|
||||
last_value = value;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[interrupt]
|
||||
fn PCNT() {
|
||||
critical_section::with(|cs| {
|
||||
let mut u0 = UNIT0.borrow_ref_mut(cs);
|
||||
let u0 = u0.as_mut().unwrap();
|
||||
if u0.interrupt_set() {
|
||||
let events = u0.get_events();
|
||||
if events.high_limit {
|
||||
VALUE.fetch_add(100, Ordering::SeqCst);
|
||||
} else if events.low_limit {
|
||||
VALUE.fetch_add(-100, Ordering::SeqCst);
|
||||
}
|
||||
u0.reset_interrupt();
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -20,6 +20,7 @@ pub use esp_hal_common::{
|
||||
macros,
|
||||
mcpwm,
|
||||
otg_fs,
|
||||
pcnt,
|
||||
peripherals,
|
||||
prelude,
|
||||
pulse_control,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user