Skip to content

Commit

Permalink
pulse counter implementation (#328)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
liebman authored Jan 17, 2023
1 parent a542735 commit ac206af
Show file tree
Hide file tree
Showing 15 changed files with 1,177 additions and 36 deletions.
8 changes: 4 additions & 4 deletions esp-hal-common/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
Expand Down
64 changes: 32 additions & 32 deletions esp-hal-common/src/gpio/esp32.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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,
Expand Down
16 changes: 16 additions & 0 deletions esp-hal-common/src/gpio/esp32s2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
16 changes: 16 additions & 0 deletions esp-hal-common/src/gpio/esp32s3.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
2 changes: 2 additions & 0 deletions esp-hal-common/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down
243 changes: 243 additions & 0 deletions esp-hal-common/src/pcnt/channel.rs
Original file line number Diff line number Diff line change
@@ -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
}
}
Loading

0 comments on commit ac206af

Please sign in to comment.