use core::marker::PhantomData;
#[cfg(feature = "alloc")]
use core::pin::Pin;
use core::ptr;
#[cfg(feature = "alloc")]
use alloc::boxed::Box;
use heapless::Vec;
use esp_idf_sys::*;
use crate::gpio::InputPin;
#[cfg(feature = "alloc")]
use crate::interrupt;
use config::*;
#[derive(Debug, Clone)]
#[non_exhaustive]
pub struct WatchEventData {
pub watch_point_value: i32,
pub zero_cross_mode: ZeroCrossMode,
}
impl From<pcnt_watch_event_data_t> for WatchEventData {
#[inline(always)]
fn from(value: pcnt_watch_event_data_t) -> Self {
Self {
watch_point_value: value.watch_point_value,
zero_cross_mode: value.zero_cross_mode.into(),
}
}
}
pub mod config {
use core::time::Duration;
use esp_idf_sys::*;
#[derive(Debug, Clone)]
pub struct UnitConfig {
pub low_limit: i32,
pub high_limit: i32,
pub intr_priority: i32,
pub accum_count: bool,
#[doc(hidden)]
#[allow(dead_code)]
pub __internal: (),
}
impl Default for UnitConfig {
fn default() -> Self {
Self {
low_limit: -1024,
high_limit: 1024,
intr_priority: 0,
accum_count: false,
__internal: (),
}
}
}
impl From<&UnitConfig> for pcnt_unit_config_t {
fn from(value: &UnitConfig) -> Self {
pcnt_unit_config_t {
low_limit: value.low_limit,
high_limit: value.high_limit,
intr_priority: value.intr_priority,
flags: pcnt_unit_config_t__bindgen_ty_1 {
_bitfield_1: pcnt_unit_config_t__bindgen_ty_1::new_bitfield_1(
value.accum_count as u32,
#[cfg(esp_idf_soc_pcnt_support_step_notify)]
{
0
},
#[cfg(esp_idf_soc_pcnt_support_step_notify)]
{
0
},
),
..Default::default()
},
}
}
}
#[derive(Debug, Clone, Default)]
pub struct GlitchFilterConfig {
pub max_glitch: Duration,
#[doc(hidden)]
#[allow(dead_code)]
pub __internal: (),
}
impl From<&GlitchFilterConfig> for pcnt_glitch_filter_config_t {
fn from(value: &GlitchFilterConfig) -> Self {
pcnt_glitch_filter_config_t {
max_glitch_ns: value.max_glitch.as_nanos().try_into().unwrap_or(u32::MAX),
}
}
}
#[derive(Debug, Clone, Default)]
pub struct ChannelConfig {
pub invert_edge_input: bool,
pub invert_level_input: bool,
pub virt_edge_io_level: bool,
pub virt_level_io_level: bool,
#[doc(hidden)]
#[allow(dead_code)]
pub __internal: (),
}
#[non_exhaustive]
pub enum ChannelLevelAction {
Keep,
Inverse,
Hold,
}
impl From<ChannelLevelAction> for pcnt_channel_level_action_t {
fn from(value: ChannelLevelAction) -> Self {
match value {
ChannelLevelAction::Keep => {
pcnt_channel_level_action_t_PCNT_CHANNEL_LEVEL_ACTION_KEEP
}
ChannelLevelAction::Inverse => {
pcnt_channel_level_action_t_PCNT_CHANNEL_LEVEL_ACTION_INVERSE
}
ChannelLevelAction::Hold => {
pcnt_channel_level_action_t_PCNT_CHANNEL_LEVEL_ACTION_HOLD
}
}
}
}
#[non_exhaustive]
pub enum ChannelEdgeAction {
Hold,
Increase,
Decrease,
}
impl From<ChannelEdgeAction> for pcnt_channel_edge_action_t {
fn from(value: ChannelEdgeAction) -> Self {
match value {
ChannelEdgeAction::Hold => pcnt_channel_edge_action_t_PCNT_CHANNEL_EDGE_ACTION_HOLD,
ChannelEdgeAction::Increase => {
pcnt_channel_edge_action_t_PCNT_CHANNEL_EDGE_ACTION_INCREASE
}
ChannelEdgeAction::Decrease => {
pcnt_channel_edge_action_t_PCNT_CHANNEL_EDGE_ACTION_DECREASE
}
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum ZeroCrossMode {
PositionZero,
NegativeZero,
NegativePosition,
PositiveNegative,
#[cfg(esp_idf_version_at_least_5_4_0)]
#[cfg_attr(feature = "nightly", doc(cfg(esp_idf_version_at_least_5_4_0)))]
Invalid,
}
impl From<pcnt_unit_zero_cross_mode_t> for ZeroCrossMode {
fn from(value: pcnt_unit_zero_cross_mode_t) -> Self {
#[allow(non_upper_case_globals)]
match value {
pcnt_unit_zero_cross_mode_t_PCNT_UNIT_ZERO_CROSS_POS_ZERO => Self::PositionZero,
pcnt_unit_zero_cross_mode_t_PCNT_UNIT_ZERO_CROSS_NEG_ZERO => Self::NegativeZero,
pcnt_unit_zero_cross_mode_t_PCNT_UNIT_ZERO_CROSS_NEG_POS => Self::NegativePosition,
pcnt_unit_zero_cross_mode_t_PCNT_UNIT_ZERO_CROSS_POS_NEG => Self::PositiveNegative,
#[cfg(esp_idf_version_at_least_5_4_0)]
pcnt_unit_zero_cross_mode_t_PCNT_UNIT_ZERO_CROSS_INVALID => Self::Invalid,
_ => unreachable!("unknown zero cross mode: {value}"),
}
}
}
}
#[derive(Debug)]
pub struct PcntChannel<'d> {
handle: pcnt_channel_handle_t,
_p: PhantomData<&'d mut ()>,
}
impl<'d> PcntChannel<'d> {
unsafe fn new(
pcnt_unit: pcnt_unit_handle_t,
edge_pin: Option<impl InputPin + 'd>,
level_pin: Option<impl InputPin + 'd>,
config: &ChannelConfig,
) -> Result<Self, EspError> {
let sys_config = pcnt_chan_config_t {
edge_gpio_num: edge_pin.map_or(-1, |p| p.pin().into()),
level_gpio_num: level_pin.map_or(-1, |p| p.pin().into()),
flags: pcnt_chan_config_t__bindgen_ty_1 {
_bitfield_1: pcnt_chan_config_t__bindgen_ty_1::new_bitfield_1(
config.invert_edge_input as u32,
config.invert_level_input as u32,
config.virt_edge_io_level as u32,
config.virt_level_io_level as u32,
#[cfg(not(esp_idf_version_at_least_6_0_0))]
{
false as u32
},
),
..Default::default()
},
};
let mut handle = ptr::null_mut();
esp!(unsafe { pcnt_new_channel(pcnt_unit, &sys_config, &mut handle) })?;
Ok(Self {
handle,
_p: PhantomData,
})
}
pub fn set_edge_action(
&mut self,
pos_act: ChannelEdgeAction,
neg_act: ChannelEdgeAction,
) -> Result<&mut Self, EspError> {
esp!(unsafe { pcnt_channel_set_edge_action(self.handle, pos_act.into(), neg_act.into()) })?;
Ok(self)
}
pub fn set_level_action(
&mut self,
high_act: ChannelLevelAction,
low_act: ChannelLevelAction,
) -> Result<&mut Self, EspError> {
esp!(unsafe {
pcnt_channel_set_level_action(self.handle, high_act.into(), low_act.into())
})?;
Ok(self)
}
}
impl<'d> Drop for PcntChannel<'d> {
fn drop(&mut self) {
unsafe {
pcnt_del_channel(self.handle);
}
}
}
unsafe impl<'d> Send for PcntChannel<'d> {}
#[cfg(feature = "alloc")]
struct DelegateUserData<'d> {
on_reach: Box<dyn FnMut(WatchEventData) + Send + 'd>,
}
pub struct PcntUnitDriver<'d> {
handle: pcnt_unit_handle_t,
channels: Vec<PcntChannel<'d>, { SOC_PCNT_CHANNELS_PER_UNIT as usize }>,
#[cfg(feature = "alloc")]
user_data: Option<Pin<Box<DelegateUserData<'d>>>>,
_p: PhantomData<&'d mut ()>,
}
impl<'d> PcntUnitDriver<'d> {
pub fn new(config: &UnitConfig) -> Result<Self, EspError> {
let sys_config = config.into();
let mut handle = ptr::null_mut();
esp!(unsafe { pcnt_new_unit(&sys_config, &mut handle) })?;
Ok(Self {
handle,
channels: Vec::new(),
#[cfg(feature = "alloc")]
user_data: None,
_p: PhantomData,
})
}
#[must_use]
pub fn handle(&self) -> pcnt_unit_handle_t {
self.handle
}
pub fn add_watch_point(&mut self, watch_point: i32) -> Result<(), EspError> {
esp!(unsafe { pcnt_unit_add_watch_point(self.handle, watch_point) })
}
pub fn add_watch_points_and_clear(
&mut self,
iterator: impl IntoIterator<Item = i32>,
) -> Result<(), EspError> {
for watch_point in iterator {
self.add_watch_point(watch_point)?;
}
self.clear_count()?;
Ok(())
}
pub fn remove_watch_point(&mut self, watch_point: i32) -> Result<(), EspError> {
esp!(unsafe { pcnt_unit_remove_watch_point(self.handle, watch_point) })
}
#[cfg(esp_idf_version_at_least_5_4_0)]
#[cfg_attr(feature = "nightly", doc(cfg(esp_idf_version_at_least_5_4_0)))]
pub fn add_watch_step(&mut self, step_interval: i32) -> Result<(), EspError> {
esp!(unsafe { pcnt_unit_add_watch_step(self.handle, step_interval) })
}
#[cfg(esp_idf_version_at_least_5_4_0)]
#[cfg_attr(feature = "nightly", doc(cfg(esp_idf_version_at_least_5_4_0)))]
pub fn remove_all_watch_step(&mut self) -> Result<(), EspError> {
#[cfg(esp_idf_version_at_least_5_5_0)]
{
esp!(unsafe { pcnt_unit_remove_all_watch_step(self.handle) })
}
#[cfg(all(esp_idf_version_at_least_5_4_0, not(esp_idf_version_at_least_5_5_0)))]
{
esp!(unsafe { pcnt_unit_remove_watch_step(self.handle) })
}
}
#[cfg(esp_idf_version_at_least_5_5_0)]
#[cfg_attr(feature = "nightly", doc(cfg(esp_idf_version_at_least_5_5_0)))]
pub fn remove_single_watch_step(&mut self, step_interval: i32) -> Result<(), EspError> {
esp!(unsafe { pcnt_unit_remove_single_watch_step(self.handle, step_interval) })
}
}
#[cfg(feature = "alloc")]
const DISABLE_EVENT_CALLBACKS: pcnt_event_callbacks_t = pcnt_event_callbacks_t { on_reach: None };
impl<'d> PcntUnitDriver<'d> {
pub fn set_glitch_filter(
&mut self,
config: Option<&GlitchFilterConfig>,
) -> Result<&mut Self, EspError> {
let sys_config = config.map(|config| config.into());
esp!(unsafe {
pcnt_unit_set_glitch_filter(self.handle, sys_config.as_ref().map_or(ptr::null(), |c| c))
})?;
Ok(self)
}
pub fn add_channel(
&mut self,
edge_pin: Option<impl InputPin + 'd>,
level_pin: Option<impl InputPin + 'd>,
config: &ChannelConfig,
) -> Result<&mut PcntChannel<'d>, EspError> {
self.channels
.push(unsafe { PcntChannel::new(self.handle, edge_pin, level_pin, config)? })
.expect("Not enough channels available.");
Ok(self.channels.last_mut().unwrap())
}
#[cfg(feature = "alloc")]
pub fn subscribe(
&mut self,
on_reach: impl FnMut(WatchEventData) + Send + 'static,
) -> Result<&mut Self, EspError> {
unsafe { self.subscribe_nonstatic(on_reach) }
}
#[cfg(feature = "alloc")]
pub unsafe fn subscribe_nonstatic(
&mut self,
on_reach: impl FnMut(WatchEventData) + Send + 'd,
) -> Result<&mut Self, EspError> {
let mut pinned = Box::pin(DelegateUserData {
on_reach: Box::new(on_reach),
});
esp!(unsafe {
pcnt_unit_register_event_callbacks(
self.handle,
&Self::ENABLE_EVENT_CALLBACKS,
(pinned.as_mut().get_unchecked_mut()) as *mut DelegateUserData
as *mut core::ffi::c_void,
)
})?;
self.user_data = Some(pinned);
Ok(self)
}
#[cfg(feature = "alloc")]
pub fn unsubscribe(&mut self) -> Result<&mut Self, EspError> {
esp!(unsafe {
pcnt_unit_register_event_callbacks(
self.handle,
&DISABLE_EVENT_CALLBACKS,
ptr::null_mut(),
)
})?;
self.user_data = None;
Ok(self)
}
#[cfg(feature = "alloc")]
const ENABLE_EVENT_CALLBACKS: pcnt_event_callbacks_t = pcnt_event_callbacks_t {
on_reach: Some(Self::delegate_on_reach),
};
pub fn enable(&mut self) -> Result<(), EspError> {
esp!(unsafe { pcnt_unit_enable(self.handle) })?;
Ok(())
}
#[cfg(feature = "alloc")]
unsafe extern "C" fn delegate_on_reach(
_handle: pcnt_unit_handle_t,
event_data: *const pcnt_watch_event_data_t,
user_data: *mut core::ffi::c_void,
) -> bool {
let user_data = &mut *(user_data as *mut DelegateUserData<'d>);
let event_data = WatchEventData::from(*event_data);
interrupt::with_isr_yield_signal(move || (user_data.on_reach)(event_data))
}
}
impl<'d> PcntUnitDriver<'d> {
pub fn disable(&mut self) -> Result<(), EspError> {
esp!(unsafe { pcnt_unit_disable(self.handle) })?;
Ok(())
}
pub fn start(&self) -> Result<(), EspError> {
esp!(unsafe { pcnt_unit_start(self.handle) })
}
pub fn stop(&self) -> Result<(), EspError> {
esp!(unsafe { pcnt_unit_stop(self.handle) })
}
pub fn clear_count(&self) -> Result<(), EspError> {
esp!(unsafe { pcnt_unit_clear_count(self.handle) })
}
pub fn get_count(&self) -> Result<i32, EspError> {
let mut count = 0;
esp!(unsafe { pcnt_unit_get_count(self.handle, &mut count) })?;
Ok(count)
}
}
unsafe impl<'d> Send for PcntUnitDriver<'d> {}
unsafe impl<'d> Sync for PcntUnitDriver<'d> {}
impl<'d> Drop for PcntUnitDriver<'d> {
fn drop(&mut self) {
let _ = self.disable();
#[cfg(feature = "alloc")]
unsafe {
pcnt_unit_register_event_callbacks(
self.handle,
&DISABLE_EVENT_CALLBACKS,
ptr::null_mut(),
)
};
unsafe {
pcnt_del_unit(self.handle);
}
}
}