diff --git a/examples/pcnt_i64_encoder.rs b/examples/pcnt_i64_encoder.rs new file mode 100644 index 00000000000..01cded8e398 --- /dev/null +++ b/examples/pcnt_i64_encoder.rs @@ -0,0 +1,143 @@ +//! PCNT decoding a rotary encoder +//! +//! To try this out, connect a rotary encoder to pins 5 and 6, the common should be grounded +//! +//! Note that PCNT only track a singed 16bit value. We use interrupts to detect a LOW and HIGH +//! threshold and track how much that accounts for and provide an i64 value result +//! + +#[cfg(all(not(feature = "riscv-ulp-hal"), any(esp32, esp32s2, esp32s3)))] +fn main() -> anyhow::Result<()> { + use anyhow::Context; + use encoder::Encoder; + use esp_idf_hal::delay::FreeRtos; + use esp_idf_hal::prelude::*; + + // Temporary. Will disappear once ESP-IDF 4.4 is released, but for now it is necessary to call this function once, + // or else some patches to the runtime implemented by esp-idf-sys might not link properly. + esp_idf_sys::link_patches(); + + println!("setup pins"); + let peripherals = Peripherals::take().context("failed to take Peripherals")?; + let mut pin_a = peripherals.pins.gpio5; + let mut pin_b = peripherals.pins.gpio6; + println!("setup encoder"); + let encoder = Encoder::new(peripherals.pcnt0, &mut pin_a, &mut pin_b)?; + + let mut last_value = 0i64; + loop { + let value = encoder.get_value()?; + if value != last_value { + println!("value: {value}"); + last_value = value; + } + FreeRtos::delay_ms(100u32); + } +} + +#[cfg(not(all(not(feature = "riscv-ulp-hal"), any(esp32, esp32s2, esp32s3))))] +fn main() { + use esp_idf_hal::delay::FreeRtos; + println!("pcnt peripheral not supported on this device!"); + loop { + FreeRtos::delay_ms(100u32); + } +} + +#[cfg(all(not(feature = "riscv-ulp-hal"), any(esp32, esp32s2, esp32s3)))] +// esp-idf encoder implementation using v4 pcnt api +mod encoder { + use std::cmp::min; + use std::sync::atomic::AtomicI64; + use std::sync::atomic::Ordering; + use std::sync::Arc; + + use esp_idf_hal::gpio::AnyInputPin; + use esp_idf_hal::gpio::InputPin; + use esp_idf_hal::pcnt::*; + use esp_idf_hal::peripheral::Peripheral; + use esp_idf_sys::EspError; + + const LOW_LIMIT: i16 = -100; + const HIGH_LIMIT: i16 = 100; + + pub struct Encoder<'d> { + unit: PcntDriver<'d>, + approx_value: Arc, + } + + impl<'d> Encoder<'d> { + pub fn new( + pcnt: impl Peripheral

+ 'd, + pin_a: impl Peripheral

+ 'd, + pin_b: impl Peripheral

+ 'd, + ) -> Result { + let mut unit = PcntDriver::new( + pcnt, + Some(pin_a), + Some(pin_b), + Option::::None, + Option::::None, + )?; + unit.channel_config( + PcntChannel::Channel0, + PinIndex::Pin0, + PinIndex::Pin1, + &mut PcntChannelConfig { + lctrl_mode: PcntControlMode::Reverse, + hctrl_mode: PcntControlMode::Keep, + pos_mode: PcntCountMode::Decrement, + neg_mode: PcntCountMode::Increment, + counter_h_lim: HIGH_LIMIT, + counter_l_lim: LOW_LIMIT, + }, + )?; + unit.channel_config( + PcntChannel::Channel1, + PinIndex::Pin1, + PinIndex::Pin0, + &mut PcntChannelConfig { + lctrl_mode: PcntControlMode::Reverse, + hctrl_mode: PcntControlMode::Keep, + pos_mode: PcntCountMode::Increment, + neg_mode: PcntCountMode::Decrement, + counter_h_lim: HIGH_LIMIT, + counter_l_lim: LOW_LIMIT, + }, + )?; + + unit.set_filter_value(min(10 * 80, 1023))?; + unit.filter_enable()?; + + let approx_value = Arc::new(AtomicI64::new(0)); + // unsafe interrupt code to catch the upper and lower limits from the encoder + // and track the overflow in `value: Arc` - I plan to use this for + // a wheeled robot's odomerty + unsafe { + let approx_value = approx_value.clone(); + unit.subscribe(move |status| { + let status = PcntEventType::from_repr_truncated(status); + if status.contains(PcntEvent::HighLimit) { + approx_value.fetch_add(HIGH_LIMIT as i64, Ordering::SeqCst); + } + if status.contains(PcntEvent::LowLimit) { + approx_value.fetch_add(LOW_LIMIT as i64, Ordering::SeqCst); + } + })?; + } + unit.event_enable(PcntEvent::HighLimit)?; + unit.event_enable(PcntEvent::LowLimit)?; + unit.counter_pause()?; + unit.counter_clear()?; + unit.counter_resume()?; + + Ok(Self { unit, approx_value }) + } + + pub fn get_value(&self) -> Result { + let value = + self.approx_value.load(Ordering::Relaxed) + self.unit.get_counter_value()? as i64; + Ok(value) + } + } +} diff --git a/src/lib.rs b/src/lib.rs index 3bf4c427026..573ed0ee973 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -44,6 +44,8 @@ pub mod ledc; pub mod mac; #[cfg(not(feature = "riscv-ulp-hal"))] pub mod modem; +#[cfg(all(not(feature = "riscv-ulp-hal"), any(esp32, esp32s2, esp32s3)))] +pub mod pcnt; pub mod peripheral; pub mod peripherals; pub mod prelude; diff --git a/src/pcnt.rs b/src/pcnt.rs new file mode 100644 index 00000000000..d5049b178f5 --- /dev/null +++ b/src/pcnt.rs @@ -0,0 +1,588 @@ +use core::fmt::Debug; +use core::marker::PhantomData; + +#[cfg(feature = "alloc")] +extern crate alloc; + +#[cfg(feature = "alloc")] +use alloc::boxed::Box; + +use esp_idf_sys::*; + +use enumset::EnumSetType; + +use crate::gpio::InputPin; +use crate::peripheral::Peripheral; + +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub enum PcntChannel { + Channel0, + Channel1, +} + +impl From for pcnt_channel_t { + fn from(value: PcntChannel) -> Self { + match value { + PcntChannel::Channel0 => pcnt_channel_t_PCNT_CHANNEL_0, + PcntChannel::Channel1 => pcnt_channel_t_PCNT_CHANNEL_1, + } + } +} + +/// PCNT channel action on signal edge +#[derive(Debug, Copy, Clone, Default, Eq, PartialEq)] +pub enum PcntCountMode { + /// Hold current count value + Hold, + /// Increase count value + #[default] + Increment, + /// Decrease count value + Decrement, +} + +impl From for pcnt_count_mode_t { + fn from(value: PcntCountMode) -> Self { + match value { + PcntCountMode::Hold => pcnt_channel_edge_action_t_PCNT_CHANNEL_EDGE_ACTION_HOLD, + PcntCountMode::Increment => { + pcnt_channel_edge_action_t_PCNT_CHANNEL_EDGE_ACTION_INCREASE + } + PcntCountMode::Decrement => { + pcnt_channel_edge_action_t_PCNT_CHANNEL_EDGE_ACTION_DECREASE + } + } + } +} + +/// PCNT channel action on control level +#[derive(Debug, Copy, Clone, Default, Eq, PartialEq)] +pub enum PcntControlMode { + /// Keep current count mode + Keep, + /// Invert current count mode (increase -> decrease, decrease -> increase) + #[default] + Reverse, + /// Hold current count value + Disable, +} + +impl From for pcnt_ctrl_mode_t { + fn from(value: PcntControlMode) -> Self { + match value { + PcntControlMode::Keep => pcnt_channel_level_action_t_PCNT_CHANNEL_LEVEL_ACTION_KEEP, + PcntControlMode::Reverse => { + pcnt_channel_level_action_t_PCNT_CHANNEL_LEVEL_ACTION_INVERSE + } + PcntControlMode::Disable => pcnt_channel_level_action_t_PCNT_CHANNEL_LEVEL_ACTION_HOLD, + } + } +} + +#[derive(Debug, EnumSetType)] +#[enumset(repr = "u32")] +pub enum PcntEvent { + /// PCNT watch point event: threshold1 value event + Threshold1 = 2, // pcnt_evt_type_t_PCNT_EVT_THRES_1 = 0x04, + /// PCNT watch point event: threshold0 value event + Threshold0 = 3, // pcnt_evt_type_t_PCNT_EVT_THRES_0 = 0x08, + /// PCNT watch point event: Minimum counter value + LowLimit = 4, // pcnt_evt_type_t_PCNT_EVT_L_LIM = 0x10, + /// PCNT watch point event: Maximum counter value + HighLimit = 5, // pcnt_evt_type_t_PCNT_EVT_H_LIM = 0x20, + /// PCNT watch point event: counter value zero event + Zero = 6, // pcnt_evt_type_t_PCNT_EVT_ZERO = 0x40, +} + +pub type PcntEventType = enumset::EnumSet; + +/// Pulse Counter configuration for a single channel +#[derive(Debug, Copy, Clone, Default)] +pub struct PcntChannelConfig { + /// PCNT low control mode + pub lctrl_mode: PcntControlMode, + /// PCNT high control mode + pub hctrl_mode: PcntControlMode, + /// PCNT positive edge count mode + pub pos_mode: PcntCountMode, + /// PCNT negative edge count mode + pub neg_mode: PcntCountMode, + /// Maximum counter value + pub counter_h_lim: i16, + /// Minimum counter value + pub counter_l_lim: i16, +} + +impl PcntChannelConfig { + pub fn new() -> Self { + Default::default() + } +} + +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub enum PinIndex { + Pin0 = 0, + Pin1 = 1, + Pin2 = 2, + Pin3 = 3, +} + +pub struct PcntDriver<'d> { + unit: pcnt_unit_t, + pins: [i32; 4], + _p: PhantomData<&'d mut ()>, +} + +macro_rules! pin_to_number { + ($pin:ident) => { + match $pin { + Some(pin) => { + crate::into_ref!(pin); + pin.pin() + } + None => PCNT_PIN_NOT_USED, + } + }; +} + +impl<'d> PcntDriver<'d> { + pub fn new( + _pcnt: impl Peripheral

+ 'd, + pin0: Option + 'd>, + pin1: Option + 'd>, + pin2: Option + 'd>, + pin3: Option + 'd>, + ) -> Result { + // consume the pins and keep only the pin number. + let pins = [ + pin_to_number!(pin0), + pin_to_number!(pin1), + pin_to_number!(pin2), + pin_to_number!(pin3), + ]; + Ok(Self { + unit: PCNT::unit(), + pins, + _p: PhantomData, + }) + } + + /// Configure Pulse Counter chanel + /// @note + /// This function will disable three events: PCNT_EVT_L_LIM, PCNT_EVT_H_LIM, PCNT_EVT_ZERO. + /// + /// @param channel Channel to configure + /// @param pulse_pin Pulse signal input pin + /// @param ctrl_pin Control signal input pin + /// @param pconfig Reference of PcntConfig + /// + /// @note Set the signal input to PCNT_PIN_NOT_USED if unused. + /// + /// returns + /// - () + /// - EspError + pub fn channel_config( + &mut self, + channel: PcntChannel, + pulse_pin: PinIndex, + ctrl_pin: PinIndex, + pconfig: &PcntChannelConfig, + ) -> Result<(), EspError> { + let config = pcnt_config_t { + pulse_gpio_num: self.pins[pulse_pin as usize], + ctrl_gpio_num: self.pins[ctrl_pin as usize], + lctrl_mode: pconfig.lctrl_mode.into(), + hctrl_mode: pconfig.hctrl_mode.into(), + pos_mode: pconfig.pos_mode.into(), + neg_mode: pconfig.neg_mode.into(), + counter_h_lim: pconfig.counter_h_lim, + counter_l_lim: pconfig.counter_l_lim, + channel: channel.into(), + unit: self.unit, + }; + + unsafe { esp!(pcnt_unit_config(&config as *const pcnt_config_t)) } + } + + /// Get pulse counter value + /// + /// returns + /// - i16 + /// - EspError + pub fn get_counter_value(&self) -> Result { + let mut value = 0i16; + unsafe { + esp!(pcnt_get_counter_value(self.unit, &mut value as *mut i16))?; + } + Ok(value) + } + + /// Pause PCNT counter of PCNT unit + /// + /// returns + /// - () + /// - EspError + pub fn counter_pause(&self) -> Result<(), EspError> { + unsafe { esp!(pcnt_counter_pause(self.unit)) } + } + + /// Resume counting for PCNT counter + /// + /// returns + /// - () + /// - EspError + pub fn counter_resume(&self) -> Result<(), EspError> { + unsafe { esp!(pcnt_counter_resume(self.unit)) } + } + + /// Clear and reset PCNT counter value to zero + /// + /// returns + /// - () + /// - EspError + pub fn counter_clear(&self) -> Result<(), EspError> { + unsafe { esp!(pcnt_counter_clear(self.unit)) } + } + + /// Enable PCNT interrupt for PCNT unit + /// @note + /// Each Pulse counter unit has five watch point events that share the same interrupt. + /// Configure events with pcnt_event_enable() and pcnt_event_disable() + /// + /// returns + /// - () + /// - EspError + pub fn intr_enable(&self) -> Result<(), EspError> { + unsafe { esp!(pcnt_intr_enable(self.unit)) } + } + + /// Disable PCNT interrupt for PCNT unit + /// + /// returns + /// - () + /// - EspError + pub fn intr_disable(&self) -> Result<(), EspError> { + unsafe { esp!(pcnt_intr_disable(self.unit)) } + } + + /// Enable PCNT event of PCNT unit + /// + /// @param evt_type Watch point event type. + /// All enabled events share the same interrupt (one interrupt per pulse counter unit). + /// returns + /// - () + /// - EspError + pub fn event_enable(&self, evt_type: PcntEvent) -> Result<(), EspError> { + let evt_type: pcnt_evt_type_t = PcntEventType::only(evt_type).as_repr(); + unsafe { esp!(pcnt_event_enable(self.unit, evt_type)) } + } + + /// Disable PCNT event of PCNT unit + /// + /// @param evt_type Watch point event type. + /// All enabled events share the same interrupt (one interrupt per pulse counter unit). + /// returns + /// - () + /// - EspError + pub fn event_disable(&self, evt_type: PcntEvent) -> Result<(), EspError> { + let evt_type: pcnt_evt_type_t = PcntEventType::only(evt_type).as_repr(); + unsafe { esp!(pcnt_event_disable(self.unit, evt_type)) } + } + + fn only_one_event_type(evt_type: PcntEventType) -> Result { + match evt_type.iter().count() { + 1 => Ok(evt_type.as_repr()), + _ => Err(EspError::from(ESP_ERR_INVALID_ARG as esp_err_t).unwrap()), + } + } + + /// Set PCNT event value of PCNT unit + /// + /// @param evt_type Watch point event type. + /// All enabled events share the same interrupt (one interrupt per pulse counter unit). + /// + /// returns + /// - () + /// - EspError + pub fn set_event_value(&self, evt_type: PcntEventType, value: i16) -> Result<(), EspError> { + let evt_type = Self::only_one_event_type(evt_type)?; + unsafe { esp!(pcnt_set_event_value(self.unit, evt_type, value)) } + } + + /// Get PCNT event value of PCNT unit + /// + /// @param evt_type Watch point event type. + /// All enabled events share the same interrupt (one interrupt per pulse counter unit). + /// + /// returns + /// - i16 + /// - EspError + pub fn get_event_value(&self, evt_type: PcntEventType) -> Result { + let evt_type = Self::only_one_event_type(evt_type)?; + let mut value = 0i16; + unsafe { + esp!(pcnt_get_event_value( + self.unit, + evt_type, + &mut value as *mut i16 + ))?; + } + Ok(value) + } + + /// Get PCNT event status of PCNT unit + /// + /// returns + /// - i32 + /// - EspError + // TODO: status is a bit field! + pub fn get_event_status(&self) -> Result { + let mut value = 0u32; + unsafe { + esp!(pcnt_get_event_status(self.unit, &mut value as *mut u32))?; + } + Ok(value) + } + + // TODO: not implementing until we can do it safely! Will need to reconfigure channels? + // + // /// Configure PCNT pulse signal input pin and control input pin + // /// + // /// @param channel PcntChannel + // /// @param pulse_io Pulse signal input pin + // /// @param ctrl_io Control signal input pin + // /// + // /// @note Set the signal input to PCNT_PIN_NOT_USED if unused. + // /// + // /// returns + // /// - () + // /// - EspError + // pub fn set_pin<'a>( + // &mut self, + // channel: PcntChannel, + // pulse_pin: Option + 'a>, + // ctrl_pin: Option + 'a>, + // ) -> Result<(), EspError> { + // } + + /// Enable PCNT input filter + /// + /// returns + /// - () + /// - EspError + pub fn filter_enable(&self) -> Result<(), EspError> { + unsafe { esp!(pcnt_filter_enable(self.unit)) } + } + + /// Disable PCNT input filter + /// + /// returns + /// - () + /// - EspError + pub fn filter_disable(&self) -> Result<(), EspError> { + unsafe { esp!(pcnt_filter_disable(self.unit)) } + } + + /// Set PCNT filter value + /// + /// @param filter_val PCNT signal filter value, counter in APB_CLK cycles. + /// Any pulses lasting shorter than this will be ignored when the filter is enabled. + /// @note + /// filter_val is a 10-bit value, so the maximum filter_val should be limited to 1023. + /// + /// returns + /// - () + /// - EspError + pub fn set_filter_value(&self, value: u16) -> Result<(), EspError> { + unsafe { esp!(pcnt_set_filter_value(self.unit, value)) } + } + + /// Get PCNT filter value + /// + /// returns + /// - i16 + /// - EspError + pub fn get_filter_value(&self) -> Result { + let mut value = 0u16; + unsafe { + esp!(pcnt_get_filter_value(self.unit, &mut value as *mut u16))?; + } + Ok(value) + } + + /// Set PCNT counter mode + /// + /// @param channel PCNT channel number + /// @param pos_mode Counter mode when detecting positive edge + /// @param neg_mode Counter mode when detecting negative edge + /// @param hctrl_mode Counter mode when control signal is high level + /// @param lctrl_mode Counter mode when control signal is low level + /// + /// returns + /// - () + /// - EspError + pub fn set_mode( + &self, + channel: PcntChannel, + pos_mode: PcntCountMode, + neg_mode: PcntCountMode, + hctrl_mode: PcntControlMode, + lctrl_mode: PcntControlMode, + ) -> Result<(), EspError> { + unsafe { + esp!(pcnt_set_mode( + self.unit, + channel.into(), + pos_mode.into(), + neg_mode.into(), + hctrl_mode.into(), + lctrl_mode.into() + )) + } + } + + /// Add ISR handler for specified unit. + /// + /// This ISR handler will be called from an ISR. So there is a stack + /// size limit (configurable as \"ISR stack size\" in menuconfig). This + /// limit is smaller compared to a global PCNT interrupt handler due + /// to the additional level of indirection. + /// + /// # Safety + /// + /// Care should be taken not to call STD, libc or FreeRTOS APIs (except for a few allowed ones) + /// in the callback passed to this function, as it is executed in an ISR context. + /// + /// @param callback Interrupt handler function. + /// + /// returns + /// - () + /// - EspError + #[cfg(feature = "alloc")] + pub unsafe fn subscribe(&self, callback: C) -> Result<(), EspError> + where + C: FnMut(u32) + Send + 'static, + { + enable_isr_service()?; + + self.unsubscribe()?; + let callback: alloc::boxed::Box = + alloc::boxed::Box::new(callback); + ISR_HANDLERS[self.unit as usize] = Some(callback); + esp!(pcnt_isr_handler_add( + self.unit, + Some(Self::handle_isr), + self.unit as *mut core::ffi::c_void, + ))?; + Ok(()) + } + + /// Remove ISR handler for specified unit. + /// + /// returns + /// - () + /// - EspError + #[cfg(feature = "alloc")] + pub fn unsubscribe(&self) -> Result<(), EspError> { + unsafe { + esp!(pcnt_isr_handler_remove(self.unit))?; + ISR_HANDLERS[self.unit as usize] = None; + } + Ok(()) + } + + #[cfg(feature = "alloc")] + unsafe extern "C" fn handle_isr(data: *mut core::ffi::c_void) { + let unit = data as pcnt_unit_t; + if let Some(f) = &mut ISR_HANDLERS[unit as usize] { + let mut value = 0u32; + esp!(pcnt_get_event_status(unit, &mut value as *mut u32)) + .expect("failed to fetch event status!"); + f(value); + } + } +} + +impl Drop for PcntDriver<'_> { + fn drop(&mut self) { + let _ = self.counter_pause(); + let _ = self.intr_disable(); + #[cfg(feature = "alloc")] + unsafe { + pcnt_isr_handler_remove(self.unit); + ISR_HANDLERS[self.unit as usize] = None + }; + } +} + +#[cfg(feature = "alloc")] +static ISR_SERVICE_ENABLED: core::sync::atomic::AtomicBool = + core::sync::atomic::AtomicBool::new(false); + +#[cfg(feature = "alloc")] +static PCNT_CS: crate::task::CriticalSection = crate::task::CriticalSection::new(); + +#[cfg(feature = "alloc")] +fn enable_isr_service() -> Result<(), EspError> { + use core::sync::atomic::Ordering; + + if !ISR_SERVICE_ENABLED.load(Ordering::SeqCst) { + let _cs = PCNT_CS.enter(); + + if !ISR_SERVICE_ENABLED.load(Ordering::SeqCst) { + esp!(unsafe { pcnt_isr_service_install(0) })?; + + ISR_SERVICE_ENABLED.store(true, Ordering::SeqCst); + } + } + + Ok(()) +} + +#[cfg(feature = "alloc")] +type IsrHandler = Option>; +#[cfg(feature = "alloc")] +static mut ISR_HANDLERS: [IsrHandler; pcnt_unit_t_PCNT_UNIT_MAX as usize] = [ + None, + None, + None, + None, + #[cfg(esp32)] + None, + #[cfg(esp32)] + None, + #[cfg(esp32)] + None, + #[cfg(esp32)] + None, +]; + +pub trait Pcnt { + fn unit() -> pcnt_unit_t; +} + +macro_rules! impl_pcnt { + ($pcnt:ident: $unit:expr) => { + crate::impl_peripheral!($pcnt); + + impl Pcnt for $pcnt { + #[inline(always)] + fn unit() -> pcnt_unit_t { + $unit + } + } + }; +} + +impl_pcnt!(PCNT0: pcnt_unit_t_PCNT_UNIT_0); +impl_pcnt!(PCNT1: pcnt_unit_t_PCNT_UNIT_1); +impl_pcnt!(PCNT2: pcnt_unit_t_PCNT_UNIT_2); +impl_pcnt!(PCNT3: pcnt_unit_t_PCNT_UNIT_3); +#[cfg(esp32)] +impl_pcnt!(PCNT4: pcnt_unit_t_PCNT_UNIT_4); +#[cfg(esp32)] +impl_pcnt!(PCNT5: pcnt_unit_t_PCNT_UNIT_5); +#[cfg(esp32)] +impl_pcnt!(PCNT6: pcnt_unit_t_PCNT_UNIT_6); +#[cfg(esp32)] +impl_pcnt!(PCNT7: pcnt_unit_t_PCNT_UNIT_7); diff --git a/src/peripherals.rs b/src/peripherals.rs index fbdcbe18b60..09999d6b9b1 100644 --- a/src/peripherals.rs +++ b/src/peripherals.rs @@ -13,6 +13,8 @@ use crate::ledc; use crate::mac; #[cfg(not(feature = "riscv-ulp-hal"))] use crate::modem; +#[cfg(all(not(feature = "riscv-ulp-hal"), any(esp32, esp32s2, esp32s3)))] +use crate::pcnt; #[cfg(not(feature = "riscv-ulp-hal"))] use crate::rmt; #[cfg(not(feature = "riscv-ulp-hal"))] @@ -59,6 +61,22 @@ pub struct Peripherals { pub spi3: spi::SPI3, pub adc1: adc::ADC1, pub adc2: adc::ADC2, + #[cfg(all(not(feature = "riscv-ulp-hal"), any(esp32, esp32s2, esp32s3)))] + pub pcnt0: pcnt::PCNT0, + #[cfg(all(not(feature = "riscv-ulp-hal"), any(esp32, esp32s2, esp32s3)))] + pub pcnt1: pcnt::PCNT1, + #[cfg(all(not(feature = "riscv-ulp-hal"), any(esp32, esp32s2, esp32s3)))] + pub pcnt2: pcnt::PCNT2, + #[cfg(all(not(feature = "riscv-ulp-hal"), any(esp32, esp32s2, esp32s3)))] + pub pcnt3: pcnt::PCNT3, + #[cfg(all(not(feature = "riscv-ulp-hal"), any(esp32)))] + pub pcnt4: pcnt::PCNT4, + #[cfg(all(not(feature = "riscv-ulp-hal"), any(esp32)))] + pub pcnt5: pcnt::PCNT5, + #[cfg(all(not(feature = "riscv-ulp-hal"), any(esp32)))] + pub pcnt6: pcnt::PCNT6, + #[cfg(all(not(feature = "riscv-ulp-hal"), any(esp32)))] + pub pcnt7: pcnt::PCNT7, #[cfg(all(esp32, esp_idf_version_major = "4"))] pub hall_sensor: crate::hall::HallSensor, #[cfg(not(feature = "riscv-ulp-hal"))] @@ -178,6 +196,22 @@ impl Peripherals { spi3: spi::SPI3::new(), adc1: adc::ADC1::new(), adc2: adc::ADC2::new(), + #[cfg(all(not(feature = "riscv-ulp-hal"), any(esp32, esp32s2, esp32s3)))] + pcnt0: pcnt::PCNT0::new(), + #[cfg(all(not(feature = "riscv-ulp-hal"), any(esp32, esp32s2, esp32s3)))] + pcnt1: pcnt::PCNT1::new(), + #[cfg(all(not(feature = "riscv-ulp-hal"), any(esp32, esp32s2, esp32s3)))] + pcnt2: pcnt::PCNT2::new(), + #[cfg(all(not(feature = "riscv-ulp-hal"), any(esp32, esp32s2, esp32s3)))] + pcnt3: pcnt::PCNT3::new(), + #[cfg(all(not(feature = "riscv-ulp-hal"), any(esp32)))] + pcnt4: pcnt::PCNT4::new(), + #[cfg(all(not(feature = "riscv-ulp-hal"), any(esp32)))] + pcnt5: pcnt::PCNT5::new(), + #[cfg(all(not(feature = "riscv-ulp-hal"), any(esp32)))] + pcnt6: pcnt::PCNT6::new(), + #[cfg(all(not(feature = "riscv-ulp-hal"), any(esp32)))] + pcnt7: pcnt::PCNT7::new(), #[cfg(all(esp32, esp_idf_version_major = "4"))] hall_sensor: crate::hall::HallSensor::new(), #[cfg(not(feature = "riscv-ulp-hal"))]