diff --git a/esp-hal-common/Cargo.toml b/esp-hal-common/Cargo.toml index bb9331e300e..e8558e381a2 100644 --- a/esp-hal-common/Cargo.toml +++ b/esp-hal-common/Cargo.toml @@ -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"] diff --git a/esp-hal-common/src/gpio/esp32.rs b/esp-hal-common/src/gpio/esp32.rs index 0c7831c862f..bd9822852c4 100644 --- a/esp-hal-common/src/gpio/esp32.rs +++ b/esp-hal-common/src/gpio/esp32.rs @@ -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, diff --git a/esp-hal-common/src/gpio/esp32s2.rs b/esp-hal-common/src/gpio/esp32s2.rs index f4ac7cb7636..ed2959cfbdb 100644 --- a/esp-hal-common/src/gpio/esp32s2.rs +++ b/esp-hal-common/src/gpio/esp32s2.rs @@ -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, diff --git a/esp-hal-common/src/gpio/esp32s3.rs b/esp-hal-common/src/gpio/esp32s3.rs index c4437d8ba5e..b63d80e67ac 100644 --- a/esp-hal-common/src/gpio/esp32s3.rs +++ b/esp-hal-common/src/gpio/esp32s3.rs @@ -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, diff --git a/esp-hal-common/src/lib.rs b/esp-hal-common/src/lib.rs index f5a2f812121..cb8fbe00f6a 100644 --- a/esp-hal-common/src/lib.rs +++ b/esp-hal-common/src/lib.rs @@ -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)] diff --git a/esp-hal-common/src/pcnt/channel.rs b/esp-hal-common/src/pcnt/channel.rs new file mode 100644 index 00000000000..00cf3492c1d --- /dev/null +++ b/esp-hal-common/src/pcnt/channel.rs @@ -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

+ '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 + } +} diff --git a/esp-hal-common/src/pcnt/mod.rs b/esp-hal-common/src/pcnt/mod.rs new file mode 100644 index 00000000000..28d27cffe43 --- /dev/null +++ b/esp-hal-common/src/pcnt/mod.rs @@ -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

+ '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) + } +} diff --git a/esp-hal-common/src/pcnt/unit.rs b/esp-hal-common/src/pcnt/unit.rs new file mode 100644 index 00000000000..9703d9caa6f --- /dev/null +++ b/esp-hal-common/src/pcnt/unit.rs @@ -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 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, +} + +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 + } +} diff --git a/esp-hal-common/src/system.rs b/esp-hal-common/src/system.rs index fb2f70e4b65..93ebf234713 100644 --- a/esp-hal-common/src/system.rs +++ b/esp-hal-common/src/system.rs @@ -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()); diff --git a/esp32-hal/examples/pcnt_encoder.rs b/esp32-hal/examples/pcnt_encoder.rs new file mode 100644 index 00000000000..819e03128ea --- /dev/null +++ b/esp32-hal/examples/pcnt_encoder.rs @@ -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>> = 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(); + } + }); +} diff --git a/esp32-hal/src/lib.rs b/esp32-hal/src/lib.rs index a213b5a1813..c9c584d36ae 100644 --- a/esp32-hal/src/lib.rs +++ b/esp32-hal/src/lib.rs @@ -19,6 +19,7 @@ pub use esp_hal_common::{ ledc, macros, mcpwm, + pcnt, peripherals, prelude, pulse_control, diff --git a/esp32s2-hal/examples/pcnt_encoder.rs b/esp32s2-hal/examples/pcnt_encoder.rs new file mode 100644 index 00000000000..2bdfab326a5 --- /dev/null +++ b/esp32s2-hal/examples/pcnt_encoder.rs @@ -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>> = 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(); + } + }); +} diff --git a/esp32s2-hal/src/lib.rs b/esp32s2-hal/src/lib.rs index 1c9f481fb53..1b4c59ec1cb 100644 --- a/esp32s2-hal/src/lib.rs +++ b/esp32s2-hal/src/lib.rs @@ -18,6 +18,7 @@ pub use esp_hal_common::{ ledc, macros, otg_fs, + pcnt, peripherals, prelude, pulse_control, diff --git a/esp32s3-hal/examples/pcnt_encoder.rs b/esp32s3-hal/examples/pcnt_encoder.rs new file mode 100644 index 00000000000..201fce31d46 --- /dev/null +++ b/esp32s3-hal/examples/pcnt_encoder.rs @@ -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>> = 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(); + } + }); +} diff --git a/esp32s3-hal/src/lib.rs b/esp32s3-hal/src/lib.rs index 870ada55901..d265c1d6ca0 100644 --- a/esp32s3-hal/src/lib.rs +++ b/esp32s3-hal/src/lib.rs @@ -20,6 +20,7 @@ pub use esp_hal_common::{ macros, mcpwm, otg_fs, + pcnt, peripherals, prelude, pulse_control,