PCNT implementation for v4 esp-idf api (will work for on v5 with v4 api) (#157)

* WIP - PCNT implementation for idf v4

* use from core not std
pass pins as PeripheralRef
store PinDriver in PcntPin

* try a allocate/deallocate stratagy for PCNT UNITS (like esp-idf v5)

* update pcnt example

* WIP - PCNT implementation for idf v4

* use from core not std
pass pins as PeripheralRef
store PinDriver in PcntPin

* try a allocate/deallocate stratagy for PCNT UNITS (like esp-idf v5)

* update pcnt example

* use Arc/Mutex for PcntPin so Pnct can be Send

* update pcnt encoder example

* add partial esp-idf 5 pulse_cnt support (no watches/events)
requires update in esp-idf-sys to choose implementation
uses pcnt on esp-idf 4
uses pulse_cnt by default on esp-idf 5

* fix pcnt compilation on esp-idf 4

* implement interrupt service

* added some documentation

* fix subscribe - trampoline did not work with captured values.
update example to support either

* no need to initialize logging in example

* use esp_idf_sys::*

* Implement From not Into

* switch from bitflags to enumset

* typo

* switch #[doc] to /// style

* gate uses of Box with alloc feature and use alloc::boxed::Box

* rename to PcntDriver and make Pcnt Peripherals

* use `impl Peripheral<P = impl InputPin>` instead of `AnyInputPin`

* small cleanup

* rename config() -> channel_config()
implement defaults on PcntChannelConfig

* some cleanup on idf v5 pulse_ctr

* no need for PcntChanConfig, just use flags

* use comment style documentation not `#[doc=""]`

* small tweak to pnct example

* review fixes (the easy ones)

* added deps for the pcnt example (dev-dependencies)

* cargo fmt

* pub const fn new for PcntChanFlags

* pub const fn new for PcntUnitFlags

* fix compilation for esp-idf 4

* pass in all pins in PcntDriver::new
use PinIndex to specify pins to a channel

* remove unused lifetime

* use a simple macro to convert Option<> pin to a pin number

* cargo fmt

* remove V5 for now
fix #cfg's for when/where pcnt is available

* fix #cfg's for when/where pcnt is available

* fix #cfg's for when/where pcnt is available

* gate access to ISR_HANDLERS in drop with `alloc`
no need for `not(feature = "riscv-ulp-hal")` as pcnt is not included for `riscv-ulp-hal`

* cargo fmt / clippy

* stub out the pcnt example on an unsuported device

* - drop esp-idf-svc from dev-deps (compile error on esp-idf 5)
- use println!() instead of log::info!() in pcnt example

* fix ocnt example compile for riscv32imc-esp-espidf

---------

Co-authored-by: Chris Liebman <chris.l@taboola.com>
This commit is contained in:
liebman 2023-02-20 23:07:45 -08:00 committed by GitHub
parent d477d54e2b
commit 72c0aacf74
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 767 additions and 0 deletions

View File

@ -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<AtomicI64>,
}
impl<'d> Encoder<'d> {
pub fn new<PCNT: Pcnt>(
pcnt: impl Peripheral<P = PCNT> + 'd,
pin_a: impl Peripheral<P = impl InputPin> + 'd,
pin_b: impl Peripheral<P = impl InputPin> + 'd,
) -> Result<Self, EspError> {
let mut unit = PcntDriver::new(
pcnt,
Some(pin_a),
Some(pin_b),
Option::<AnyInputPin>::None,
Option::<AnyInputPin>::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<AtomicI64>` - 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<i64, EspError> {
let value =
self.approx_value.load(Ordering::Relaxed) + self.unit.get_counter_value()? as i64;
Ok(value)
}
}
}

View File

@ -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;

588
src/pcnt.rs Normal file
View File

@ -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<PcntChannel> 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<PcntCountMode> 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<PcntControlMode> 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<PcntEvent>;
/// 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: Pcnt>(
_pcnt: impl Peripheral<P = PCNT> + 'd,
pin0: Option<impl Peripheral<P = impl InputPin> + 'd>,
pin1: Option<impl Peripheral<P = impl InputPin> + 'd>,
pin2: Option<impl Peripheral<P = impl InputPin> + 'd>,
pin3: Option<impl Peripheral<P = impl InputPin> + 'd>,
) -> Result<Self, EspError> {
// 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<i16, EspError> {
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<pcnt_evt_type_t, EspError> {
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<i16, EspError> {
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<u32, EspError> {
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<impl Peripheral<P = impl InputPin> + 'a>,
// ctrl_pin: Option<impl Peripheral<P = impl InputPin> + '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<u16, EspError> {
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<C>(&self, callback: C) -> Result<(), EspError>
where
C: FnMut(u32) + Send + 'static,
{
enable_isr_service()?;
self.unsubscribe()?;
let callback: alloc::boxed::Box<dyn FnMut(u32) + 'static> =
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<Box<dyn FnMut(u32)>>;
#[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);

View File

@ -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"))]