From 123902eb3c711ae329adbb83132de30b12eda853 Mon Sep 17 00:00:00 2001 From: Christopher Liebman Date: Sun, 6 Nov 2022 15:52:42 -0800 Subject: [PATCH 01/48] WIP - PCNT implementation for idf v4 --- Cargo.toml | 1 + examples/pcnt_i64_encoder.rs | 103 ++++++++ src/lib.rs | 2 + src/pcnt.rs | 449 +++++++++++++++++++++++++++++++++++ 4 files changed, 555 insertions(+) create mode 100644 examples/pcnt_i64_encoder.rs create mode 100644 src/pcnt.rs diff --git a/Cargo.toml b/Cargo.toml index 2f6c94e0b36..618b9fed2df 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,6 +30,7 @@ critical-section = { version = "1.1", optional = true } heapless = "0.7" embassy-sync = { version = "0.1", optional = true } edge-executor = { version = "0.3", optional = true, default-features = false } +bitflags = "2.0.0-rc.1" [build-dependencies] embuild = "0.30.4" diff --git a/examples/pcnt_i64_encoder.rs b/examples/pcnt_i64_encoder.rs new file mode 100644 index 00000000000..2de71484300 --- /dev/null +++ b/examples/pcnt_i64_encoder.rs @@ -0,0 +1,103 @@ +//! PCNT decoding a rotery encoder +//! +//! To try this out, connect a rotery 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 +//! +use std::{sync::{atomic::{Ordering, AtomicI64}, Arc}, cmp::min}; + +use anyhow; +use anyhow::Context; +use embedded_hal_0_2::blocking::delay::DelayMs; +use esp_idf_hal::{delay::FreeRtos as delay}; +use esp_idf_hal::prelude::*; +use esp_idf_hal::gpio::Pull; +use tracing::{info, Level}; +use tracing_subscriber::FmtSubscriber; +use esp_idf_sys as _; // If using the `binstart` feature of `esp-idf-sys`, always keep this module imported +use esp_idf_hal::pcnt::*; + +fn main() -> anyhow::Result<()> { + // 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(); + + let subscriber = FmtSubscriber::builder() + // all spans/events with a level higher than TRACE (e.g, debug, info, warn, etc.) + // will be written to stdout. + .with_max_level(Level::TRACE) + // completes the builder. + .finish(); + + tracing::subscriber::set_global_default(subscriber) + .expect("setting default subscriber failed"); + + info!("setup pins"); + let peripherals = Peripherals::take().context("failed to take Peripherals")?; + let m1_enc1_pin = peripherals.pins.gpio5; + let m1_enc2_pin = peripherals.pins.gpio6; + let m1_enc1_pin = PcntPin::new(m1_enc1_pin, Pull::Up)?; + let m1_enc2_pin = PcntPin::new(m1_enc2_pin, Pull::Up)?; + info!("creating pcnt unit 0"); + let mut pcnt = Pcnt::new(PcntUnit::Unit0); + info!("configure pcnt chanel 0"); + const POS_LIMIT: i16 = 100; + const NEG_LIMIT: i16 = -100; + let mut config = PcntConfig { + pulse_pin: Some(m1_enc1_pin.clone()), + ctrl_pin: Some(m1_enc2_pin.clone()), + lctrl_mode: PcntControlMode::Reverse, + hctrl_mode: PcntControlMode::Keep, + pos_mode: PcntCountMode::Decrement, + neg_mode: PcntCountMode::Increment, + counter_h_lim: POS_LIMIT, + counter_l_lim: NEG_LIMIT, + channel: PcntChannel::Channel0, + }; + pcnt.config(&config).context("configuring CHANNEL0")?; + + info!("configure pcnt chanel 1"); + config.channel = PcntChannel::Channel1; + config.pulse_pin = Some(m1_enc2_pin.clone()); + config.ctrl_pin = Some(m1_enc1_pin.clone()); + config.pos_mode = PcntCountMode::Increment; + config.neg_mode = PcntCountMode::Decrement; + pcnt.config(&config).context("configuring CHANNEL1")?; + pcnt.set_filter_value(min(10*80, 1023))?; + pcnt.filter_enable()?; + let 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 value = value.clone(); + pcnt.subscribe(move |status| { + let status = PcntEventType::from_bits_retain(status); + if status.contains(PcntEventType::H_LIM) { + value.fetch_add(POS_LIMIT as i64, Ordering::SeqCst); + } + if status.contains(PcntEventType::L_LIM) { + value.fetch_add(NEG_LIMIT as i64, Ordering::SeqCst); + } + })?; + } + pcnt.event_enable(PcntEventType::H_LIM)?; + pcnt.event_enable(PcntEventType::L_LIM)?; + info!("starting pcnt counter"); + pcnt.counter_pause()?; + pcnt.counter_clear()?; + pcnt.counter_resume()?; + + let mut last_value = 0i64; + loop { + let pcnt_value = pcnt.get_counter_value()? as i64; + let acc_value = value.load(Ordering::SeqCst); + let value = acc_value + pcnt_value; + if value != last_value { + info!("value: {value} pct={pcnt_value} acc={acc_value}"); + last_value = value; + } + delay.delay_ms(100u32); + } +} diff --git a/src/lib.rs b/src/lib.rs index 899c86b3b71..4410e8d4314 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -46,6 +46,8 @@ pub mod mac; pub mod modem; pub mod peripheral; pub mod peripherals; +#[cfg(not(feature = "riscv-ulp-hal"))] +pub mod pcnt; pub mod prelude; #[cfg(not(feature = "riscv-ulp-hal"))] pub mod reset; diff --git a/src/pcnt.rs b/src/pcnt.rs new file mode 100644 index 00000000000..ee8d6950f69 --- /dev/null +++ b/src/pcnt.rs @@ -0,0 +1,449 @@ +use std::sync::Arc; +use std::fmt::Debug; + +use esp_idf_sys::esp; +use esp_idf_sys::pcnt_config_t; +use esp_idf_sys::pcnt_unit_t; +use esp_idf_sys::EspError; + +use bitflags::bitflags; + +use crate::gpio::IOPin; +use crate::gpio::PinDriver; +use crate::gpio::Pull; +use crate::peripheral::Peripheral; + +#[allow(dead_code)] +#[derive(Debug, Copy, Clone)] +pub enum PcntChannel { + Channel0, + Channel1, +} + +impl Into for PcntChannel { + fn into(self) -> esp_idf_sys::pcnt_channel_t { + match self { + PcntChannel::Channel0 => esp_idf_sys::pcnt_channel_t_PCNT_CHANNEL_0, + PcntChannel::Channel1 => esp_idf_sys::pcnt_channel_t_PCNT_CHANNEL_1, + } + } +} + +#[allow(dead_code)] +#[derive(Debug, Clone, Copy)] +pub enum PcntUnit { + Unit0, + Unit1, + Unit2, + Unit3, + #[cfg(not(esp32s3))] + Unit4, + #[cfg(not(esp32s3))] + Unit5, + #[cfg(not(esp32s3))] + Unit6, + #[cfg(not(esp32s3))] + Unit7, +} + +impl Into for PcntUnit { + fn into(self) -> esp_idf_sys::pcnt_unit_t { + match self { + PcntUnit::Unit0 => esp_idf_sys::pcnt_unit_t_PCNT_UNIT_0, + PcntUnit::Unit1 => esp_idf_sys::pcnt_unit_t_PCNT_UNIT_1, + PcntUnit::Unit2 => esp_idf_sys::pcnt_unit_t_PCNT_UNIT_2, + PcntUnit::Unit3 => esp_idf_sys::pcnt_unit_t_PCNT_UNIT_3, + #[cfg(not(esp32s3))] + PcntUnit::Unit4 => esp_idf_sys::pcnt_unit_t_PCNT_UNIT_4, + #[cfg(not(esp32s3))] + PcntUnit::Unit5 => esp_idf_sys::pcnt_unit_t_PCNT_UNIT_5, + #[cfg(not(esp32s3))] + PcntUnit::Unit6 => esp_idf_sys::pcnt_unit_t_PCNT_UNIT_6, + #[cfg(not(esp32s3))] + PcntUnit::Unit7 => esp_idf_sys::pcnt_unit_t_PCNT_UNIT_7, + } + } +} + +#[allow(dead_code)] +#[derive(Debug, Copy, Clone)] +pub enum PcntCountMode { + Hold, + Increment, + Decrement, +} + +impl Into for PcntCountMode { + fn into(self) -> esp_idf_sys::pcnt_count_mode_t { + match self { + PcntCountMode::Hold => { + esp_idf_sys::pcnt_channel_edge_action_t_PCNT_CHANNEL_EDGE_ACTION_HOLD + } + PcntCountMode::Increment => { + esp_idf_sys::pcnt_channel_edge_action_t_PCNT_CHANNEL_EDGE_ACTION_INCREASE + } + PcntCountMode::Decrement => { + esp_idf_sys::pcnt_channel_edge_action_t_PCNT_CHANNEL_EDGE_ACTION_DECREASE + } + } + } +} + +#[allow(dead_code)] +#[derive(Debug, Copy, Clone)] +pub enum PcntControlMode { + Keep, + Reverse, + Disable, +} + +impl Into for PcntControlMode { + fn into(self) -> esp_idf_sys::pcnt_ctrl_mode_t { + match self { + PcntControlMode::Keep => { + esp_idf_sys::pcnt_channel_edge_action_t_PCNT_CHANNEL_EDGE_ACTION_HOLD + } + PcntControlMode::Reverse => { + esp_idf_sys::pcnt_channel_edge_action_t_PCNT_CHANNEL_EDGE_ACTION_INCREASE + } + PcntControlMode::Disable => { + esp_idf_sys::pcnt_channel_edge_action_t_PCNT_CHANNEL_EDGE_ACTION_DECREASE + } + } + } +} + +bitflags! { + #[allow(dead_code)] + #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] + pub struct PcntEventType: u32 { + const THRES_1 = esp_idf_sys::pcnt_evt_type_t_PCNT_EVT_THRES_1; + const THRES_0 = esp_idf_sys::pcnt_evt_type_t_PCNT_EVT_THRES_0; + const L_LIM = esp_idf_sys::pcnt_evt_type_t_PCNT_EVT_L_LIM; + const H_LIM = esp_idf_sys::pcnt_evt_type_t_PCNT_EVT_H_LIM; + const ZERO = esp_idf_sys::pcnt_evt_type_t_PCNT_EVT_ZERO; + } +} + +#[doc = " @brief Pulse Counter configuration for a single channel"] +#[derive(Debug)] +pub struct PcntConfig { + #[doc = "< Pulse input GPIO number, if you want to use GPIO16, enter pulse_gpio_num = 16, a negative value will be ignored"] + pub pulse_pin: Option, + #[doc = "< Control signal input GPIO number, a negative value will be ignored"] + pub ctrl_pin: Option, + #[doc = "< PCNT low control mode"] + pub lctrl_mode: PcntControlMode, + #[doc = "< PCNT high control mode"] + pub hctrl_mode: PcntControlMode, + #[doc = "< PCNT positive edge count mode"] + pub pos_mode: PcntCountMode, + #[doc = "< PCNT negative edge count mode"] + pub neg_mode: PcntCountMode, + #[doc = "< Maximum counter value"] + pub counter_h_lim: i16, + #[doc = "< Minimum counter value"] + pub counter_l_lim: i16, + #[doc = "< the PCNT channel"] + pub channel: PcntChannel, +} + +impl Into for &PcntConfig { + fn into(self) -> pcnt_config_t { + pcnt_config_t { + pulse_gpio_num: match &self.pulse_pin { + Some(pin) => pin.pin_num(), + None => esp_idf_sys::PCNT_PIN_NOT_USED, + }, + ctrl_gpio_num: match &self.ctrl_pin { + Some(pin) => pin.pin_num(), + None => esp_idf_sys::PCNT_PIN_NOT_USED, + }, + lctrl_mode: self.lctrl_mode.into(), + hctrl_mode: self.hctrl_mode.into(), + pos_mode: self.pos_mode.into(), + neg_mode: self.neg_mode.into(), + counter_h_lim: self.counter_h_lim, + counter_l_lim: self.counter_l_lim, + channel: self.channel.into(), + ..Default::default() + } + } +} + +#[derive(Debug, Clone)] +pub struct PcntPin { + pin: Arc +} + +impl PcntPin { + pub fn new<'d>(pin: impl Peripheral

+ 'd, pull_mode: Pull) -> Result + { + crate::into_ref!(pin); + let p = PcntPin { pin: Arc::new(pin.pin()) }; + PinDriver::input(pin)?.into_input()?.set_pull(pull_mode)?; + Ok(p) + } + + fn pin_num(&self) -> i32 { + *self.pin + } +} + + +#[derive(Debug)] +struct PcntChannelPins { + pulse: Option, + ctrl: Option, +} + +#[allow(dead_code)] +#[derive(Debug)] +pub struct Pcnt { + unit: pcnt_unit_t, + channel_pins: [PcntChannelPins; esp_idf_sys::pcnt_channel_t_PCNT_CHANNEL_MAX as usize] +} + +#[allow(dead_code)] +impl Pcnt { + pub fn new(unit: PcntUnit) -> Pcnt { + Pcnt { + unit: unit.into(), + channel_pins: [PcntChannelPins { pulse: None, ctrl: None }, PcntChannelPins { pulse: None, ctrl: None }], + } + } + + fn get_pin_numbers(&self, channel: PcntChannel) -> (i32, i32) { + ( + match &self.channel_pins[channel as usize].pulse { + Some(pin) => pin.pin_num(), + None => esp_idf_sys::PCNT_PIN_NOT_USED, + }, + match &self.channel_pins[channel as usize].ctrl { + Some(pin) => pin.pin_num(), + None => esp_idf_sys::PCNT_PIN_NOT_USED, + }, + ) + } + + pub fn config(&mut self, pconfig: &PcntConfig) -> Result<(), EspError> { + let mut config: pcnt_config_t = pconfig.into(); + config.unit = self.unit.into(); + self.channel_pins[config.channel as usize].pulse = pconfig.pulse_pin.clone(); + self.channel_pins[config.channel as usize].ctrl = pconfig.pulse_pin.clone(); + unsafe { + esp!(esp_idf_sys::pcnt_unit_config( + &config as *const pcnt_config_t + )) + } + } + + pub fn get_counter_value(&self) -> Result { + let mut value = 0i16; + unsafe { + esp!(esp_idf_sys::pcnt_get_counter_value( + self.unit, + &mut value as *mut i16 + ))?; + } + Ok(value) + } + + pub fn counter_pause(&self) -> Result<(), EspError> { + unsafe { esp!(esp_idf_sys::pcnt_counter_pause(self.unit)) } + } + + pub fn counter_resume(&self) -> Result<(), EspError> { + unsafe { esp!(esp_idf_sys::pcnt_counter_resume(self.unit)) } + } + + pub fn counter_clear(&self) -> Result<(), EspError> { + unsafe { esp!(esp_idf_sys::pcnt_counter_clear(self.unit)) } + } + + pub fn intr_enable(&self) -> Result<(), EspError> { + unsafe { esp!(esp_idf_sys::pcnt_intr_enable(self.unit)) } + } + + pub fn intr_disable(&self) -> Result<(), EspError> { + unsafe { esp!(esp_idf_sys::pcnt_intr_disable(self.unit)) } + } + + pub fn event_enable(&self, evt_type: PcntEventType) -> Result<(), EspError> { + let evt_type: esp_idf_sys::pcnt_evt_type_t = evt_type.bits(); + unsafe { esp!(esp_idf_sys::pcnt_event_enable(self.unit, evt_type)) } + } + + pub fn event_disable(&self, evt_type: PcntEventType) -> Result<(), EspError> { + let evt_type: esp_idf_sys::pcnt_evt_type_t = evt_type.bits(); + unsafe { esp!(esp_idf_sys::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.bits()), + _ =>Err(EspError::from(esp_idf_sys::ESP_ERR_INVALID_ARG as esp_idf_sys::esp_err_t).unwrap()), + } + } + + 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!(esp_idf_sys::pcnt_set_event_value( + self.unit, evt_type, value + )) + } + } + + 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!(esp_idf_sys::pcnt_get_event_value( + self.unit, + evt_type, + &mut value as *mut i16 + ))?; + } + Ok(value) + } + + // TODO: status is a bit field! + pub fn get_event_status(&self) -> Result { + let mut value = 0u32; + unsafe { + esp!(esp_idf_sys::pcnt_get_event_status( + self.unit, + &mut value as *mut u32 + ))?; + } + Ok(value) + } + + pub fn set_pin( + &mut self, + channel: PcntChannel, + pulse_pin: Option, + ctrl_pin: Option, + ) -> Result<(), EspError> { + self.channel_pins[channel as usize].pulse = pulse_pin.clone(); + self.channel_pins[channel as usize].ctrl = pulse_pin.clone(); + let pulse_io_num = match &pulse_pin { + Some(pin) => pin.pin_num(), + None => esp_idf_sys::PCNT_PIN_NOT_USED, + }; + let ctrl_io_num = match &ctrl_pin { + Some(pin) => pin.pin_num(), + None => esp_idf_sys::PCNT_PIN_NOT_USED, + }; + unsafe { + esp!(esp_idf_sys::pcnt_set_pin( + self.unit, + channel.into(), + pulse_io_num, + ctrl_io_num + )) + } + } + + pub fn filter_enable(&self) -> Result<(), EspError> { + unsafe { esp!(esp_idf_sys::pcnt_filter_enable(self.unit)) } + } + + pub fn filter_disable(&self) -> Result<(), EspError> { + unsafe { esp!(esp_idf_sys::pcnt_filter_disable(self.unit)) } + } + + pub fn set_filter_value(&self, value: u16) -> Result<(), EspError> { + unsafe { esp!(esp_idf_sys::pcnt_set_filter_value(self.unit, value)) } + } + + pub fn get_filter_value(&self) -> Result { + let mut value = 0u16; + unsafe { + esp!(esp_idf_sys::pcnt_get_filter_value( + self.unit, + &mut value as *mut u16 + ))?; + } + Ok(value) + } + + pub unsafe fn subscribe(&self, callback: impl FnMut(u32) + Send + 'static) -> Result<(), EspError> { + enable_isr_service()?; + + //self.unsubscribe(); + let callback: Box = Box::new(callback); + ISR_HANDLERS[self.unit as usize] = Some(callback); + esp!(esp_idf_sys::pcnt_isr_handler_add( + self.unit, + Some(Self::handle_isr), + self.unit as *mut esp_idf_sys::c_types::c_void, + ))?; + Ok(()) + } + + pub fn unsubscribe(&self) -> Result<(), EspError> { + unsafe { + esp!(esp_idf_sys::pcnt_isr_handler_remove(self.unit))?; + ISR_HANDLERS[self.unit as usize] = None; + } + Ok(()) + } + + unsafe extern "C" fn handle_isr(data: *mut esp_idf_sys::c_types::c_void) { + let unit = data as pcnt_unit_t; + if let Some(f) = &mut ISR_HANDLERS[unit as usize] { + let mut value = 0u32; + esp!(esp_idf_sys::pcnt_get_event_status( + unit, + &mut value as *mut u32 + )).expect("failed to fetch event status!"); + f(value); + } + } +} + +#[cfg(all(not(feature = "riscv-ulp-hal"), feature = "alloc"))] +static ISR_SERVICE_ENABLED: core::sync::atomic::AtomicBool = + core::sync::atomic::AtomicBool::new(false); + +#[cfg(all(not(feature = "riscv-ulp-hal"), feature = "alloc"))] +static ISR_SERVICE_ENABLED_CS: crate::task::CriticalSection = crate::task::CriticalSection::new(); + +#[allow(dead_code)] +fn enable_isr_service() -> Result<(), EspError> { + use core::sync::atomic::Ordering; + + if !ISR_SERVICE_ENABLED.load(Ordering::SeqCst) { + let _ = ISR_SERVICE_ENABLED_CS.enter(); + + if !ISR_SERVICE_ENABLED.load(Ordering::SeqCst) { + esp!(unsafe { esp_idf_sys::pcnt_isr_service_install(0) })?; + + ISR_SERVICE_ENABLED.store(true, Ordering::SeqCst); + } + } + + Ok(()) +} + +#[allow(dead_code)] +static mut ISR_HANDLERS: [Option>; esp_idf_sys::pcnt_unit_t_PCNT_UNIT_MAX as usize] = [ + None, None, None, None, + #[cfg(not(esp32s3))] + None, + #[cfg(not(esp32s3))] + None, + #[cfg(not(esp32s3))] + None, + #[cfg(not(esp32s3))] + None, +]; + +impl Drop for Pcnt { + fn drop(&mut self) { + let _ = self.intr_disable(); + unsafe {ISR_HANDLERS[self.unit as usize] = None}; + } +} From e2ec50aae1eb51b03593f1ad0f40500c492bab1e Mon Sep 17 00:00:00 2001 From: Christopher Liebman Date: Mon, 7 Nov 2022 13:20:24 -0800 Subject: [PATCH 02/48] use from core not std pass pins as PeripheralRef store PinDriver in PcntPin --- src/pcnt.rs | 126 +++++++++++++++++++++++++--------------------------- 1 file changed, 60 insertions(+), 66 deletions(-) diff --git a/src/pcnt.rs b/src/pcnt.rs index ee8d6950f69..31306f01763 100644 --- a/src/pcnt.rs +++ b/src/pcnt.rs @@ -1,5 +1,7 @@ -use std::sync::Arc; -use std::fmt::Debug; +use core::borrow::Borrow; +use core::fmt; +use core::fmt::Debug; +use core::marker::PhantomData; use esp_idf_sys::esp; use esp_idf_sys::pcnt_config_t; @@ -8,10 +10,13 @@ use esp_idf_sys::EspError; use bitflags::bitflags; +use crate::gpio::AnyIOPin; use crate::gpio::IOPin; +use crate::gpio::Input; use crate::gpio::PinDriver; use crate::gpio::Pull; use crate::peripheral::Peripheral; +use crate::peripheral::PeripheralRef; #[allow(dead_code)] #[derive(Debug, Copy, Clone)] @@ -127,11 +132,11 @@ bitflags! { #[doc = " @brief Pulse Counter configuration for a single channel"] #[derive(Debug)] -pub struct PcntConfig { +pub struct PcntConfig<'d, T: Borrow>> { #[doc = "< Pulse input GPIO number, if you want to use GPIO16, enter pulse_gpio_num = 16, a negative value will be ignored"] - pub pulse_pin: Option, + pub pulse_pin: Option, #[doc = "< Control signal input GPIO number, a negative value will be ignored"] - pub ctrl_pin: Option, + pub ctrl_pin: Option, #[doc = "< PCNT low control mode"] pub lctrl_mode: PcntControlMode, #[doc = "< PCNT high control mode"] @@ -146,91 +151,80 @@ pub struct PcntConfig { pub counter_l_lim: i16, #[doc = "< the PCNT channel"] pub channel: PcntChannel, + pub _p: PhantomData<&'d ()>, } -impl Into for &PcntConfig { - fn into(self) -> pcnt_config_t { - pcnt_config_t { - pulse_gpio_num: match &self.pulse_pin { - Some(pin) => pin.pin_num(), - None => esp_idf_sys::PCNT_PIN_NOT_USED, - }, - ctrl_gpio_num: match &self.ctrl_pin { - Some(pin) => pin.pin_num(), - None => esp_idf_sys::PCNT_PIN_NOT_USED, - }, - lctrl_mode: self.lctrl_mode.into(), - hctrl_mode: self.hctrl_mode.into(), - pos_mode: self.pos_mode.into(), - neg_mode: self.neg_mode.into(), - counter_h_lim: self.counter_h_lim, - counter_l_lim: self.counter_l_lim, - channel: self.channel.into(), - ..Default::default() - } - } +pub struct PcntPin<'d> { + pin: PinDriver<'d, AnyIOPin, Input> } -#[derive(Debug, Clone)] -pub struct PcntPin { - pin: Arc -} - -impl PcntPin { - pub fn new<'d>(pin: impl Peripheral

+ 'd, pull_mode: Pull) -> Result +impl<'d> PcntPin<'d> { + pub fn new(pin: impl Peripheral

+ 'd, pull_mode: Pull) -> Result { - crate::into_ref!(pin); - let p = PcntPin { pin: Arc::new(pin.pin()) }; - PinDriver::input(pin)?.into_input()?.set_pull(pull_mode)?; - Ok(p) + let pin: PeripheralRef = pin.into_ref().map_into(); + let mut pin = PinDriver::input(pin)?; + pin.set_pull(pull_mode)?; + Ok(Self{pin}) + } fn pin_num(&self) -> i32 { - *self.pin + self.pin.pin() } } +impl<'d> Debug for PcntPin<'d> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "PcntPin: {{pin: {}}}", self.pin_num()) + } +} #[derive(Debug)] -struct PcntChannelPins { - pulse: Option, - ctrl: Option, +struct PcntChannelPins <'d, T: Borrow>> +{ + pulse: Option, + ctrl: Option, + _p: PhantomData<&'d ()>, } #[allow(dead_code)] #[derive(Debug)] -pub struct Pcnt { +pub struct Pcnt<'d, T: Borrow>> { unit: pcnt_unit_t, - channel_pins: [PcntChannelPins; esp_idf_sys::pcnt_channel_t_PCNT_CHANNEL_MAX as usize] + channel_pins: [PcntChannelPins<'d, T>; esp_idf_sys::pcnt_channel_t_PCNT_CHANNEL_MAX as usize] } #[allow(dead_code)] -impl Pcnt { - pub fn new(unit: PcntUnit) -> Pcnt { +impl<'d, T: Borrow>> Pcnt<'d, T> { + pub fn new(unit: PcntUnit) -> Pcnt<'d, T> { Pcnt { unit: unit.into(), - channel_pins: [PcntChannelPins { pulse: None, ctrl: None }, PcntChannelPins { pulse: None, ctrl: None }], + channel_pins: [PcntChannelPins { pulse: None, ctrl: None, _p: PhantomData::default() }, PcntChannelPins { pulse: None, ctrl: None, _p: PhantomData::default() }], } } - fn get_pin_numbers(&self, channel: PcntChannel) -> (i32, i32) { - ( - match &self.channel_pins[channel as usize].pulse { - Some(pin) => pin.pin_num(), + pub fn config(&mut self, pconfig: &mut PcntConfig<'d, T>) -> Result<(), EspError> { + let mut config = pcnt_config_t { + pulse_gpio_num: match &pconfig.pulse_pin { + Some(pin) => pin.borrow().pin_num(), None => esp_idf_sys::PCNT_PIN_NOT_USED, }, - match &self.channel_pins[channel as usize].ctrl { - Some(pin) => pin.pin_num(), + ctrl_gpio_num: match &pconfig.ctrl_pin { + Some(pin) => pin.borrow().pin_num(), None => esp_idf_sys::PCNT_PIN_NOT_USED, }, - ) - } - - pub fn config(&mut self, pconfig: &PcntConfig) -> Result<(), EspError> { - let mut config: pcnt_config_t = pconfig.into(); + 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: pconfig.channel.into(), + ..Default::default() + }; config.unit = self.unit.into(); - self.channel_pins[config.channel as usize].pulse = pconfig.pulse_pin.clone(); - self.channel_pins[config.channel as usize].ctrl = pconfig.pulse_pin.clone(); + self.channel_pins[config.channel as usize].pulse = pconfig.pulse_pin.take(); + self.channel_pins[config.channel as usize].ctrl = pconfig.pulse_pin.take(); unsafe { esp!(esp_idf_sys::pcnt_unit_config( &config as *const pcnt_config_t @@ -323,19 +317,19 @@ impl Pcnt { pub fn set_pin( &mut self, channel: PcntChannel, - pulse_pin: Option, - ctrl_pin: Option, + pulse_pin: Option, + ctrl_pin: Option, ) -> Result<(), EspError> { - self.channel_pins[channel as usize].pulse = pulse_pin.clone(); - self.channel_pins[channel as usize].ctrl = pulse_pin.clone(); let pulse_io_num = match &pulse_pin { - Some(pin) => pin.pin_num(), + Some(pin) => pin.borrow().pin_num(), None => esp_idf_sys::PCNT_PIN_NOT_USED, }; let ctrl_io_num = match &ctrl_pin { - Some(pin) => pin.pin_num(), + Some(pin) => pin.borrow().pin_num(), None => esp_idf_sys::PCNT_PIN_NOT_USED, }; + self.channel_pins[channel as usize].pulse = pulse_pin; + self.channel_pins[channel as usize].ctrl = ctrl_pin; unsafe { esp!(esp_idf_sys::pcnt_set_pin( self.unit, @@ -441,7 +435,7 @@ static mut ISR_HANDLERS: [Option>; esp_idf_sys::pcnt_unit_t_ None, ]; -impl Drop for Pcnt { +impl<'d, T: Borrow>> Drop for Pcnt<'d, T> { fn drop(&mut self) { let _ = self.intr_disable(); unsafe {ISR_HANDLERS[self.unit as usize] = None}; From 4b129c8a85a42b62f39dcbe03a4df55d4eba3afb Mon Sep 17 00:00:00 2001 From: Christopher Liebman Date: Mon, 7 Nov 2022 16:29:41 -0800 Subject: [PATCH 03/48] try a allocate/deallocate stratagy for PCNT UNITS (like esp-idf v5) --- src/pcnt.rs | 101 ++++++++++++++++++++++++++-------------------------- 1 file changed, 50 insertions(+), 51 deletions(-) diff --git a/src/pcnt.rs b/src/pcnt.rs index 31306f01763..9ca1c8d97e7 100644 --- a/src/pcnt.rs +++ b/src/pcnt.rs @@ -18,6 +18,11 @@ use crate::gpio::Pull; use crate::peripheral::Peripheral; use crate::peripheral::PeripheralRef; +#[cfg(esp_idf_version_major = "4")] +type UnitHandle = pcnt_unit_t; +#[cfg(esp_idf_version_major = "5")] +type UnitHandle = pcnt_unit_handle_t; + #[allow(dead_code)] #[derive(Debug, Copy, Clone)] pub enum PcntChannel { @@ -34,43 +39,7 @@ impl Into for PcntChannel { } } -#[allow(dead_code)] -#[derive(Debug, Clone, Copy)] -pub enum PcntUnit { - Unit0, - Unit1, - Unit2, - Unit3, - #[cfg(not(esp32s3))] - Unit4, - #[cfg(not(esp32s3))] - Unit5, - #[cfg(not(esp32s3))] - Unit6, - #[cfg(not(esp32s3))] - Unit7, -} - -impl Into for PcntUnit { - fn into(self) -> esp_idf_sys::pcnt_unit_t { - match self { - PcntUnit::Unit0 => esp_idf_sys::pcnt_unit_t_PCNT_UNIT_0, - PcntUnit::Unit1 => esp_idf_sys::pcnt_unit_t_PCNT_UNIT_1, - PcntUnit::Unit2 => esp_idf_sys::pcnt_unit_t_PCNT_UNIT_2, - PcntUnit::Unit3 => esp_idf_sys::pcnt_unit_t_PCNT_UNIT_3, - #[cfg(not(esp32s3))] - PcntUnit::Unit4 => esp_idf_sys::pcnt_unit_t_PCNT_UNIT_4, - #[cfg(not(esp32s3))] - PcntUnit::Unit5 => esp_idf_sys::pcnt_unit_t_PCNT_UNIT_5, - #[cfg(not(esp32s3))] - PcntUnit::Unit6 => esp_idf_sys::pcnt_unit_t_PCNT_UNIT_6, - #[cfg(not(esp32s3))] - PcntUnit::Unit7 => esp_idf_sys::pcnt_unit_t_PCNT_UNIT_7, - } - } -} -#[allow(dead_code)] #[derive(Debug, Copy, Clone)] pub enum PcntCountMode { Hold, @@ -94,7 +63,6 @@ impl Into for PcntCountMode { } } -#[allow(dead_code)] #[derive(Debug, Copy, Clone)] pub enum PcntControlMode { Keep, @@ -187,20 +155,18 @@ struct PcntChannelPins <'d, T: Borrow>> _p: PhantomData<&'d ()>, } -#[allow(dead_code)] #[derive(Debug)] pub struct Pcnt<'d, T: Borrow>> { unit: pcnt_unit_t, channel_pins: [PcntChannelPins<'d, T>; esp_idf_sys::pcnt_channel_t_PCNT_CHANNEL_MAX as usize] } -#[allow(dead_code)] impl<'d, T: Borrow>> Pcnt<'d, T> { - pub fn new(unit: PcntUnit) -> Pcnt<'d, T> { - Pcnt { - unit: unit.into(), + pub fn new() -> Result { + Ok(Pcnt { + unit: unit_allocate()?, channel_pins: [PcntChannelPins { pulse: None, ctrl: None, _p: PhantomData::default() }, PcntChannelPins { pulse: None, ctrl: None, _p: PhantomData::default() }], - } + }) } pub fn config(&mut self, pconfig: &mut PcntConfig<'d, T>) -> Result<(), EspError> { @@ -398,19 +364,26 @@ impl<'d, T: Borrow>> Pcnt<'d, T> { } } +impl<'d, T: Borrow>> Drop for Pcnt<'d, T> { + fn drop(&mut self) { + let _ = self.intr_disable(); + unsafe {ISR_HANDLERS[self.unit as usize] = None}; + unit_deallocate(self.unit) + } +} + #[cfg(all(not(feature = "riscv-ulp-hal"), feature = "alloc"))] static ISR_SERVICE_ENABLED: core::sync::atomic::AtomicBool = core::sync::atomic::AtomicBool::new(false); #[cfg(all(not(feature = "riscv-ulp-hal"), feature = "alloc"))] -static ISR_SERVICE_ENABLED_CS: crate::task::CriticalSection = crate::task::CriticalSection::new(); +static PCNT_CS: crate::task::CriticalSection = crate::task::CriticalSection::new(); -#[allow(dead_code)] fn enable_isr_service() -> Result<(), EspError> { use core::sync::atomic::Ordering; if !ISR_SERVICE_ENABLED.load(Ordering::SeqCst) { - let _ = ISR_SERVICE_ENABLED_CS.enter(); + let _ = PCNT_CS.enter(); if !ISR_SERVICE_ENABLED.load(Ordering::SeqCst) { esp!(unsafe { esp_idf_sys::pcnt_isr_service_install(0) })?; @@ -422,7 +395,6 @@ fn enable_isr_service() -> Result<(), EspError> { Ok(()) } -#[allow(dead_code)] static mut ISR_HANDLERS: [Option>; esp_idf_sys::pcnt_unit_t_PCNT_UNIT_MAX as usize] = [ None, None, None, None, #[cfg(not(esp32s3))] @@ -435,9 +407,36 @@ static mut ISR_HANDLERS: [Option>; esp_idf_sys::pcnt_unit_t_ None, ]; -impl<'d, T: Borrow>> Drop for Pcnt<'d, T> { - fn drop(&mut self) { - let _ = self.intr_disable(); - unsafe {ISR_HANDLERS[self.unit as usize] = None}; +#[cfg(esp_idf_version_major = "4")] +static mut PCNT_UNITS: [Option; esp_idf_sys::pcnt_unit_t_PCNT_UNIT_MAX as usize] = [ + Some(esp_idf_sys::pcnt_unit_t_PCNT_UNIT_0), + Some(esp_idf_sys::pcnt_unit_t_PCNT_UNIT_1), + Some(esp_idf_sys::pcnt_unit_t_PCNT_UNIT_2), + Some(esp_idf_sys::pcnt_unit_t_PCNT_UNIT_3), + #[cfg(not(esp32s3))] + Some(esp_idf_sys::pcnt_unit_t_PCNT_UNIT_4), + #[cfg(not(esp32s3))] + Some(esp_idf_sys::pcnt_unit_t_PCNT_UNIT_5), + #[cfg(not(esp32s3))] + Some(esp_idf_sys::pcnt_unit_t_PCNT_UNIT_6), + #[cfg(not(esp32s3))] + Some(esp_idf_sys::pcnt_unit_t_PCNT_UNIT_7), +]; +#[cfg(esp_idf_version_major = "4")] +fn unit_allocate() -> Result { + let _ = PCNT_CS.enter(); + for i in 0..esp_idf_sys::pcnt_unit_t_PCNT_UNIT_MAX { + if let Some(unit) = unsafe { PCNT_UNITS[i as usize].take() } { + return Ok(unit); + } + } + Err(EspError::from(esp_idf_sys::ESP_ERR_NO_MEM as esp_idf_sys::esp_err_t).unwrap()) +} + +#[cfg(esp_idf_version_major = "4")] +fn unit_deallocate(unit: UnitHandle) { + let _ = PCNT_CS.enter(); + unsafe { + PCNT_UNITS[unit as usize] = Some(unit); } } From 8c7180ed005c7cd87d0afab25fe8571af69d58d3 Mon Sep 17 00:00:00 2001 From: Christopher Liebman Date: Tue, 8 Nov 2022 06:45:31 -0800 Subject: [PATCH 04/48] update pcnt example --- examples/pcnt_i64_encoder.rs | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/examples/pcnt_i64_encoder.rs b/examples/pcnt_i64_encoder.rs index 2de71484300..7ff30fbfe24 100644 --- a/examples/pcnt_i64_encoder.rs +++ b/examples/pcnt_i64_encoder.rs @@ -10,7 +10,7 @@ use std::{sync::{atomic::{Ordering, AtomicI64}, Arc}, cmp::min}; use anyhow; use anyhow::Context; use embedded_hal_0_2::blocking::delay::DelayMs; -use esp_idf_hal::{delay::FreeRtos as delay}; +use esp_idf_hal::delay::FreeRtos as delay; use esp_idf_hal::prelude::*; use esp_idf_hal::gpio::Pull; use tracing::{info, Level}; @@ -40,13 +40,13 @@ fn main() -> anyhow::Result<()> { let m1_enc1_pin = PcntPin::new(m1_enc1_pin, Pull::Up)?; let m1_enc2_pin = PcntPin::new(m1_enc2_pin, Pull::Up)?; info!("creating pcnt unit 0"); - let mut pcnt = Pcnt::new(PcntUnit::Unit0); + let mut pcnt = Pcnt::new()?; info!("configure pcnt chanel 0"); const POS_LIMIT: i16 = 100; const NEG_LIMIT: i16 = -100; let mut config = PcntConfig { - pulse_pin: Some(m1_enc1_pin.clone()), - ctrl_pin: Some(m1_enc2_pin.clone()), + pulse_pin: Some(&m1_enc1_pin), + ctrl_pin: Some(&m1_enc2_pin), lctrl_mode: PcntControlMode::Reverse, hctrl_mode: PcntControlMode::Keep, pos_mode: PcntCountMode::Decrement, @@ -54,16 +54,17 @@ fn main() -> anyhow::Result<()> { counter_h_lim: POS_LIMIT, counter_l_lim: NEG_LIMIT, channel: PcntChannel::Channel0, + _p: std::marker::PhantomData, }; - pcnt.config(&config).context("configuring CHANNEL0")?; + pcnt.config(&mut config).context("configuring CHANNEL0")?; info!("configure pcnt chanel 1"); config.channel = PcntChannel::Channel1; - config.pulse_pin = Some(m1_enc2_pin.clone()); - config.ctrl_pin = Some(m1_enc1_pin.clone()); + config.pulse_pin = Some(&m1_enc2_pin); + config.ctrl_pin = Some(&m1_enc1_pin); config.pos_mode = PcntCountMode::Increment; config.neg_mode = PcntCountMode::Decrement; - pcnt.config(&config).context("configuring CHANNEL1")?; + pcnt.config(&mut config).context("configuring CHANNEL1")?; pcnt.set_filter_value(min(10*80, 1023))?; pcnt.filter_enable()?; let value = Arc::new(AtomicI64::new(0)); From 510939d37015383c9c8c9f38c747033c36309d28 Mon Sep 17 00:00:00 2001 From: Christopher Liebman Date: Sun, 6 Nov 2022 15:52:42 -0800 Subject: [PATCH 05/48] WIP - PCNT implementation for idf v4 --- Cargo.toml | 1 + examples/pcnt_i64_encoder.rs | 103 ++++++++ src/lib.rs | 2 + src/pcnt.rs | 449 +++++++++++++++++++++++++++++++++++ 4 files changed, 555 insertions(+) create mode 100644 examples/pcnt_i64_encoder.rs create mode 100644 src/pcnt.rs diff --git a/Cargo.toml b/Cargo.toml index 9a822965698..79ba1aa832b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,6 +30,7 @@ critical-section = { version = "1.1", optional = true } heapless = "0.7" embassy-sync = { version = "0.1", optional = true } edge-executor = { version = "0.3", optional = true, default-features = false } +bitflags = "2.0.0-rc.1" [build-dependencies] embuild = "0.30.4" diff --git a/examples/pcnt_i64_encoder.rs b/examples/pcnt_i64_encoder.rs new file mode 100644 index 00000000000..2de71484300 --- /dev/null +++ b/examples/pcnt_i64_encoder.rs @@ -0,0 +1,103 @@ +//! PCNT decoding a rotery encoder +//! +//! To try this out, connect a rotery 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 +//! +use std::{sync::{atomic::{Ordering, AtomicI64}, Arc}, cmp::min}; + +use anyhow; +use anyhow::Context; +use embedded_hal_0_2::blocking::delay::DelayMs; +use esp_idf_hal::{delay::FreeRtos as delay}; +use esp_idf_hal::prelude::*; +use esp_idf_hal::gpio::Pull; +use tracing::{info, Level}; +use tracing_subscriber::FmtSubscriber; +use esp_idf_sys as _; // If using the `binstart` feature of `esp-idf-sys`, always keep this module imported +use esp_idf_hal::pcnt::*; + +fn main() -> anyhow::Result<()> { + // 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(); + + let subscriber = FmtSubscriber::builder() + // all spans/events with a level higher than TRACE (e.g, debug, info, warn, etc.) + // will be written to stdout. + .with_max_level(Level::TRACE) + // completes the builder. + .finish(); + + tracing::subscriber::set_global_default(subscriber) + .expect("setting default subscriber failed"); + + info!("setup pins"); + let peripherals = Peripherals::take().context("failed to take Peripherals")?; + let m1_enc1_pin = peripherals.pins.gpio5; + let m1_enc2_pin = peripherals.pins.gpio6; + let m1_enc1_pin = PcntPin::new(m1_enc1_pin, Pull::Up)?; + let m1_enc2_pin = PcntPin::new(m1_enc2_pin, Pull::Up)?; + info!("creating pcnt unit 0"); + let mut pcnt = Pcnt::new(PcntUnit::Unit0); + info!("configure pcnt chanel 0"); + const POS_LIMIT: i16 = 100; + const NEG_LIMIT: i16 = -100; + let mut config = PcntConfig { + pulse_pin: Some(m1_enc1_pin.clone()), + ctrl_pin: Some(m1_enc2_pin.clone()), + lctrl_mode: PcntControlMode::Reverse, + hctrl_mode: PcntControlMode::Keep, + pos_mode: PcntCountMode::Decrement, + neg_mode: PcntCountMode::Increment, + counter_h_lim: POS_LIMIT, + counter_l_lim: NEG_LIMIT, + channel: PcntChannel::Channel0, + }; + pcnt.config(&config).context("configuring CHANNEL0")?; + + info!("configure pcnt chanel 1"); + config.channel = PcntChannel::Channel1; + config.pulse_pin = Some(m1_enc2_pin.clone()); + config.ctrl_pin = Some(m1_enc1_pin.clone()); + config.pos_mode = PcntCountMode::Increment; + config.neg_mode = PcntCountMode::Decrement; + pcnt.config(&config).context("configuring CHANNEL1")?; + pcnt.set_filter_value(min(10*80, 1023))?; + pcnt.filter_enable()?; + let 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 value = value.clone(); + pcnt.subscribe(move |status| { + let status = PcntEventType::from_bits_retain(status); + if status.contains(PcntEventType::H_LIM) { + value.fetch_add(POS_LIMIT as i64, Ordering::SeqCst); + } + if status.contains(PcntEventType::L_LIM) { + value.fetch_add(NEG_LIMIT as i64, Ordering::SeqCst); + } + })?; + } + pcnt.event_enable(PcntEventType::H_LIM)?; + pcnt.event_enable(PcntEventType::L_LIM)?; + info!("starting pcnt counter"); + pcnt.counter_pause()?; + pcnt.counter_clear()?; + pcnt.counter_resume()?; + + let mut last_value = 0i64; + loop { + let pcnt_value = pcnt.get_counter_value()? as i64; + let acc_value = value.load(Ordering::SeqCst); + let value = acc_value + pcnt_value; + if value != last_value { + info!("value: {value} pct={pcnt_value} acc={acc_value}"); + last_value = value; + } + delay.delay_ms(100u32); + } +} diff --git a/src/lib.rs b/src/lib.rs index 899c86b3b71..4410e8d4314 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -46,6 +46,8 @@ pub mod mac; pub mod modem; pub mod peripheral; pub mod peripherals; +#[cfg(not(feature = "riscv-ulp-hal"))] +pub mod pcnt; pub mod prelude; #[cfg(not(feature = "riscv-ulp-hal"))] pub mod reset; diff --git a/src/pcnt.rs b/src/pcnt.rs new file mode 100644 index 00000000000..ee8d6950f69 --- /dev/null +++ b/src/pcnt.rs @@ -0,0 +1,449 @@ +use std::sync::Arc; +use std::fmt::Debug; + +use esp_idf_sys::esp; +use esp_idf_sys::pcnt_config_t; +use esp_idf_sys::pcnt_unit_t; +use esp_idf_sys::EspError; + +use bitflags::bitflags; + +use crate::gpio::IOPin; +use crate::gpio::PinDriver; +use crate::gpio::Pull; +use crate::peripheral::Peripheral; + +#[allow(dead_code)] +#[derive(Debug, Copy, Clone)] +pub enum PcntChannel { + Channel0, + Channel1, +} + +impl Into for PcntChannel { + fn into(self) -> esp_idf_sys::pcnt_channel_t { + match self { + PcntChannel::Channel0 => esp_idf_sys::pcnt_channel_t_PCNT_CHANNEL_0, + PcntChannel::Channel1 => esp_idf_sys::pcnt_channel_t_PCNT_CHANNEL_1, + } + } +} + +#[allow(dead_code)] +#[derive(Debug, Clone, Copy)] +pub enum PcntUnit { + Unit0, + Unit1, + Unit2, + Unit3, + #[cfg(not(esp32s3))] + Unit4, + #[cfg(not(esp32s3))] + Unit5, + #[cfg(not(esp32s3))] + Unit6, + #[cfg(not(esp32s3))] + Unit7, +} + +impl Into for PcntUnit { + fn into(self) -> esp_idf_sys::pcnt_unit_t { + match self { + PcntUnit::Unit0 => esp_idf_sys::pcnt_unit_t_PCNT_UNIT_0, + PcntUnit::Unit1 => esp_idf_sys::pcnt_unit_t_PCNT_UNIT_1, + PcntUnit::Unit2 => esp_idf_sys::pcnt_unit_t_PCNT_UNIT_2, + PcntUnit::Unit3 => esp_idf_sys::pcnt_unit_t_PCNT_UNIT_3, + #[cfg(not(esp32s3))] + PcntUnit::Unit4 => esp_idf_sys::pcnt_unit_t_PCNT_UNIT_4, + #[cfg(not(esp32s3))] + PcntUnit::Unit5 => esp_idf_sys::pcnt_unit_t_PCNT_UNIT_5, + #[cfg(not(esp32s3))] + PcntUnit::Unit6 => esp_idf_sys::pcnt_unit_t_PCNT_UNIT_6, + #[cfg(not(esp32s3))] + PcntUnit::Unit7 => esp_idf_sys::pcnt_unit_t_PCNT_UNIT_7, + } + } +} + +#[allow(dead_code)] +#[derive(Debug, Copy, Clone)] +pub enum PcntCountMode { + Hold, + Increment, + Decrement, +} + +impl Into for PcntCountMode { + fn into(self) -> esp_idf_sys::pcnt_count_mode_t { + match self { + PcntCountMode::Hold => { + esp_idf_sys::pcnt_channel_edge_action_t_PCNT_CHANNEL_EDGE_ACTION_HOLD + } + PcntCountMode::Increment => { + esp_idf_sys::pcnt_channel_edge_action_t_PCNT_CHANNEL_EDGE_ACTION_INCREASE + } + PcntCountMode::Decrement => { + esp_idf_sys::pcnt_channel_edge_action_t_PCNT_CHANNEL_EDGE_ACTION_DECREASE + } + } + } +} + +#[allow(dead_code)] +#[derive(Debug, Copy, Clone)] +pub enum PcntControlMode { + Keep, + Reverse, + Disable, +} + +impl Into for PcntControlMode { + fn into(self) -> esp_idf_sys::pcnt_ctrl_mode_t { + match self { + PcntControlMode::Keep => { + esp_idf_sys::pcnt_channel_edge_action_t_PCNT_CHANNEL_EDGE_ACTION_HOLD + } + PcntControlMode::Reverse => { + esp_idf_sys::pcnt_channel_edge_action_t_PCNT_CHANNEL_EDGE_ACTION_INCREASE + } + PcntControlMode::Disable => { + esp_idf_sys::pcnt_channel_edge_action_t_PCNT_CHANNEL_EDGE_ACTION_DECREASE + } + } + } +} + +bitflags! { + #[allow(dead_code)] + #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] + pub struct PcntEventType: u32 { + const THRES_1 = esp_idf_sys::pcnt_evt_type_t_PCNT_EVT_THRES_1; + const THRES_0 = esp_idf_sys::pcnt_evt_type_t_PCNT_EVT_THRES_0; + const L_LIM = esp_idf_sys::pcnt_evt_type_t_PCNT_EVT_L_LIM; + const H_LIM = esp_idf_sys::pcnt_evt_type_t_PCNT_EVT_H_LIM; + const ZERO = esp_idf_sys::pcnt_evt_type_t_PCNT_EVT_ZERO; + } +} + +#[doc = " @brief Pulse Counter configuration for a single channel"] +#[derive(Debug)] +pub struct PcntConfig { + #[doc = "< Pulse input GPIO number, if you want to use GPIO16, enter pulse_gpio_num = 16, a negative value will be ignored"] + pub pulse_pin: Option, + #[doc = "< Control signal input GPIO number, a negative value will be ignored"] + pub ctrl_pin: Option, + #[doc = "< PCNT low control mode"] + pub lctrl_mode: PcntControlMode, + #[doc = "< PCNT high control mode"] + pub hctrl_mode: PcntControlMode, + #[doc = "< PCNT positive edge count mode"] + pub pos_mode: PcntCountMode, + #[doc = "< PCNT negative edge count mode"] + pub neg_mode: PcntCountMode, + #[doc = "< Maximum counter value"] + pub counter_h_lim: i16, + #[doc = "< Minimum counter value"] + pub counter_l_lim: i16, + #[doc = "< the PCNT channel"] + pub channel: PcntChannel, +} + +impl Into for &PcntConfig { + fn into(self) -> pcnt_config_t { + pcnt_config_t { + pulse_gpio_num: match &self.pulse_pin { + Some(pin) => pin.pin_num(), + None => esp_idf_sys::PCNT_PIN_NOT_USED, + }, + ctrl_gpio_num: match &self.ctrl_pin { + Some(pin) => pin.pin_num(), + None => esp_idf_sys::PCNT_PIN_NOT_USED, + }, + lctrl_mode: self.lctrl_mode.into(), + hctrl_mode: self.hctrl_mode.into(), + pos_mode: self.pos_mode.into(), + neg_mode: self.neg_mode.into(), + counter_h_lim: self.counter_h_lim, + counter_l_lim: self.counter_l_lim, + channel: self.channel.into(), + ..Default::default() + } + } +} + +#[derive(Debug, Clone)] +pub struct PcntPin { + pin: Arc +} + +impl PcntPin { + pub fn new<'d>(pin: impl Peripheral

+ 'd, pull_mode: Pull) -> Result + { + crate::into_ref!(pin); + let p = PcntPin { pin: Arc::new(pin.pin()) }; + PinDriver::input(pin)?.into_input()?.set_pull(pull_mode)?; + Ok(p) + } + + fn pin_num(&self) -> i32 { + *self.pin + } +} + + +#[derive(Debug)] +struct PcntChannelPins { + pulse: Option, + ctrl: Option, +} + +#[allow(dead_code)] +#[derive(Debug)] +pub struct Pcnt { + unit: pcnt_unit_t, + channel_pins: [PcntChannelPins; esp_idf_sys::pcnt_channel_t_PCNT_CHANNEL_MAX as usize] +} + +#[allow(dead_code)] +impl Pcnt { + pub fn new(unit: PcntUnit) -> Pcnt { + Pcnt { + unit: unit.into(), + channel_pins: [PcntChannelPins { pulse: None, ctrl: None }, PcntChannelPins { pulse: None, ctrl: None }], + } + } + + fn get_pin_numbers(&self, channel: PcntChannel) -> (i32, i32) { + ( + match &self.channel_pins[channel as usize].pulse { + Some(pin) => pin.pin_num(), + None => esp_idf_sys::PCNT_PIN_NOT_USED, + }, + match &self.channel_pins[channel as usize].ctrl { + Some(pin) => pin.pin_num(), + None => esp_idf_sys::PCNT_PIN_NOT_USED, + }, + ) + } + + pub fn config(&mut self, pconfig: &PcntConfig) -> Result<(), EspError> { + let mut config: pcnt_config_t = pconfig.into(); + config.unit = self.unit.into(); + self.channel_pins[config.channel as usize].pulse = pconfig.pulse_pin.clone(); + self.channel_pins[config.channel as usize].ctrl = pconfig.pulse_pin.clone(); + unsafe { + esp!(esp_idf_sys::pcnt_unit_config( + &config as *const pcnt_config_t + )) + } + } + + pub fn get_counter_value(&self) -> Result { + let mut value = 0i16; + unsafe { + esp!(esp_idf_sys::pcnt_get_counter_value( + self.unit, + &mut value as *mut i16 + ))?; + } + Ok(value) + } + + pub fn counter_pause(&self) -> Result<(), EspError> { + unsafe { esp!(esp_idf_sys::pcnt_counter_pause(self.unit)) } + } + + pub fn counter_resume(&self) -> Result<(), EspError> { + unsafe { esp!(esp_idf_sys::pcnt_counter_resume(self.unit)) } + } + + pub fn counter_clear(&self) -> Result<(), EspError> { + unsafe { esp!(esp_idf_sys::pcnt_counter_clear(self.unit)) } + } + + pub fn intr_enable(&self) -> Result<(), EspError> { + unsafe { esp!(esp_idf_sys::pcnt_intr_enable(self.unit)) } + } + + pub fn intr_disable(&self) -> Result<(), EspError> { + unsafe { esp!(esp_idf_sys::pcnt_intr_disable(self.unit)) } + } + + pub fn event_enable(&self, evt_type: PcntEventType) -> Result<(), EspError> { + let evt_type: esp_idf_sys::pcnt_evt_type_t = evt_type.bits(); + unsafe { esp!(esp_idf_sys::pcnt_event_enable(self.unit, evt_type)) } + } + + pub fn event_disable(&self, evt_type: PcntEventType) -> Result<(), EspError> { + let evt_type: esp_idf_sys::pcnt_evt_type_t = evt_type.bits(); + unsafe { esp!(esp_idf_sys::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.bits()), + _ =>Err(EspError::from(esp_idf_sys::ESP_ERR_INVALID_ARG as esp_idf_sys::esp_err_t).unwrap()), + } + } + + 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!(esp_idf_sys::pcnt_set_event_value( + self.unit, evt_type, value + )) + } + } + + 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!(esp_idf_sys::pcnt_get_event_value( + self.unit, + evt_type, + &mut value as *mut i16 + ))?; + } + Ok(value) + } + + // TODO: status is a bit field! + pub fn get_event_status(&self) -> Result { + let mut value = 0u32; + unsafe { + esp!(esp_idf_sys::pcnt_get_event_status( + self.unit, + &mut value as *mut u32 + ))?; + } + Ok(value) + } + + pub fn set_pin( + &mut self, + channel: PcntChannel, + pulse_pin: Option, + ctrl_pin: Option, + ) -> Result<(), EspError> { + self.channel_pins[channel as usize].pulse = pulse_pin.clone(); + self.channel_pins[channel as usize].ctrl = pulse_pin.clone(); + let pulse_io_num = match &pulse_pin { + Some(pin) => pin.pin_num(), + None => esp_idf_sys::PCNT_PIN_NOT_USED, + }; + let ctrl_io_num = match &ctrl_pin { + Some(pin) => pin.pin_num(), + None => esp_idf_sys::PCNT_PIN_NOT_USED, + }; + unsafe { + esp!(esp_idf_sys::pcnt_set_pin( + self.unit, + channel.into(), + pulse_io_num, + ctrl_io_num + )) + } + } + + pub fn filter_enable(&self) -> Result<(), EspError> { + unsafe { esp!(esp_idf_sys::pcnt_filter_enable(self.unit)) } + } + + pub fn filter_disable(&self) -> Result<(), EspError> { + unsafe { esp!(esp_idf_sys::pcnt_filter_disable(self.unit)) } + } + + pub fn set_filter_value(&self, value: u16) -> Result<(), EspError> { + unsafe { esp!(esp_idf_sys::pcnt_set_filter_value(self.unit, value)) } + } + + pub fn get_filter_value(&self) -> Result { + let mut value = 0u16; + unsafe { + esp!(esp_idf_sys::pcnt_get_filter_value( + self.unit, + &mut value as *mut u16 + ))?; + } + Ok(value) + } + + pub unsafe fn subscribe(&self, callback: impl FnMut(u32) + Send + 'static) -> Result<(), EspError> { + enable_isr_service()?; + + //self.unsubscribe(); + let callback: Box = Box::new(callback); + ISR_HANDLERS[self.unit as usize] = Some(callback); + esp!(esp_idf_sys::pcnt_isr_handler_add( + self.unit, + Some(Self::handle_isr), + self.unit as *mut esp_idf_sys::c_types::c_void, + ))?; + Ok(()) + } + + pub fn unsubscribe(&self) -> Result<(), EspError> { + unsafe { + esp!(esp_idf_sys::pcnt_isr_handler_remove(self.unit))?; + ISR_HANDLERS[self.unit as usize] = None; + } + Ok(()) + } + + unsafe extern "C" fn handle_isr(data: *mut esp_idf_sys::c_types::c_void) { + let unit = data as pcnt_unit_t; + if let Some(f) = &mut ISR_HANDLERS[unit as usize] { + let mut value = 0u32; + esp!(esp_idf_sys::pcnt_get_event_status( + unit, + &mut value as *mut u32 + )).expect("failed to fetch event status!"); + f(value); + } + } +} + +#[cfg(all(not(feature = "riscv-ulp-hal"), feature = "alloc"))] +static ISR_SERVICE_ENABLED: core::sync::atomic::AtomicBool = + core::sync::atomic::AtomicBool::new(false); + +#[cfg(all(not(feature = "riscv-ulp-hal"), feature = "alloc"))] +static ISR_SERVICE_ENABLED_CS: crate::task::CriticalSection = crate::task::CriticalSection::new(); + +#[allow(dead_code)] +fn enable_isr_service() -> Result<(), EspError> { + use core::sync::atomic::Ordering; + + if !ISR_SERVICE_ENABLED.load(Ordering::SeqCst) { + let _ = ISR_SERVICE_ENABLED_CS.enter(); + + if !ISR_SERVICE_ENABLED.load(Ordering::SeqCst) { + esp!(unsafe { esp_idf_sys::pcnt_isr_service_install(0) })?; + + ISR_SERVICE_ENABLED.store(true, Ordering::SeqCst); + } + } + + Ok(()) +} + +#[allow(dead_code)] +static mut ISR_HANDLERS: [Option>; esp_idf_sys::pcnt_unit_t_PCNT_UNIT_MAX as usize] = [ + None, None, None, None, + #[cfg(not(esp32s3))] + None, + #[cfg(not(esp32s3))] + None, + #[cfg(not(esp32s3))] + None, + #[cfg(not(esp32s3))] + None, +]; + +impl Drop for Pcnt { + fn drop(&mut self) { + let _ = self.intr_disable(); + unsafe {ISR_HANDLERS[self.unit as usize] = None}; + } +} From c10cb633ac5908dfb4f2652a4d9c7557b2fa6aa2 Mon Sep 17 00:00:00 2001 From: Christopher Liebman Date: Mon, 7 Nov 2022 13:20:24 -0800 Subject: [PATCH 06/48] use from core not std pass pins as PeripheralRef store PinDriver in PcntPin --- src/pcnt.rs | 126 +++++++++++++++++++++++++--------------------------- 1 file changed, 60 insertions(+), 66 deletions(-) diff --git a/src/pcnt.rs b/src/pcnt.rs index ee8d6950f69..31306f01763 100644 --- a/src/pcnt.rs +++ b/src/pcnt.rs @@ -1,5 +1,7 @@ -use std::sync::Arc; -use std::fmt::Debug; +use core::borrow::Borrow; +use core::fmt; +use core::fmt::Debug; +use core::marker::PhantomData; use esp_idf_sys::esp; use esp_idf_sys::pcnt_config_t; @@ -8,10 +10,13 @@ use esp_idf_sys::EspError; use bitflags::bitflags; +use crate::gpio::AnyIOPin; use crate::gpio::IOPin; +use crate::gpio::Input; use crate::gpio::PinDriver; use crate::gpio::Pull; use crate::peripheral::Peripheral; +use crate::peripheral::PeripheralRef; #[allow(dead_code)] #[derive(Debug, Copy, Clone)] @@ -127,11 +132,11 @@ bitflags! { #[doc = " @brief Pulse Counter configuration for a single channel"] #[derive(Debug)] -pub struct PcntConfig { +pub struct PcntConfig<'d, T: Borrow>> { #[doc = "< Pulse input GPIO number, if you want to use GPIO16, enter pulse_gpio_num = 16, a negative value will be ignored"] - pub pulse_pin: Option, + pub pulse_pin: Option, #[doc = "< Control signal input GPIO number, a negative value will be ignored"] - pub ctrl_pin: Option, + pub ctrl_pin: Option, #[doc = "< PCNT low control mode"] pub lctrl_mode: PcntControlMode, #[doc = "< PCNT high control mode"] @@ -146,91 +151,80 @@ pub struct PcntConfig { pub counter_l_lim: i16, #[doc = "< the PCNT channel"] pub channel: PcntChannel, + pub _p: PhantomData<&'d ()>, } -impl Into for &PcntConfig { - fn into(self) -> pcnt_config_t { - pcnt_config_t { - pulse_gpio_num: match &self.pulse_pin { - Some(pin) => pin.pin_num(), - None => esp_idf_sys::PCNT_PIN_NOT_USED, - }, - ctrl_gpio_num: match &self.ctrl_pin { - Some(pin) => pin.pin_num(), - None => esp_idf_sys::PCNT_PIN_NOT_USED, - }, - lctrl_mode: self.lctrl_mode.into(), - hctrl_mode: self.hctrl_mode.into(), - pos_mode: self.pos_mode.into(), - neg_mode: self.neg_mode.into(), - counter_h_lim: self.counter_h_lim, - counter_l_lim: self.counter_l_lim, - channel: self.channel.into(), - ..Default::default() - } - } +pub struct PcntPin<'d> { + pin: PinDriver<'d, AnyIOPin, Input> } -#[derive(Debug, Clone)] -pub struct PcntPin { - pin: Arc -} - -impl PcntPin { - pub fn new<'d>(pin: impl Peripheral

+ 'd, pull_mode: Pull) -> Result +impl<'d> PcntPin<'d> { + pub fn new(pin: impl Peripheral

+ 'd, pull_mode: Pull) -> Result { - crate::into_ref!(pin); - let p = PcntPin { pin: Arc::new(pin.pin()) }; - PinDriver::input(pin)?.into_input()?.set_pull(pull_mode)?; - Ok(p) + let pin: PeripheralRef = pin.into_ref().map_into(); + let mut pin = PinDriver::input(pin)?; + pin.set_pull(pull_mode)?; + Ok(Self{pin}) + } fn pin_num(&self) -> i32 { - *self.pin + self.pin.pin() } } +impl<'d> Debug for PcntPin<'d> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "PcntPin: {{pin: {}}}", self.pin_num()) + } +} #[derive(Debug)] -struct PcntChannelPins { - pulse: Option, - ctrl: Option, +struct PcntChannelPins <'d, T: Borrow>> +{ + pulse: Option, + ctrl: Option, + _p: PhantomData<&'d ()>, } #[allow(dead_code)] #[derive(Debug)] -pub struct Pcnt { +pub struct Pcnt<'d, T: Borrow>> { unit: pcnt_unit_t, - channel_pins: [PcntChannelPins; esp_idf_sys::pcnt_channel_t_PCNT_CHANNEL_MAX as usize] + channel_pins: [PcntChannelPins<'d, T>; esp_idf_sys::pcnt_channel_t_PCNT_CHANNEL_MAX as usize] } #[allow(dead_code)] -impl Pcnt { - pub fn new(unit: PcntUnit) -> Pcnt { +impl<'d, T: Borrow>> Pcnt<'d, T> { + pub fn new(unit: PcntUnit) -> Pcnt<'d, T> { Pcnt { unit: unit.into(), - channel_pins: [PcntChannelPins { pulse: None, ctrl: None }, PcntChannelPins { pulse: None, ctrl: None }], + channel_pins: [PcntChannelPins { pulse: None, ctrl: None, _p: PhantomData::default() }, PcntChannelPins { pulse: None, ctrl: None, _p: PhantomData::default() }], } } - fn get_pin_numbers(&self, channel: PcntChannel) -> (i32, i32) { - ( - match &self.channel_pins[channel as usize].pulse { - Some(pin) => pin.pin_num(), + pub fn config(&mut self, pconfig: &mut PcntConfig<'d, T>) -> Result<(), EspError> { + let mut config = pcnt_config_t { + pulse_gpio_num: match &pconfig.pulse_pin { + Some(pin) => pin.borrow().pin_num(), None => esp_idf_sys::PCNT_PIN_NOT_USED, }, - match &self.channel_pins[channel as usize].ctrl { - Some(pin) => pin.pin_num(), + ctrl_gpio_num: match &pconfig.ctrl_pin { + Some(pin) => pin.borrow().pin_num(), None => esp_idf_sys::PCNT_PIN_NOT_USED, }, - ) - } - - pub fn config(&mut self, pconfig: &PcntConfig) -> Result<(), EspError> { - let mut config: pcnt_config_t = pconfig.into(); + 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: pconfig.channel.into(), + ..Default::default() + }; config.unit = self.unit.into(); - self.channel_pins[config.channel as usize].pulse = pconfig.pulse_pin.clone(); - self.channel_pins[config.channel as usize].ctrl = pconfig.pulse_pin.clone(); + self.channel_pins[config.channel as usize].pulse = pconfig.pulse_pin.take(); + self.channel_pins[config.channel as usize].ctrl = pconfig.pulse_pin.take(); unsafe { esp!(esp_idf_sys::pcnt_unit_config( &config as *const pcnt_config_t @@ -323,19 +317,19 @@ impl Pcnt { pub fn set_pin( &mut self, channel: PcntChannel, - pulse_pin: Option, - ctrl_pin: Option, + pulse_pin: Option, + ctrl_pin: Option, ) -> Result<(), EspError> { - self.channel_pins[channel as usize].pulse = pulse_pin.clone(); - self.channel_pins[channel as usize].ctrl = pulse_pin.clone(); let pulse_io_num = match &pulse_pin { - Some(pin) => pin.pin_num(), + Some(pin) => pin.borrow().pin_num(), None => esp_idf_sys::PCNT_PIN_NOT_USED, }; let ctrl_io_num = match &ctrl_pin { - Some(pin) => pin.pin_num(), + Some(pin) => pin.borrow().pin_num(), None => esp_idf_sys::PCNT_PIN_NOT_USED, }; + self.channel_pins[channel as usize].pulse = pulse_pin; + self.channel_pins[channel as usize].ctrl = ctrl_pin; unsafe { esp!(esp_idf_sys::pcnt_set_pin( self.unit, @@ -441,7 +435,7 @@ static mut ISR_HANDLERS: [Option>; esp_idf_sys::pcnt_unit_t_ None, ]; -impl Drop for Pcnt { +impl<'d, T: Borrow>> Drop for Pcnt<'d, T> { fn drop(&mut self) { let _ = self.intr_disable(); unsafe {ISR_HANDLERS[self.unit as usize] = None}; From bbc1714476dc3d9981cc6878039ca8ab3c634444 Mon Sep 17 00:00:00 2001 From: Christopher Liebman Date: Mon, 7 Nov 2022 16:29:41 -0800 Subject: [PATCH 07/48] try a allocate/deallocate stratagy for PCNT UNITS (like esp-idf v5) --- src/pcnt.rs | 101 ++++++++++++++++++++++++++-------------------------- 1 file changed, 50 insertions(+), 51 deletions(-) diff --git a/src/pcnt.rs b/src/pcnt.rs index 31306f01763..9ca1c8d97e7 100644 --- a/src/pcnt.rs +++ b/src/pcnt.rs @@ -18,6 +18,11 @@ use crate::gpio::Pull; use crate::peripheral::Peripheral; use crate::peripheral::PeripheralRef; +#[cfg(esp_idf_version_major = "4")] +type UnitHandle = pcnt_unit_t; +#[cfg(esp_idf_version_major = "5")] +type UnitHandle = pcnt_unit_handle_t; + #[allow(dead_code)] #[derive(Debug, Copy, Clone)] pub enum PcntChannel { @@ -34,43 +39,7 @@ impl Into for PcntChannel { } } -#[allow(dead_code)] -#[derive(Debug, Clone, Copy)] -pub enum PcntUnit { - Unit0, - Unit1, - Unit2, - Unit3, - #[cfg(not(esp32s3))] - Unit4, - #[cfg(not(esp32s3))] - Unit5, - #[cfg(not(esp32s3))] - Unit6, - #[cfg(not(esp32s3))] - Unit7, -} - -impl Into for PcntUnit { - fn into(self) -> esp_idf_sys::pcnt_unit_t { - match self { - PcntUnit::Unit0 => esp_idf_sys::pcnt_unit_t_PCNT_UNIT_0, - PcntUnit::Unit1 => esp_idf_sys::pcnt_unit_t_PCNT_UNIT_1, - PcntUnit::Unit2 => esp_idf_sys::pcnt_unit_t_PCNT_UNIT_2, - PcntUnit::Unit3 => esp_idf_sys::pcnt_unit_t_PCNT_UNIT_3, - #[cfg(not(esp32s3))] - PcntUnit::Unit4 => esp_idf_sys::pcnt_unit_t_PCNT_UNIT_4, - #[cfg(not(esp32s3))] - PcntUnit::Unit5 => esp_idf_sys::pcnt_unit_t_PCNT_UNIT_5, - #[cfg(not(esp32s3))] - PcntUnit::Unit6 => esp_idf_sys::pcnt_unit_t_PCNT_UNIT_6, - #[cfg(not(esp32s3))] - PcntUnit::Unit7 => esp_idf_sys::pcnt_unit_t_PCNT_UNIT_7, - } - } -} -#[allow(dead_code)] #[derive(Debug, Copy, Clone)] pub enum PcntCountMode { Hold, @@ -94,7 +63,6 @@ impl Into for PcntCountMode { } } -#[allow(dead_code)] #[derive(Debug, Copy, Clone)] pub enum PcntControlMode { Keep, @@ -187,20 +155,18 @@ struct PcntChannelPins <'d, T: Borrow>> _p: PhantomData<&'d ()>, } -#[allow(dead_code)] #[derive(Debug)] pub struct Pcnt<'d, T: Borrow>> { unit: pcnt_unit_t, channel_pins: [PcntChannelPins<'d, T>; esp_idf_sys::pcnt_channel_t_PCNT_CHANNEL_MAX as usize] } -#[allow(dead_code)] impl<'d, T: Borrow>> Pcnt<'d, T> { - pub fn new(unit: PcntUnit) -> Pcnt<'d, T> { - Pcnt { - unit: unit.into(), + pub fn new() -> Result { + Ok(Pcnt { + unit: unit_allocate()?, channel_pins: [PcntChannelPins { pulse: None, ctrl: None, _p: PhantomData::default() }, PcntChannelPins { pulse: None, ctrl: None, _p: PhantomData::default() }], - } + }) } pub fn config(&mut self, pconfig: &mut PcntConfig<'d, T>) -> Result<(), EspError> { @@ -398,19 +364,26 @@ impl<'d, T: Borrow>> Pcnt<'d, T> { } } +impl<'d, T: Borrow>> Drop for Pcnt<'d, T> { + fn drop(&mut self) { + let _ = self.intr_disable(); + unsafe {ISR_HANDLERS[self.unit as usize] = None}; + unit_deallocate(self.unit) + } +} + #[cfg(all(not(feature = "riscv-ulp-hal"), feature = "alloc"))] static ISR_SERVICE_ENABLED: core::sync::atomic::AtomicBool = core::sync::atomic::AtomicBool::new(false); #[cfg(all(not(feature = "riscv-ulp-hal"), feature = "alloc"))] -static ISR_SERVICE_ENABLED_CS: crate::task::CriticalSection = crate::task::CriticalSection::new(); +static PCNT_CS: crate::task::CriticalSection = crate::task::CriticalSection::new(); -#[allow(dead_code)] fn enable_isr_service() -> Result<(), EspError> { use core::sync::atomic::Ordering; if !ISR_SERVICE_ENABLED.load(Ordering::SeqCst) { - let _ = ISR_SERVICE_ENABLED_CS.enter(); + let _ = PCNT_CS.enter(); if !ISR_SERVICE_ENABLED.load(Ordering::SeqCst) { esp!(unsafe { esp_idf_sys::pcnt_isr_service_install(0) })?; @@ -422,7 +395,6 @@ fn enable_isr_service() -> Result<(), EspError> { Ok(()) } -#[allow(dead_code)] static mut ISR_HANDLERS: [Option>; esp_idf_sys::pcnt_unit_t_PCNT_UNIT_MAX as usize] = [ None, None, None, None, #[cfg(not(esp32s3))] @@ -435,9 +407,36 @@ static mut ISR_HANDLERS: [Option>; esp_idf_sys::pcnt_unit_t_ None, ]; -impl<'d, T: Borrow>> Drop for Pcnt<'d, T> { - fn drop(&mut self) { - let _ = self.intr_disable(); - unsafe {ISR_HANDLERS[self.unit as usize] = None}; +#[cfg(esp_idf_version_major = "4")] +static mut PCNT_UNITS: [Option; esp_idf_sys::pcnt_unit_t_PCNT_UNIT_MAX as usize] = [ + Some(esp_idf_sys::pcnt_unit_t_PCNT_UNIT_0), + Some(esp_idf_sys::pcnt_unit_t_PCNT_UNIT_1), + Some(esp_idf_sys::pcnt_unit_t_PCNT_UNIT_2), + Some(esp_idf_sys::pcnt_unit_t_PCNT_UNIT_3), + #[cfg(not(esp32s3))] + Some(esp_idf_sys::pcnt_unit_t_PCNT_UNIT_4), + #[cfg(not(esp32s3))] + Some(esp_idf_sys::pcnt_unit_t_PCNT_UNIT_5), + #[cfg(not(esp32s3))] + Some(esp_idf_sys::pcnt_unit_t_PCNT_UNIT_6), + #[cfg(not(esp32s3))] + Some(esp_idf_sys::pcnt_unit_t_PCNT_UNIT_7), +]; +#[cfg(esp_idf_version_major = "4")] +fn unit_allocate() -> Result { + let _ = PCNT_CS.enter(); + for i in 0..esp_idf_sys::pcnt_unit_t_PCNT_UNIT_MAX { + if let Some(unit) = unsafe { PCNT_UNITS[i as usize].take() } { + return Ok(unit); + } + } + Err(EspError::from(esp_idf_sys::ESP_ERR_NO_MEM as esp_idf_sys::esp_err_t).unwrap()) +} + +#[cfg(esp_idf_version_major = "4")] +fn unit_deallocate(unit: UnitHandle) { + let _ = PCNT_CS.enter(); + unsafe { + PCNT_UNITS[unit as usize] = Some(unit); } } From da9b29c0d0814ed8d14605ab0575e142cd68127c Mon Sep 17 00:00:00 2001 From: Christopher Liebman Date: Tue, 8 Nov 2022 06:45:31 -0800 Subject: [PATCH 08/48] update pcnt example --- examples/pcnt_i64_encoder.rs | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/examples/pcnt_i64_encoder.rs b/examples/pcnt_i64_encoder.rs index 2de71484300..7ff30fbfe24 100644 --- a/examples/pcnt_i64_encoder.rs +++ b/examples/pcnt_i64_encoder.rs @@ -10,7 +10,7 @@ use std::{sync::{atomic::{Ordering, AtomicI64}, Arc}, cmp::min}; use anyhow; use anyhow::Context; use embedded_hal_0_2::blocking::delay::DelayMs; -use esp_idf_hal::{delay::FreeRtos as delay}; +use esp_idf_hal::delay::FreeRtos as delay; use esp_idf_hal::prelude::*; use esp_idf_hal::gpio::Pull; use tracing::{info, Level}; @@ -40,13 +40,13 @@ fn main() -> anyhow::Result<()> { let m1_enc1_pin = PcntPin::new(m1_enc1_pin, Pull::Up)?; let m1_enc2_pin = PcntPin::new(m1_enc2_pin, Pull::Up)?; info!("creating pcnt unit 0"); - let mut pcnt = Pcnt::new(PcntUnit::Unit0); + let mut pcnt = Pcnt::new()?; info!("configure pcnt chanel 0"); const POS_LIMIT: i16 = 100; const NEG_LIMIT: i16 = -100; let mut config = PcntConfig { - pulse_pin: Some(m1_enc1_pin.clone()), - ctrl_pin: Some(m1_enc2_pin.clone()), + pulse_pin: Some(&m1_enc1_pin), + ctrl_pin: Some(&m1_enc2_pin), lctrl_mode: PcntControlMode::Reverse, hctrl_mode: PcntControlMode::Keep, pos_mode: PcntCountMode::Decrement, @@ -54,16 +54,17 @@ fn main() -> anyhow::Result<()> { counter_h_lim: POS_LIMIT, counter_l_lim: NEG_LIMIT, channel: PcntChannel::Channel0, + _p: std::marker::PhantomData, }; - pcnt.config(&config).context("configuring CHANNEL0")?; + pcnt.config(&mut config).context("configuring CHANNEL0")?; info!("configure pcnt chanel 1"); config.channel = PcntChannel::Channel1; - config.pulse_pin = Some(m1_enc2_pin.clone()); - config.ctrl_pin = Some(m1_enc1_pin.clone()); + config.pulse_pin = Some(&m1_enc2_pin); + config.ctrl_pin = Some(&m1_enc1_pin); config.pos_mode = PcntCountMode::Increment; config.neg_mode = PcntCountMode::Decrement; - pcnt.config(&config).context("configuring CHANNEL1")?; + pcnt.config(&mut config).context("configuring CHANNEL1")?; pcnt.set_filter_value(min(10*80, 1023))?; pcnt.filter_enable()?; let value = Arc::new(AtomicI64::new(0)); From f47a77a439d398a371a86d2f6ec7af523a897e46 Mon Sep 17 00:00:00 2001 From: Christopher Liebman Date: Sun, 20 Nov 2022 11:25:45 -0800 Subject: [PATCH 09/48] use Arc/Mutex for PcntPin so Pnct can be Send --- src/pcnt.rs | 57 +++++++++++++++++++++++++++-------------------------- 1 file changed, 29 insertions(+), 28 deletions(-) diff --git a/src/pcnt.rs b/src/pcnt.rs index 9ca1c8d97e7..9e35134e3e4 100644 --- a/src/pcnt.rs +++ b/src/pcnt.rs @@ -1,7 +1,7 @@ -use core::borrow::Borrow; use core::fmt; use core::fmt::Debug; -use core::marker::PhantomData; +use std::sync::Mutex; +use std::sync::Arc; use esp_idf_sys::esp; use esp_idf_sys::pcnt_config_t; @@ -100,11 +100,11 @@ bitflags! { #[doc = " @brief Pulse Counter configuration for a single channel"] #[derive(Debug)] -pub struct PcntConfig<'d, T: Borrow>> { +pub struct PcntConfig<'d> { #[doc = "< Pulse input GPIO number, if you want to use GPIO16, enter pulse_gpio_num = 16, a negative value will be ignored"] - pub pulse_pin: Option, + pub pulse_pin: Option>, #[doc = "< Control signal input GPIO number, a negative value will be ignored"] - pub ctrl_pin: Option, + pub ctrl_pin: Option>, #[doc = "< PCNT low control mode"] pub lctrl_mode: PcntControlMode, #[doc = "< PCNT high control mode"] @@ -119,11 +119,11 @@ pub struct PcntConfig<'d, T: Borrow>> { pub counter_l_lim: i16, #[doc = "< the PCNT channel"] pub channel: PcntChannel, - pub _p: PhantomData<&'d ()>, } +#[derive(Clone)] pub struct PcntPin<'d> { - pin: PinDriver<'d, AnyIOPin, Input> + pin: Arc>> } impl<'d> PcntPin<'d> { @@ -132,12 +132,14 @@ impl<'d> PcntPin<'d> { let pin: PeripheralRef = pin.into_ref().map_into(); let mut pin = PinDriver::input(pin)?; pin.set_pull(pull_mode)?; - Ok(Self{pin}) + Ok(Self { + pin: Arc::new(Mutex::new(pin)) + }) } fn pin_num(&self) -> i32 { - self.pin.pin() + self.pin.lock().unwrap().pin() } } @@ -148,35 +150,34 @@ impl<'d> Debug for PcntPin<'d> { } #[derive(Debug)] -struct PcntChannelPins <'d, T: Borrow>> +struct PcntChannelPins<'d> { - pulse: Option, - ctrl: Option, - _p: PhantomData<&'d ()>, + pulse: Option>, + ctrl: Option>, } #[derive(Debug)] -pub struct Pcnt<'d, T: Borrow>> { +pub struct Pcnt<'d> { unit: pcnt_unit_t, - channel_pins: [PcntChannelPins<'d, T>; esp_idf_sys::pcnt_channel_t_PCNT_CHANNEL_MAX as usize] + channel_pins: [PcntChannelPins<'d>; esp_idf_sys::pcnt_channel_t_PCNT_CHANNEL_MAX as usize] } -impl<'d, T: Borrow>> Pcnt<'d, T> { +impl<'d> Pcnt<'d> { pub fn new() -> Result { Ok(Pcnt { unit: unit_allocate()?, - channel_pins: [PcntChannelPins { pulse: None, ctrl: None, _p: PhantomData::default() }, PcntChannelPins { pulse: None, ctrl: None, _p: PhantomData::default() }], + channel_pins: [PcntChannelPins { pulse: None, ctrl: None }, PcntChannelPins { pulse: None, ctrl: None }], }) } - pub fn config(&mut self, pconfig: &mut PcntConfig<'d, T>) -> Result<(), EspError> { - let mut config = pcnt_config_t { + pub fn config(&mut self, pconfig: &mut PcntConfig<'d>) -> Result<(), EspError> { + let config = pcnt_config_t { pulse_gpio_num: match &pconfig.pulse_pin { - Some(pin) => pin.borrow().pin_num(), + Some(pin) => pin.pin_num(), None => esp_idf_sys::PCNT_PIN_NOT_USED, }, ctrl_gpio_num: match &pconfig.ctrl_pin { - Some(pin) => pin.borrow().pin_num(), + Some(pin) => pin.pin_num(), None => esp_idf_sys::PCNT_PIN_NOT_USED, }, lctrl_mode: pconfig.lctrl_mode.into(), @@ -186,9 +187,9 @@ impl<'d, T: Borrow>> Pcnt<'d, T> { counter_h_lim: pconfig.counter_h_lim, counter_l_lim: pconfig.counter_l_lim, channel: pconfig.channel.into(), - ..Default::default() + unit: self.unit, }; - config.unit = self.unit.into(); + self.channel_pins[config.channel as usize].pulse = pconfig.pulse_pin.take(); self.channel_pins[config.channel as usize].ctrl = pconfig.pulse_pin.take(); unsafe { @@ -283,15 +284,15 @@ impl<'d, T: Borrow>> Pcnt<'d, T> { pub fn set_pin( &mut self, channel: PcntChannel, - pulse_pin: Option, - ctrl_pin: Option, + pulse_pin: Option>, + ctrl_pin: Option>, ) -> Result<(), EspError> { let pulse_io_num = match &pulse_pin { - Some(pin) => pin.borrow().pin_num(), + Some(pin) => pin.pin_num(), None => esp_idf_sys::PCNT_PIN_NOT_USED, }; let ctrl_io_num = match &ctrl_pin { - Some(pin) => pin.borrow().pin_num(), + Some(pin) => pin.pin_num(), None => esp_idf_sys::PCNT_PIN_NOT_USED, }; self.channel_pins[channel as usize].pulse = pulse_pin; @@ -364,7 +365,7 @@ impl<'d, T: Borrow>> Pcnt<'d, T> { } } -impl<'d, T: Borrow>> Drop for Pcnt<'d, T> { +impl<'d> Drop for Pcnt<'d> { fn drop(&mut self) { let _ = self.intr_disable(); unsafe {ISR_HANDLERS[self.unit as usize] = None}; From 08b161e9bd415fea7c0987cd0d0b40fda6fa338e Mon Sep 17 00:00:00 2001 From: Christopher Liebman Date: Sun, 20 Nov 2022 13:12:06 -0800 Subject: [PATCH 10/48] update pcnt encoder example --- examples/pcnt_i64_encoder.rs | 37 ++++++++++++------------------------ 1 file changed, 12 insertions(+), 25 deletions(-) diff --git a/examples/pcnt_i64_encoder.rs b/examples/pcnt_i64_encoder.rs index 7ff30fbfe24..2538e35ad26 100644 --- a/examples/pcnt_i64_encoder.rs +++ b/examples/pcnt_i64_encoder.rs @@ -9,12 +9,10 @@ use std::{sync::{atomic::{Ordering, AtomicI64}, Arc}, cmp::min}; use anyhow; use anyhow::Context; -use embedded_hal_0_2::blocking::delay::DelayMs; +use embedded_hal::delay::DelayUs; use esp_idf_hal::delay::FreeRtos as delay; use esp_idf_hal::prelude::*; use esp_idf_hal::gpio::Pull; -use tracing::{info, Level}; -use tracing_subscriber::FmtSubscriber; use esp_idf_sys as _; // If using the `binstart` feature of `esp-idf-sys`, always keep this module imported use esp_idf_hal::pcnt::*; @@ -23,30 +21,20 @@ fn main() -> anyhow::Result<()> { // or else some patches to the runtime implemented by esp-idf-sys might not link properly. esp_idf_sys::link_patches(); - let subscriber = FmtSubscriber::builder() - // all spans/events with a level higher than TRACE (e.g, debug, info, warn, etc.) - // will be written to stdout. - .with_max_level(Level::TRACE) - // completes the builder. - .finish(); - - tracing::subscriber::set_global_default(subscriber) - .expect("setting default subscriber failed"); - - info!("setup pins"); + println!("setup pins"); let peripherals = Peripherals::take().context("failed to take Peripherals")?; let m1_enc1_pin = peripherals.pins.gpio5; let m1_enc2_pin = peripherals.pins.gpio6; let m1_enc1_pin = PcntPin::new(m1_enc1_pin, Pull::Up)?; let m1_enc2_pin = PcntPin::new(m1_enc2_pin, Pull::Up)?; - info!("creating pcnt unit 0"); + println!("creating pcnt unit 0"); let mut pcnt = Pcnt::new()?; - info!("configure pcnt chanel 0"); + println!("configure pcnt chanel 0"); const POS_LIMIT: i16 = 100; const NEG_LIMIT: i16 = -100; let mut config = PcntConfig { - pulse_pin: Some(&m1_enc1_pin), - ctrl_pin: Some(&m1_enc2_pin), + pulse_pin: Some(m1_enc1_pin.clone()), + ctrl_pin: Some(m1_enc2_pin.clone()), lctrl_mode: PcntControlMode::Reverse, hctrl_mode: PcntControlMode::Keep, pos_mode: PcntCountMode::Decrement, @@ -54,14 +42,13 @@ fn main() -> anyhow::Result<()> { counter_h_lim: POS_LIMIT, counter_l_lim: NEG_LIMIT, channel: PcntChannel::Channel0, - _p: std::marker::PhantomData, }; pcnt.config(&mut config).context("configuring CHANNEL0")?; - info!("configure pcnt chanel 1"); + println!("configure pcnt chanel 1"); config.channel = PcntChannel::Channel1; - config.pulse_pin = Some(&m1_enc2_pin); - config.ctrl_pin = Some(&m1_enc1_pin); + config.pulse_pin = Some(m1_enc2_pin); + config.ctrl_pin = Some(m1_enc1_pin); config.pos_mode = PcntCountMode::Increment; config.neg_mode = PcntCountMode::Decrement; pcnt.config(&mut config).context("configuring CHANNEL1")?; @@ -85,7 +72,7 @@ fn main() -> anyhow::Result<()> { } pcnt.event_enable(PcntEventType::H_LIM)?; pcnt.event_enable(PcntEventType::L_LIM)?; - info!("starting pcnt counter"); + println!("starting pcnt counter"); pcnt.counter_pause()?; pcnt.counter_clear()?; pcnt.counter_resume()?; @@ -96,9 +83,9 @@ fn main() -> anyhow::Result<()> { let acc_value = value.load(Ordering::SeqCst); let value = acc_value + pcnt_value; if value != last_value { - info!("value: {value} pct={pcnt_value} acc={acc_value}"); + println!("value: {value} pct={pcnt_value} acc={acc_value}"); last_value = value; } - delay.delay_ms(100u32); + delay.delay_ms(100u32)?; } } From 82dff5778e6203f9b6eddf8ca4fae1b45b3ce129 Mon Sep 17 00:00:00 2001 From: Christopher Liebman Date: Sun, 4 Dec 2022 13:03:45 -0800 Subject: [PATCH 11/48] 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 --- Cargo.toml | 4 + src/lib.rs | 4 +- src/pcnt.rs | 95 +++---------- src/pulse_cnt.rs | 347 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 375 insertions(+), 75 deletions(-) create mode 100644 src/pulse_cnt.rs diff --git a/Cargo.toml b/Cargo.toml index 20010bd8243..f2c05925acb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,6 +18,10 @@ default = ["std", "alloc", "esp-idf-sys"] std = ["alloc", "esp-idf-sys/std", "edge-executor?/std"] alloc = [] riscv-ulp-hal = [] +# by default esp-idf-sys includes driver/pulse_cnt.h when enabled driver/pcnt.h +# will be included instead (only used when compiling esp-idf > 4, esp-idf 4 always +# incudes driver/pcnt.h) +pcnt4 = ["esp-idf-sys/pcnt4"] [dependencies] nb = "1.0.0" diff --git a/src/lib.rs b/src/lib.rs index 4410e8d4314..e7ac33b84c8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -46,8 +46,10 @@ pub mod mac; pub mod modem; pub mod peripheral; pub mod peripherals; -#[cfg(not(feature = "riscv-ulp-hal"))] +#[cfg(all(not(feature = "riscv-ulp-hal"), feature = "pcnt4"))] pub mod pcnt; +#[cfg(all(not(feature = "riscv-ulp-hal"), not(feature = "pcnt4")))] +pub mod pulse_cnt; pub mod prelude; #[cfg(not(feature = "riscv-ulp-hal"))] pub mod reset; diff --git a/src/pcnt.rs b/src/pcnt.rs index 9e35134e3e4..b3f3e493ee6 100644 --- a/src/pcnt.rs +++ b/src/pcnt.rs @@ -1,7 +1,5 @@ -use core::fmt; use core::fmt::Debug; -use std::sync::Mutex; -use std::sync::Arc; + use esp_idf_sys::esp; use esp_idf_sys::pcnt_config_t; @@ -10,18 +8,10 @@ use esp_idf_sys::EspError; use bitflags::bitflags; -use crate::gpio::AnyIOPin; -use crate::gpio::IOPin; -use crate::gpio::Input; -use crate::gpio::PinDriver; -use crate::gpio::Pull; -use crate::peripheral::Peripheral; -use crate::peripheral::PeripheralRef; +use crate::gpio::AnyInputPin; +use crate::gpio::Pin; -#[cfg(esp_idf_version_major = "4")] type UnitHandle = pcnt_unit_t; -#[cfg(esp_idf_version_major = "5")] -type UnitHandle = pcnt_unit_handle_t; #[allow(dead_code)] #[derive(Debug, Copy, Clone)] @@ -99,12 +89,11 @@ bitflags! { } #[doc = " @brief Pulse Counter configuration for a single channel"] -#[derive(Debug)] pub struct PcntConfig<'d> { #[doc = "< Pulse input GPIO number, if you want to use GPIO16, enter pulse_gpio_num = 16, a negative value will be ignored"] - pub pulse_pin: Option>, + pub pulse_pin: Option<&'d AnyInputPin>, #[doc = "< Control signal input GPIO number, a negative value will be ignored"] - pub ctrl_pin: Option>, + pub ctrl_pin: Option<&'d AnyInputPin>, #[doc = "< PCNT low control mode"] pub lctrl_mode: PcntControlMode, #[doc = "< PCNT high control mode"] @@ -121,63 +110,26 @@ pub struct PcntConfig<'d> { pub channel: PcntChannel, } -#[derive(Clone)] -pub struct PcntPin<'d> { - pin: Arc>> -} - -impl<'d> PcntPin<'d> { - pub fn new(pin: impl Peripheral

+ 'd, pull_mode: Pull) -> Result - { - let pin: PeripheralRef = pin.into_ref().map_into(); - let mut pin = PinDriver::input(pin)?; - pin.set_pull(pull_mode)?; - Ok(Self { - pin: Arc::new(Mutex::new(pin)) - }) - - } - - fn pin_num(&self) -> i32 { - self.pin.lock().unwrap().pin() - } -} - -impl<'d> Debug for PcntPin<'d> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "PcntPin: {{pin: {}}}", self.pin_num()) - } -} - #[derive(Debug)] -struct PcntChannelPins<'d> -{ - pulse: Option>, - ctrl: Option>, -} - -#[derive(Debug)] -pub struct Pcnt<'d> { +pub struct Pcnt { unit: pcnt_unit_t, - channel_pins: [PcntChannelPins<'d>; esp_idf_sys::pcnt_channel_t_PCNT_CHANNEL_MAX as usize] } -impl<'d> Pcnt<'d> { +impl<'d> Pcnt { pub fn new() -> Result { Ok(Pcnt { unit: unit_allocate()?, - channel_pins: [PcntChannelPins { pulse: None, ctrl: None }, PcntChannelPins { pulse: None, ctrl: None }], }) } - pub fn config(&mut self, pconfig: &mut PcntConfig<'d>) -> Result<(), EspError> { + pub fn config(&mut self, pconfig: & PcntConfig) -> Result<(), EspError> { let config = pcnt_config_t { - pulse_gpio_num: match &pconfig.pulse_pin { - Some(pin) => pin.pin_num(), + pulse_gpio_num: match pconfig.pulse_pin { + Some(pin) => pin.pin(), None => esp_idf_sys::PCNT_PIN_NOT_USED, }, - ctrl_gpio_num: match &pconfig.ctrl_pin { - Some(pin) => pin.pin_num(), + ctrl_gpio_num: match pconfig.ctrl_pin { + Some(pin) => pin.pin(), None => esp_idf_sys::PCNT_PIN_NOT_USED, }, lctrl_mode: pconfig.lctrl_mode.into(), @@ -190,8 +142,6 @@ impl<'d> Pcnt<'d> { unit: self.unit, }; - self.channel_pins[config.channel as usize].pulse = pconfig.pulse_pin.take(); - self.channel_pins[config.channel as usize].ctrl = pconfig.pulse_pin.take(); unsafe { esp!(esp_idf_sys::pcnt_unit_config( &config as *const pcnt_config_t @@ -284,19 +234,17 @@ impl<'d> Pcnt<'d> { pub fn set_pin( &mut self, channel: PcntChannel, - pulse_pin: Option>, - ctrl_pin: Option>, + pulse_pin: Option<&AnyInputPin>, + ctrl_pin: Option<&AnyInputPin>, ) -> Result<(), EspError> { - let pulse_io_num = match &pulse_pin { - Some(pin) => pin.pin_num(), + let pulse_io_num = match pulse_pin { + Some(pin) => pin.pin(), None => esp_idf_sys::PCNT_PIN_NOT_USED, }; - let ctrl_io_num = match &ctrl_pin { - Some(pin) => pin.pin_num(), + let ctrl_io_num = match ctrl_pin { + Some(pin) => pin.pin(), None => esp_idf_sys::PCNT_PIN_NOT_USED, }; - self.channel_pins[channel as usize].pulse = pulse_pin; - self.channel_pins[channel as usize].ctrl = ctrl_pin; unsafe { esp!(esp_idf_sys::pcnt_set_pin( self.unit, @@ -365,8 +313,9 @@ impl<'d> Pcnt<'d> { } } -impl<'d> Drop for Pcnt<'d> { +impl Drop for Pcnt { fn drop(&mut self) { + let _ = self.counter_pause(); let _ = self.intr_disable(); unsafe {ISR_HANDLERS[self.unit as usize] = None}; unit_deallocate(self.unit) @@ -408,7 +357,6 @@ static mut ISR_HANDLERS: [Option>; esp_idf_sys::pcnt_unit_t_ None, ]; -#[cfg(esp_idf_version_major = "4")] static mut PCNT_UNITS: [Option; esp_idf_sys::pcnt_unit_t_PCNT_UNIT_MAX as usize] = [ Some(esp_idf_sys::pcnt_unit_t_PCNT_UNIT_0), Some(esp_idf_sys::pcnt_unit_t_PCNT_UNIT_1), @@ -423,7 +371,7 @@ static mut PCNT_UNITS: [Option; esp_idf_sys::pcnt_unit_t_PCNT_UNIT_M #[cfg(not(esp32s3))] Some(esp_idf_sys::pcnt_unit_t_PCNT_UNIT_7), ]; -#[cfg(esp_idf_version_major = "4")] + fn unit_allocate() -> Result { let _ = PCNT_CS.enter(); for i in 0..esp_idf_sys::pcnt_unit_t_PCNT_UNIT_MAX { @@ -434,7 +382,6 @@ fn unit_allocate() -> Result { Err(EspError::from(esp_idf_sys::ESP_ERR_NO_MEM as esp_idf_sys::esp_err_t).unwrap()) } -#[cfg(esp_idf_version_major = "4")] fn unit_deallocate(unit: UnitHandle) { let _ = PCNT_CS.enter(); unsafe { diff --git a/src/pulse_cnt.rs b/src/pulse_cnt.rs new file mode 100644 index 00000000000..a897e6a8307 --- /dev/null +++ b/src/pulse_cnt.rs @@ -0,0 +1,347 @@ + + +use esp_idf_sys::esp; +use esp_idf_sys::pcnt_chan_config_t; +// channel flags +use esp_idf_sys::pcnt_chan_config_t__bindgen_ty_1; +use esp_idf_sys::pcnt_channel_edge_action_t; +use esp_idf_sys::pcnt_channel_handle_t; +use esp_idf_sys::pcnt_channel_level_action_t; +use esp_idf_sys::pcnt_channel_set_edge_action; +use esp_idf_sys::pcnt_channel_set_level_action; +use esp_idf_sys::pcnt_del_channel; +use esp_idf_sys::pcnt_del_unit; +use esp_idf_sys::pcnt_new_channel; +use esp_idf_sys::pcnt_new_unit; +use esp_idf_sys::pcnt_unit_config_t; +use esp_idf_sys::pcnt_unit_config_t__bindgen_ty_1; +use esp_idf_sys::pcnt_unit_disable; +use esp_idf_sys::pcnt_unit_enable; +use esp_idf_sys::pcnt_unit_get_count; +use esp_idf_sys::pcnt_unit_handle_t; +use esp_idf_sys::EspError; +use esp_idf_sys::pcnt_unit_start; +use esp_idf_sys::pcnt_unit_stop; + +use crate::gpio::AnyInputPin; +use crate::gpio::Pin; + +#[derive(Debug, Copy, Clone)] +pub enum PcntEdgeAction { + #[doc = "< Hold current count value"] + Hold, + #[doc = "< Increase count value"] + Increase, + #[doc = "< Decrease count value"] + Decrease, +} + +impl Into for PcntEdgeAction { + fn into(self) -> pcnt_channel_edge_action_t { + match self { + PcntEdgeAction::Hold => esp_idf_sys::pcnt_channel_edge_action_t_PCNT_CHANNEL_EDGE_ACTION_HOLD, + PcntEdgeAction::Increase => esp_idf_sys::pcnt_channel_edge_action_t_PCNT_CHANNEL_EDGE_ACTION_INCREASE, + PcntEdgeAction::Decrease => esp_idf_sys::pcnt_channel_edge_action_t_PCNT_CHANNEL_EDGE_ACTION_DECREASE, + } + } +} + +#[derive(Debug, Copy, Clone)] +pub enum PcntLevelAction { + #[doc = "< Keep current count mode"] + Keep, + #[doc = "< Invert current count mode (increase -> decrease, decrease -> increase)"] + Inverse, + #[doc = "< Hold current count value"] + Hold, +} + +impl Into for PcntLevelAction { + fn into(self) -> pcnt_channel_edge_action_t { + match self { + PcntLevelAction::Keep => esp_idf_sys::pcnt_channel_level_action_t_PCNT_CHANNEL_LEVEL_ACTION_KEEP, + PcntLevelAction::Inverse => esp_idf_sys::pcnt_channel_level_action_t_PCNT_CHANNEL_LEVEL_ACTION_INVERSE, + PcntLevelAction::Hold => esp_idf_sys::pcnt_channel_level_action_t_PCNT_CHANNEL_LEVEL_ACTION_HOLD, + } + } +} + +#[derive(Debug, Default, Copy, Clone)] +pub struct PcntChanFlags(pcnt_chan_config_t__bindgen_ty_1); + +impl PcntChanFlags { + #[inline] + pub fn invert_edge_input(&self) -> u32 { + self.0.invert_edge_input() + } + + #[inline] + pub fn set_invert_edge_input(&mut self, val: u32) { + self.0.set_invert_edge_input(val) + } + + #[inline] + pub fn invert_level_input(&self) -> u32 { + self.0.invert_level_input() + } + + #[inline] + pub fn set_invert_level_input(&mut self, val: u32) { + self.0.set_invert_level_input(val) + } + + #[inline] + pub fn virt_edge_io_level(&self) -> u32 { + self.0.virt_edge_io_level() + } + + #[inline] + pub fn set_virt_edge_io_level(&mut self, val: u32) { + self.0.set_virt_edge_io_level(val) + } + + #[inline] + pub fn virt_level_io_level(&self) -> u32 { + self.0.virt_level_io_level() + } + + #[inline] + pub fn set_virt_level_io_level(&mut self, val: u32) { + self.0.set_virt_level_io_level(val) + } + + #[inline] + pub fn io_loop_back(&self) -> u32 { + self.0.io_loop_back() + } + + #[inline] + pub fn set_io_loop_back(&mut self, val: u32) { + self.0.set_io_loop_back(val) + } +} + +#[doc = " @brief PCNT channel configuration"] +#[derive(Default)] +pub struct PcntChanConfig<'a> { + #[doc = "< GPIO number used by the edge signal, input mode with pull up enabled. Set to -1 if unused"] + pub edge_pin: Option<&'a AnyInputPin>, + #[doc = "< GPIO number used by the level signal, input mode with pull up enabled. Set to -1 if unused"] + pub level_pin: Option<&'a AnyInputPin>, + #[doc = "< Channel config flags"] + pub flags: PcntChanFlags, +} + + +#[doc = " @brief PCNT channel"] +#[derive(Debug)] +pub struct PcntChannel(pcnt_channel_handle_t); + +impl PcntChannel { + #[doc = " @brief Set channel actions when edge signal changes (e.g. falling or rising edge occurred)."] + #[doc = " The edge signal is input from the `edge_pin` configured in `PcntChanConfig`."] + #[doc = " We use these actions to control when and how to change the counter value."] + #[doc = ""] + #[doc = " @param[in] pos_act Action on posedge signal"] + #[doc = " @param[in] neg_act Action on negedge signal"] + #[doc = " @return"] + #[doc = " @return"] + #[doc = " - (): on success"] + #[doc = " - EspError: on failure"] + pub fn set_edge_action(&self,pos_act: PcntEdgeAction, neg_act: PcntEdgeAction) -> Result<(), EspError> { + unsafe { + esp!(pcnt_channel_set_edge_action(self.0, pos_act.into(), neg_act.into())) + } + } + + #[doc = " @brief Set channel actions when level signal changes (e.g. signal level goes from high to low)."] + #[doc = " The level signal is input from the `level_gpio_num` configured in `pcnt_chan_config_t`."] + #[doc = " We use these actions to control when and how to change the counting mode."] + #[doc = ""] + #[doc = " @param[in] chan PCNT channel handle created by `pcnt_new_channel()`"] + #[doc = " @param[in] high_act Action on high level signal"] + #[doc = " @param[in] low_act Action on low level signal"] + #[doc = " @return"] + #[doc = " - ESP_OK: Set level action for PCNT channel successfully"] + #[doc = " - ESP_ERR_INVALID_ARG: Set level action for PCNT channel failed because of invalid argument"] + #[doc = " - ESP_FAIL: Set level action for PCNT channel failed because of other error"] + pub fn set_level_action(&self, high_act: PcntLevelAction, low_act: PcntLevelAction) -> Result<(), EspError> { + unsafe { + esp!(pcnt_channel_set_level_action(self.0, high_act.into(), low_act.into())) + } + } +} + +impl Drop for PcntChannel { + fn drop(&mut self) { + unsafe { + esp!(pcnt_del_channel(self.0)).unwrap(); + } + } +} + +#[derive(Debug, Default, Copy, Clone)] +pub struct PcntUnitFlags(pcnt_unit_config_t__bindgen_ty_1); +impl PcntUnitFlags { + #[inline] + pub fn accum_count(&self) -> u32 { + self.0.accum_count() + } + + #[inline] + pub fn set_accum_count(&mut self, val: u32) -> Self { + self.0.set_accum_count(val); + *self + } +} + +#[doc = " @brief PCNT unit configuration"] +#[derive(Debug, Default, Copy, Clone)] +pub struct PcntUnitConfig { + #[doc = " @brief the low limit value, should be < 0"] + pub low_limit: i32, + #[doc = " @brief the high limit value, should be > 0"] + pub high_limit: i32, + pub flags: PcntUnitFlags, +} + +#[doc = " @brief PCNT unit"] +#[derive(Debug)] +pub struct PcntUnit(pcnt_unit_handle_t); + +impl PcntUnit { + #[doc = " @brief Create a new PCNT unit"] + #[doc = ""] + #[doc = " @note The newly created PCNT unit is put in the init state."] + #[doc = ""] + #[doc = " @param[in] config PCNT unit configuration"] + #[doc = " @return"] + #[doc = " - PcntUnit: on success"] + #[doc = " - EspError: on failure"] + pub fn new(config: &PcntUnitConfig) -> Result { + let config = pcnt_unit_config_t { + low_limit: config.low_limit, + high_limit: config.high_limit, + flags: config.flags.0, + }; + let mut unit: pcnt_unit_handle_t = std::ptr::null_mut(); + unsafe { + esp!(pcnt_new_unit(&config, &mut unit))?; + } + Ok(Self(unit)) + } + + #[doc = " @brief Enable the PCNT unit"] + #[doc = ""] + #[doc = " @note This function will transit the unit state from init to enable."] + #[doc = " @note This function will enable the interrupt service, if it's lazy installed in `subscribe()`."] + #[doc = " @note This function will acquire the PM lock if it's lazy installed in `set_glitch_filter()`."] + #[doc = " @note Enable a PCNT unit doesn't mean to start it. See also `start()` for how to start the PCNT counter."] + #[doc = ""] + #[doc = " @return"] + #[doc = " - (): on success"] + #[doc = " - EspError: on failure"] + pub fn enable(&self) -> Result<(), EspError> { + unsafe { + esp!(pcnt_unit_enable(self.0)) + } + } + + #[doc = " @brief Disable the PCNT unit"] + #[doc = ""] + #[doc = " @note This function will do the opposite work to the `enable()`"] + #[doc = " @note Disable a PCNT unit doesn't mean to stop it. See also `stop()` for how to stop the PCNT counter."] + #[doc = ""] + #[doc = " @return"] + #[doc = " - (): on success"] + #[doc = " - EspError: on failure"] + pub fn disable(&self) -> Result<(), EspError> { + unsafe { + esp!(pcnt_unit_disable(self.0)) + } + } + + #[doc = " @brief Start the PCNT unit, the counter will start to count according to the edge and/or level input signals"] + #[doc = ""] + #[doc = " @note This function should be called when the unit is in the enable state (i.e. after calling `pcnt_unit_enable()`)"] + #[doc = " @note This function is allowed to run within ISR context"] + #[doc = " @note This function will be placed into IRAM if `CONFIG_PCNT_CTRL_FUNC_IN_IRAM` is on, so that it's allowed to be executed when Cache is disabled"] + #[doc = ""] + #[doc = " @return"] + #[doc = " - (): on success"] + #[doc = " - EspError: on failure"] + pub fn start(&self) -> Result<(), EspError> { + unsafe { + esp!(pcnt_unit_start(self.0)) + } + } + + #[doc = " @brief Stop PCNT from counting"] + #[doc = ""] + #[doc = " @note This function should be called when the unit is in the enable state (i.e. after calling `pcnt_unit_enable()`)"] + #[doc = " @note The stop operation won't clear the counter. Also see `pcnt_unit_clear_count()` for how to clear pulse count value."] + #[doc = " @note This function is allowed to run within ISR context"] + #[doc = " @note This function will be placed into IRAM if `CONFIG_PCNT_CTRL_FUNC_IN_IRAM`, so that it is allowed to be executed when Cache is disabled"] + #[doc = ""] + #[doc = " @return"] + #[doc = " - (): on success"] + #[doc = " - EspError: on failure"] + pub fn stop(&self) -> Result<(), EspError> { + unsafe { + esp!(pcnt_unit_stop(self.0)) + } + } + + + #[doc = " @brief Get PCNT count value"] + #[doc = ""] + #[doc = " @note This function is allowed to run within ISR context"] + #[doc = " @note This function will be placed into IRAM if `CONFIG_PCNT_CTRL_FUNC_IN_IRAM`, so that it's allowed to be executed when Cache is disabled"] + #[doc = ""] + #[doc = " @param[out] value Returned count value"] + #[doc = " @return"] + #[doc = " - value: on success"] + #[doc = " - EspError: on failure"] + pub fn get_count(&self) -> Result { + let mut value: esp_idf_sys::c_types::c_int = 0; + unsafe { + esp!(pcnt_unit_get_count(self.0, &mut value))?; + } + Ok(value) + } + + #[doc = " @brief Create PCNT channel for specific unit, each PCNT has several channels associated with it"] + #[doc = ""] + #[doc = " @note This function should be called when the unit is in init state (i.e. before calling `pcnt_unit_enable()`)"] + #[doc = ""] + #[doc = " @param[in] config PCNT channel configuration"] + #[doc = " @return"] + #[doc = " - PcntChannel: on success"] + #[doc = " - EspError: on failure"] + pub fn channel(&self, config: &PcntChanConfig) -> Result { + let config = pcnt_chan_config_t { + edge_gpio_num: match config.edge_pin { + Some(pin) => pin.pin(), + None => -1, + }, + level_gpio_num: match config.level_pin { + Some(pin) => pin.pin(), + None => -1, + }, + flags: config.flags.0, + }; + let mut channel: pcnt_channel_handle_t = std::ptr::null_mut(); + unsafe { + esp!(pcnt_new_channel(self.0, &config, &mut channel))?; + } + Ok(PcntChannel(channel)) + } +} + +impl Drop for PcntUnit { + fn drop(&mut self) { + unsafe { + esp!(pcnt_del_unit(self.0)).unwrap(); + } + } +} From 8e3b8ea7baa3b7e9ab0052fd43edc37c31c16f03 Mon Sep 17 00:00:00 2001 From: Christopher Liebman Date: Sun, 4 Dec 2022 13:23:31 -0800 Subject: [PATCH 12/48] fix pcnt compilation on esp-idf 4 --- src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index e7ac33b84c8..e36f0114190 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -46,9 +46,9 @@ pub mod mac; pub mod modem; pub mod peripheral; pub mod peripherals; -#[cfg(all(not(feature = "riscv-ulp-hal"), feature = "pcnt4"))] +#[cfg(all(not(feature = "riscv-ulp-hal"), any(feature = "pcnt4", esp_idf_version_major = "4")))] pub mod pcnt; -#[cfg(all(not(feature = "riscv-ulp-hal"), not(feature = "pcnt4")))] +#[cfg(all(not(feature = "riscv-ulp-hal"), not(feature = "pcnt4"), not(esp_idf_version_major = "4")))] pub mod pulse_cnt; pub mod prelude; #[cfg(not(feature = "riscv-ulp-hal"))] From bdfc2da3386d4869f8e86c06b6e789371782b603 Mon Sep 17 00:00:00 2001 From: Christopher Liebman Date: Mon, 5 Dec 2022 16:24:40 -0800 Subject: [PATCH 13/48] implement interrupt service --- src/pulse_cnt.rs | 185 ++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 174 insertions(+), 11 deletions(-) diff --git a/src/pulse_cnt.rs b/src/pulse_cnt.rs index a897e6a8307..a9b95e5e9c9 100644 --- a/src/pulse_cnt.rs +++ b/src/pulse_cnt.rs @@ -1,5 +1,6 @@ + use esp_idf_sys::esp; use esp_idf_sys::pcnt_chan_config_t; // channel flags @@ -11,21 +12,92 @@ use esp_idf_sys::pcnt_channel_set_edge_action; use esp_idf_sys::pcnt_channel_set_level_action; use esp_idf_sys::pcnt_del_channel; use esp_idf_sys::pcnt_del_unit; +use esp_idf_sys::pcnt_event_callbacks_t; +use esp_idf_sys::pcnt_glitch_filter_config_t; use esp_idf_sys::pcnt_new_channel; use esp_idf_sys::pcnt_new_unit; +use esp_idf_sys::pcnt_unit_add_watch_point; +use esp_idf_sys::pcnt_unit_clear_count; use esp_idf_sys::pcnt_unit_config_t; +// unit flags use esp_idf_sys::pcnt_unit_config_t__bindgen_ty_1; use esp_idf_sys::pcnt_unit_disable; use esp_idf_sys::pcnt_unit_enable; use esp_idf_sys::pcnt_unit_get_count; use esp_idf_sys::pcnt_unit_handle_t; use esp_idf_sys::EspError; +use esp_idf_sys::pcnt_unit_register_event_callbacks; +use esp_idf_sys::pcnt_unit_remove_watch_point; +use esp_idf_sys::pcnt_unit_set_glitch_filter; use esp_idf_sys::pcnt_unit_start; use esp_idf_sys::pcnt_unit_stop; +use esp_idf_sys::pcnt_unit_zero_cross_mode_t; +use esp_idf_sys::pcnt_watch_event_data_t; use crate::gpio::AnyInputPin; use crate::gpio::Pin; +#[doc = " @brief PCNT glitch filter configuration"] +#[derive(Debug, Default, Copy, Clone)] +pub struct PcntFilterConfig { + #[doc = "< Pulse width smaller than this threshold will be treated as glitch and ignored, in the unit of ns"] + pub max_glitch_ns: u32, +} + +impl Into for &PcntFilterConfig { + fn into(self) -> pcnt_glitch_filter_config_t { + pcnt_glitch_filter_config_t { + max_glitch_ns: self.max_glitch_ns + } + } +} + +#[doc = " @brief PCNT unit zero cross mode"] +#[derive(Debug, Copy, Clone)] +pub enum PcntZeroCrossMode { + #[doc = "< start from positive value, end to zero, i.e. +N->0"] + PosZero, + #[doc = "< start from negative value, end to zero, i.e. -N->0"] + NegZero, + #[doc = "< start from negative value, end to positive value, i.e. -N->+M"] + NegPos, + #[doc = "< start from positive value, end to negative value, i.e. +N->-M"] + PosNeg, +} + +impl From for PcntZeroCrossMode { + fn from(x: pcnt_unit_zero_cross_mode_t) -> Self { + match x { + esp_idf_sys::pcnt_unit_zero_cross_mode_t_PCNT_UNIT_ZERO_CROSS_POS_ZERO => PcntZeroCrossMode::PosZero, + esp_idf_sys::pcnt_unit_zero_cross_mode_t_PCNT_UNIT_ZERO_CROSS_NEG_ZERO => PcntZeroCrossMode::NegZero, + esp_idf_sys::pcnt_unit_zero_cross_mode_t_PCNT_UNIT_ZERO_CROSS_NEG_POS => PcntZeroCrossMode::NegPos, + esp_idf_sys::pcnt_unit_zero_cross_mode_t_PCNT_UNIT_ZERO_CROSS_POS_NEG => PcntZeroCrossMode::PosNeg, + _ => panic!("unknown pcnt_unit_zero_cross_mode_t value {}", x), + } + } +} + +#[doc = " @brief PCNT watch event data"] +#[derive(Debug, Copy, Clone)] +#[allow(dead_code)] +pub struct PcntWatchEventData { + #[doc = "< Watch point value that triggered the event"] + pub watch_point_value: i32, + #[doc = "< Zero cross mode"] + pub zero_cross_mode: PcntZeroCrossMode, +} + +impl From<*const pcnt_watch_event_data_t> for PcntWatchEventData { + fn from(x: *const pcnt_watch_event_data_t) -> Self { + unsafe { + Self { + watch_point_value: (*x).watch_point_value, + zero_cross_mode: (*x).zero_cross_mode.into(), + } + } + } +} + #[derive(Debug, Copy, Clone)] pub enum PcntEdgeAction { #[doc = "< Hold current count value"] @@ -207,7 +279,9 @@ pub struct PcntUnitConfig { #[doc = " @brief PCNT unit"] #[derive(Debug)] -pub struct PcntUnit(pcnt_unit_handle_t); +pub struct PcntUnit { + unit: pcnt_unit_handle_t, +} impl PcntUnit { #[doc = " @brief Create a new PCNT unit"] @@ -228,7 +302,29 @@ impl PcntUnit { unsafe { esp!(pcnt_new_unit(&config, &mut unit))?; } - Ok(Self(unit)) + Ok(Self { + unit, + }) + } + + #[doc = " @brief Set glitch filter for PCNT unit"] + #[doc = ""] + #[doc = " @note The glitch filter module is clocked from APB, and APB frequency can be changed during DFS, which in return make the filter out of action."] + #[doc = " So this function will lazy-install a PM lock internally when the power management is enabled. With this lock, the APB frequency won't be changed."] + #[doc = " The PM lock can be uninstalled when the unit is dropped."] + #[doc = " @note This function should be called when the PCNT unit is in the init state (i.e. before calling `enable()`)"] + #[doc = ""] + #[doc = " @param[in] config PCNT filter configuration, set config to None means disabling the filter function"] + #[doc = " @return"] + #[doc = " - (): on success"] + #[doc = " - EspError: on failure"] + pub fn set_filter(&self, config: Option<&PcntFilterConfig>) -> Result<(), EspError> { + unsafe { + esp!(pcnt_unit_set_glitch_filter(self.unit, match config { + Some(x) => &x.into(), + None => std::ptr::null(), + })) + } } #[doc = " @brief Enable the PCNT unit"] @@ -243,7 +339,7 @@ impl PcntUnit { #[doc = " - EspError: on failure"] pub fn enable(&self) -> Result<(), EspError> { unsafe { - esp!(pcnt_unit_enable(self.0)) + esp!(pcnt_unit_enable(self.unit)) } } @@ -257,7 +353,7 @@ impl PcntUnit { #[doc = " - EspError: on failure"] pub fn disable(&self) -> Result<(), EspError> { unsafe { - esp!(pcnt_unit_disable(self.0)) + esp!(pcnt_unit_disable(self.unit)) } } @@ -272,7 +368,7 @@ impl PcntUnit { #[doc = " - EspError: on failure"] pub fn start(&self) -> Result<(), EspError> { unsafe { - esp!(pcnt_unit_start(self.0)) + esp!(pcnt_unit_start(self.unit)) } } @@ -288,10 +384,24 @@ impl PcntUnit { #[doc = " - EspError: on failure"] pub fn stop(&self) -> Result<(), EspError> { unsafe { - esp!(pcnt_unit_stop(self.0)) + esp!(pcnt_unit_stop(self.unit)) } } + #[doc = " @brief Clear PCNT pulse count value to zero"] + #[doc = ""] + #[doc = " @note It's recommended to call this function after adding a watch point by `pcnt_unit_add_watch_point()`, so that the newly added watch point is effective immediately."] + #[doc = " @note This function is allowed to run within ISR context"] + #[doc = " @note This function will be placed into IRAM if `CONFIG_PCNT_CTRL_FUNC_IN_IRAM`, so that it's allowed to be executed when Cache is disabled"] + #[doc = ""] + #[doc = " @return"] + #[doc = " - (): on success"] + #[doc = " - EspError: on failure"] + pub fn clear_count(&self) -> Result<(), EspError> { + unsafe { + esp!(pcnt_unit_clear_count(self.unit)) + } + } #[doc = " @brief Get PCNT count value"] #[doc = ""] @@ -304,12 +414,65 @@ impl PcntUnit { #[doc = " - EspError: on failure"] pub fn get_count(&self) -> Result { let mut value: esp_idf_sys::c_types::c_int = 0; - unsafe { - esp!(pcnt_unit_get_count(self.0, &mut value))?; - } + unsafe { esp!(pcnt_unit_get_count(self.unit, &mut value))?; } Ok(value) } + #[doc = " @brief Add a watch point for PCNT unit, PCNT will generate an event when the counter value reaches the watch point value"] + #[doc = ""] + #[doc = " @param[in] watch_point Value to be watched"] + #[doc = " @return"] + #[doc = " - value: on success"] + #[doc = " - EspError: on failure"] + pub fn add_watch_point(&self, watch_point: i32) -> Result<(), EspError> { + unsafe {esp!(pcnt_unit_add_watch_point(self.unit, watch_point))} + } + + #[doc = " @brief Remove a watch point for PCNT unit"] + #[doc = ""] + #[doc = " @param[in] watch_point Watch point value"] + #[doc = " @return"] + #[doc = " - value: on success"] + #[doc = " - EspError: on failure"] + pub fn remove_watch_point(&self, watch_point: i32) -> Result<(), EspError> { + unsafe {esp!(pcnt_unit_remove_watch_point(self.unit, watch_point))} + } + + #[doc = " @brief subscribe to PCNT events"] + #[doc = ""] + #[doc = " @note User registered callbacks are expected to be runnable within ISR context"] + #[doc = " @note The first call to this function needs to be before the call to `pcnt_unit_enable`"] + #[doc = " @note User can deregister a previously registered callback by calling this function and passing None."] + #[doc = ""] + #[doc = " @return"] + #[doc = " - value: on success"] + #[doc = " - EspError: on failure"] + pub fn subscribe(&self, callback: F ) -> Result<(), EspError> + where + F: FnMut(PcntWatchEventData)->bool + Send + 'static + { + let mut closure = callback; + let cbs = pcnt_event_callbacks_t { + on_reach: Self::get_trampoline(&closure), + }; + let data = &mut closure as *mut _ as *mut esp_idf_sys::c_types::c_void; + unsafe {esp!(pcnt_unit_register_event_callbacks(self.unit, &cbs, data as *mut esp_idf_sys::c_types::c_void))} + } + + pub fn get_trampoline(_closure: &F) -> esp_idf_sys::pcnt_watch_cb_t + where + F: FnMut(PcntWatchEventData)->bool + { + Some(Self::trampoline::) + } + + unsafe extern "C" fn trampoline(_unit: pcnt_unit_handle_t, edata: *const pcnt_watch_event_data_t, data: *mut esp_idf_sys::c_types::c_void) -> bool + where + F: FnMut(PcntWatchEventData)->bool + { + let f = &mut *(data as *mut F); + f(edata.into()) + } #[doc = " @brief Create PCNT channel for specific unit, each PCNT has several channels associated with it"] #[doc = ""] #[doc = " @note This function should be called when the unit is in init state (i.e. before calling `pcnt_unit_enable()`)"] @@ -332,7 +495,7 @@ impl PcntUnit { }; let mut channel: pcnt_channel_handle_t = std::ptr::null_mut(); unsafe { - esp!(pcnt_new_channel(self.0, &config, &mut channel))?; + esp!(pcnt_new_channel(self.unit, &config, &mut channel))?; } Ok(PcntChannel(channel)) } @@ -341,7 +504,7 @@ impl PcntUnit { impl Drop for PcntUnit { fn drop(&mut self) { unsafe { - esp!(pcnt_del_unit(self.0)).unwrap(); + esp!(pcnt_del_unit(self.unit)).unwrap(); } } } From 6582fc423f117ec4e4c8c1e9cb8ef60257ceded3 Mon Sep 17 00:00:00 2001 From: Christopher Liebman Date: Tue, 6 Dec 2022 15:44:36 -0800 Subject: [PATCH 14/48] added some documentation --- src/pcnt.rs | 191 ++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 172 insertions(+), 19 deletions(-) diff --git a/src/pcnt.rs b/src/pcnt.rs index b3f3e493ee6..18d7eadd068 100644 --- a/src/pcnt.rs +++ b/src/pcnt.rs @@ -3,6 +3,7 @@ use core::fmt::Debug; use esp_idf_sys::esp; use esp_idf_sys::pcnt_config_t; +use esp_idf_sys::pcnt_set_mode; use esp_idf_sys::pcnt_unit_t; use esp_idf_sys::EspError; @@ -29,49 +30,44 @@ impl Into for PcntChannel { } } - +#[doc = " @brief PCNT channel action on signal edge"] #[derive(Debug, Copy, Clone)] pub enum PcntCountMode { + #[doc = "< Hold current count value"] Hold, + #[doc = "< Increase count value"] Increment, + #[doc = "< Decrease count value"] Decrement, } impl Into for PcntCountMode { fn into(self) -> esp_idf_sys::pcnt_count_mode_t { match self { - PcntCountMode::Hold => { - esp_idf_sys::pcnt_channel_edge_action_t_PCNT_CHANNEL_EDGE_ACTION_HOLD - } - PcntCountMode::Increment => { - esp_idf_sys::pcnt_channel_edge_action_t_PCNT_CHANNEL_EDGE_ACTION_INCREASE - } - PcntCountMode::Decrement => { - esp_idf_sys::pcnt_channel_edge_action_t_PCNT_CHANNEL_EDGE_ACTION_DECREASE - } + PcntCountMode::Hold => esp_idf_sys::pcnt_channel_edge_action_t_PCNT_CHANNEL_EDGE_ACTION_HOLD, + PcntCountMode::Increment => esp_idf_sys::pcnt_channel_edge_action_t_PCNT_CHANNEL_EDGE_ACTION_INCREASE, + PcntCountMode::Decrement => esp_idf_sys::pcnt_channel_edge_action_t_PCNT_CHANNEL_EDGE_ACTION_DECREASE, } } } +#[doc = " @brief PCNT channel action on control level"] #[derive(Debug, Copy, Clone)] pub enum PcntControlMode { + #[doc = "< Keep current count mode"] Keep, + #[doc = "< Invert current count mode (increase -> decrease, decrease -> increase)"] Reverse, + #[doc = "< Hold current count value"] Disable, } impl Into for PcntControlMode { fn into(self) -> esp_idf_sys::pcnt_ctrl_mode_t { match self { - PcntControlMode::Keep => { - esp_idf_sys::pcnt_channel_edge_action_t_PCNT_CHANNEL_EDGE_ACTION_HOLD - } - PcntControlMode::Reverse => { - esp_idf_sys::pcnt_channel_edge_action_t_PCNT_CHANNEL_EDGE_ACTION_INCREASE - } - PcntControlMode::Disable => { - esp_idf_sys::pcnt_channel_edge_action_t_PCNT_CHANNEL_EDGE_ACTION_DECREASE - } + PcntControlMode::Keep => esp_idf_sys::pcnt_channel_level_action_t_PCNT_CHANNEL_LEVEL_ACTION_KEEP, + PcntControlMode::Reverse => esp_idf_sys::pcnt_channel_level_action_t_PCNT_CHANNEL_LEVEL_ACTION_INVERSE, + PcntControlMode::Disable => esp_idf_sys::pcnt_channel_level_action_t_PCNT_CHANNEL_LEVEL_ACTION_HOLD, } } } @@ -80,10 +76,15 @@ bitflags! { #[allow(dead_code)] #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct PcntEventType: u32 { + #[doc = "< PCNT watch point event: threshold1 value event"] const THRES_1 = esp_idf_sys::pcnt_evt_type_t_PCNT_EVT_THRES_1; + #[doc = "< PCNT watch point event: threshold0 value event"] const THRES_0 = esp_idf_sys::pcnt_evt_type_t_PCNT_EVT_THRES_0; + #[doc = "< PCNT watch point event: Minimum counter value"] const L_LIM = esp_idf_sys::pcnt_evt_type_t_PCNT_EVT_L_LIM; + #[doc = "< PCNT watch point event: Maximum counter value"] const H_LIM = esp_idf_sys::pcnt_evt_type_t_PCNT_EVT_H_LIM; + #[doc = "< PCNT watch point event: counter value zero event"] const ZERO = esp_idf_sys::pcnt_evt_type_t_PCNT_EVT_ZERO; } } @@ -122,6 +123,15 @@ impl<'d> Pcnt { }) } + #[doc = " @brief Configure Pulse Counter unit"] + #[doc = " @note"] + #[doc = " This function will disable three events: PCNT_EVT_L_LIM, PCNT_EVT_H_LIM, PCNT_EVT_ZERO."] + #[doc = ""] + #[doc = " @param pconfig Reference of PcntConfig"] + #[doc = ""] + #[doc = " @return"] + #[doc = " - ()"] + #[doc = " - EspError"] pub fn config(&mut self, pconfig: & PcntConfig) -> Result<(), EspError> { let config = pcnt_config_t { pulse_gpio_num: match pconfig.pulse_pin { @@ -149,6 +159,11 @@ impl<'d> Pcnt { } } + #[doc = " @brief Get pulse counter value"] + #[doc = ""] + #[doc = " @return"] + #[doc = " - i16"] + #[doc = " - EspError"] pub fn get_counter_value(&self) -> Result { let mut value = 0i16; unsafe { @@ -160,31 +175,73 @@ impl<'d> Pcnt { Ok(value) } + #[doc = " @brief Pause PCNT counter of PCNT unit"] + #[doc = ""] + #[doc = " @return"] + #[doc = " - ()"] + #[doc = " - EspError"] pub fn counter_pause(&self) -> Result<(), EspError> { unsafe { esp!(esp_idf_sys::pcnt_counter_pause(self.unit)) } } + #[doc = " @brief Resume counting for PCNT counter"] + #[doc = ""] + #[doc = " @return"] + #[doc = " - ()"] + #[doc = " - EspError"] pub fn counter_resume(&self) -> Result<(), EspError> { unsafe { esp!(esp_idf_sys::pcnt_counter_resume(self.unit)) } } + #[doc = " @brief Clear and reset PCNT counter value to zero"] + #[doc = ""] + #[doc = " @return"] + #[doc = " - ()"] + #[doc = " - EspError"] pub fn counter_clear(&self) -> Result<(), EspError> { unsafe { esp!(esp_idf_sys::pcnt_counter_clear(self.unit)) } } + #[doc = " @brief Enable PCNT interrupt for PCNT unit"] + #[doc = " @note"] + #[doc = " Each Pulse counter unit has five watch point events that share the same interrupt."] + #[doc = " Configure events with pcnt_event_enable() and pcnt_event_disable()"] + #[doc = ""] + #[doc = " @return"] + #[doc = " - ()"] + #[doc = " - EspError"] pub fn intr_enable(&self) -> Result<(), EspError> { unsafe { esp!(esp_idf_sys::pcnt_intr_enable(self.unit)) } } + #[doc = " @brief Disable PCNT interrupt for PCNT unit"] + #[doc = ""] + #[doc = " @return"] + #[doc = " - ()"] + #[doc = " - EspError"] pub fn intr_disable(&self) -> Result<(), EspError> { unsafe { esp!(esp_idf_sys::pcnt_intr_disable(self.unit)) } } + #[doc = " @brief Enable PCNT event of PCNT unit"] + #[doc = ""] + #[doc = " @param evt_type Watch point event type."] + #[doc = " All enabled events share the same interrupt (one interrupt per pulse counter unit)."] + #[doc = " @return"] + #[doc = " - ()"] + #[doc = " - EspError"] pub fn event_enable(&self, evt_type: PcntEventType) -> Result<(), EspError> { let evt_type: esp_idf_sys::pcnt_evt_type_t = evt_type.bits(); unsafe { esp!(esp_idf_sys::pcnt_event_enable(self.unit, evt_type)) } } + #[doc = " @brief Disable PCNT event of PCNT unit"] + #[doc = ""] + #[doc = " @param evt_type Watch point event type."] + #[doc = " All enabled events share the same interrupt (one interrupt per pulse counter unit)."] + #[doc = " @return"] + #[doc = " - ()"] + #[doc = " - EspError"] pub fn event_disable(&self, evt_type: PcntEventType) -> Result<(), EspError> { let evt_type: esp_idf_sys::pcnt_evt_type_t = evt_type.bits(); unsafe { esp!(esp_idf_sys::pcnt_event_disable(self.unit, evt_type)) } @@ -197,6 +254,14 @@ impl<'d> Pcnt { } } + #[doc = " @brief Set PCNT event value of PCNT unit"] + #[doc = ""] + #[doc = " @param evt_type Watch point event type."] + #[doc = " All enabled events share the same interrupt (one interrupt per pulse counter unit)."] + #[doc = ""] + #[doc = " @return"] + #[doc = " - ()"] + #[doc = " - 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 { @@ -206,6 +271,14 @@ impl<'d> Pcnt { } } + #[doc = " @brief Get PCNT event value of PCNT unit"] + #[doc = ""] + #[doc = " @param evt_type Watch point event type."] + #[doc = " All enabled events share the same interrupt (one interrupt per pulse counter unit)."] + #[doc = ""] + #[doc = " @return"] + #[doc = " - i16"] + #[doc = " - 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; @@ -219,6 +292,11 @@ impl<'d> Pcnt { Ok(value) } + #[doc = " @brief Get PCNT event status of PCNT unit"] + #[doc = ""] + #[doc = " @return"] + #[doc = " - i32"] + #[doc = " - EspError"] // TODO: status is a bit field! pub fn get_event_status(&self) -> Result { let mut value = 0u32; @@ -231,6 +309,17 @@ impl<'d> Pcnt { Ok(value) } + #[doc = " @brief Configure PCNT pulse signal input pin and control input pin"] + #[doc = ""] + #[doc = " @param channel PcntChannel"] + #[doc = " @param pulse_io Pulse signal input pin"] + #[doc = " @param ctrl_io Control signal input pin"] + #[doc = ""] + #[doc = " @note Set the signal input to PCNT_PIN_NOT_USED if unused."] + #[doc = ""] + #[doc = " @return"] + #[doc = " - ()"] + #[doc = " - EspError"] pub fn set_pin( &mut self, channel: PcntChannel, @@ -255,18 +344,43 @@ impl<'d> Pcnt { } } + #[doc = " @brief Enable PCNT input filter"] + #[doc = ""] + #[doc = " @return"] + #[doc = " - ()"] + #[doc = " - EspError"] pub fn filter_enable(&self) -> Result<(), EspError> { unsafe { esp!(esp_idf_sys::pcnt_filter_enable(self.unit)) } } + #[doc = " @brief Disable PCNT input filter"] + #[doc = ""] + #[doc = " @return"] + #[doc = " - ()"] + #[doc = " - EspError"] pub fn filter_disable(&self) -> Result<(), EspError> { unsafe { esp!(esp_idf_sys::pcnt_filter_disable(self.unit)) } } + #[doc = " @brief Set PCNT filter value"] + #[doc = ""] + #[doc = " @param filter_val PCNT signal filter value, counter in APB_CLK cycles."] + #[doc = " Any pulses lasting shorter than this will be ignored when the filter is enabled."] + #[doc = " @note"] + #[doc = " filter_val is a 10-bit value, so the maximum filter_val should be limited to 1023."] + #[doc = ""] + #[doc = " @return"] + #[doc = " - ()"] + #[doc = " - EspError"] pub fn set_filter_value(&self, value: u16) -> Result<(), EspError> { unsafe { esp!(esp_idf_sys::pcnt_set_filter_value(self.unit, value)) } } + #[doc = " @brief Get PCNT filter value"] + #[doc = ""] + #[doc = " @return"] + #[doc = " - i16"] + #[doc = " - EspError"] pub fn get_filter_value(&self) -> Result { let mut value = 0u16; unsafe { @@ -278,6 +392,40 @@ impl<'d> Pcnt { Ok(value) } + #[doc = " @brief Set PCNT counter mode"] + #[doc = ""] + #[doc = " @param channel PCNT channel number"] + #[doc = " @param pos_mode Counter mode when detecting positive edge"] + #[doc = " @param neg_mode Counter mode when detecting negative edge"] + #[doc = " @param hctrl_mode Counter mode when control signal is high level"] + #[doc = " @param lctrl_mode Counter mode when control signal is low level"] + #[doc = ""] + #[doc = " @return"] + #[doc = " - ()"] + #[doc = " - 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())) + } + } + + #[doc = " @brief Add ISR handler for specified unit."] + #[doc = ""] + #[doc = " This ISR handler will be called from an ISR. So there is a stack"] + #[doc = " size limit (configurable as \"ISR stack size\" in menuconfig). This"] + #[doc = " limit is smaller compared to a global PCNT interrupt handler due"] + #[doc = " to the additional level of indirection."] + #[doc = ""] + #[doc = " @param callback Interrupt handler function."] + #[doc = ""] + #[doc = " @return"] + #[doc = " - ()"] + #[doc = " - EspError"] pub unsafe fn subscribe(&self, callback: impl FnMut(u32) + Send + 'static) -> Result<(), EspError> { enable_isr_service()?; @@ -292,6 +440,11 @@ impl<'d> Pcnt { Ok(()) } + #[doc = " @brief Remove ISR handler for specified unit."] + #[doc = ""] + #[doc = " @return"] + #[doc = " - ()"] + #[doc = " - EspError"] pub fn unsubscribe(&self) -> Result<(), EspError> { unsafe { esp!(esp_idf_sys::pcnt_isr_handler_remove(self.unit))?; From 2fa6de345d0e0a554db762e1a5b2d2641306366d Mon Sep 17 00:00:00 2001 From: Christopher Liebman Date: Wed, 7 Dec 2022 13:32:43 -0800 Subject: [PATCH 15/48] fix subscribe - trampoline did not work with captured values. update example to support either --- examples/pcnt_i64_encoder.rs | 234 +++++++++++++++++++++++++---------- src/pulse_cnt.rs | 92 +++++++++++--- 2 files changed, 245 insertions(+), 81 deletions(-) diff --git a/examples/pcnt_i64_encoder.rs b/examples/pcnt_i64_encoder.rs index 2538e35ad26..0a29344ebf1 100644 --- a/examples/pcnt_i64_encoder.rs +++ b/examples/pcnt_i64_encoder.rs @@ -5,87 +5,191 @@ //! 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 //! -use std::{sync::{atomic::{Ordering, AtomicI64}, Arc}, cmp::min}; use anyhow; use anyhow::Context; -use embedded_hal::delay::DelayUs; -use esp_idf_hal::delay::FreeRtos as delay; + +use esp_idf_hal::delay::FreeRtos; +use esp_idf_hal::gpio::AnyInputPin; use esp_idf_hal::prelude::*; -use esp_idf_hal::gpio::Pull; -use esp_idf_sys as _; // If using the `binstart` feature of `esp-idf-sys`, always keep this module imported -use esp_idf_hal::pcnt::*; + +use encoder::Encoder; fn main() -> anyhow::Result<()> { // 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"); + // Bind the log crate to the ESP Logging facilities + esp_idf_svc::log::EspLogger::initialize_default(); + let peripherals = Peripherals::take().context("failed to take Peripherals")?; - let m1_enc1_pin = peripherals.pins.gpio5; - let m1_enc2_pin = peripherals.pins.gpio6; - let m1_enc1_pin = PcntPin::new(m1_enc1_pin, Pull::Up)?; - let m1_enc2_pin = PcntPin::new(m1_enc2_pin, Pull::Up)?; - println!("creating pcnt unit 0"); - let mut pcnt = Pcnt::new()?; - println!("configure pcnt chanel 0"); - const POS_LIMIT: i16 = 100; - const NEG_LIMIT: i16 = -100; - let mut config = PcntConfig { - pulse_pin: Some(m1_enc1_pin.clone()), - ctrl_pin: Some(m1_enc2_pin.clone()), - lctrl_mode: PcntControlMode::Reverse, - hctrl_mode: PcntControlMode::Keep, - pos_mode: PcntCountMode::Decrement, - neg_mode: PcntCountMode::Increment, - counter_h_lim: POS_LIMIT, - counter_l_lim: NEG_LIMIT, - channel: PcntChannel::Channel0, - }; - pcnt.config(&mut config).context("configuring CHANNEL0")?; - - println!("configure pcnt chanel 1"); - config.channel = PcntChannel::Channel1; - config.pulse_pin = Some(m1_enc2_pin); - config.ctrl_pin = Some(m1_enc1_pin); - config.pos_mode = PcntCountMode::Increment; - config.neg_mode = PcntCountMode::Decrement; - pcnt.config(&mut config).context("configuring CHANNEL1")?; - pcnt.set_filter_value(min(10*80, 1023))?; - pcnt.filter_enable()?; - let 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 value = value.clone(); - pcnt.subscribe(move |status| { - let status = PcntEventType::from_bits_retain(status); - if status.contains(PcntEventType::H_LIM) { - value.fetch_add(POS_LIMIT as i64, Ordering::SeqCst); - } - if status.contains(PcntEventType::L_LIM) { - value.fetch_add(NEG_LIMIT as i64, Ordering::SeqCst); - } - })?; - } - pcnt.event_enable(PcntEventType::H_LIM)?; - pcnt.event_enable(PcntEventType::L_LIM)?; - println!("starting pcnt counter"); - pcnt.counter_pause()?; - pcnt.counter_clear()?; - pcnt.counter_resume()?; + let pin_a: AnyInputPin = peripherals.pins.gpio5.into(); + let pin_b: AnyInputPin = peripherals.pins.gpio6.into(); + + let encoder = Encoder::new(&pin_a, &pin_b)?; let mut last_value = 0i64; loop { - let pcnt_value = pcnt.get_counter_value()? as i64; - let acc_value = value.load(Ordering::SeqCst); - let value = acc_value + pcnt_value; + let value = encoder.get_value()?; if value != last_value { - println!("value: {value} pct={pcnt_value} acc={acc_value}"); + println!("value: {value}"); last_value = value; } - delay.delay_ms(100u32)?; + FreeRtos::delay_ms(100u32); + } +} + + +// esp-idf encoder implementation using v4 pcnt api +#[cfg(any(feature = "pcnt4", esp_idf_version_major = "4"))] +mod encoder { + use std::cmp::min; + use std::sync::Arc; + use std::sync::atomic::AtomicI64; + use std::sync::atomic::Ordering; + + use esp_idf_hal::gpio::AnyInputPin; + use esp_idf_hal::pcnt::*; + use esp_idf_sys::EspError; + + const LOW_LIMIT: i16 = -100; + const HIGH_LIMIT: i16 = 100; + + pub struct Encoder { + unit: Pcnt, + approx_value: Arc, + } + + impl Encoder { + pub fn new(pin_a: &AnyInputPin, pin_b: &AnyInputPin) -> Result { + let mut unit = Pcnt::new()?; + unit.config(&mut PcntConfig { + pulse_pin: Some(pin_a), + ctrl_pin: Some(pin_b), + 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, + channel: PcntChannel::Channel0, + })?; + unit.config(&mut PcntConfig { + pulse_pin: Some(pin_b), + ctrl_pin: Some(pin_a), + 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, + channel: PcntChannel::Channel1, + })?; + 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_bits_retain(status); + if status.contains(PcntEventType::H_LIM) { + approx_value.fetch_add(HIGH_LIMIT as i64, Ordering::SeqCst); + } + if status.contains(PcntEventType::L_LIM) { + approx_value.fetch_add(LOW_LIMIT as i64, Ordering::SeqCst); + } + })?; + } + unit.event_enable(PcntEventType::H_LIM)?; + unit.event_enable(PcntEventType::L_LIM)?; + 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) + } + } +} + +// esp-idf v5 encoder implementation using pulse_cnt api +#[cfg(not(any(feature = "pcnt4", esp_idf_version_major = "4")))] +mod encoder { + use std::sync::Arc; + use std::sync::atomic::AtomicI64; + use std::sync::atomic::Ordering; + + use esp_idf_hal::gpio::AnyInputPin; + use esp_idf_hal::pulse_cnt::*; + use esp_idf_sys::EspError; + + const LOW_LIMIT: i32 = -100; + const HIGH_LIMIT: i32 = 100; + + pub struct Encoder { + unit: PcntUnit, + _channels: [PcntChannel; 2], // we don't use but don't want to drop + approx_value: Arc, + } + + impl Encoder { + pub fn new(pin_a: &AnyInputPin, pin_b: &AnyInputPin) -> Result { + let mut unit = PcntUnit::new(&PcntUnitConfig { + low_limit: LOW_LIMIT, + high_limit: HIGH_LIMIT, + ..Default::default() + })?; + let channel0 = unit.channel(&PcntChanConfig { + edge_pin: Some(&pin_a), + level_pin: Some(&pin_b), + ..Default::default() + })?; + channel0.set_level_action(PcntLevelAction::Keep, PcntLevelAction::Inverse)?; + channel0.set_edge_action(PcntEdgeAction::Decrease, PcntEdgeAction::Increase)?; + let channel1 = unit.channel(&PcntChanConfig { + edge_pin: Some(&pin_b), + level_pin: Some(&pin_a), + ..Default::default() + })?; + channel1.set_level_action(PcntLevelAction::Keep, PcntLevelAction::Inverse)?; + channel1.set_edge_action(PcntEdgeAction::Increase, PcntEdgeAction::Decrease)?; + + unit.add_watch_point(LOW_LIMIT)?; + unit.add_watch_point(HIGH_LIMIT)?; + + let approx_value = Arc::new(AtomicI64::new(0)); + { + let approx_value = approx_value.clone(); + unit.subscribe(move |event| { + approx_value.fetch_add(event.watch_point_value.into(), Ordering::SeqCst); + false // no high priority task woken + })?; + } + + unit.enable()?; + unit.start()?; + + Ok(Self { + unit, + _channels: [channel0, channel1], + approx_value, + }) + } + + pub fn get_value(&self) -> Result { + Ok(self.approx_value.load(Ordering::SeqCst) + self.unit.get_count()? as i64) + } } } diff --git a/src/pulse_cnt.rs b/src/pulse_cnt.rs index a9b95e5e9c9..df043a57296 100644 --- a/src/pulse_cnt.rs +++ b/src/pulse_cnt.rs @@ -281,6 +281,7 @@ pub struct PcntUnitConfig { #[derive(Debug)] pub struct PcntUnit { unit: pcnt_unit_handle_t, + isr_id: Option, } impl PcntUnit { @@ -304,6 +305,7 @@ impl PcntUnit { } Ok(Self { unit, + isr_id: None, }) } @@ -447,32 +449,52 @@ impl PcntUnit { #[doc = " @return"] #[doc = " - value: on success"] #[doc = " - EspError: on failure"] - pub fn subscribe(&self, callback: F ) -> Result<(), EspError> + pub fn subscribe(&mut self, callback: F ) -> Result<(), EspError> where F: FnMut(PcntWatchEventData)->bool + Send + 'static { - let mut closure = callback; + let id = match self.isr_id { + Some(x) => x, + None => { + let x = allocate_isr_id(callback); + self.isr_id = Some(x); + x + } + }; let cbs = pcnt_event_callbacks_t { - on_reach: Self::get_trampoline(&closure), + on_reach: Some(Self::handle_isr), }; - let data = &mut closure as *mut _ as *mut esp_idf_sys::c_types::c_void; + let data = id as *mut esp_idf_sys::c_types::c_void; unsafe {esp!(pcnt_unit_register_event_callbacks(self.unit, &cbs, data as *mut esp_idf_sys::c_types::c_void))} } - pub fn get_trampoline(_closure: &F) -> esp_idf_sys::pcnt_watch_cb_t - where - F: FnMut(PcntWatchEventData)->bool - { - Some(Self::trampoline::) + unsafe extern "C" fn handle_isr(_unit: pcnt_unit_handle_t, edata: *const pcnt_watch_event_data_t, data: *mut esp_idf_sys::c_types::c_void) -> bool { + let id = data as usize; + match &mut ISR_HANDLERS[id] { + Some(f) => f(edata.into()), + None => panic!("isr handler called with no ISR!"), + } } - - unsafe extern "C" fn trampoline(_unit: pcnt_unit_handle_t, edata: *const pcnt_watch_event_data_t, data: *mut esp_idf_sys::c_types::c_void) -> bool - where - F: FnMut(PcntWatchEventData)->bool - { - let f = &mut *(data as *mut F); - f(edata.into()) + + #[doc = " @brief Unsubscribe to PCNT events"] + #[doc = ""] + #[doc = " @param unit PCNT unit number"] + #[doc = ""] + #[doc = " @return"] + #[doc = " - value: on success"] + #[doc = " - EspError: on failure"] + pub fn unsubscribe(&mut self) -> Result<(), EspError> { + if let Some(id) = self.isr_id.take() { + unsafe { + esp!(pcnt_unit_register_event_callbacks(self.unit, &pcnt_event_callbacks_t { + on_reach: None, + }, 0 as *mut esp_idf_sys::c_types::c_void))?; + } + free_isr_id(id); + } + Ok(()) } + #[doc = " @brief Create PCNT channel for specific unit, each PCNT has several channels associated with it"] #[doc = ""] #[doc = " @note This function should be called when the unit is in init state (i.e. before calling `pcnt_unit_enable()`)"] @@ -508,3 +530,41 @@ impl Drop for PcntUnit { } } } + +static PCNT_CS: crate::task::CriticalSection = crate::task::CriticalSection::new(); + +#[cfg(esp32s3)] +const PCNT_UNIT_MAX: usize = 4; +#[cfg(not(esp32s3))] +const PCNT_UNIT_MAX: usize = 8; + +static mut ISR_HANDLERS: [Optionbool>>; PCNT_UNIT_MAX] = [ + None, None, None, None, + #[cfg(not(esp32s3))] + None, + #[cfg(not(esp32s3))] + None, + #[cfg(not(esp32s3))] + None, + #[cfg(not(esp32s3))] + None, +]; + +fn allocate_isr_id(callback: impl FnMut(PcntWatchEventData)->bool + 'static ) -> usize { + let _ = PCNT_CS.enter(); + for i in 0..PCNT_UNIT_MAX { + unsafe { + if ISR_HANDLERS[i].is_none() { + ISR_HANDLERS[i] = Some(Box::new(callback)); + return i; + } + } + } + panic!("all ISR slots are full!"); +} + +fn free_isr_id(id: usize) { + unsafe { + ISR_HANDLERS[id] = None; + } +} From 17de23e067a84ceebcd8a5bbfed0be43a06d7e60 Mon Sep 17 00:00:00 2001 From: Christopher Liebman Date: Wed, 7 Dec 2022 13:37:38 -0800 Subject: [PATCH 16/48] no need to initialize logging in example --- examples/pcnt_i64_encoder.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/examples/pcnt_i64_encoder.rs b/examples/pcnt_i64_encoder.rs index 0a29344ebf1..53f1f8e9f97 100644 --- a/examples/pcnt_i64_encoder.rs +++ b/examples/pcnt_i64_encoder.rs @@ -20,9 +20,6 @@ fn main() -> anyhow::Result<()> { // or else some patches to the runtime implemented by esp-idf-sys might not link properly. esp_idf_sys::link_patches(); - // Bind the log crate to the ESP Logging facilities - esp_idf_svc::log::EspLogger::initialize_default(); - let peripherals = Peripherals::take().context("failed to take Peripherals")?; let pin_a: AnyInputPin = peripherals.pins.gpio5.into(); let pin_b: AnyInputPin = peripherals.pins.gpio6.into(); From b124a923199b954bc5b4c90ddaa636da29cbfb49 Mon Sep 17 00:00:00 2001 From: Christopher Liebman Date: Fri, 23 Dec 2022 07:06:11 -0800 Subject: [PATCH 17/48] use esp_idf_sys::* --- src/pcnt.rs | 130 +++++++++++++++++++++++++--------------------------- 1 file changed, 63 insertions(+), 67 deletions(-) diff --git a/src/pcnt.rs b/src/pcnt.rs index 18d7eadd068..9a97af5fc59 100644 --- a/src/pcnt.rs +++ b/src/pcnt.rs @@ -1,11 +1,7 @@ use core::fmt::Debug; -use esp_idf_sys::esp; -use esp_idf_sys::pcnt_config_t; -use esp_idf_sys::pcnt_set_mode; -use esp_idf_sys::pcnt_unit_t; -use esp_idf_sys::EspError; +use esp_idf_sys::*; use bitflags::bitflags; @@ -21,11 +17,11 @@ pub enum PcntChannel { Channel1, } -impl Into for PcntChannel { - fn into(self) -> esp_idf_sys::pcnt_channel_t { +impl Into for PcntChannel { + fn into(self) -> pcnt_channel_t { match self { - PcntChannel::Channel0 => esp_idf_sys::pcnt_channel_t_PCNT_CHANNEL_0, - PcntChannel::Channel1 => esp_idf_sys::pcnt_channel_t_PCNT_CHANNEL_1, + PcntChannel::Channel0 => pcnt_channel_t_PCNT_CHANNEL_0, + PcntChannel::Channel1 => pcnt_channel_t_PCNT_CHANNEL_1, } } } @@ -41,12 +37,12 @@ pub enum PcntCountMode { Decrement, } -impl Into for PcntCountMode { - fn into(self) -> esp_idf_sys::pcnt_count_mode_t { +impl Into for PcntCountMode { + fn into(self) -> pcnt_count_mode_t { match self { - PcntCountMode::Hold => esp_idf_sys::pcnt_channel_edge_action_t_PCNT_CHANNEL_EDGE_ACTION_HOLD, - PcntCountMode::Increment => esp_idf_sys::pcnt_channel_edge_action_t_PCNT_CHANNEL_EDGE_ACTION_INCREASE, - PcntCountMode::Decrement => esp_idf_sys::pcnt_channel_edge_action_t_PCNT_CHANNEL_EDGE_ACTION_DECREASE, + 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, } } } @@ -62,12 +58,12 @@ pub enum PcntControlMode { Disable, } -impl Into for PcntControlMode { - fn into(self) -> esp_idf_sys::pcnt_ctrl_mode_t { +impl Into for PcntControlMode { + fn into(self) -> pcnt_ctrl_mode_t { match self { - PcntControlMode::Keep => esp_idf_sys::pcnt_channel_level_action_t_PCNT_CHANNEL_LEVEL_ACTION_KEEP, - PcntControlMode::Reverse => esp_idf_sys::pcnt_channel_level_action_t_PCNT_CHANNEL_LEVEL_ACTION_INVERSE, - PcntControlMode::Disable => esp_idf_sys::pcnt_channel_level_action_t_PCNT_CHANNEL_LEVEL_ACTION_HOLD, + 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, } } } @@ -77,15 +73,15 @@ bitflags! { #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct PcntEventType: u32 { #[doc = "< PCNT watch point event: threshold1 value event"] - const THRES_1 = esp_idf_sys::pcnt_evt_type_t_PCNT_EVT_THRES_1; + const THRES_1 = pcnt_evt_type_t_PCNT_EVT_THRES_1; #[doc = "< PCNT watch point event: threshold0 value event"] - const THRES_0 = esp_idf_sys::pcnt_evt_type_t_PCNT_EVT_THRES_0; + const THRES_0 = pcnt_evt_type_t_PCNT_EVT_THRES_0; #[doc = "< PCNT watch point event: Minimum counter value"] - const L_LIM = esp_idf_sys::pcnt_evt_type_t_PCNT_EVT_L_LIM; + const L_LIM = pcnt_evt_type_t_PCNT_EVT_L_LIM; #[doc = "< PCNT watch point event: Maximum counter value"] - const H_LIM = esp_idf_sys::pcnt_evt_type_t_PCNT_EVT_H_LIM; + const H_LIM = pcnt_evt_type_t_PCNT_EVT_H_LIM; #[doc = "< PCNT watch point event: counter value zero event"] - const ZERO = esp_idf_sys::pcnt_evt_type_t_PCNT_EVT_ZERO; + const ZERO = pcnt_evt_type_t_PCNT_EVT_ZERO; } } @@ -136,11 +132,11 @@ impl<'d> Pcnt { let config = pcnt_config_t { pulse_gpio_num: match pconfig.pulse_pin { Some(pin) => pin.pin(), - None => esp_idf_sys::PCNT_PIN_NOT_USED, + None => PCNT_PIN_NOT_USED, }, ctrl_gpio_num: match pconfig.ctrl_pin { Some(pin) => pin.pin(), - None => esp_idf_sys::PCNT_PIN_NOT_USED, + None => PCNT_PIN_NOT_USED, }, lctrl_mode: pconfig.lctrl_mode.into(), hctrl_mode: pconfig.hctrl_mode.into(), @@ -153,7 +149,7 @@ impl<'d> Pcnt { }; unsafe { - esp!(esp_idf_sys::pcnt_unit_config( + esp!(pcnt_unit_config( &config as *const pcnt_config_t )) } @@ -167,7 +163,7 @@ impl<'d> Pcnt { pub fn get_counter_value(&self) -> Result { let mut value = 0i16; unsafe { - esp!(esp_idf_sys::pcnt_get_counter_value( + esp!(pcnt_get_counter_value( self.unit, &mut value as *mut i16 ))?; @@ -181,7 +177,7 @@ impl<'d> Pcnt { #[doc = " - ()"] #[doc = " - EspError"] pub fn counter_pause(&self) -> Result<(), EspError> { - unsafe { esp!(esp_idf_sys::pcnt_counter_pause(self.unit)) } + unsafe { esp!(pcnt_counter_pause(self.unit)) } } #[doc = " @brief Resume counting for PCNT counter"] @@ -190,7 +186,7 @@ impl<'d> Pcnt { #[doc = " - ()"] #[doc = " - EspError"] pub fn counter_resume(&self) -> Result<(), EspError> { - unsafe { esp!(esp_idf_sys::pcnt_counter_resume(self.unit)) } + unsafe { esp!(pcnt_counter_resume(self.unit)) } } #[doc = " @brief Clear and reset PCNT counter value to zero"] @@ -199,7 +195,7 @@ impl<'d> Pcnt { #[doc = " - ()"] #[doc = " - EspError"] pub fn counter_clear(&self) -> Result<(), EspError> { - unsafe { esp!(esp_idf_sys::pcnt_counter_clear(self.unit)) } + unsafe { esp!(pcnt_counter_clear(self.unit)) } } #[doc = " @brief Enable PCNT interrupt for PCNT unit"] @@ -211,7 +207,7 @@ impl<'d> Pcnt { #[doc = " - ()"] #[doc = " - EspError"] pub fn intr_enable(&self) -> Result<(), EspError> { - unsafe { esp!(esp_idf_sys::pcnt_intr_enable(self.unit)) } + unsafe { esp!(pcnt_intr_enable(self.unit)) } } #[doc = " @brief Disable PCNT interrupt for PCNT unit"] @@ -220,7 +216,7 @@ impl<'d> Pcnt { #[doc = " - ()"] #[doc = " - EspError"] pub fn intr_disable(&self) -> Result<(), EspError> { - unsafe { esp!(esp_idf_sys::pcnt_intr_disable(self.unit)) } + unsafe { esp!(pcnt_intr_disable(self.unit)) } } #[doc = " @brief Enable PCNT event of PCNT unit"] @@ -231,8 +227,8 @@ impl<'d> Pcnt { #[doc = " - ()"] #[doc = " - EspError"] pub fn event_enable(&self, evt_type: PcntEventType) -> Result<(), EspError> { - let evt_type: esp_idf_sys::pcnt_evt_type_t = evt_type.bits(); - unsafe { esp!(esp_idf_sys::pcnt_event_enable(self.unit, evt_type)) } + let evt_type: pcnt_evt_type_t = evt_type.bits(); + unsafe { esp!(pcnt_event_enable(self.unit, evt_type)) } } #[doc = " @brief Disable PCNT event of PCNT unit"] @@ -243,14 +239,14 @@ impl<'d> Pcnt { #[doc = " - ()"] #[doc = " - EspError"] pub fn event_disable(&self, evt_type: PcntEventType) -> Result<(), EspError> { - let evt_type: esp_idf_sys::pcnt_evt_type_t = evt_type.bits(); - unsafe { esp!(esp_idf_sys::pcnt_event_disable(self.unit, evt_type)) } + let evt_type: pcnt_evt_type_t = evt_type.bits(); + unsafe { esp!(pcnt_event_disable(self.unit, evt_type)) } } - fn only_one_event_type(evt_type: PcntEventType) -> Result { + fn only_one_event_type(evt_type: PcntEventType) -> Result { match evt_type.iter().count() { 1 => Ok(evt_type.bits()), - _ =>Err(EspError::from(esp_idf_sys::ESP_ERR_INVALID_ARG as esp_idf_sys::esp_err_t).unwrap()), + _ =>Err(EspError::from(ESP_ERR_INVALID_ARG as esp_err_t).unwrap()), } } @@ -265,7 +261,7 @@ impl<'d> Pcnt { 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!(esp_idf_sys::pcnt_set_event_value( + esp!(pcnt_set_event_value( self.unit, evt_type, value )) } @@ -283,7 +279,7 @@ impl<'d> Pcnt { let evt_type = Self::only_one_event_type(evt_type)?; let mut value = 0i16; unsafe { - esp!(esp_idf_sys::pcnt_get_event_value( + esp!(pcnt_get_event_value( self.unit, evt_type, &mut value as *mut i16 @@ -301,7 +297,7 @@ impl<'d> Pcnt { pub fn get_event_status(&self) -> Result { let mut value = 0u32; unsafe { - esp!(esp_idf_sys::pcnt_get_event_status( + esp!(pcnt_get_event_status( self.unit, &mut value as *mut u32 ))?; @@ -328,14 +324,14 @@ impl<'d> Pcnt { ) -> Result<(), EspError> { let pulse_io_num = match pulse_pin { Some(pin) => pin.pin(), - None => esp_idf_sys::PCNT_PIN_NOT_USED, + None => PCNT_PIN_NOT_USED, }; let ctrl_io_num = match ctrl_pin { Some(pin) => pin.pin(), - None => esp_idf_sys::PCNT_PIN_NOT_USED, + None => PCNT_PIN_NOT_USED, }; unsafe { - esp!(esp_idf_sys::pcnt_set_pin( + esp!(pcnt_set_pin( self.unit, channel.into(), pulse_io_num, @@ -350,7 +346,7 @@ impl<'d> Pcnt { #[doc = " - ()"] #[doc = " - EspError"] pub fn filter_enable(&self) -> Result<(), EspError> { - unsafe { esp!(esp_idf_sys::pcnt_filter_enable(self.unit)) } + unsafe { esp!(pcnt_filter_enable(self.unit)) } } #[doc = " @brief Disable PCNT input filter"] @@ -359,7 +355,7 @@ impl<'d> Pcnt { #[doc = " - ()"] #[doc = " - EspError"] pub fn filter_disable(&self) -> Result<(), EspError> { - unsafe { esp!(esp_idf_sys::pcnt_filter_disable(self.unit)) } + unsafe { esp!(pcnt_filter_disable(self.unit)) } } #[doc = " @brief Set PCNT filter value"] @@ -373,7 +369,7 @@ impl<'d> Pcnt { #[doc = " - ()"] #[doc = " - EspError"] pub fn set_filter_value(&self, value: u16) -> Result<(), EspError> { - unsafe { esp!(esp_idf_sys::pcnt_set_filter_value(self.unit, value)) } + unsafe { esp!(pcnt_set_filter_value(self.unit, value)) } } #[doc = " @brief Get PCNT filter value"] @@ -384,7 +380,7 @@ impl<'d> Pcnt { pub fn get_filter_value(&self) -> Result { let mut value = 0u16; unsafe { - esp!(esp_idf_sys::pcnt_get_filter_value( + esp!(pcnt_get_filter_value( self.unit, &mut value as *mut u16 ))?; @@ -432,10 +428,10 @@ impl<'d> Pcnt { //self.unsubscribe(); let callback: Box = Box::new(callback); ISR_HANDLERS[self.unit as usize] = Some(callback); - esp!(esp_idf_sys::pcnt_isr_handler_add( + esp!(pcnt_isr_handler_add( self.unit, Some(Self::handle_isr), - self.unit as *mut esp_idf_sys::c_types::c_void, + self.unit as *mut c_types::c_void, ))?; Ok(()) } @@ -447,17 +443,17 @@ impl<'d> Pcnt { #[doc = " - EspError"] pub fn unsubscribe(&self) -> Result<(), EspError> { unsafe { - esp!(esp_idf_sys::pcnt_isr_handler_remove(self.unit))?; + esp!(pcnt_isr_handler_remove(self.unit))?; ISR_HANDLERS[self.unit as usize] = None; } Ok(()) } - unsafe extern "C" fn handle_isr(data: *mut esp_idf_sys::c_types::c_void) { + unsafe extern "C" fn handle_isr(data: *mut c_types::c_void) { let unit = data as pcnt_unit_t; if let Some(f) = &mut ISR_HANDLERS[unit as usize] { let mut value = 0u32; - esp!(esp_idf_sys::pcnt_get_event_status( + esp!(pcnt_get_event_status( unit, &mut value as *mut u32 )).expect("failed to fetch event status!"); @@ -489,7 +485,7 @@ fn enable_isr_service() -> Result<(), EspError> { let _ = PCNT_CS.enter(); if !ISR_SERVICE_ENABLED.load(Ordering::SeqCst) { - esp!(unsafe { esp_idf_sys::pcnt_isr_service_install(0) })?; + esp!(unsafe { pcnt_isr_service_install(0) })?; ISR_SERVICE_ENABLED.store(true, Ordering::SeqCst); } @@ -498,7 +494,7 @@ fn enable_isr_service() -> Result<(), EspError> { Ok(()) } -static mut ISR_HANDLERS: [Option>; esp_idf_sys::pcnt_unit_t_PCNT_UNIT_MAX as usize] = [ +static mut ISR_HANDLERS: [Option>; pcnt_unit_t_PCNT_UNIT_MAX as usize] = [ None, None, None, None, #[cfg(not(esp32s3))] None, @@ -510,29 +506,29 @@ static mut ISR_HANDLERS: [Option>; esp_idf_sys::pcnt_unit_t_ None, ]; -static mut PCNT_UNITS: [Option; esp_idf_sys::pcnt_unit_t_PCNT_UNIT_MAX as usize] = [ - Some(esp_idf_sys::pcnt_unit_t_PCNT_UNIT_0), - Some(esp_idf_sys::pcnt_unit_t_PCNT_UNIT_1), - Some(esp_idf_sys::pcnt_unit_t_PCNT_UNIT_2), - Some(esp_idf_sys::pcnt_unit_t_PCNT_UNIT_3), +static mut PCNT_UNITS: [Option; pcnt_unit_t_PCNT_UNIT_MAX as usize] = [ + Some(pcnt_unit_t_PCNT_UNIT_0), + Some(pcnt_unit_t_PCNT_UNIT_1), + Some(pcnt_unit_t_PCNT_UNIT_2), + Some(pcnt_unit_t_PCNT_UNIT_3), #[cfg(not(esp32s3))] - Some(esp_idf_sys::pcnt_unit_t_PCNT_UNIT_4), + Some(pcnt_unit_t_PCNT_UNIT_4), #[cfg(not(esp32s3))] - Some(esp_idf_sys::pcnt_unit_t_PCNT_UNIT_5), + Some(pcnt_unit_t_PCNT_UNIT_5), #[cfg(not(esp32s3))] - Some(esp_idf_sys::pcnt_unit_t_PCNT_UNIT_6), + Some(pcnt_unit_t_PCNT_UNIT_6), #[cfg(not(esp32s3))] - Some(esp_idf_sys::pcnt_unit_t_PCNT_UNIT_7), + Some(pcnt_unit_t_PCNT_UNIT_7), ]; fn unit_allocate() -> Result { let _ = PCNT_CS.enter(); - for i in 0..esp_idf_sys::pcnt_unit_t_PCNT_UNIT_MAX { + for i in 0..pcnt_unit_t_PCNT_UNIT_MAX { if let Some(unit) = unsafe { PCNT_UNITS[i as usize].take() } { return Ok(unit); } } - Err(EspError::from(esp_idf_sys::ESP_ERR_NO_MEM as esp_idf_sys::esp_err_t).unwrap()) + Err(EspError::from(ESP_ERR_NO_MEM as esp_err_t).unwrap()) } fn unit_deallocate(unit: UnitHandle) { From 22faf317f3e2ceafe278650626aeadaf34e1d154 Mon Sep 17 00:00:00 2001 From: Christopher Liebman Date: Fri, 23 Dec 2022 08:03:50 -0800 Subject: [PATCH 18/48] Implement From not Into --- src/pcnt.rs | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/pcnt.rs b/src/pcnt.rs index 9a97af5fc59..b758e129fd3 100644 --- a/src/pcnt.rs +++ b/src/pcnt.rs @@ -17,9 +17,9 @@ pub enum PcntChannel { Channel1, } -impl Into for PcntChannel { - fn into(self) -> pcnt_channel_t { - match self { +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, } @@ -37,9 +37,9 @@ pub enum PcntCountMode { Decrement, } -impl Into for PcntCountMode { - fn into(self) -> pcnt_count_mode_t { - match self { +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, @@ -58,9 +58,9 @@ pub enum PcntControlMode { Disable, } -impl Into for PcntControlMode { - fn into(self) -> pcnt_ctrl_mode_t { - match self { +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, @@ -431,7 +431,7 @@ impl<'d> Pcnt { esp!(pcnt_isr_handler_add( self.unit, Some(Self::handle_isr), - self.unit as *mut c_types::c_void, + self.unit as *mut core::ffi::c_void, ))?; Ok(()) } @@ -449,7 +449,7 @@ impl<'d> Pcnt { Ok(()) } - unsafe extern "C" fn handle_isr(data: *mut c_types::c_void) { + 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; From a021f5d87af3a6d88b453c289065322e29402696 Mon Sep 17 00:00:00 2001 From: Christopher Liebman Date: Fri, 23 Dec 2022 10:53:31 -0800 Subject: [PATCH 19/48] switch from bitflags to enumset --- Cargo.toml | 1 - examples/pcnt_i64_encoder.rs | 24 +++++++++++++-------- src/pcnt.rs | 42 ++++++++++++++++++------------------ 3 files changed, 36 insertions(+), 31 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f05f1cb7fd9..b340c1537af 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,7 +35,6 @@ heapless = "0.7" embassy-sync = { version = "0.1", optional = true } edge-executor = { version = "0.3", optional = true, default-features = false } enumset = { version = "1", default-features = false } -bitflags = "2.0.0-rc.1" [build-dependencies] embuild = "0.31" diff --git a/examples/pcnt_i64_encoder.rs b/examples/pcnt_i64_encoder.rs index 53f1f8e9f97..6b7fa2dd3fa 100644 --- a/examples/pcnt_i64_encoder.rs +++ b/examples/pcnt_i64_encoder.rs @@ -8,6 +8,7 @@ use anyhow; use anyhow::Context; +use log::*; use esp_idf_hal::delay::FreeRtos; use esp_idf_hal::gpio::AnyInputPin; @@ -20,17 +21,22 @@ fn main() -> anyhow::Result<()> { // or else some patches to the runtime implemented by esp-idf-sys might not link properly. esp_idf_sys::link_patches(); + // Bind the log crate to the ESP Logging facilities + esp_idf_svc::log::EspLogger::initialize_default(); + + info!("setup pins"); let peripherals = Peripherals::take().context("failed to take Peripherals")?; let pin_a: AnyInputPin = peripherals.pins.gpio5.into(); let pin_b: AnyInputPin = peripherals.pins.gpio6.into(); + info!("setup encoder"); let encoder = Encoder::new(&pin_a, &pin_b)?; let mut last_value = 0i64; loop { let value = encoder.get_value()?; if value != last_value { - println!("value: {value}"); + info!("value: {value}"); last_value = value; } FreeRtos::delay_ms(100u32); @@ -39,7 +45,7 @@ fn main() -> anyhow::Result<()> { // esp-idf encoder implementation using v4 pcnt api -#[cfg(any(feature = "pcnt4", esp_idf_version_major = "4"))] +#[cfg(any(feature = "pcnt", esp_idf_version_major = "4"))] mod encoder { use std::cmp::min; use std::sync::Arc; @@ -93,17 +99,17 @@ mod encoder { unsafe { let approx_value = approx_value.clone(); unit.subscribe(move |status| { - let status = PcntEventType::from_bits_retain(status); - if status.contains(PcntEventType::H_LIM) { + 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(PcntEventType::L_LIM) { + if status.contains(PcntEvent::LowLimit) { approx_value.fetch_add(LOW_LIMIT as i64, Ordering::SeqCst); } })?; } - unit.event_enable(PcntEventType::H_LIM)?; - unit.event_enable(PcntEventType::L_LIM)?; + unit.event_enable(PcntEvent::HighLimit)?; + unit.event_enable(PcntEvent::LowLimit)?; unit.counter_pause()?; unit.counter_clear()?; unit.counter_resume()?; @@ -122,7 +128,7 @@ mod encoder { } // esp-idf v5 encoder implementation using pulse_cnt api -#[cfg(not(any(feature = "pcnt4", esp_idf_version_major = "4")))] +#[cfg(not(any(feature = "pcnt", esp_idf_version_major = "4")))] mod encoder { use std::sync::Arc; use std::sync::atomic::AtomicI64; @@ -189,4 +195,4 @@ mod encoder { Ok(self.approx_value.load(Ordering::SeqCst) + self.unit.get_count()? as i64) } } -} +} \ No newline at end of file diff --git a/src/pcnt.rs b/src/pcnt.rs index b758e129fd3..6cb79e94a68 100644 --- a/src/pcnt.rs +++ b/src/pcnt.rs @@ -3,7 +3,7 @@ use core::fmt::Debug; use esp_idf_sys::*; -use bitflags::bitflags; +use enumset::EnumSetType; use crate::gpio::AnyInputPin; use crate::gpio::Pin; @@ -68,23 +68,23 @@ impl From for pcnt_ctrl_mode_t { } } -bitflags! { - #[allow(dead_code)] - #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] - pub struct PcntEventType: u32 { - #[doc = "< PCNT watch point event: threshold1 value event"] - const THRES_1 = pcnt_evt_type_t_PCNT_EVT_THRES_1; - #[doc = "< PCNT watch point event: threshold0 value event"] - const THRES_0 = pcnt_evt_type_t_PCNT_EVT_THRES_0; - #[doc = "< PCNT watch point event: Minimum counter value"] - const L_LIM = pcnt_evt_type_t_PCNT_EVT_L_LIM; - #[doc = "< PCNT watch point event: Maximum counter value"] - const H_LIM = pcnt_evt_type_t_PCNT_EVT_H_LIM; - #[doc = "< PCNT watch point event: counter value zero event"] - const ZERO = pcnt_evt_type_t_PCNT_EVT_ZERO; - } +#[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; + #[doc = " @brief Pulse Counter configuration for a single channel"] pub struct PcntConfig<'d> { #[doc = "< Pulse input GPIO number, if you want to use GPIO16, enter pulse_gpio_num = 16, a negative value will be ignored"] @@ -226,8 +226,8 @@ impl<'d> Pcnt { #[doc = " @return"] #[doc = " - ()"] #[doc = " - EspError"] - pub fn event_enable(&self, evt_type: PcntEventType) -> Result<(), EspError> { - let evt_type: pcnt_evt_type_t = evt_type.bits(); + 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)) } } @@ -238,14 +238,14 @@ impl<'d> Pcnt { #[doc = " @return"] #[doc = " - ()"] #[doc = " - EspError"] - pub fn event_disable(&self, evt_type: PcntEventType) -> Result<(), EspError> { - let evt_type: pcnt_evt_type_t = evt_type.bits(); + 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.bits()), + 1 => Ok(evt_type.as_repr()), _ =>Err(EspError::from(ESP_ERR_INVALID_ARG as esp_err_t).unwrap()), } } From 62cca6b8871351842e6685764855568891d7e8f9 Mon Sep 17 00:00:00 2001 From: Christopher Liebman Date: Fri, 23 Dec 2022 10:55:40 -0800 Subject: [PATCH 20/48] typo --- examples/pcnt_i64_encoder.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/pcnt_i64_encoder.rs b/examples/pcnt_i64_encoder.rs index 6b7fa2dd3fa..1eea5fbf894 100644 --- a/examples/pcnt_i64_encoder.rs +++ b/examples/pcnt_i64_encoder.rs @@ -1,6 +1,6 @@ -//! PCNT decoding a rotery encoder +//! PCNT decoding a rotary encoder //! -//! To try this out, connect a rotery encoder to pins 5 and 6, the common should be grounded +//! 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 From d4ddf2f6a04d8aedc8902b00b1dc46981fe8969f Mon Sep 17 00:00:00 2001 From: Christopher Liebman Date: Fri, 23 Dec 2022 11:41:28 -0800 Subject: [PATCH 21/48] switch #[doc] to /// style --- src/pcnt.rs | 318 ++++++++++++++++++++++++++-------------------------- 1 file changed, 159 insertions(+), 159 deletions(-) diff --git a/src/pcnt.rs b/src/pcnt.rs index 6cb79e94a68..bab18dafd9e 100644 --- a/src/pcnt.rs +++ b/src/pcnt.rs @@ -26,14 +26,14 @@ impl From for pcnt_channel_t { } } -#[doc = " @brief PCNT channel action on signal edge"] +/// PCNT channel action on signal edge #[derive(Debug, Copy, Clone)] pub enum PcntCountMode { - #[doc = "< Hold current count value"] + /// Hold current count value Hold, - #[doc = "< Increase count value"] + /// Increase count value Increment, - #[doc = "< Decrease count value"] + /// Decrease count value Decrement, } @@ -47,14 +47,14 @@ impl From for pcnt_count_mode_t { } } -#[doc = " @brief PCNT channel action on control level"] +/// PCNT channel action on control level #[derive(Debug, Copy, Clone)] pub enum PcntControlMode { - #[doc = "< Keep current count mode"] + /// Keep current count mode Keep, - #[doc = "< Invert current count mode (increase -> decrease, decrease -> increase)"] + /// Invert current count mode (increase -> decrease, decrease -> increase) Reverse, - #[doc = "< Hold current count value"] + /// Hold current count value Disable, } @@ -85,25 +85,25 @@ pub enum PcntEvent { pub type PcntEventType = enumset::EnumSet; -#[doc = " @brief Pulse Counter configuration for a single channel"] +/// Pulse Counter configuration for a single channel pub struct PcntConfig<'d> { - #[doc = "< Pulse input GPIO number, if you want to use GPIO16, enter pulse_gpio_num = 16, a negative value will be ignored"] + /// Pulse input GPIO number, if you want to use GPIO16, enter pulse_gpio_num = 16, a negative value will be ignored pub pulse_pin: Option<&'d AnyInputPin>, - #[doc = "< Control signal input GPIO number, a negative value will be ignored"] + /// Control signal input GPIO number, a negative value will be ignored pub ctrl_pin: Option<&'d AnyInputPin>, - #[doc = "< PCNT low control mode"] + /// PCNT low control mode pub lctrl_mode: PcntControlMode, - #[doc = "< PCNT high control mode"] + /// PCNT high control mode pub hctrl_mode: PcntControlMode, - #[doc = "< PCNT positive edge count mode"] + /// PCNT positive edge count mode pub pos_mode: PcntCountMode, - #[doc = "< PCNT negative edge count mode"] + /// PCNT negative edge count mode pub neg_mode: PcntCountMode, - #[doc = "< Maximum counter value"] + /// Maximum counter value pub counter_h_lim: i16, - #[doc = "< Minimum counter value"] + /// Minimum counter value pub counter_l_lim: i16, - #[doc = "< the PCNT channel"] + /// the PCNT channel pub channel: PcntChannel, } @@ -119,15 +119,15 @@ impl<'d> Pcnt { }) } - #[doc = " @brief Configure Pulse Counter unit"] - #[doc = " @note"] - #[doc = " This function will disable three events: PCNT_EVT_L_LIM, PCNT_EVT_H_LIM, PCNT_EVT_ZERO."] - #[doc = ""] - #[doc = " @param pconfig Reference of PcntConfig"] - #[doc = ""] - #[doc = " @return"] - #[doc = " - ()"] - #[doc = " - EspError"] + /// Configure Pulse Counter unit + /// @note + /// This function will disable three events: PCNT_EVT_L_LIM, PCNT_EVT_H_LIM, PCNT_EVT_ZERO. + /// + /// @param pconfig Reference of PcntConfig + /// + /// returns + /// - () + /// - EspError pub fn config(&mut self, pconfig: & PcntConfig) -> Result<(), EspError> { let config = pcnt_config_t { pulse_gpio_num: match pconfig.pulse_pin { @@ -155,11 +155,11 @@ impl<'d> Pcnt { } } - #[doc = " @brief Get pulse counter value"] - #[doc = ""] - #[doc = " @return"] - #[doc = " - i16"] - #[doc = " - EspError"] + /// Get pulse counter value + /// + /// returns + /// - i16 + /// - EspError pub fn get_counter_value(&self) -> Result { let mut value = 0i16; unsafe { @@ -171,73 +171,73 @@ impl<'d> Pcnt { Ok(value) } - #[doc = " @brief Pause PCNT counter of PCNT unit"] - #[doc = ""] - #[doc = " @return"] - #[doc = " - ()"] - #[doc = " - EspError"] + /// Pause PCNT counter of PCNT unit + /// + /// returns + /// - () + /// - EspError pub fn counter_pause(&self) -> Result<(), EspError> { unsafe { esp!(pcnt_counter_pause(self.unit)) } } - #[doc = " @brief Resume counting for PCNT counter"] - #[doc = ""] - #[doc = " @return"] - #[doc = " - ()"] - #[doc = " - EspError"] + /// Resume counting for PCNT counter + /// + /// returns + /// - () + /// - EspError pub fn counter_resume(&self) -> Result<(), EspError> { unsafe { esp!(pcnt_counter_resume(self.unit)) } } - #[doc = " @brief Clear and reset PCNT counter value to zero"] - #[doc = ""] - #[doc = " @return"] - #[doc = " - ()"] - #[doc = " - EspError"] + /// Clear and reset PCNT counter value to zero + /// + /// returns + /// - () + /// - EspError pub fn counter_clear(&self) -> Result<(), EspError> { unsafe { esp!(pcnt_counter_clear(self.unit)) } } - #[doc = " @brief Enable PCNT interrupt for PCNT unit"] - #[doc = " @note"] - #[doc = " Each Pulse counter unit has five watch point events that share the same interrupt."] - #[doc = " Configure events with pcnt_event_enable() and pcnt_event_disable()"] - #[doc = ""] - #[doc = " @return"] - #[doc = " - ()"] - #[doc = " - EspError"] + /// 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)) } } - #[doc = " @brief Disable PCNT interrupt for PCNT unit"] - #[doc = ""] - #[doc = " @return"] - #[doc = " - ()"] - #[doc = " - EspError"] + /// Disable PCNT interrupt for PCNT unit + /// + /// returns + /// - () + /// - EspError pub fn intr_disable(&self) -> Result<(), EspError> { unsafe { esp!(pcnt_intr_disable(self.unit)) } } - #[doc = " @brief Enable PCNT event of PCNT unit"] - #[doc = ""] - #[doc = " @param evt_type Watch point event type."] - #[doc = " All enabled events share the same interrupt (one interrupt per pulse counter unit)."] - #[doc = " @return"] - #[doc = " - ()"] - #[doc = " - EspError"] + /// 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)) } } - #[doc = " @brief Disable PCNT event of PCNT unit"] - #[doc = ""] - #[doc = " @param evt_type Watch point event type."] - #[doc = " All enabled events share the same interrupt (one interrupt per pulse counter unit)."] - #[doc = " @return"] - #[doc = " - ()"] - #[doc = " - EspError"] + /// 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)) } @@ -250,14 +250,14 @@ impl<'d> Pcnt { } } - #[doc = " @brief Set PCNT event value of PCNT unit"] - #[doc = ""] - #[doc = " @param evt_type Watch point event type."] - #[doc = " All enabled events share the same interrupt (one interrupt per pulse counter unit)."] - #[doc = ""] - #[doc = " @return"] - #[doc = " - ()"] - #[doc = " - EspError"] + /// 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 { @@ -267,14 +267,14 @@ impl<'d> Pcnt { } } - #[doc = " @brief Get PCNT event value of PCNT unit"] - #[doc = ""] - #[doc = " @param evt_type Watch point event type."] - #[doc = " All enabled events share the same interrupt (one interrupt per pulse counter unit)."] - #[doc = ""] - #[doc = " @return"] - #[doc = " - i16"] - #[doc = " - EspError"] + /// 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; @@ -288,11 +288,11 @@ impl<'d> Pcnt { Ok(value) } - #[doc = " @brief Get PCNT event status of PCNT unit"] - #[doc = ""] - #[doc = " @return"] - #[doc = " - i32"] - #[doc = " - EspError"] + /// 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; @@ -305,17 +305,17 @@ impl<'d> Pcnt { Ok(value) } - #[doc = " @brief Configure PCNT pulse signal input pin and control input pin"] - #[doc = ""] - #[doc = " @param channel PcntChannel"] - #[doc = " @param pulse_io Pulse signal input pin"] - #[doc = " @param ctrl_io Control signal input pin"] - #[doc = ""] - #[doc = " @note Set the signal input to PCNT_PIN_NOT_USED if unused."] - #[doc = ""] - #[doc = " @return"] - #[doc = " - ()"] - #[doc = " - EspError"] + /// 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( &mut self, channel: PcntChannel, @@ -340,43 +340,43 @@ impl<'d> Pcnt { } } - #[doc = " @brief Enable PCNT input filter"] - #[doc = ""] - #[doc = " @return"] - #[doc = " - ()"] - #[doc = " - EspError"] + /// Enable PCNT input filter + /// + /// returns + /// - () + /// - EspError pub fn filter_enable(&self) -> Result<(), EspError> { unsafe { esp!(pcnt_filter_enable(self.unit)) } } - #[doc = " @brief Disable PCNT input filter"] - #[doc = ""] - #[doc = " @return"] - #[doc = " - ()"] - #[doc = " - EspError"] + /// Disable PCNT input filter + /// + /// returns + /// - () + /// - EspError pub fn filter_disable(&self) -> Result<(), EspError> { unsafe { esp!(pcnt_filter_disable(self.unit)) } } - #[doc = " @brief Set PCNT filter value"] - #[doc = ""] - #[doc = " @param filter_val PCNT signal filter value, counter in APB_CLK cycles."] - #[doc = " Any pulses lasting shorter than this will be ignored when the filter is enabled."] - #[doc = " @note"] - #[doc = " filter_val is a 10-bit value, so the maximum filter_val should be limited to 1023."] - #[doc = ""] - #[doc = " @return"] - #[doc = " - ()"] - #[doc = " - EspError"] + /// 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)) } } - #[doc = " @brief Get PCNT filter value"] - #[doc = ""] - #[doc = " @return"] - #[doc = " - i16"] - #[doc = " - EspError"] + /// Get PCNT filter value + /// + /// returns + /// - i16 + /// - EspError pub fn get_filter_value(&self) -> Result { let mut value = 0u16; unsafe { @@ -388,17 +388,17 @@ impl<'d> Pcnt { Ok(value) } - #[doc = " @brief Set PCNT counter mode"] - #[doc = ""] - #[doc = " @param channel PCNT channel number"] - #[doc = " @param pos_mode Counter mode when detecting positive edge"] - #[doc = " @param neg_mode Counter mode when detecting negative edge"] - #[doc = " @param hctrl_mode Counter mode when control signal is high level"] - #[doc = " @param lctrl_mode Counter mode when control signal is low level"] - #[doc = ""] - #[doc = " @return"] - #[doc = " - ()"] - #[doc = " - EspError"] + /// 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, @@ -410,18 +410,18 @@ impl<'d> Pcnt { } } - #[doc = " @brief Add ISR handler for specified unit."] - #[doc = ""] - #[doc = " This ISR handler will be called from an ISR. So there is a stack"] - #[doc = " size limit (configurable as \"ISR stack size\" in menuconfig). This"] - #[doc = " limit is smaller compared to a global PCNT interrupt handler due"] - #[doc = " to the additional level of indirection."] - #[doc = ""] - #[doc = " @param callback Interrupt handler function."] - #[doc = ""] - #[doc = " @return"] - #[doc = " - ()"] - #[doc = " - EspError"] + /// 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. + /// + /// @param callback Interrupt handler function. + /// + /// returns + /// - () + /// - EspError pub unsafe fn subscribe(&self, callback: impl FnMut(u32) + Send + 'static) -> Result<(), EspError> { enable_isr_service()?; @@ -436,11 +436,11 @@ impl<'d> Pcnt { Ok(()) } - #[doc = " @brief Remove ISR handler for specified unit."] - #[doc = ""] - #[doc = " @return"] - #[doc = " - ()"] - #[doc = " - EspError"] + /// Remove ISR handler for specified unit. + /// + /// returns + /// - () + /// - EspError pub fn unsubscribe(&self) -> Result<(), EspError> { unsafe { esp!(pcnt_isr_handler_remove(self.unit))?; From 92d2304422f5eb4fe7bd4fa9c12c5f9caaf07fa7 Mon Sep 17 00:00:00 2001 From: Christopher Liebman Date: Fri, 23 Dec 2022 12:01:31 -0800 Subject: [PATCH 22/48] gate uses of Box with alloc feature and use alloc::boxed::Box --- src/pcnt.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/pcnt.rs b/src/pcnt.rs index bab18dafd9e..6731da29e37 100644 --- a/src/pcnt.rs +++ b/src/pcnt.rs @@ -1,5 +1,10 @@ use core::fmt::Debug; +#[cfg(feature = "alloc")] +extern crate alloc; + +#[cfg(feature = "alloc")] +use alloc::boxed::Box; use esp_idf_sys::*; @@ -417,11 +422,17 @@ impl<'d> Pcnt { /// 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: impl FnMut(u32) + Send + 'static) -> Result<(), EspError> { enable_isr_service()?; @@ -441,6 +452,7 @@ impl<'d> Pcnt { /// returns /// - () /// - EspError + #[cfg(feature = "alloc")] pub fn unsubscribe(&self) -> Result<(), EspError> { unsafe { esp!(pcnt_isr_handler_remove(self.unit))?; @@ -449,6 +461,7 @@ impl<'d> Pcnt { 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] { @@ -478,6 +491,7 @@ static ISR_SERVICE_ENABLED: core::sync::atomic::AtomicBool = #[cfg(all(not(feature = "riscv-ulp-hal"), 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; @@ -494,6 +508,7 @@ fn enable_isr_service() -> Result<(), EspError> { Ok(()) } +#[cfg(feature = "alloc")] static mut ISR_HANDLERS: [Option>; pcnt_unit_t_PCNT_UNIT_MAX as usize] = [ None, None, None, None, #[cfg(not(esp32s3))] From 94867cc3f75496c666063cc693b1e0f33e1ff8c9 Mon Sep 17 00:00:00 2001 From: Christopher Liebman Date: Fri, 23 Dec 2022 13:39:06 -0800 Subject: [PATCH 23/48] rename to PcntDriver and make Pcnt Peripherals --- examples/pcnt_i64_encoder.rs | 14 ++++--- src/pcnt.rs | 71 ++++++++++++++++++------------------ src/peripherals.rs | 34 +++++++++++++++++ 3 files changed, 77 insertions(+), 42 deletions(-) diff --git a/examples/pcnt_i64_encoder.rs b/examples/pcnt_i64_encoder.rs index 1eea5fbf894..83eae8f9789 100644 --- a/examples/pcnt_i64_encoder.rs +++ b/examples/pcnt_i64_encoder.rs @@ -30,7 +30,7 @@ fn main() -> anyhow::Result<()> { let pin_b: AnyInputPin = peripherals.pins.gpio6.into(); info!("setup encoder"); - let encoder = Encoder::new(&pin_a, &pin_b)?; + let encoder = Encoder::new(peripherals.pcnt0, &pin_a, &pin_b)?; let mut last_value = 0i64; loop { @@ -54,19 +54,21 @@ mod encoder { use esp_idf_hal::gpio::AnyInputPin; 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 { - unit: Pcnt, + pub struct Encoder<'d> { + unit: PcntDriver<'d>, approx_value: Arc, } - impl Encoder { - pub fn new(pin_a: &AnyInputPin, pin_b: &AnyInputPin) -> Result { - let mut unit = Pcnt::new()?; + impl<'d> Encoder<'d> { + //pub fn new(unit: PcntDriver<'d>, pin_a: &AnyInputPin, pin_b: &AnyInputPin) -> Result { + pub fn new(pcnt: impl Peripheral

+ 'd, pin_a: &AnyInputPin, pin_b: &AnyInputPin) -> Result { + let mut unit = PcntDriver::new(pcnt)?; unit.config(&mut PcntConfig { pulse_pin: Some(pin_a), ctrl_pin: Some(pin_b), diff --git a/src/pcnt.rs b/src/pcnt.rs index 6731da29e37..0f0a7d4784d 100644 --- a/src/pcnt.rs +++ b/src/pcnt.rs @@ -1,4 +1,5 @@ use core::fmt::Debug; +use core::marker::PhantomData; #[cfg(feature = "alloc")] extern crate alloc; @@ -12,8 +13,7 @@ use enumset::EnumSetType; use crate::gpio::AnyInputPin; use crate::gpio::Pin; - -type UnitHandle = pcnt_unit_t; +use crate::peripheral::Peripheral; #[allow(dead_code)] #[derive(Debug, Copy, Clone)] @@ -113,14 +113,16 @@ pub struct PcntConfig<'d> { } #[derive(Debug)] -pub struct Pcnt { +pub struct PcntDriver<'d> { unit: pcnt_unit_t, + _p: PhantomData<&'d mut ()>, } -impl<'d> Pcnt { - pub fn new() -> Result { - Ok(Pcnt { - unit: unit_allocate()?, +impl<'d> PcntDriver<'d> { + pub fn new(_pcnt: impl Peripheral

+ 'd) -> Result { + Ok(Self { + unit: PCNT::unit(), + _p: PhantomData, }) } @@ -475,12 +477,11 @@ impl<'d> Pcnt { } } -impl Drop for Pcnt { +impl Drop for PcntDriver<'_> { fn drop(&mut self) { let _ = self.counter_pause(); let _ = self.intr_disable(); unsafe {ISR_HANDLERS[self.unit as usize] = None}; - unit_deallocate(self.unit) } } @@ -521,34 +522,32 @@ static mut ISR_HANDLERS: [Option>; pcnt_unit_t_PCNT_UNIT_MAX None, ]; -static mut PCNT_UNITS: [Option; pcnt_unit_t_PCNT_UNIT_MAX as usize] = [ - Some(pcnt_unit_t_PCNT_UNIT_0), - Some(pcnt_unit_t_PCNT_UNIT_1), - Some(pcnt_unit_t_PCNT_UNIT_2), - Some(pcnt_unit_t_PCNT_UNIT_3), - #[cfg(not(esp32s3))] - Some(pcnt_unit_t_PCNT_UNIT_4), - #[cfg(not(esp32s3))] - Some(pcnt_unit_t_PCNT_UNIT_5), - #[cfg(not(esp32s3))] - Some(pcnt_unit_t_PCNT_UNIT_6), - #[cfg(not(esp32s3))] - Some(pcnt_unit_t_PCNT_UNIT_7), -]; +pub trait Pcnt { + fn unit() -> pcnt_unit_t; +} + +macro_rules! impl_pcnt { + ($pcnt:ident: $unit:expr) => { + crate::impl_peripheral!($pcnt); -fn unit_allocate() -> Result { - let _ = PCNT_CS.enter(); - for i in 0..pcnt_unit_t_PCNT_UNIT_MAX { - if let Some(unit) = unsafe { PCNT_UNITS[i as usize].take() } { - return Ok(unit); + impl Pcnt for $pcnt { + #[inline(always)] + fn unit() -> pcnt_unit_t { + $unit + } } - } - Err(EspError::from(ESP_ERR_NO_MEM as esp_err_t).unwrap()) + }; } -fn unit_deallocate(unit: UnitHandle) { - let _ = PCNT_CS.enter(); - unsafe { - PCNT_UNITS[unit as usize] = Some(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(not(esp32s3))] +impl_pcnt!(PCNT4: pcnt_unit_t_PCNT_UNIT_4); +#[cfg(not(esp32s3))] +impl_pcnt!(PCNT5: pcnt_unit_t_PCNT_UNIT_5); +#[cfg(not(esp32s3))] +impl_pcnt!(PCNT6: pcnt_unit_t_PCNT_UNIT_6); +#[cfg(not(esp32s3))] +impl_pcnt!(PCNT7: pcnt_unit_t_PCNT_UNIT_7); diff --git a/src/peripherals.rs b/src/peripherals.rs index fbdcbe18b60..0156ee0f864 100644 --- a/src/peripherals.rs +++ b/src/peripherals.rs @@ -14,6 +14,8 @@ use crate::mac; #[cfg(not(feature = "riscv-ulp-hal"))] use crate::modem; #[cfg(not(feature = "riscv-ulp-hal"))] +use crate::pcnt; +#[cfg(not(feature = "riscv-ulp-hal"))] use crate::rmt; #[cfg(not(feature = "riscv-ulp-hal"))] use crate::spi; @@ -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(feature = "pcnt4", esp_idf_version_major = "4")))] + pub pcnt0: pcnt::PCNT0, + #[cfg(all(not(feature = "riscv-ulp-hal"), any(feature = "pcnt4", esp_idf_version_major = "4")))] + pub pcnt1: pcnt::PCNT1, + #[cfg(all(not(feature = "riscv-ulp-hal"), any(feature = "pcnt4", esp_idf_version_major = "4")))] + pub pcnt2: pcnt::PCNT2, + #[cfg(all(not(feature = "riscv-ulp-hal"), any(feature = "pcnt4", esp_idf_version_major = "4")))] + pub pcnt3: pcnt::PCNT3, + #[cfg(all(not(esp32s3), not(feature = "riscv-ulp-hal"), any(feature = "pcnt4", esp_idf_version_major = "4")))] + pub pcnt4: pcnt::PCNT4, + #[cfg(all(not(esp32s3), not(feature = "riscv-ulp-hal"), any(feature = "pcnt4", esp_idf_version_major = "4")))] + pub pcnt5: pcnt::PCNT5, + #[cfg(all(not(esp32s3), not(feature = "riscv-ulp-hal"), any(feature = "pcnt4", esp_idf_version_major = "4")))] + pub pcnt6: pcnt::PCNT6, + #[cfg(all(not(esp32s3), not(feature = "riscv-ulp-hal"), any(feature = "pcnt4", esp_idf_version_major = "4")))] + 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(feature = "pcnt4", esp_idf_version_major = "4")))] + pcnt0: pcnt::PCNT0::new(), + #[cfg(all(not(feature = "riscv-ulp-hal"), any(feature = "pcnt4", esp_idf_version_major = "4")))] + pcnt1: pcnt::PCNT1::new(), + #[cfg(all(not(feature = "riscv-ulp-hal"), any(feature = "pcnt4", esp_idf_version_major = "4")))] + pcnt2: pcnt::PCNT2::new(), + #[cfg(all(not(feature = "riscv-ulp-hal"), any(feature = "pcnt4", esp_idf_version_major = "4")))] + pcnt3: pcnt::PCNT3::new(), + #[cfg(all(not(esp32s3), not(feature = "riscv-ulp-hal"), any(feature = "pcnt4", esp_idf_version_major = "4")))] + pcnt4: pcnt::PCNT4::new(), + #[cfg(all(not(esp32s3), not(feature = "riscv-ulp-hal"), any(feature = "pcnt4", esp_idf_version_major = "4")))] + pcnt5: pcnt::PCNT5::new(), + #[cfg(all(not(esp32s3), not(feature = "riscv-ulp-hal"), any(feature = "pcnt4", esp_idf_version_major = "4")))] + pcnt6: pcnt::PCNT6::new(), + #[cfg(all(not(esp32s3), not(feature = "riscv-ulp-hal"), any(feature = "pcnt4", esp_idf_version_major = "4")))] + pcnt7: pcnt::PCNT7::new(), #[cfg(all(esp32, esp_idf_version_major = "4"))] hall_sensor: crate::hall::HallSensor::new(), #[cfg(not(feature = "riscv-ulp-hal"))] From 9ce3bbdfeebece1704d034468040c41309b69613 Mon Sep 17 00:00:00 2001 From: Christopher Liebman Date: Fri, 23 Dec 2022 14:57:23 -0800 Subject: [PATCH 24/48] use `impl Peripheral

` instead of `AnyInputPin` --- examples/pcnt_i64_encoder.rs | 24 +++++++++--------- src/pcnt.rs | 47 +++++++++++++++++++++++------------- 2 files changed, 41 insertions(+), 30 deletions(-) diff --git a/examples/pcnt_i64_encoder.rs b/examples/pcnt_i64_encoder.rs index 83eae8f9789..734f4c654b4 100644 --- a/examples/pcnt_i64_encoder.rs +++ b/examples/pcnt_i64_encoder.rs @@ -11,7 +11,6 @@ use anyhow::Context; use log::*; use esp_idf_hal::delay::FreeRtos; -use esp_idf_hal::gpio::AnyInputPin; use esp_idf_hal::prelude::*; use encoder::Encoder; @@ -26,11 +25,11 @@ fn main() -> anyhow::Result<()> { info!("setup pins"); let peripherals = Peripherals::take().context("failed to take Peripherals")?; - let pin_a: AnyInputPin = peripherals.pins.gpio5.into(); - let pin_b: AnyInputPin = peripherals.pins.gpio6.into(); + let pin_a = peripherals.pins.gpio5; + let pin_b = peripherals.pins.gpio6; info!("setup encoder"); - let encoder = Encoder::new(peripherals.pcnt0, &pin_a, &pin_b)?; + let encoder = Encoder::new(peripherals.pcnt0, pin_a, pin_b)?; let mut last_value = 0i64; loop { @@ -52,7 +51,7 @@ mod encoder { use std::sync::atomic::AtomicI64; use std::sync::atomic::Ordering; - 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; @@ -66,12 +65,13 @@ mod encoder { } impl<'d> Encoder<'d> { - //pub fn new(unit: PcntDriver<'d>, pin_a: &AnyInputPin, pin_b: &AnyInputPin) -> Result { - pub fn new(pcnt: impl Peripheral

+ 'd, pin_a: &AnyInputPin, pin_b: &AnyInputPin) -> Result { + pub fn new<'a, PCNT: Pcnt>( + pcnt: impl Peripheral

+ 'd, + mut pin_a: impl Peripheral

, + mut pin_b: impl Peripheral

+ ) -> Result { let mut unit = PcntDriver::new(pcnt)?; - unit.config(&mut PcntConfig { - pulse_pin: Some(pin_a), - ctrl_pin: Some(pin_b), + unit.config(Some(&mut pin_a), Some(&mut pin_b), &mut PcntConfig { lctrl_mode: PcntControlMode::Reverse, hctrl_mode: PcntControlMode::Keep, pos_mode: PcntCountMode::Decrement, @@ -80,9 +80,7 @@ mod encoder { counter_l_lim: LOW_LIMIT, channel: PcntChannel::Channel0, })?; - unit.config(&mut PcntConfig { - pulse_pin: Some(pin_b), - ctrl_pin: Some(pin_a), + unit.config(Some(&mut pin_b), Some(&mut pin_a), &mut PcntConfig { lctrl_mode: PcntControlMode::Reverse, hctrl_mode: PcntControlMode::Keep, pos_mode: PcntCountMode::Increment, diff --git a/src/pcnt.rs b/src/pcnt.rs index 0f0a7d4784d..09606841281 100644 --- a/src/pcnt.rs +++ b/src/pcnt.rs @@ -11,8 +11,7 @@ use esp_idf_sys::*; use enumset::EnumSetType; -use crate::gpio::AnyInputPin; -use crate::gpio::Pin; +use crate::gpio::InputPin; use crate::peripheral::Peripheral; #[allow(dead_code)] @@ -91,11 +90,7 @@ pub enum PcntEvent { pub type PcntEventType = enumset::EnumSet; /// Pulse Counter configuration for a single channel -pub struct PcntConfig<'d> { - /// Pulse input GPIO number, if you want to use GPIO16, enter pulse_gpio_num = 16, a negative value will be ignored - pub pulse_pin: Option<&'d AnyInputPin>, - /// Control signal input GPIO number, a negative value will be ignored - pub ctrl_pin: Option<&'d AnyInputPin>, +pub struct PcntConfig { /// PCNT low control mode pub lctrl_mode: PcntControlMode, /// PCNT high control mode @@ -135,14 +130,26 @@ impl<'d> PcntDriver<'d> { /// returns /// - () /// - EspError - pub fn config(&mut self, pconfig: & PcntConfig) -> Result<(), EspError> { + pub fn config<'a>( + &mut self, + pulse_pin: Option + 'a>, + ctrl_pin: Option + 'a>, + pconfig: & PcntConfig + ) -> Result<(), EspError> { + let config = pcnt_config_t { - pulse_gpio_num: match pconfig.pulse_pin { - Some(pin) => pin.pin(), + pulse_gpio_num: match pulse_pin { + Some(pin) => { + crate::into_ref!(pin); + pin.pin() + }, None => PCNT_PIN_NOT_USED, }, - ctrl_gpio_num: match pconfig.ctrl_pin { - Some(pin) => pin.pin(), + ctrl_gpio_num: match ctrl_pin { + Some(pin) => { + crate::into_ref!(pin); + pin.pin() + }, None => PCNT_PIN_NOT_USED, }, lctrl_mode: pconfig.lctrl_mode.into(), @@ -323,18 +330,24 @@ impl<'d> PcntDriver<'d> { /// returns /// - () /// - EspError - pub fn set_pin( + pub fn set_pin<'a>( &mut self, channel: PcntChannel, - pulse_pin: Option<&AnyInputPin>, - ctrl_pin: Option<&AnyInputPin>, + pulse_pin: Option + 'a>, + ctrl_pin: Option + 'a>, ) -> Result<(), EspError> { let pulse_io_num = match pulse_pin { - Some(pin) => pin.pin(), + Some(pin) => { + crate::into_ref!(pin); + pin.pin() + }, None => PCNT_PIN_NOT_USED, }; let ctrl_io_num = match ctrl_pin { - Some(pin) => pin.pin(), + Some(pin) => { + crate::into_ref!(pin); + pin.pin() + }, None => PCNT_PIN_NOT_USED, }; unsafe { From f638198bf796111f6a15f0754b1ec3f0f917f120 Mon Sep 17 00:00:00 2001 From: Christopher Liebman Date: Fri, 23 Dec 2022 15:12:26 -0800 Subject: [PATCH 25/48] small cleanup --- src/pcnt.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/pcnt.rs b/src/pcnt.rs index 09606841281..e7b73cb4f65 100644 --- a/src/pcnt.rs +++ b/src/pcnt.rs @@ -14,7 +14,6 @@ use enumset::EnumSetType; use crate::gpio::InputPin; use crate::peripheral::Peripheral; -#[allow(dead_code)] #[derive(Debug, Copy, Clone)] pub enum PcntChannel { Channel0, @@ -125,8 +124,12 @@ impl<'d> PcntDriver<'d> { /// @note /// This function will disable three events: PCNT_EVT_L_LIM, PCNT_EVT_H_LIM, PCNT_EVT_ZERO. /// + /// @param pulse_io Pulse signal input pin + /// @param ctrl_io Control signal input pin /// @param pconfig Reference of PcntConfig /// + /// @note Set the signal input to PCNT_PIN_NOT_USED if unused. + /// /// returns /// - () /// - EspError From 87556babc988797a991ffd7934d4091e4b3a85b4 Mon Sep 17 00:00:00 2001 From: Christopher Liebman Date: Fri, 23 Dec 2022 15:42:38 -0800 Subject: [PATCH 26/48] rename config() -> channel_config() implement defaults on PcntChannelConfig --- examples/pcnt_i64_encoder.rs | 6 ++---- src/pcnt.rs | 31 ++++++++++++++++++++----------- 2 files changed, 22 insertions(+), 15 deletions(-) diff --git a/examples/pcnt_i64_encoder.rs b/examples/pcnt_i64_encoder.rs index 734f4c654b4..aa93f3b6d01 100644 --- a/examples/pcnt_i64_encoder.rs +++ b/examples/pcnt_i64_encoder.rs @@ -71,23 +71,21 @@ mod encoder { mut pin_b: impl Peripheral

) -> Result { let mut unit = PcntDriver::new(pcnt)?; - unit.config(Some(&mut pin_a), Some(&mut pin_b), &mut PcntConfig { + unit.channel_config(PcntChannel::Channel0, Some(&mut pin_a), Some(&mut pin_b), &mut PcntConfig { 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, - channel: PcntChannel::Channel0, })?; - unit.config(Some(&mut pin_b), Some(&mut pin_a), &mut PcntConfig { + unit.channel_config(PcntChannel::Channel1, Some(&mut pin_b), Some(&mut pin_a), &mut PcntConfig { 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, - channel: PcntChannel::Channel1, })?; unit.set_filter_value(min(10 * 80, 1023))?; unit.filter_enable()?; diff --git a/src/pcnt.rs b/src/pcnt.rs index e7b73cb4f65..fa7b4b5a51f 100644 --- a/src/pcnt.rs +++ b/src/pcnt.rs @@ -30,11 +30,12 @@ impl From for pcnt_channel_t { } /// PCNT channel action on signal edge -#[derive(Debug, Copy, Clone)] +#[derive(Debug, Copy, Clone, Default)] pub enum PcntCountMode { /// Hold current count value Hold, /// Increase count value + #[default] Increment, /// Decrease count value Decrement, @@ -51,11 +52,12 @@ impl From for pcnt_count_mode_t { } /// PCNT channel action on control level -#[derive(Debug, Copy, Clone)] +#[derive(Debug, Copy, Clone, Default)] pub enum PcntControlMode { /// Keep current count mode Keep, /// Invert current count mode (increase -> decrease, decrease -> increase) + #[default] Reverse, /// Hold current count value Disable, @@ -89,7 +91,8 @@ pub enum PcntEvent { pub type PcntEventType = enumset::EnumSet; /// Pulse Counter configuration for a single channel -pub struct PcntConfig { +#[derive(Debug, Copy, Clone, Default)] +pub struct PcntChannelConfig { /// PCNT low control mode pub lctrl_mode: PcntControlMode, /// PCNT high control mode @@ -102,8 +105,12 @@ pub struct PcntConfig { pub counter_h_lim: i16, /// Minimum counter value pub counter_l_lim: i16, - /// the PCNT channel - pub channel: PcntChannel, +} + +impl PcntChannelConfig { + pub fn new() -> Self { + Default::default() + } } #[derive(Debug)] @@ -120,12 +127,13 @@ impl<'d> PcntDriver<'d> { }) } - /// Configure Pulse Counter unit + /// Configure Pulse Counter chanel /// @note /// This function will disable three events: PCNT_EVT_L_LIM, PCNT_EVT_H_LIM, PCNT_EVT_ZERO. /// - /// @param pulse_io Pulse signal input pin - /// @param ctrl_io Control signal input pin + /// @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. @@ -133,11 +141,12 @@ impl<'d> PcntDriver<'d> { /// returns /// - () /// - EspError - pub fn config<'a>( + pub fn channel_config<'a>( &mut self, + channel: PcntChannel, pulse_pin: Option + 'a>, ctrl_pin: Option + 'a>, - pconfig: & PcntConfig + pconfig: &PcntChannelConfig ) -> Result<(), EspError> { let config = pcnt_config_t { @@ -161,7 +170,7 @@ impl<'d> PcntDriver<'d> { neg_mode: pconfig.neg_mode.into(), counter_h_lim: pconfig.counter_h_lim, counter_l_lim: pconfig.counter_l_lim, - channel: pconfig.channel.into(), + channel: channel.into(), unit: self.unit, }; From 4a1c611fd92b8949ed367084665036d1aa46896b Mon Sep 17 00:00:00 2001 From: Chris Liebman Date: Sun, 25 Dec 2022 13:18:17 -0800 Subject: [PATCH 27/48] some cleanup on idf v5 pulse_ctr --- examples/pcnt_i64_encoder.rs | 20 +++++----- src/peripherals.rs | 2 +- src/pulse_cnt.rs | 72 ++++++++++-------------------------- 3 files changed, 31 insertions(+), 63 deletions(-) diff --git a/examples/pcnt_i64_encoder.rs b/examples/pcnt_i64_encoder.rs index aa93f3b6d01..66a075a416a 100644 --- a/examples/pcnt_i64_encoder.rs +++ b/examples/pcnt_i64_encoder.rs @@ -29,7 +29,10 @@ fn main() -> anyhow::Result<()> { let pin_b = peripherals.pins.gpio6; info!("setup encoder"); + #[cfg(any(feature = "pcnt", esp_idf_version_major = "4"))] let encoder = Encoder::new(peripherals.pcnt0, pin_a, pin_b)?; + #[cfg(not(any(feature = "pcnt", esp_idf_version_major = "4")))] + let encoder = Encoder::new(pin_a, pin_b)?; let mut last_value = 0i64; loop { @@ -71,7 +74,7 @@ mod encoder { mut pin_b: impl Peripheral

) -> Result { let mut unit = PcntDriver::new(pcnt)?; - unit.channel_config(PcntChannel::Channel0, Some(&mut pin_a), Some(&mut pin_b), &mut PcntConfig { + unit.channel_config(PcntChannel::Channel0, Some(&mut pin_a), Some(&mut pin_b), &mut PcntChannelConfig { lctrl_mode: PcntControlMode::Reverse, hctrl_mode: PcntControlMode::Keep, pos_mode: PcntCountMode::Decrement, @@ -79,7 +82,7 @@ mod encoder { counter_h_lim: HIGH_LIMIT, counter_l_lim: LOW_LIMIT, })?; - unit.channel_config(PcntChannel::Channel1, Some(&mut pin_b), Some(&mut pin_a), &mut PcntConfig { + unit.channel_config(PcntChannel::Channel1, Some(&mut pin_b), Some(&mut pin_a), &mut PcntChannelConfig { lctrl_mode: PcntControlMode::Reverse, hctrl_mode: PcntControlMode::Keep, pos_mode: PcntCountMode::Increment, @@ -132,7 +135,8 @@ mod encoder { use std::sync::atomic::AtomicI64; use std::sync::atomic::Ordering; - use esp_idf_hal::gpio::AnyInputPin; + use esp_idf_hal::gpio::InputPin; + use esp_idf_hal::peripheral::Peripheral; use esp_idf_hal::pulse_cnt::*; use esp_idf_sys::EspError; @@ -146,22 +150,18 @@ mod encoder { } impl Encoder { - pub fn new(pin_a: &AnyInputPin, pin_b: &AnyInputPin) -> Result { + pub fn new(mut pin_a: impl Peripheral

, mut pin_b: impl Peripheral

) -> Result { let mut unit = PcntUnit::new(&PcntUnitConfig { low_limit: LOW_LIMIT, high_limit: HIGH_LIMIT, ..Default::default() })?; - let channel0 = unit.channel(&PcntChanConfig { - edge_pin: Some(&pin_a), - level_pin: Some(&pin_b), + let channel0 = unit.channel(Some(&mut pin_a), Some(&mut pin_b), &PcntChanConfig { ..Default::default() })?; channel0.set_level_action(PcntLevelAction::Keep, PcntLevelAction::Inverse)?; channel0.set_edge_action(PcntEdgeAction::Decrease, PcntEdgeAction::Increase)?; - let channel1 = unit.channel(&PcntChanConfig { - edge_pin: Some(&pin_b), - level_pin: Some(&pin_a), + let channel1 = unit.channel(Some(&mut pin_b), Some(&mut pin_a), &PcntChanConfig { ..Default::default() })?; channel1.set_level_action(PcntLevelAction::Keep, PcntLevelAction::Inverse)?; diff --git a/src/peripherals.rs b/src/peripherals.rs index 0156ee0f864..4bd2effbcf7 100644 --- a/src/peripherals.rs +++ b/src/peripherals.rs @@ -13,7 +13,7 @@ use crate::ledc; use crate::mac; #[cfg(not(feature = "riscv-ulp-hal"))] use crate::modem; -#[cfg(not(feature = "riscv-ulp-hal"))] +#[cfg(all(not(feature = "riscv-ulp-hal"), any(feature = "pcnt4", esp_idf_version_major = "4")))] use crate::pcnt; #[cfg(not(feature = "riscv-ulp-hal"))] use crate::rmt; diff --git a/src/pulse_cnt.rs b/src/pulse_cnt.rs index df043a57296..145ee7323c5 100644 --- a/src/pulse_cnt.rs +++ b/src/pulse_cnt.rs @@ -1,41 +1,7 @@ - - -use esp_idf_sys::esp; -use esp_idf_sys::pcnt_chan_config_t; -// channel flags -use esp_idf_sys::pcnt_chan_config_t__bindgen_ty_1; -use esp_idf_sys::pcnt_channel_edge_action_t; -use esp_idf_sys::pcnt_channel_handle_t; -use esp_idf_sys::pcnt_channel_level_action_t; -use esp_idf_sys::pcnt_channel_set_edge_action; -use esp_idf_sys::pcnt_channel_set_level_action; -use esp_idf_sys::pcnt_del_channel; -use esp_idf_sys::pcnt_del_unit; -use esp_idf_sys::pcnt_event_callbacks_t; -use esp_idf_sys::pcnt_glitch_filter_config_t; -use esp_idf_sys::pcnt_new_channel; -use esp_idf_sys::pcnt_new_unit; -use esp_idf_sys::pcnt_unit_add_watch_point; -use esp_idf_sys::pcnt_unit_clear_count; -use esp_idf_sys::pcnt_unit_config_t; -// unit flags -use esp_idf_sys::pcnt_unit_config_t__bindgen_ty_1; -use esp_idf_sys::pcnt_unit_disable; -use esp_idf_sys::pcnt_unit_enable; -use esp_idf_sys::pcnt_unit_get_count; -use esp_idf_sys::pcnt_unit_handle_t; -use esp_idf_sys::EspError; -use esp_idf_sys::pcnt_unit_register_event_callbacks; -use esp_idf_sys::pcnt_unit_remove_watch_point; -use esp_idf_sys::pcnt_unit_set_glitch_filter; -use esp_idf_sys::pcnt_unit_start; -use esp_idf_sys::pcnt_unit_stop; -use esp_idf_sys::pcnt_unit_zero_cross_mode_t; -use esp_idf_sys::pcnt_watch_event_data_t; - -use crate::gpio::AnyInputPin; -use crate::gpio::Pin; +use esp_idf_sys::*; +use crate::gpio::InputPin; +use crate::peripheral::Peripheral; #[doc = " @brief PCNT glitch filter configuration"] #[derive(Debug, Default, Copy, Clone)] @@ -195,11 +161,7 @@ impl PcntChanFlags { #[doc = " @brief PCNT channel configuration"] #[derive(Default)] -pub struct PcntChanConfig<'a> { - #[doc = "< GPIO number used by the edge signal, input mode with pull up enabled. Set to -1 if unused"] - pub edge_pin: Option<&'a AnyInputPin>, - #[doc = "< GPIO number used by the level signal, input mode with pull up enabled. Set to -1 if unused"] - pub level_pin: Option<&'a AnyInputPin>, +pub struct PcntChanConfig { #[doc = "< Channel config flags"] pub flags: PcntChanFlags, } @@ -415,7 +377,7 @@ impl PcntUnit { #[doc = " - value: on success"] #[doc = " - EspError: on failure"] pub fn get_count(&self) -> Result { - let mut value: esp_idf_sys::c_types::c_int = 0; + let mut value: core::ffi::c_int = 0; unsafe { esp!(pcnt_unit_get_count(self.unit, &mut value))?; } Ok(value) } @@ -464,11 +426,11 @@ impl PcntUnit { let cbs = pcnt_event_callbacks_t { on_reach: Some(Self::handle_isr), }; - let data = id as *mut esp_idf_sys::c_types::c_void; - unsafe {esp!(pcnt_unit_register_event_callbacks(self.unit, &cbs, data as *mut esp_idf_sys::c_types::c_void))} + let data = id as *mut core::ffi::c_void; + unsafe {esp!(pcnt_unit_register_event_callbacks(self.unit, &cbs, data as *mut core::ffi::c_void))} } - unsafe extern "C" fn handle_isr(_unit: pcnt_unit_handle_t, edata: *const pcnt_watch_event_data_t, data: *mut esp_idf_sys::c_types::c_void) -> bool { + unsafe extern "C" fn handle_isr(_unit: pcnt_unit_handle_t, edata: *const pcnt_watch_event_data_t, data: *mut core::ffi::c_void) -> bool { let id = data as usize; match &mut ISR_HANDLERS[id] { Some(f) => f(edata.into()), @@ -488,7 +450,7 @@ impl PcntUnit { unsafe { esp!(pcnt_unit_register_event_callbacks(self.unit, &pcnt_event_callbacks_t { on_reach: None, - }, 0 as *mut esp_idf_sys::c_types::c_void))?; + }, 0 as *mut core::ffi::c_void))?; } free_isr_id(id); } @@ -503,14 +465,20 @@ impl PcntUnit { #[doc = " @return"] #[doc = " - PcntChannel: on success"] #[doc = " - EspError: on failure"] - pub fn channel(&self, config: &PcntChanConfig) -> Result { + pub fn channel<'a>(&self, edge_pin: Option+'a>, level_pin: Option+'a>, config: &PcntChanConfig) -> Result { let config = pcnt_chan_config_t { - edge_gpio_num: match config.edge_pin { - Some(pin) => pin.pin(), + edge_gpio_num: match edge_pin { + Some(pin) => { + crate::into_ref!(pin); + pin.pin() + }, None => -1, }, - level_gpio_num: match config.level_pin { - Some(pin) => pin.pin(), + level_gpio_num: match level_pin { + Some(pin) => { + crate::into_ref!(pin); + pin.pin() + }, None => -1, }, flags: config.flags.0, From 1a02770ff228fe607ad3140109ec2e5def8f9401 Mon Sep 17 00:00:00 2001 From: Chris Liebman Date: Sun, 25 Dec 2022 13:26:45 -0800 Subject: [PATCH 28/48] no need for PcntChanConfig, just use flags --- examples/pcnt_i64_encoder.rs | 8 ++------ src/pulse_cnt.rs | 17 +++++++---------- 2 files changed, 9 insertions(+), 16 deletions(-) diff --git a/examples/pcnt_i64_encoder.rs b/examples/pcnt_i64_encoder.rs index 66a075a416a..af38b287dd4 100644 --- a/examples/pcnt_i64_encoder.rs +++ b/examples/pcnt_i64_encoder.rs @@ -156,14 +156,10 @@ mod encoder { high_limit: HIGH_LIMIT, ..Default::default() })?; - let channel0 = unit.channel(Some(&mut pin_a), Some(&mut pin_b), &PcntChanConfig { - ..Default::default() - })?; + let channel0 = unit.channel(Some(&mut pin_a), Some(&mut pin_b), PcntChanFlags::default())?; channel0.set_level_action(PcntLevelAction::Keep, PcntLevelAction::Inverse)?; channel0.set_edge_action(PcntEdgeAction::Decrease, PcntEdgeAction::Increase)?; - let channel1 = unit.channel(Some(&mut pin_b), Some(&mut pin_a), &PcntChanConfig { - ..Default::default() - })?; + let channel1 = unit.channel(Some(&mut pin_b), Some(&mut pin_a), PcntChanFlags::default())?; channel1.set_level_action(PcntLevelAction::Keep, PcntLevelAction::Inverse)?; channel1.set_edge_action(PcntEdgeAction::Increase, PcntEdgeAction::Decrease)?; diff --git a/src/pulse_cnt.rs b/src/pulse_cnt.rs index 145ee7323c5..76d285138ac 100644 --- a/src/pulse_cnt.rs +++ b/src/pulse_cnt.rs @@ -159,14 +159,6 @@ impl PcntChanFlags { } } -#[doc = " @brief PCNT channel configuration"] -#[derive(Default)] -pub struct PcntChanConfig { - #[doc = "< Channel config flags"] - pub flags: PcntChanFlags, -} - - #[doc = " @brief PCNT channel"] #[derive(Debug)] pub struct PcntChannel(pcnt_channel_handle_t); @@ -465,7 +457,12 @@ impl PcntUnit { #[doc = " @return"] #[doc = " - PcntChannel: on success"] #[doc = " - EspError: on failure"] - pub fn channel<'a>(&self, edge_pin: Option+'a>, level_pin: Option+'a>, config: &PcntChanConfig) -> Result { + pub fn channel<'a>( + &self, + edge_pin: Option+'a>, + level_pin: Option+'a>, + flags: PcntChanFlags + ) -> Result { let config = pcnt_chan_config_t { edge_gpio_num: match edge_pin { Some(pin) => { @@ -481,7 +478,7 @@ impl PcntUnit { }, None => -1, }, - flags: config.flags.0, + flags: flags.0, }; let mut channel: pcnt_channel_handle_t = std::ptr::null_mut(); unsafe { From 10b02f831b51b4167209392c28b7152cad00109e Mon Sep 17 00:00:00 2001 From: Christopher Liebman Date: Mon, 26 Dec 2022 13:25:15 -0800 Subject: [PATCH 29/48] use comment style documentation not `#[doc=""]` --- src/pulse_cnt.rs | 302 +++++++++++++++++++++++------------------------ 1 file changed, 150 insertions(+), 152 deletions(-) diff --git a/src/pulse_cnt.rs b/src/pulse_cnt.rs index 76d285138ac..736d1fd3bc1 100644 --- a/src/pulse_cnt.rs +++ b/src/pulse_cnt.rs @@ -3,10 +3,10 @@ use esp_idf_sys::*; use crate::gpio::InputPin; use crate::peripheral::Peripheral; -#[doc = " @brief PCNT glitch filter configuration"] +/// @brief PCNT glitch filter configuration #[derive(Debug, Default, Copy, Clone)] pub struct PcntFilterConfig { - #[doc = "< Pulse width smaller than this threshold will be treated as glitch and ignored, in the unit of ns"] + ///< Pulse width smaller than this threshold will be treated as glitch and ignored, in the unit of ns pub max_glitch_ns: u32, } @@ -18,16 +18,16 @@ impl Into for &PcntFilterConfig { } } -#[doc = " @brief PCNT unit zero cross mode"] +/// @brief PCNT unit zero cross mode #[derive(Debug, Copy, Clone)] pub enum PcntZeroCrossMode { - #[doc = "< start from positive value, end to zero, i.e. +N->0"] + ///< start from positive value, end to zero, i.e. +N->0 PosZero, - #[doc = "< start from negative value, end to zero, i.e. -N->0"] + ///< start from negative value, end to zero, i.e. -N->0 NegZero, - #[doc = "< start from negative value, end to positive value, i.e. -N->+M"] + ///< start from negative value, end to positive value, i.e. -N->+M NegPos, - #[doc = "< start from positive value, end to negative value, i.e. +N->-M"] + ///< start from positive value, end to negative value, i.e. +N->-M PosNeg, } @@ -43,13 +43,13 @@ impl From for PcntZeroCrossMode { } } -#[doc = " @brief PCNT watch event data"] +/// @brief PCNT watch event data #[derive(Debug, Copy, Clone)] #[allow(dead_code)] pub struct PcntWatchEventData { - #[doc = "< Watch point value that triggered the event"] + ///< Watch point value that triggered the event pub watch_point_value: i32, - #[doc = "< Zero cross mode"] + ///< Zero cross mode pub zero_cross_mode: PcntZeroCrossMode, } @@ -66,11 +66,11 @@ impl From<*const pcnt_watch_event_data_t> for PcntWatchEventData { #[derive(Debug, Copy, Clone)] pub enum PcntEdgeAction { - #[doc = "< Hold current count value"] + ///< Hold current count value Hold, - #[doc = "< Increase count value"] + ///< Increase count value Increase, - #[doc = "< Decrease count value"] + ///< Decrease count value Decrease, } @@ -86,11 +86,11 @@ impl Into for PcntEdgeAction { #[derive(Debug, Copy, Clone)] pub enum PcntLevelAction { - #[doc = "< Keep current count mode"] + ///< Keep current count mode Keep, - #[doc = "< Invert current count mode (increase -> decrease, decrease -> increase)"] + ///< Invert current count mode (increase -> decrease, decrease -> increase) Inverse, - #[doc = "< Hold current count value"] + ///< Hold current count value Hold, } @@ -159,38 +159,36 @@ impl PcntChanFlags { } } -#[doc = " @brief PCNT channel"] +/// @brief PCNT channel #[derive(Debug)] pub struct PcntChannel(pcnt_channel_handle_t); impl PcntChannel { - #[doc = " @brief Set channel actions when edge signal changes (e.g. falling or rising edge occurred)."] - #[doc = " The edge signal is input from the `edge_pin` configured in `PcntChanConfig`."] - #[doc = " We use these actions to control when and how to change the counter value."] - #[doc = ""] - #[doc = " @param[in] pos_act Action on posedge signal"] - #[doc = " @param[in] neg_act Action on negedge signal"] - #[doc = " @return"] - #[doc = " @return"] - #[doc = " - (): on success"] - #[doc = " - EspError: on failure"] + /// @brief Set channel actions when edge signal changes (e.g. falling or rising edge occurred). + /// The edge signal is input from the `edge_pin` configured in `PcntChanConfig`. + /// We use these actions to control when and how to change the counter value. + /// + /// @param[in] pos_act Action on posedge signal + /// @param[in] neg_act Action on negedge signal + /// @return + /// - (): on success + /// - EspError: on failure pub fn set_edge_action(&self,pos_act: PcntEdgeAction, neg_act: PcntEdgeAction) -> Result<(), EspError> { unsafe { esp!(pcnt_channel_set_edge_action(self.0, pos_act.into(), neg_act.into())) } } - #[doc = " @brief Set channel actions when level signal changes (e.g. signal level goes from high to low)."] - #[doc = " The level signal is input from the `level_gpio_num` configured in `pcnt_chan_config_t`."] - #[doc = " We use these actions to control when and how to change the counting mode."] - #[doc = ""] - #[doc = " @param[in] chan PCNT channel handle created by `pcnt_new_channel()`"] - #[doc = " @param[in] high_act Action on high level signal"] - #[doc = " @param[in] low_act Action on low level signal"] - #[doc = " @return"] - #[doc = " - ESP_OK: Set level action for PCNT channel successfully"] - #[doc = " - ESP_ERR_INVALID_ARG: Set level action for PCNT channel failed because of invalid argument"] - #[doc = " - ESP_FAIL: Set level action for PCNT channel failed because of other error"] + /// @brief Set channel actions when level signal changes (e.g. signal level goes from high to low). + /// The level signal is input from the `level_gpio_num` configured in `pcnt_chan_config_t`. + /// We use these actions to control when and how to change the counting mode. + /// + /// @param[in] chan PCNT channel handle created by `pcnt_new_channel()` + /// @param[in] high_act Action on high level signal + /// @param[in] low_act Action on low level signal + /// @return + /// - (): on success + /// - EspError: on failure pub fn set_level_action(&self, high_act: PcntLevelAction, low_act: PcntLevelAction) -> Result<(), EspError> { unsafe { esp!(pcnt_channel_set_level_action(self.0, high_act.into(), low_act.into())) @@ -221,17 +219,17 @@ impl PcntUnitFlags { } } -#[doc = " @brief PCNT unit configuration"] +/// @brief PCNT unit configuration #[derive(Debug, Default, Copy, Clone)] pub struct PcntUnitConfig { - #[doc = " @brief the low limit value, should be < 0"] + /// @brief the low limit value, should be < 0 pub low_limit: i32, - #[doc = " @brief the high limit value, should be > 0"] + /// @brief the high limit value, should be > 0 pub high_limit: i32, pub flags: PcntUnitFlags, } -#[doc = " @brief PCNT unit"] +/// @brief PCNT unit #[derive(Debug)] pub struct PcntUnit { unit: pcnt_unit_handle_t, @@ -239,14 +237,14 @@ pub struct PcntUnit { } impl PcntUnit { - #[doc = " @brief Create a new PCNT unit"] - #[doc = ""] - #[doc = " @note The newly created PCNT unit is put in the init state."] - #[doc = ""] - #[doc = " @param[in] config PCNT unit configuration"] - #[doc = " @return"] - #[doc = " - PcntUnit: on success"] - #[doc = " - EspError: on failure"] + /// @brief Create a new PCNT unit + /// + /// @note The newly created PCNT unit is put in the init state. + /// + /// @param[in] config PCNT unit configuration + /// @return + /// - PcntUnit: on success + /// - EspError: on failure pub fn new(config: &PcntUnitConfig) -> Result { let config = pcnt_unit_config_t { low_limit: config.low_limit, @@ -263,17 +261,17 @@ impl PcntUnit { }) } - #[doc = " @brief Set glitch filter for PCNT unit"] - #[doc = ""] - #[doc = " @note The glitch filter module is clocked from APB, and APB frequency can be changed during DFS, which in return make the filter out of action."] - #[doc = " So this function will lazy-install a PM lock internally when the power management is enabled. With this lock, the APB frequency won't be changed."] - #[doc = " The PM lock can be uninstalled when the unit is dropped."] - #[doc = " @note This function should be called when the PCNT unit is in the init state (i.e. before calling `enable()`)"] - #[doc = ""] - #[doc = " @param[in] config PCNT filter configuration, set config to None means disabling the filter function"] - #[doc = " @return"] - #[doc = " - (): on success"] - #[doc = " - EspError: on failure"] + /// @brief Set glitch filter for PCNT unit + /// + /// @note The glitch filter module is clocked from APB, and APB frequency can be changed during DFS, which in return make the filter out of action. + /// So this function will lazy-install a PM lock internally when the power management is enabled. With this lock, the APB frequency won't be changed. + /// The PM lock can be uninstalled when the unit is dropped. + /// @note This function should be called when the PCNT unit is in the init state (i.e. before calling `enable()`) + /// + /// @param[in] config PCNT filter configuration, set config to None means disabling the filter function + /// @return + /// - (): on success + /// - EspError: on failure pub fn set_filter(&self, config: Option<&PcntFilterConfig>) -> Result<(), EspError> { unsafe { esp!(pcnt_unit_set_glitch_filter(self.unit, match config { @@ -283,126 +281,126 @@ impl PcntUnit { } } - #[doc = " @brief Enable the PCNT unit"] - #[doc = ""] - #[doc = " @note This function will transit the unit state from init to enable."] - #[doc = " @note This function will enable the interrupt service, if it's lazy installed in `subscribe()`."] - #[doc = " @note This function will acquire the PM lock if it's lazy installed in `set_glitch_filter()`."] - #[doc = " @note Enable a PCNT unit doesn't mean to start it. See also `start()` for how to start the PCNT counter."] - #[doc = ""] - #[doc = " @return"] - #[doc = " - (): on success"] - #[doc = " - EspError: on failure"] + /// @brief Enable the PCNT unit + /// + /// @note This function will transit the unit state from init to enable. + /// @note This function will enable the interrupt service, if it's lazy installed in `subscribe()`. + /// @note This function will acquire the PM lock if it's lazy installed in `set_glitch_filter()`. + /// @note Enable a PCNT unit doesn't mean to start it. See also `start()` for how to start the PCNT counter. + /// + /// @return + /// - (): on success + /// - EspError: on failure pub fn enable(&self) -> Result<(), EspError> { unsafe { esp!(pcnt_unit_enable(self.unit)) } } - #[doc = " @brief Disable the PCNT unit"] - #[doc = ""] - #[doc = " @note This function will do the opposite work to the `enable()`"] - #[doc = " @note Disable a PCNT unit doesn't mean to stop it. See also `stop()` for how to stop the PCNT counter."] - #[doc = ""] - #[doc = " @return"] - #[doc = " - (): on success"] - #[doc = " - EspError: on failure"] + /// @brief Disable the PCNT unit + /// + /// @note This function will do the opposite work to the `enable()` + /// @note Disable a PCNT unit doesn't mean to stop it. See also `stop()` for how to stop the PCNT counter. + /// + /// @return + /// - (): on success + /// - EspError: on failure pub fn disable(&self) -> Result<(), EspError> { unsafe { esp!(pcnt_unit_disable(self.unit)) } } - #[doc = " @brief Start the PCNT unit, the counter will start to count according to the edge and/or level input signals"] - #[doc = ""] - #[doc = " @note This function should be called when the unit is in the enable state (i.e. after calling `pcnt_unit_enable()`)"] - #[doc = " @note This function is allowed to run within ISR context"] - #[doc = " @note This function will be placed into IRAM if `CONFIG_PCNT_CTRL_FUNC_IN_IRAM` is on, so that it's allowed to be executed when Cache is disabled"] - #[doc = ""] - #[doc = " @return"] - #[doc = " - (): on success"] - #[doc = " - EspError: on failure"] + /// @brief Start the PCNT unit, the counter will start to count according to the edge and/or level input signals + /// + /// @note This function should be called when the unit is in the enable state (i.e. after calling `pcnt_unit_enable()`) + /// @note This function is allowed to run within ISR context + /// @note This function will be placed into IRAM if `CONFIG_PCNT_CTRL_FUNC_IN_IRAM` is on, so that it's allowed to be executed when Cache is disabled + /// + /// @return + /// - (): on success + /// - EspError: on failure pub fn start(&self) -> Result<(), EspError> { unsafe { esp!(pcnt_unit_start(self.unit)) } } - #[doc = " @brief Stop PCNT from counting"] - #[doc = ""] - #[doc = " @note This function should be called when the unit is in the enable state (i.e. after calling `pcnt_unit_enable()`)"] - #[doc = " @note The stop operation won't clear the counter. Also see `pcnt_unit_clear_count()` for how to clear pulse count value."] - #[doc = " @note This function is allowed to run within ISR context"] - #[doc = " @note This function will be placed into IRAM if `CONFIG_PCNT_CTRL_FUNC_IN_IRAM`, so that it is allowed to be executed when Cache is disabled"] - #[doc = ""] - #[doc = " @return"] - #[doc = " - (): on success"] - #[doc = " - EspError: on failure"] + /// @brief Stop PCNT from counting + /// + /// @note This function should be called when the unit is in the enable state (i.e. after calling `pcnt_unit_enable()`) + /// @note The stop operation won't clear the counter. Also see `pcnt_unit_clear_count()` for how to clear pulse count value. + /// @note This function is allowed to run within ISR context + /// @note This function will be placed into IRAM if `CONFIG_PCNT_CTRL_FUNC_IN_IRAM`, so that it is allowed to be executed when Cache is disabled + /// + /// @return + /// - (): on success + /// - EspError: on failure pub fn stop(&self) -> Result<(), EspError> { unsafe { esp!(pcnt_unit_stop(self.unit)) } } - #[doc = " @brief Clear PCNT pulse count value to zero"] - #[doc = ""] - #[doc = " @note It's recommended to call this function after adding a watch point by `pcnt_unit_add_watch_point()`, so that the newly added watch point is effective immediately."] - #[doc = " @note This function is allowed to run within ISR context"] - #[doc = " @note This function will be placed into IRAM if `CONFIG_PCNT_CTRL_FUNC_IN_IRAM`, so that it's allowed to be executed when Cache is disabled"] - #[doc = ""] - #[doc = " @return"] - #[doc = " - (): on success"] - #[doc = " - EspError: on failure"] + /// @brief Clear PCNT pulse count value to zero + /// + /// @note It's recommended to call this function after adding a watch point by `pcnt_unit_add_watch_point()`, so that the newly added watch point is effective immediately. + /// @note This function is allowed to run within ISR context + /// @note This function will be placed into IRAM if `CONFIG_PCNT_CTRL_FUNC_IN_IRAM`, so that it's allowed to be executed when Cache is disabled + /// + /// @return + /// - (): on success + /// - EspError: on failure pub fn clear_count(&self) -> Result<(), EspError> { unsafe { esp!(pcnt_unit_clear_count(self.unit)) } } - #[doc = " @brief Get PCNT count value"] - #[doc = ""] - #[doc = " @note This function is allowed to run within ISR context"] - #[doc = " @note This function will be placed into IRAM if `CONFIG_PCNT_CTRL_FUNC_IN_IRAM`, so that it's allowed to be executed when Cache is disabled"] - #[doc = ""] - #[doc = " @param[out] value Returned count value"] - #[doc = " @return"] - #[doc = " - value: on success"] - #[doc = " - EspError: on failure"] + /// @brief Get PCNT count value + /// + /// @note This function is allowed to run within ISR context + /// @note This function will be placed into IRAM if `CONFIG_PCNT_CTRL_FUNC_IN_IRAM`, so that it's allowed to be executed when Cache is disabled + /// + /// @param[out] value Returned count value + /// @return + /// - value: on success + /// - EspError: on failure pub fn get_count(&self) -> Result { let mut value: core::ffi::c_int = 0; unsafe { esp!(pcnt_unit_get_count(self.unit, &mut value))?; } Ok(value) } - #[doc = " @brief Add a watch point for PCNT unit, PCNT will generate an event when the counter value reaches the watch point value"] - #[doc = ""] - #[doc = " @param[in] watch_point Value to be watched"] - #[doc = " @return"] - #[doc = " - value: on success"] - #[doc = " - EspError: on failure"] + /// @brief Add a watch point for PCNT unit, PCNT will generate an event when the counter value reaches the watch point value + /// + /// @param[in] watch_point Value to be watched + /// @return + /// - value: on success + /// - EspError: on failure pub fn add_watch_point(&self, watch_point: i32) -> Result<(), EspError> { unsafe {esp!(pcnt_unit_add_watch_point(self.unit, watch_point))} } - #[doc = " @brief Remove a watch point for PCNT unit"] - #[doc = ""] - #[doc = " @param[in] watch_point Watch point value"] - #[doc = " @return"] - #[doc = " - value: on success"] - #[doc = " - EspError: on failure"] + /// @brief Remove a watch point for PCNT unit + /// + /// @param[in] watch_point Watch point value + /// @return + /// - value: on success + /// - EspError: on failure pub fn remove_watch_point(&self, watch_point: i32) -> Result<(), EspError> { unsafe {esp!(pcnt_unit_remove_watch_point(self.unit, watch_point))} } - #[doc = " @brief subscribe to PCNT events"] - #[doc = ""] - #[doc = " @note User registered callbacks are expected to be runnable within ISR context"] - #[doc = " @note The first call to this function needs to be before the call to `pcnt_unit_enable`"] - #[doc = " @note User can deregister a previously registered callback by calling this function and passing None."] - #[doc = ""] - #[doc = " @return"] - #[doc = " - value: on success"] - #[doc = " - EspError: on failure"] + /// @brief subscribe to PCNT events + /// + /// @note User registered callbacks are expected to be runnable within ISR context + /// @note The first call to this function needs to be before the call to `pcnt_unit_enable` + /// @note User can deregister a previously registered callback by calling this function and passing None. + /// + /// @return + /// - value: on success + /// - EspError: on failure pub fn subscribe(&mut self, callback: F ) -> Result<(), EspError> where F: FnMut(PcntWatchEventData)->bool + Send + 'static @@ -430,13 +428,13 @@ impl PcntUnit { } } - #[doc = " @brief Unsubscribe to PCNT events"] - #[doc = ""] - #[doc = " @param unit PCNT unit number"] - #[doc = ""] - #[doc = " @return"] - #[doc = " - value: on success"] - #[doc = " - EspError: on failure"] + /// @brief Unsubscribe to PCNT events + /// + /// @param unit PCNT unit number + /// + /// @return + /// - value: on success + /// - EspError: on failure pub fn unsubscribe(&mut self) -> Result<(), EspError> { if let Some(id) = self.isr_id.take() { unsafe { @@ -449,14 +447,14 @@ impl PcntUnit { Ok(()) } - #[doc = " @brief Create PCNT channel for specific unit, each PCNT has several channels associated with it"] - #[doc = ""] - #[doc = " @note This function should be called when the unit is in init state (i.e. before calling `pcnt_unit_enable()`)"] - #[doc = ""] - #[doc = " @param[in] config PCNT channel configuration"] - #[doc = " @return"] - #[doc = " - PcntChannel: on success"] - #[doc = " - EspError: on failure"] + /// @brief Create PCNT channel for specific unit, each PCNT has several channels associated with it + /// + /// @note This function should be called when the unit is in init state (i.e. before calling `pcnt_unit_enable()`) + /// + /// @param[in] config PCNT channel configuration + /// @return + /// - PcntChannel: on success + /// - EspError: on failure pub fn channel<'a>( &self, edge_pin: Option+'a>, From 499aac63d4337bbd65d48e043424e2b1a80d5ebd Mon Sep 17 00:00:00 2001 From: Christopher Liebman Date: Thu, 29 Dec 2022 08:54:49 -0800 Subject: [PATCH 30/48] small tweak to pnct example --- examples/pcnt_i64_encoder.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/examples/pcnt_i64_encoder.rs b/examples/pcnt_i64_encoder.rs index af38b287dd4..f597a434ca9 100644 --- a/examples/pcnt_i64_encoder.rs +++ b/examples/pcnt_i64_encoder.rs @@ -25,14 +25,13 @@ fn main() -> anyhow::Result<()> { info!("setup pins"); let peripherals = Peripherals::take().context("failed to take Peripherals")?; - let pin_a = peripherals.pins.gpio5; - let pin_b = peripherals.pins.gpio6; - + let mut pin_a = peripherals.pins.gpio5; + let mut pin_b = peripherals.pins.gpio6; info!("setup encoder"); #[cfg(any(feature = "pcnt", esp_idf_version_major = "4"))] - let encoder = Encoder::new(peripherals.pcnt0, pin_a, pin_b)?; + let encoder = Encoder::new(peripherals.pcnt0, &mut pin_a, &mut pin_b)?; #[cfg(not(any(feature = "pcnt", esp_idf_version_major = "4")))] - let encoder = Encoder::new(pin_a, pin_b)?; + let encoder = Encoder::new(&mut pin_a, &mut pin_b)?; let mut last_value = 0i64; loop { @@ -90,9 +89,10 @@ mod encoder { 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 From bc735a7efdbc136da80c7bccb70b778805504c5a Mon Sep 17 00:00:00 2001 From: Christopher Liebman Date: Tue, 24 Jan 2023 09:48:29 -0800 Subject: [PATCH 31/48] review fixes (the easy ones) --- examples/pcnt_i64_encoder.rs | 74 +++++++++------- src/lib.rs | 15 +++- src/pcnt.rs | 164 ++++++++++++++++++----------------- src/peripherals.rs | 93 ++++++++++++++++---- src/pulse_cnt.rs | 29 ++++--- 5 files changed, 229 insertions(+), 146 deletions(-) diff --git a/examples/pcnt_i64_encoder.rs b/examples/pcnt_i64_encoder.rs index f597a434ca9..217d7041347 100644 --- a/examples/pcnt_i64_encoder.rs +++ b/examples/pcnt_i64_encoder.rs @@ -10,7 +10,7 @@ use anyhow; use anyhow::Context; use log::*; -use esp_idf_hal::delay::FreeRtos; +use esp_idf_hal::delay::FreeRtos; use esp_idf_hal::prelude::*; use encoder::Encoder; @@ -44,14 +44,13 @@ fn main() -> anyhow::Result<()> { } } - // esp-idf encoder implementation using v4 pcnt api #[cfg(any(feature = "pcnt", esp_idf_version_major = "4"))] mod encoder { use std::cmp::min; - use std::sync::Arc; use std::sync::atomic::AtomicI64; use std::sync::atomic::Ordering; + use std::sync::Arc; use esp_idf_hal::gpio::InputPin; use esp_idf_hal::pcnt::*; @@ -70,25 +69,35 @@ mod encoder { pub fn new<'a, PCNT: Pcnt>( pcnt: impl Peripheral

+ 'd, mut pin_a: impl Peripheral

, - mut pin_b: impl Peripheral

+ mut pin_b: impl Peripheral

, ) -> Result { let mut unit = PcntDriver::new(pcnt)?; - unit.channel_config(PcntChannel::Channel0, Some(&mut pin_a), Some(&mut pin_b), &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, Some(&mut pin_b), Some(&mut pin_a), &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.channel_config( + PcntChannel::Channel0, + Some(&mut pin_a), + Some(&mut pin_b), + &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, + Some(&mut pin_b), + Some(&mut pin_a), + &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()?; @@ -115,14 +124,12 @@ mod encoder { unit.counter_clear()?; unit.counter_resume()?; - Ok(Self { - unit, - approx_value, - }) + 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; + let value = + self.approx_value.load(Ordering::Relaxed) + self.unit.get_counter_value()? as i64; Ok(value) } } @@ -131,9 +138,9 @@ mod encoder { // esp-idf v5 encoder implementation using pulse_cnt api #[cfg(not(any(feature = "pcnt", esp_idf_version_major = "4")))] mod encoder { - use std::sync::Arc; use std::sync::atomic::AtomicI64; use std::sync::atomic::Ordering; + use std::sync::Arc; use esp_idf_hal::gpio::InputPin; use esp_idf_hal::peripheral::Peripheral; @@ -150,16 +157,21 @@ mod encoder { } impl Encoder { - pub fn new(mut pin_a: impl Peripheral

, mut pin_b: impl Peripheral

) -> Result { + pub fn new( + mut pin_a: impl Peripheral

, + mut pin_b: impl Peripheral

, + ) -> Result { let mut unit = PcntUnit::new(&PcntUnitConfig { low_limit: LOW_LIMIT, high_limit: HIGH_LIMIT, ..Default::default() })?; - let channel0 = unit.channel(Some(&mut pin_a), Some(&mut pin_b), PcntChanFlags::default())?; + let channel0 = + unit.channel(Some(&mut pin_a), Some(&mut pin_b), PcntChanFlags::default())?; channel0.set_level_action(PcntLevelAction::Keep, PcntLevelAction::Inverse)?; channel0.set_edge_action(PcntEdgeAction::Decrease, PcntEdgeAction::Increase)?; - let channel1 = unit.channel(Some(&mut pin_b), Some(&mut pin_a), PcntChanFlags::default())?; + let channel1 = + unit.channel(Some(&mut pin_b), Some(&mut pin_a), PcntChanFlags::default())?; channel1.set_level_action(PcntLevelAction::Keep, PcntLevelAction::Inverse)?; channel1.set_edge_action(PcntEdgeAction::Increase, PcntEdgeAction::Decrease)?; @@ -177,7 +189,7 @@ mod encoder { unit.enable()?; unit.start()?; - + Ok(Self { unit, _channels: [channel0, channel1], @@ -189,4 +201,4 @@ mod encoder { Ok(self.approx_value.load(Ordering::SeqCst) + self.unit.get_count()? as i64) } } -} \ No newline at end of file +} diff --git a/src/lib.rs b/src/lib.rs index d129d5eb016..9b7ce93fccb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -44,13 +44,20 @@ pub mod ledc; pub mod mac; #[cfg(not(feature = "riscv-ulp-hal"))] pub mod modem; +#[cfg(all( + not(feature = "riscv-ulp-hal"), + any(feature = "pcnt4", esp_idf_version_major = "4") +))] +pub mod pcnt; pub mod peripheral; pub mod peripherals; -#[cfg(all(not(feature = "riscv-ulp-hal"), any(feature = "pcnt4", esp_idf_version_major = "4")))] -pub mod pcnt; -#[cfg(all(not(feature = "riscv-ulp-hal"), not(feature = "pcnt4"), not(esp_idf_version_major = "4")))] -pub mod pulse_cnt; pub mod prelude; +#[cfg(all( + not(feature = "riscv-ulp-hal"), + not(feature = "pcnt4"), + not(esp_idf_version_major = "4") +))] +pub mod pulse_cnt; #[cfg(not(feature = "riscv-ulp-hal"))] pub mod reset; #[cfg(not(feature = "riscv-ulp-hal"))] diff --git a/src/pcnt.rs b/src/pcnt.rs index fa7b4b5a51f..4e0ff75eb35 100644 --- a/src/pcnt.rs +++ b/src/pcnt.rs @@ -14,7 +14,7 @@ use enumset::EnumSetType; use crate::gpio::InputPin; use crate::peripheral::Peripheral; -#[derive(Debug, Copy, Clone)] +#[derive(Debug, Copy, Clone, Eq, PartialEq)] pub enum PcntChannel { Channel0, Channel1, @@ -30,7 +30,7 @@ impl From for pcnt_channel_t { } /// PCNT channel action on signal edge -#[derive(Debug, Copy, Clone, Default)] +#[derive(Debug, Copy, Clone, Default, Eq, PartialEq)] pub enum PcntCountMode { /// Hold current count value Hold, @@ -45,14 +45,18 @@ 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, + 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)] +#[derive(Debug, Copy, Clone, Default, Eq, PartialEq)] pub enum PcntControlMode { /// Keep current count mode Keep, @@ -67,7 +71,9 @@ 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::Reverse => { + pcnt_channel_level_action_t_PCNT_CHANNEL_LEVEL_ACTION_INVERSE + } PcntControlMode::Disable => pcnt_channel_level_action_t_PCNT_CHANNEL_LEVEL_ACTION_HOLD, } } @@ -130,14 +136,14 @@ impl<'d> PcntDriver<'d> { /// 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 @@ -146,22 +152,21 @@ impl<'d> PcntDriver<'d> { channel: PcntChannel, pulse_pin: Option + 'a>, ctrl_pin: Option + 'a>, - pconfig: &PcntChannelConfig + pconfig: &PcntChannelConfig, ) -> Result<(), EspError> { - let config = pcnt_config_t { pulse_gpio_num: match pulse_pin { Some(pin) => { crate::into_ref!(pin); pin.pin() - }, + } None => PCNT_PIN_NOT_USED, }, ctrl_gpio_num: match ctrl_pin { Some(pin) => { crate::into_ref!(pin); pin.pin() - }, + } None => PCNT_PIN_NOT_USED, }, lctrl_mode: pconfig.lctrl_mode.into(), @@ -174,31 +179,24 @@ impl<'d> PcntDriver<'d> { unit: self.unit, }; - unsafe { - esp!(pcnt_unit_config( - &config as *const pcnt_config_t - )) - } + 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 - ))?; + esp!(pcnt_get_counter_value(self.unit, &mut value as *mut i16))?; } Ok(value) } /// Pause PCNT counter of PCNT unit - /// + /// /// returns /// - () /// - EspError @@ -207,7 +205,7 @@ impl<'d> PcntDriver<'d> { } /// Resume counting for PCNT counter - /// + /// /// returns /// - () /// - EspError @@ -216,7 +214,7 @@ impl<'d> PcntDriver<'d> { } /// Clear and reset PCNT counter value to zero - /// + /// /// returns /// - () /// - EspError @@ -228,7 +226,7 @@ impl<'d> PcntDriver<'d> { /// @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 @@ -237,7 +235,7 @@ impl<'d> PcntDriver<'d> { } /// Disable PCNT interrupt for PCNT unit - /// + /// /// returns /// - () /// - EspError @@ -246,7 +244,7 @@ impl<'d> PcntDriver<'d> { } /// 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 @@ -258,7 +256,7 @@ impl<'d> PcntDriver<'d> { } /// 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 @@ -272,32 +270,28 @@ impl<'d> PcntDriver<'d> { 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()), + _ => 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 - )) - } + 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 @@ -315,7 +309,7 @@ impl<'d> PcntDriver<'d> { } /// Get PCNT event status of PCNT unit - /// + /// /// returns /// - i32 /// - EspError @@ -323,22 +317,19 @@ impl<'d> PcntDriver<'d> { pub fn get_event_status(&self) -> Result { let mut value = 0u32; unsafe { - esp!(pcnt_get_event_status( - self.unit, - &mut value as *mut u32 - ))?; + esp!(pcnt_get_event_status(self.unit, &mut value as *mut u32))?; } Ok(value) } /// 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 @@ -352,14 +343,14 @@ impl<'d> PcntDriver<'d> { Some(pin) => { crate::into_ref!(pin); pin.pin() - }, + } None => PCNT_PIN_NOT_USED, }; let ctrl_io_num = match ctrl_pin { Some(pin) => { crate::into_ref!(pin); pin.pin() - }, + } None => PCNT_PIN_NOT_USED, }; unsafe { @@ -373,7 +364,7 @@ impl<'d> PcntDriver<'d> { } /// Enable PCNT input filter - /// + /// /// returns /// - () /// - EspError @@ -382,7 +373,7 @@ impl<'d> PcntDriver<'d> { } /// Disable PCNT input filter - /// + /// /// returns /// - () /// - EspError @@ -391,12 +382,12 @@ impl<'d> PcntDriver<'d> { } /// 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 @@ -405,66 +396,78 @@ impl<'d> PcntDriver<'d> { } /// 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 - ))?; + 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, + pub fn set_mode( + &self, channel: PcntChannel, pos_mode: PcntCountMode, neg_mode: PcntCountMode, hctrl_mode: PcntControlMode, - lctrl_mode: PcntControlMode) -> Result<(), EspError> { + 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())) + 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: impl FnMut(u32) + Send + 'static) -> Result<(), EspError> { + pub unsafe fn subscribe( + &self, + callback: C, + ) -> Result<(), EspError> + where + C: FnMut(u32) + Send + 'static + { enable_isr_service()?; - //self.unsubscribe(); - let callback: Box = Box::new(callback); + 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, @@ -475,7 +478,7 @@ impl<'d> PcntDriver<'d> { } /// Remove ISR handler for specified unit. - /// + /// /// returns /// - () /// - EspError @@ -493,10 +496,8 @@ impl<'d> PcntDriver<'d> { 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!"); + esp!(pcnt_get_event_status(unit, &mut value as *mut u32)) + .expect("failed to fetch event status!"); f(value); } } @@ -506,7 +507,7 @@ impl Drop for PcntDriver<'_> { fn drop(&mut self) { let _ = self.counter_pause(); let _ = self.intr_disable(); - unsafe {ISR_HANDLERS[self.unit as usize] = None}; + unsafe { ISR_HANDLERS[self.unit as usize] = None }; } } @@ -522,7 +523,7 @@ fn enable_isr_service() -> Result<(), EspError> { use core::sync::atomic::Ordering; if !ISR_SERVICE_ENABLED.load(Ordering::SeqCst) { - let _ = PCNT_CS.enter(); + let _cs = PCNT_CS.enter(); if !ISR_SERVICE_ENABLED.load(Ordering::SeqCst) { esp!(unsafe { pcnt_isr_service_install(0) })?; @@ -536,7 +537,10 @@ fn enable_isr_service() -> Result<(), EspError> { #[cfg(feature = "alloc")] static mut ISR_HANDLERS: [Option>; pcnt_unit_t_PCNT_UNIT_MAX as usize] = [ - None, None, None, None, + None, + None, + None, + None, #[cfg(not(esp32s3))] None, #[cfg(not(esp32s3))] @@ -544,7 +548,7 @@ static mut ISR_HANDLERS: [Option>; pcnt_unit_t_PCNT_UNIT_MAX #[cfg(not(esp32s3))] None, #[cfg(not(esp32s3))] - None, + None, ]; pub trait Pcnt { diff --git a/src/peripherals.rs b/src/peripherals.rs index 4bd2effbcf7..6002f0c65be 100644 --- a/src/peripherals.rs +++ b/src/peripherals.rs @@ -13,7 +13,10 @@ use crate::ledc; use crate::mac; #[cfg(not(feature = "riscv-ulp-hal"))] use crate::modem; -#[cfg(all(not(feature = "riscv-ulp-hal"), any(feature = "pcnt4", esp_idf_version_major = "4")))] +#[cfg(all( + not(feature = "riscv-ulp-hal"), + any(feature = "pcnt4", esp_idf_version_major = "4") +))] use crate::pcnt; #[cfg(not(feature = "riscv-ulp-hal"))] use crate::rmt; @@ -61,21 +64,49 @@ pub struct Peripherals { pub spi3: spi::SPI3, pub adc1: adc::ADC1, pub adc2: adc::ADC2, - #[cfg(all(not(feature = "riscv-ulp-hal"), any(feature = "pcnt4", esp_idf_version_major = "4")))] + #[cfg(all( + not(feature = "riscv-ulp-hal"), + any(feature = "pcnt4", esp_idf_version_major = "4") + ))] pub pcnt0: pcnt::PCNT0, - #[cfg(all(not(feature = "riscv-ulp-hal"), any(feature = "pcnt4", esp_idf_version_major = "4")))] + #[cfg(all( + not(feature = "riscv-ulp-hal"), + any(feature = "pcnt4", esp_idf_version_major = "4") + ))] pub pcnt1: pcnt::PCNT1, - #[cfg(all(not(feature = "riscv-ulp-hal"), any(feature = "pcnt4", esp_idf_version_major = "4")))] + #[cfg(all( + not(feature = "riscv-ulp-hal"), + any(feature = "pcnt4", esp_idf_version_major = "4") + ))] pub pcnt2: pcnt::PCNT2, - #[cfg(all(not(feature = "riscv-ulp-hal"), any(feature = "pcnt4", esp_idf_version_major = "4")))] + #[cfg(all( + not(feature = "riscv-ulp-hal"), + any(feature = "pcnt4", esp_idf_version_major = "4") + ))] pub pcnt3: pcnt::PCNT3, - #[cfg(all(not(esp32s3), not(feature = "riscv-ulp-hal"), any(feature = "pcnt4", esp_idf_version_major = "4")))] + #[cfg(all( + not(esp32s3), + not(feature = "riscv-ulp-hal"), + any(feature = "pcnt4", esp_idf_version_major = "4") + ))] pub pcnt4: pcnt::PCNT4, - #[cfg(all(not(esp32s3), not(feature = "riscv-ulp-hal"), any(feature = "pcnt4", esp_idf_version_major = "4")))] + #[cfg(all( + not(esp32s3), + not(feature = "riscv-ulp-hal"), + any(feature = "pcnt4", esp_idf_version_major = "4") + ))] pub pcnt5: pcnt::PCNT5, - #[cfg(all(not(esp32s3), not(feature = "riscv-ulp-hal"), any(feature = "pcnt4", esp_idf_version_major = "4")))] + #[cfg(all( + not(esp32s3), + not(feature = "riscv-ulp-hal"), + any(feature = "pcnt4", esp_idf_version_major = "4") + ))] pub pcnt6: pcnt::PCNT6, - #[cfg(all(not(esp32s3), not(feature = "riscv-ulp-hal"), any(feature = "pcnt4", esp_idf_version_major = "4")))] + #[cfg(all( + not(esp32s3), + not(feature = "riscv-ulp-hal"), + any(feature = "pcnt4", esp_idf_version_major = "4") + ))] pub pcnt7: pcnt::PCNT7, #[cfg(all(esp32, esp_idf_version_major = "4"))] pub hall_sensor: crate::hall::HallSensor, @@ -196,21 +227,49 @@ impl Peripherals { spi3: spi::SPI3::new(), adc1: adc::ADC1::new(), adc2: adc::ADC2::new(), - #[cfg(all(not(feature = "riscv-ulp-hal"), any(feature = "pcnt4", esp_idf_version_major = "4")))] + #[cfg(all( + not(feature = "riscv-ulp-hal"), + any(feature = "pcnt4", esp_idf_version_major = "4") + ))] pcnt0: pcnt::PCNT0::new(), - #[cfg(all(not(feature = "riscv-ulp-hal"), any(feature = "pcnt4", esp_idf_version_major = "4")))] + #[cfg(all( + not(feature = "riscv-ulp-hal"), + any(feature = "pcnt4", esp_idf_version_major = "4") + ))] pcnt1: pcnt::PCNT1::new(), - #[cfg(all(not(feature = "riscv-ulp-hal"), any(feature = "pcnt4", esp_idf_version_major = "4")))] + #[cfg(all( + not(feature = "riscv-ulp-hal"), + any(feature = "pcnt4", esp_idf_version_major = "4") + ))] pcnt2: pcnt::PCNT2::new(), - #[cfg(all(not(feature = "riscv-ulp-hal"), any(feature = "pcnt4", esp_idf_version_major = "4")))] + #[cfg(all( + not(feature = "riscv-ulp-hal"), + any(feature = "pcnt4", esp_idf_version_major = "4") + ))] pcnt3: pcnt::PCNT3::new(), - #[cfg(all(not(esp32s3), not(feature = "riscv-ulp-hal"), any(feature = "pcnt4", esp_idf_version_major = "4")))] + #[cfg(all( + not(esp32s3), + not(feature = "riscv-ulp-hal"), + any(feature = "pcnt4", esp_idf_version_major = "4") + ))] pcnt4: pcnt::PCNT4::new(), - #[cfg(all(not(esp32s3), not(feature = "riscv-ulp-hal"), any(feature = "pcnt4", esp_idf_version_major = "4")))] + #[cfg(all( + not(esp32s3), + not(feature = "riscv-ulp-hal"), + any(feature = "pcnt4", esp_idf_version_major = "4") + ))] pcnt5: pcnt::PCNT5::new(), - #[cfg(all(not(esp32s3), not(feature = "riscv-ulp-hal"), any(feature = "pcnt4", esp_idf_version_major = "4")))] + #[cfg(all( + not(esp32s3), + not(feature = "riscv-ulp-hal"), + any(feature = "pcnt4", esp_idf_version_major = "4") + ))] pcnt6: pcnt::PCNT6::new(), - #[cfg(all(not(esp32s3), not(feature = "riscv-ulp-hal"), any(feature = "pcnt4", esp_idf_version_major = "4")))] + #[cfg(all( + not(esp32s3), + not(feature = "riscv-ulp-hal"), + any(feature = "pcnt4", esp_idf_version_major = "4") + ))] pcnt7: pcnt::PCNT7::new(), #[cfg(all(esp32, esp_idf_version_major = "4"))] hall_sensor: crate::hall::HallSensor::new(), diff --git a/src/pulse_cnt.rs b/src/pulse_cnt.rs index 736d1fd3bc1..3166e4aeef6 100644 --- a/src/pulse_cnt.rs +++ b/src/pulse_cnt.rs @@ -10,16 +10,16 @@ pub struct PcntFilterConfig { pub max_glitch_ns: u32, } -impl Into for &PcntFilterConfig { - fn into(self) -> pcnt_glitch_filter_config_t { +impl From<&PcntFilterConfig> for pcnt_glitch_filter_config_t { + fn from(value: &PcntFilterConfig) -> Self { pcnt_glitch_filter_config_t { - max_glitch_ns: self.max_glitch_ns + max_glitch_ns: value.max_glitch_ns } } } /// @brief PCNT unit zero cross mode -#[derive(Debug, Copy, Clone)] +#[derive(Debug, Copy, Clone, Eq, PartialEq)] pub enum PcntZeroCrossMode { ///< start from positive value, end to zero, i.e. +N->0 PosZero, @@ -64,7 +64,7 @@ impl From<*const pcnt_watch_event_data_t> for PcntWatchEventData { } } -#[derive(Debug, Copy, Clone)] +#[derive(Debug, Copy, Clone, Eq, PartialEq)] pub enum PcntEdgeAction { ///< Hold current count value Hold, @@ -74,9 +74,9 @@ pub enum PcntEdgeAction { Decrease, } -impl Into for PcntEdgeAction { - fn into(self) -> pcnt_channel_edge_action_t { - match self { +impl From for pcnt_channel_edge_action_t { + fn from(value: PcntEdgeAction) -> Self { + match value { PcntEdgeAction::Hold => esp_idf_sys::pcnt_channel_edge_action_t_PCNT_CHANNEL_EDGE_ACTION_HOLD, PcntEdgeAction::Increase => esp_idf_sys::pcnt_channel_edge_action_t_PCNT_CHANNEL_EDGE_ACTION_INCREASE, PcntEdgeAction::Decrease => esp_idf_sys::pcnt_channel_edge_action_t_PCNT_CHANNEL_EDGE_ACTION_DECREASE, @@ -84,7 +84,7 @@ impl Into for PcntEdgeAction { } } -#[derive(Debug, Copy, Clone)] +#[derive(Debug, Copy, Clone, Eq, PartialEq)] pub enum PcntLevelAction { ///< Keep current count mode Keep, @@ -94,9 +94,9 @@ pub enum PcntLevelAction { Hold, } -impl Into for PcntLevelAction { - fn into(self) -> pcnt_channel_edge_action_t { - match self { +impl From for pcnt_channel_level_action_t { + fn from(value: PcntLevelAction) -> Self { + match value { PcntLevelAction::Keep => esp_idf_sys::pcnt_channel_level_action_t_PCNT_CHANNEL_LEVEL_ACTION_KEEP, PcntLevelAction::Inverse => esp_idf_sys::pcnt_channel_level_action_t_PCNT_CHANNEL_LEVEL_ACTION_INVERSE, PcntLevelAction::Hold => esp_idf_sys::pcnt_channel_level_action_t_PCNT_CHANNEL_LEVEL_ACTION_HOLD, @@ -220,7 +220,7 @@ impl PcntUnitFlags { } /// @brief PCNT unit configuration -#[derive(Debug, Default, Copy, Clone)] +#[derive(Debug, Default, Clone)] pub struct PcntUnitConfig { /// @brief the low limit value, should be < 0 pub low_limit: i32, @@ -514,7 +514,7 @@ static mut ISR_HANDLERS: [Optionbool>>; PCNT ]; fn allocate_isr_id(callback: impl FnMut(PcntWatchEventData)->bool + 'static ) -> usize { - let _ = PCNT_CS.enter(); + let _cs = PCNT_CS.enter(); for i in 0..PCNT_UNIT_MAX { unsafe { if ISR_HANDLERS[i].is_none() { @@ -527,6 +527,7 @@ fn allocate_isr_id(callback: impl FnMut(PcntWatchEventData)->bool + 'static ) -> } fn free_isr_id(id: usize) { + let _cs = PCNT_CS.enter(); unsafe { ISR_HANDLERS[id] = None; } From 527dfb648a8b451163e652233bc598c58b2dbbff Mon Sep 17 00:00:00 2001 From: Christopher Liebman Date: Tue, 24 Jan 2023 10:02:05 -0800 Subject: [PATCH 32/48] added deps for the pcnt example (dev-dependencies) --- Cargo.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index b340c1537af..deb207bd596 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -42,7 +42,9 @@ anyhow = "1" [dev-dependencies] anyhow = "1" +esp-idf-svc = { version = "0.45.0" } esp-idf-sys = { version = "0.32", features = ["native", "binstart"] } mipidsi = "0.5.0" display-interface-spi = "0.4.1" embedded-graphics = "0.7.1" +log = { version = "0.4" } From e8804cf701de02f41ceafa90666618ae44b1f38d Mon Sep 17 00:00:00 2001 From: Christopher Liebman Date: Tue, 24 Jan 2023 10:04:34 -0800 Subject: [PATCH 33/48] cargo fmt --- src/pcnt.rs | 10 ++- src/pulse_cnt.rs | 160 ++++++++++++++++++++++++++++++----------------- 2 files changed, 105 insertions(+), 65 deletions(-) diff --git a/src/pcnt.rs b/src/pcnt.rs index 4e0ff75eb35..184228e7fa5 100644 --- a/src/pcnt.rs +++ b/src/pcnt.rs @@ -457,17 +457,15 @@ impl<'d> PcntDriver<'d> { /// - () /// - EspError #[cfg(feature = "alloc")] - pub unsafe fn subscribe( - &self, - callback: C, - ) -> Result<(), EspError> + pub unsafe fn subscribe(&self, callback: C) -> Result<(), EspError> where - C: FnMut(u32) + Send + 'static + C: FnMut(u32) + Send + 'static, { enable_isr_service()?; self.unsubscribe(); - let callback: alloc::boxed::Box = alloc::boxed::Box::new(callback); + 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, diff --git a/src/pulse_cnt.rs b/src/pulse_cnt.rs index 3166e4aeef6..682342a31c2 100644 --- a/src/pulse_cnt.rs +++ b/src/pulse_cnt.rs @@ -1,7 +1,6 @@ - -use esp_idf_sys::*; use crate::gpio::InputPin; use crate::peripheral::Peripheral; +use esp_idf_sys::*; /// @brief PCNT glitch filter configuration #[derive(Debug, Default, Copy, Clone)] @@ -13,7 +12,7 @@ pub struct PcntFilterConfig { impl From<&PcntFilterConfig> for pcnt_glitch_filter_config_t { fn from(value: &PcntFilterConfig) -> Self { pcnt_glitch_filter_config_t { - max_glitch_ns: value.max_glitch_ns + max_glitch_ns: value.max_glitch_ns, } } } @@ -34,10 +33,18 @@ pub enum PcntZeroCrossMode { impl From for PcntZeroCrossMode { fn from(x: pcnt_unit_zero_cross_mode_t) -> Self { match x { - esp_idf_sys::pcnt_unit_zero_cross_mode_t_PCNT_UNIT_ZERO_CROSS_POS_ZERO => PcntZeroCrossMode::PosZero, - esp_idf_sys::pcnt_unit_zero_cross_mode_t_PCNT_UNIT_ZERO_CROSS_NEG_ZERO => PcntZeroCrossMode::NegZero, - esp_idf_sys::pcnt_unit_zero_cross_mode_t_PCNT_UNIT_ZERO_CROSS_NEG_POS => PcntZeroCrossMode::NegPos, - esp_idf_sys::pcnt_unit_zero_cross_mode_t_PCNT_UNIT_ZERO_CROSS_POS_NEG => PcntZeroCrossMode::PosNeg, + esp_idf_sys::pcnt_unit_zero_cross_mode_t_PCNT_UNIT_ZERO_CROSS_POS_ZERO => { + PcntZeroCrossMode::PosZero + } + esp_idf_sys::pcnt_unit_zero_cross_mode_t_PCNT_UNIT_ZERO_CROSS_NEG_ZERO => { + PcntZeroCrossMode::NegZero + } + esp_idf_sys::pcnt_unit_zero_cross_mode_t_PCNT_UNIT_ZERO_CROSS_NEG_POS => { + PcntZeroCrossMode::NegPos + } + esp_idf_sys::pcnt_unit_zero_cross_mode_t_PCNT_UNIT_ZERO_CROSS_POS_NEG => { + PcntZeroCrossMode::PosNeg + } _ => panic!("unknown pcnt_unit_zero_cross_mode_t value {}", x), } } @@ -77,9 +84,15 @@ pub enum PcntEdgeAction { impl From for pcnt_channel_edge_action_t { fn from(value: PcntEdgeAction) -> Self { match value { - PcntEdgeAction::Hold => esp_idf_sys::pcnt_channel_edge_action_t_PCNT_CHANNEL_EDGE_ACTION_HOLD, - PcntEdgeAction::Increase => esp_idf_sys::pcnt_channel_edge_action_t_PCNT_CHANNEL_EDGE_ACTION_INCREASE, - PcntEdgeAction::Decrease => esp_idf_sys::pcnt_channel_edge_action_t_PCNT_CHANNEL_EDGE_ACTION_DECREASE, + PcntEdgeAction::Hold => { + esp_idf_sys::pcnt_channel_edge_action_t_PCNT_CHANNEL_EDGE_ACTION_HOLD + } + PcntEdgeAction::Increase => { + esp_idf_sys::pcnt_channel_edge_action_t_PCNT_CHANNEL_EDGE_ACTION_INCREASE + } + PcntEdgeAction::Decrease => { + esp_idf_sys::pcnt_channel_edge_action_t_PCNT_CHANNEL_EDGE_ACTION_DECREASE + } } } } @@ -97,9 +110,15 @@ pub enum PcntLevelAction { impl From for pcnt_channel_level_action_t { fn from(value: PcntLevelAction) -> Self { match value { - PcntLevelAction::Keep => esp_idf_sys::pcnt_channel_level_action_t_PCNT_CHANNEL_LEVEL_ACTION_KEEP, - PcntLevelAction::Inverse => esp_idf_sys::pcnt_channel_level_action_t_PCNT_CHANNEL_LEVEL_ACTION_INVERSE, - PcntLevelAction::Hold => esp_idf_sys::pcnt_channel_level_action_t_PCNT_CHANNEL_LEVEL_ACTION_HOLD, + PcntLevelAction::Keep => { + esp_idf_sys::pcnt_channel_level_action_t_PCNT_CHANNEL_LEVEL_ACTION_KEEP + } + PcntLevelAction::Inverse => { + esp_idf_sys::pcnt_channel_level_action_t_PCNT_CHANNEL_LEVEL_ACTION_INVERSE + } + PcntLevelAction::Hold => { + esp_idf_sys::pcnt_channel_level_action_t_PCNT_CHANNEL_LEVEL_ACTION_HOLD + } } } } @@ -173,9 +192,17 @@ impl PcntChannel { /// @return /// - (): on success /// - EspError: on failure - pub fn set_edge_action(&self,pos_act: PcntEdgeAction, neg_act: PcntEdgeAction) -> Result<(), EspError> { + pub fn set_edge_action( + &self, + pos_act: PcntEdgeAction, + neg_act: PcntEdgeAction, + ) -> Result<(), EspError> { unsafe { - esp!(pcnt_channel_set_edge_action(self.0, pos_act.into(), neg_act.into())) + esp!(pcnt_channel_set_edge_action( + self.0, + pos_act.into(), + neg_act.into() + )) } } @@ -189,9 +216,17 @@ impl PcntChannel { /// @return /// - (): on success /// - EspError: on failure - pub fn set_level_action(&self, high_act: PcntLevelAction, low_act: PcntLevelAction) -> Result<(), EspError> { + pub fn set_level_action( + &self, + high_act: PcntLevelAction, + low_act: PcntLevelAction, + ) -> Result<(), EspError> { unsafe { - esp!(pcnt_channel_set_level_action(self.0, high_act.into(), low_act.into())) + esp!(pcnt_channel_set_level_action( + self.0, + high_act.into(), + low_act.into() + )) } } } @@ -255,10 +290,7 @@ impl PcntUnit { unsafe { esp!(pcnt_new_unit(&config, &mut unit))?; } - Ok(Self { - unit, - isr_id: None, - }) + Ok(Self { unit, isr_id: None }) } /// @brief Set glitch filter for PCNT unit @@ -274,10 +306,13 @@ impl PcntUnit { /// - EspError: on failure pub fn set_filter(&self, config: Option<&PcntFilterConfig>) -> Result<(), EspError> { unsafe { - esp!(pcnt_unit_set_glitch_filter(self.unit, match config { - Some(x) => &x.into(), - None => std::ptr::null(), - })) + esp!(pcnt_unit_set_glitch_filter( + self.unit, + match config { + Some(x) => &x.into(), + None => std::ptr::null(), + } + )) } } @@ -292,9 +327,7 @@ impl PcntUnit { /// - (): on success /// - EspError: on failure pub fn enable(&self) -> Result<(), EspError> { - unsafe { - esp!(pcnt_unit_enable(self.unit)) - } + unsafe { esp!(pcnt_unit_enable(self.unit)) } } /// @brief Disable the PCNT unit @@ -306,9 +339,7 @@ impl PcntUnit { /// - (): on success /// - EspError: on failure pub fn disable(&self) -> Result<(), EspError> { - unsafe { - esp!(pcnt_unit_disable(self.unit)) - } + unsafe { esp!(pcnt_unit_disable(self.unit)) } } /// @brief Start the PCNT unit, the counter will start to count according to the edge and/or level input signals @@ -321,9 +352,7 @@ impl PcntUnit { /// - (): on success /// - EspError: on failure pub fn start(&self) -> Result<(), EspError> { - unsafe { - esp!(pcnt_unit_start(self.unit)) - } + unsafe { esp!(pcnt_unit_start(self.unit)) } } /// @brief Stop PCNT from counting @@ -337,9 +366,7 @@ impl PcntUnit { /// - (): on success /// - EspError: on failure pub fn stop(&self) -> Result<(), EspError> { - unsafe { - esp!(pcnt_unit_stop(self.unit)) - } + unsafe { esp!(pcnt_unit_stop(self.unit)) } } /// @brief Clear PCNT pulse count value to zero @@ -352,9 +379,7 @@ impl PcntUnit { /// - (): on success /// - EspError: on failure pub fn clear_count(&self) -> Result<(), EspError> { - unsafe { - esp!(pcnt_unit_clear_count(self.unit)) - } + unsafe { esp!(pcnt_unit_clear_count(self.unit)) } } /// @brief Get PCNT count value @@ -368,7 +393,9 @@ impl PcntUnit { /// - EspError: on failure pub fn get_count(&self) -> Result { let mut value: core::ffi::c_int = 0; - unsafe { esp!(pcnt_unit_get_count(self.unit, &mut value))?; } + unsafe { + esp!(pcnt_unit_get_count(self.unit, &mut value))?; + } Ok(value) } @@ -379,7 +406,7 @@ impl PcntUnit { /// - value: on success /// - EspError: on failure pub fn add_watch_point(&self, watch_point: i32) -> Result<(), EspError> { - unsafe {esp!(pcnt_unit_add_watch_point(self.unit, watch_point))} + unsafe { esp!(pcnt_unit_add_watch_point(self.unit, watch_point)) } } /// @brief Remove a watch point for PCNT unit @@ -389,7 +416,7 @@ impl PcntUnit { /// - value: on success /// - EspError: on failure pub fn remove_watch_point(&self, watch_point: i32) -> Result<(), EspError> { - unsafe {esp!(pcnt_unit_remove_watch_point(self.unit, watch_point))} + unsafe { esp!(pcnt_unit_remove_watch_point(self.unit, watch_point)) } } /// @brief subscribe to PCNT events @@ -401,9 +428,9 @@ impl PcntUnit { /// @return /// - value: on success /// - EspError: on failure - pub fn subscribe(&mut self, callback: F ) -> Result<(), EspError> + pub fn subscribe(&mut self, callback: F) -> Result<(), EspError> where - F: FnMut(PcntWatchEventData)->bool + Send + 'static + F: FnMut(PcntWatchEventData) -> bool + Send + 'static, { let id = match self.isr_id { Some(x) => x, @@ -417,10 +444,20 @@ impl PcntUnit { on_reach: Some(Self::handle_isr), }; let data = id as *mut core::ffi::c_void; - unsafe {esp!(pcnt_unit_register_event_callbacks(self.unit, &cbs, data as *mut core::ffi::c_void))} + unsafe { + esp!(pcnt_unit_register_event_callbacks( + self.unit, + &cbs, + data as *mut core::ffi::c_void + )) + } } - unsafe extern "C" fn handle_isr(_unit: pcnt_unit_handle_t, edata: *const pcnt_watch_event_data_t, data: *mut core::ffi::c_void) -> bool { + unsafe extern "C" fn handle_isr( + _unit: pcnt_unit_handle_t, + edata: *const pcnt_watch_event_data_t, + data: *mut core::ffi::c_void, + ) -> bool { let id = data as usize; match &mut ISR_HANDLERS[id] { Some(f) => f(edata.into()), @@ -438,9 +475,11 @@ impl PcntUnit { pub fn unsubscribe(&mut self) -> Result<(), EspError> { if let Some(id) = self.isr_id.take() { unsafe { - esp!(pcnt_unit_register_event_callbacks(self.unit, &pcnt_event_callbacks_t { - on_reach: None, - }, 0 as *mut core::ffi::c_void))?; + esp!(pcnt_unit_register_event_callbacks( + self.unit, + &pcnt_event_callbacks_t { on_reach: None }, + 0 as *mut core::ffi::c_void + ))?; } free_isr_id(id); } @@ -457,23 +496,23 @@ impl PcntUnit { /// - EspError: on failure pub fn channel<'a>( &self, - edge_pin: Option+'a>, - level_pin: Option+'a>, - flags: PcntChanFlags + edge_pin: Option + 'a>, + level_pin: Option + 'a>, + flags: PcntChanFlags, ) -> Result { let config = pcnt_chan_config_t { edge_gpio_num: match edge_pin { Some(pin) => { crate::into_ref!(pin); pin.pin() - }, + } None => -1, }, level_gpio_num: match level_pin { Some(pin) => { crate::into_ref!(pin); pin.pin() - }, + } None => -1, }, flags: flags.0, @@ -501,8 +540,11 @@ const PCNT_UNIT_MAX: usize = 4; #[cfg(not(esp32s3))] const PCNT_UNIT_MAX: usize = 8; -static mut ISR_HANDLERS: [Optionbool>>; PCNT_UNIT_MAX] = [ - None, None, None, None, +static mut ISR_HANDLERS: [Option bool>>; PCNT_UNIT_MAX] = [ + None, + None, + None, + None, #[cfg(not(esp32s3))] None, #[cfg(not(esp32s3))] @@ -510,10 +552,10 @@ static mut ISR_HANDLERS: [Optionbool>>; PCNT #[cfg(not(esp32s3))] None, #[cfg(not(esp32s3))] - None, + None, ]; -fn allocate_isr_id(callback: impl FnMut(PcntWatchEventData)->bool + 'static ) -> usize { +fn allocate_isr_id(callback: impl FnMut(PcntWatchEventData) -> bool + 'static) -> usize { let _cs = PCNT_CS.enter(); for i in 0..PCNT_UNIT_MAX { unsafe { From ec63abd415340f466574e6dc25bd6c4b9a1cc2ef Mon Sep 17 00:00:00 2001 From: Christopher Liebman Date: Tue, 24 Jan 2023 10:26:37 -0800 Subject: [PATCH 34/48] pub const fn new for PcntChanFlags --- src/pulse_cnt.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/pulse_cnt.rs b/src/pulse_cnt.rs index 682342a31c2..24c34f78c63 100644 --- a/src/pulse_cnt.rs +++ b/src/pulse_cnt.rs @@ -127,6 +127,15 @@ impl From for pcnt_channel_level_action_t { pub struct PcntChanFlags(pcnt_chan_config_t__bindgen_ty_1); impl PcntChanFlags { + pub const fn new() -> Self { + // uglyness due to bindgen bitfield generation + Self(pcnt_chan_config_t__bindgen_ty_1 { + _bitfield_align_1: [0u8; 0], + _bitfield_1: __BindgenBitfieldUnit::new([0u8; 1]), + __bindgen_padding_0: [0u8; 3], + }) + } + #[inline] pub fn invert_edge_input(&self) -> u32 { self.0.invert_edge_input() From e6f235bf853d1c8be4c1e624bb2edfcb5710a91b Mon Sep 17 00:00:00 2001 From: Christopher Liebman Date: Tue, 24 Jan 2023 10:28:44 -0800 Subject: [PATCH 35/48] pub const fn new for PcntUnitFlags --- src/pulse_cnt.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/pulse_cnt.rs b/src/pulse_cnt.rs index 24c34f78c63..cb79a01b7d5 100644 --- a/src/pulse_cnt.rs +++ b/src/pulse_cnt.rs @@ -251,6 +251,15 @@ impl Drop for PcntChannel { #[derive(Debug, Default, Copy, Clone)] pub struct PcntUnitFlags(pcnt_unit_config_t__bindgen_ty_1); impl PcntUnitFlags { + pub const fn new() -> Self { + // uglyness due to bindgen bitfield generation + Self(pcnt_unit_config_t__bindgen_ty_1 { + _bitfield_align_1: [0u8; 0], + _bitfield_1: __BindgenBitfieldUnit::new([0u8; 1]), + __bindgen_padding_0: [0u8; 3], + }) + } + #[inline] pub fn accum_count(&self) -> u32 { self.0.accum_count() From 8bbedb72c672b9e93960314a88b2b3c0ff6a996f Mon Sep 17 00:00:00 2001 From: Christopher Liebman Date: Tue, 24 Jan 2023 15:04:22 -0800 Subject: [PATCH 36/48] fix compilation for esp-idf 4 --- src/pcnt.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pcnt.rs b/src/pcnt.rs index 184228e7fa5..32b8e03b34b 100644 --- a/src/pcnt.rs +++ b/src/pcnt.rs @@ -457,13 +457,13 @@ impl<'d> PcntDriver<'d> { /// - () /// - EspError #[cfg(feature = "alloc")] - pub unsafe fn subscribe(&self, callback: C) -> Result<(), EspError> + pub unsafe fn subscribe(&self, callback: C) -> Result<(), EspError> where C: FnMut(u32) + Send + 'static, { enable_isr_service()?; - self.unsubscribe(); + self.unsubscribe()?; let callback: alloc::boxed::Box = alloc::boxed::Box::new(callback); ISR_HANDLERS[self.unit as usize] = Some(callback); From 82b118f8232186cf2611b81c1d03468e103f2bc9 Mon Sep 17 00:00:00 2001 From: Christopher Liebman Date: Wed, 25 Jan 2023 09:07:24 -0800 Subject: [PATCH 37/48] pass in all pins in PcntDriver::new use PinIndex to specify pins to a channel --- examples/pcnt_i64_encoder.rs | 21 ++++-- src/pcnt.rs | 135 ++++++++++++++++++++--------------- 2 files changed, 90 insertions(+), 66 deletions(-) diff --git a/examples/pcnt_i64_encoder.rs b/examples/pcnt_i64_encoder.rs index 217d7041347..e6bb07e4afd 100644 --- a/examples/pcnt_i64_encoder.rs +++ b/examples/pcnt_i64_encoder.rs @@ -52,6 +52,7 @@ mod encoder { 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; @@ -68,14 +69,20 @@ mod encoder { impl<'d> Encoder<'d> { pub fn new<'a, PCNT: Pcnt>( pcnt: impl Peripheral

+ 'd, - mut pin_a: impl Peripheral

, - mut pin_b: impl Peripheral

, + pin_a: impl Peripheral

+ 'd, + pin_b: impl Peripheral

+ 'd, ) -> Result { - let mut unit = PcntDriver::new(pcnt)?; + let mut unit = PcntDriver::new( + pcnt, + Some(pin_a), + Some(pin_b), + Option::::None, + Option::::None, + )?; unit.channel_config( PcntChannel::Channel0, - Some(&mut pin_a), - Some(&mut pin_b), + PinIndex::Pin0, + PinIndex::Pin1, &mut PcntChannelConfig { lctrl_mode: PcntControlMode::Reverse, hctrl_mode: PcntControlMode::Keep, @@ -87,8 +94,8 @@ mod encoder { )?; unit.channel_config( PcntChannel::Channel1, - Some(&mut pin_b), - Some(&mut pin_a), + PinIndex::Pin1, + PinIndex::Pin0, &mut PcntChannelConfig { lctrl_mode: PcntControlMode::Reverse, hctrl_mode: PcntControlMode::Keep, diff --git a/src/pcnt.rs b/src/pcnt.rs index 32b8e03b34b..fcb9ab947dc 100644 --- a/src/pcnt.rs +++ b/src/pcnt.rs @@ -119,16 +119,62 @@ impl PcntChannelConfig { } } -#[derive(Debug)] +#[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 ()>, } impl<'d> PcntDriver<'d> { - pub fn new(_pcnt: impl Peripheral

+ 'd) -> Result { + 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 = [ + match pin0 { + Some(pin) => { + crate::into_ref!(pin); + pin.pin() + } + None => PCNT_PIN_NOT_USED, + }, + match pin1 { + Some(pin) => { + crate::into_ref!(pin); + pin.pin() + } + None => PCNT_PIN_NOT_USED, + }, + match pin2 { + Some(pin) => { + crate::into_ref!(pin); + pin.pin() + } + None => PCNT_PIN_NOT_USED, + }, + match pin3 { + Some(pin) => { + crate::into_ref!(pin); + pin.pin() + } + None => PCNT_PIN_NOT_USED, + }, + ]; Ok(Self { unit: PCNT::unit(), + pins, _p: PhantomData, }) } @@ -150,25 +196,13 @@ impl<'d> PcntDriver<'d> { pub fn channel_config<'a>( &mut self, channel: PcntChannel, - pulse_pin: Option + 'a>, - ctrl_pin: Option + 'a>, + pulse_pin: PinIndex, + ctrl_pin: PinIndex, pconfig: &PcntChannelConfig, ) -> Result<(), EspError> { let config = pcnt_config_t { - pulse_gpio_num: match pulse_pin { - Some(pin) => { - crate::into_ref!(pin); - pin.pin() - } - None => PCNT_PIN_NOT_USED, - }, - ctrl_gpio_num: match ctrl_pin { - Some(pin) => { - crate::into_ref!(pin); - pin.pin() - } - None => PCNT_PIN_NOT_USED, - }, + 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(), @@ -322,46 +356,26 @@ impl<'d> PcntDriver<'d> { Ok(value) } - /// 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> { - let pulse_io_num = match pulse_pin { - Some(pin) => { - crate::into_ref!(pin); - pin.pin() - } - None => PCNT_PIN_NOT_USED, - }; - let ctrl_io_num = match ctrl_pin { - Some(pin) => { - crate::into_ref!(pin); - pin.pin() - } - None => PCNT_PIN_NOT_USED, - }; - unsafe { - esp!(pcnt_set_pin( - self.unit, - channel.into(), - pulse_io_num, - ctrl_io_num - )) - } - } + // 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 /// @@ -505,7 +519,10 @@ impl Drop for PcntDriver<'_> { fn drop(&mut self) { let _ = self.counter_pause(); let _ = self.intr_disable(); - unsafe { ISR_HANDLERS[self.unit as usize] = None }; + unsafe { + pcnt_isr_handler_remove(self.unit); + ISR_HANDLERS[self.unit as usize] = None + }; } } From 5fa2f06faefec248bb38e35de6bf27ae9f391e16 Mon Sep 17 00:00:00 2001 From: Christopher Liebman Date: Wed, 25 Jan 2023 09:12:10 -0800 Subject: [PATCH 38/48] remove unused lifetime --- examples/pcnt_i64_encoder.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/pcnt_i64_encoder.rs b/examples/pcnt_i64_encoder.rs index e6bb07e4afd..86a6c89210a 100644 --- a/examples/pcnt_i64_encoder.rs +++ b/examples/pcnt_i64_encoder.rs @@ -67,7 +67,7 @@ mod encoder { } impl<'d> Encoder<'d> { - pub fn new<'a, PCNT: Pcnt>( + pub fn new( pcnt: impl Peripheral

+ 'd, pin_a: impl Peripheral

+ 'd, pin_b: impl Peripheral

+ 'd, From a380f72adb64f751984a559565d8e633e27d0b20 Mon Sep 17 00:00:00 2001 From: Christopher Liebman Date: Wed, 25 Jan 2023 09:29:33 -0800 Subject: [PATCH 39/48] use a simple macro to convert Option<> pin to a pin number --- src/pcnt.rs | 44 ++++++++++++++++---------------------------- 1 file changed, 16 insertions(+), 28 deletions(-) diff --git a/src/pcnt.rs b/src/pcnt.rs index fcb9ab947dc..8649b78fd35 100644 --- a/src/pcnt.rs +++ b/src/pcnt.rs @@ -133,6 +133,18 @@ pub struct PcntDriver<'d> { _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, @@ -143,34 +155,10 @@ impl<'d> PcntDriver<'d> { ) -> Result { // consume the pins and keep only the pin number. let pins = [ - match pin0 { - Some(pin) => { - crate::into_ref!(pin); - pin.pin() - } - None => PCNT_PIN_NOT_USED, - }, - match pin1 { - Some(pin) => { - crate::into_ref!(pin); - pin.pin() - } - None => PCNT_PIN_NOT_USED, - }, - match pin2 { - Some(pin) => { - crate::into_ref!(pin); - pin.pin() - } - None => PCNT_PIN_NOT_USED, - }, - match pin3 { - Some(pin) => { - crate::into_ref!(pin); - pin.pin() - } - None => PCNT_PIN_NOT_USED, - }, + pin_to_number!(pin0), + pin_to_number!(pin1), + pin_to_number!(pin2), + pin_to_number!(pin3), ]; Ok(Self { unit: PCNT::unit(), From 82285fddfb23f0fa2802aeb5bd1cb748c0a2d722 Mon Sep 17 00:00:00 2001 From: Christopher Liebman Date: Sat, 28 Jan 2023 06:46:05 -0800 Subject: [PATCH 40/48] cargo fmt --- src/pcnt.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pcnt.rs b/src/pcnt.rs index 8649b78fd35..0f66696e159 100644 --- a/src/pcnt.rs +++ b/src/pcnt.rs @@ -139,10 +139,10 @@ macro_rules! pin_to_number { Some(pin) => { crate::into_ref!(pin); pin.pin() - }, + } None => PCNT_PIN_NOT_USED, } - } + }; } impl<'d> PcntDriver<'d> { From 024aaf4cf3b883e8517b45973d545eaf961fda8c Mon Sep 17 00:00:00 2001 From: Christopher Liebman Date: Sun, 29 Jan 2023 09:09:47 -0800 Subject: [PATCH 41/48] remove V5 for now fix #cfg's for when/where pcnt is available --- Cargo.toml | 4 - examples/pcnt_i64_encoder.rs | 72 ----- src/lib.rs | 11 +- src/pcnt.rs | 6 +- src/peripherals.rs | 44 +-- src/pulse_cnt.rs | 594 ----------------------------------- 6 files changed, 13 insertions(+), 718 deletions(-) delete mode 100644 src/pulse_cnt.rs diff --git a/Cargo.toml b/Cargo.toml index deb207bd596..57bc17d8cc1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,10 +18,6 @@ default = ["std", "alloc", "esp-idf-sys"] std = ["alloc", "esp-idf-sys/std", "edge-executor?/std"] alloc = [] riscv-ulp-hal = [] -# by default esp-idf-sys includes driver/pulse_cnt.h when enabled driver/pcnt.h -# will be included instead (only used when compiling esp-idf > 4, esp-idf 4 always -# incudes driver/pcnt.h) -pcnt4 = ["esp-idf-sys/pcnt4"] [dependencies] nb = "1.0.0" diff --git a/examples/pcnt_i64_encoder.rs b/examples/pcnt_i64_encoder.rs index 86a6c89210a..124513332e9 100644 --- a/examples/pcnt_i64_encoder.rs +++ b/examples/pcnt_i64_encoder.rs @@ -28,10 +28,7 @@ fn main() -> anyhow::Result<()> { let mut pin_a = peripherals.pins.gpio5; let mut pin_b = peripherals.pins.gpio6; info!("setup encoder"); - #[cfg(any(feature = "pcnt", esp_idf_version_major = "4"))] let encoder = Encoder::new(peripherals.pcnt0, &mut pin_a, &mut pin_b)?; - #[cfg(not(any(feature = "pcnt", esp_idf_version_major = "4")))] - let encoder = Encoder::new(&mut pin_a, &mut pin_b)?; let mut last_value = 0i64; loop { @@ -45,7 +42,6 @@ fn main() -> anyhow::Result<()> { } // esp-idf encoder implementation using v4 pcnt api -#[cfg(any(feature = "pcnt", esp_idf_version_major = "4"))] mod encoder { use std::cmp::min; use std::sync::atomic::AtomicI64; @@ -141,71 +137,3 @@ mod encoder { } } } - -// esp-idf v5 encoder implementation using pulse_cnt api -#[cfg(not(any(feature = "pcnt", esp_idf_version_major = "4")))] -mod encoder { - use std::sync::atomic::AtomicI64; - use std::sync::atomic::Ordering; - use std::sync::Arc; - - use esp_idf_hal::gpio::InputPin; - use esp_idf_hal::peripheral::Peripheral; - use esp_idf_hal::pulse_cnt::*; - use esp_idf_sys::EspError; - - const LOW_LIMIT: i32 = -100; - const HIGH_LIMIT: i32 = 100; - - pub struct Encoder { - unit: PcntUnit, - _channels: [PcntChannel; 2], // we don't use but don't want to drop - approx_value: Arc, - } - - impl Encoder { - pub fn new( - mut pin_a: impl Peripheral

, - mut pin_b: impl Peripheral

, - ) -> Result { - let mut unit = PcntUnit::new(&PcntUnitConfig { - low_limit: LOW_LIMIT, - high_limit: HIGH_LIMIT, - ..Default::default() - })?; - let channel0 = - unit.channel(Some(&mut pin_a), Some(&mut pin_b), PcntChanFlags::default())?; - channel0.set_level_action(PcntLevelAction::Keep, PcntLevelAction::Inverse)?; - channel0.set_edge_action(PcntEdgeAction::Decrease, PcntEdgeAction::Increase)?; - let channel1 = - unit.channel(Some(&mut pin_b), Some(&mut pin_a), PcntChanFlags::default())?; - channel1.set_level_action(PcntLevelAction::Keep, PcntLevelAction::Inverse)?; - channel1.set_edge_action(PcntEdgeAction::Increase, PcntEdgeAction::Decrease)?; - - unit.add_watch_point(LOW_LIMIT)?; - unit.add_watch_point(HIGH_LIMIT)?; - - let approx_value = Arc::new(AtomicI64::new(0)); - { - let approx_value = approx_value.clone(); - unit.subscribe(move |event| { - approx_value.fetch_add(event.watch_point_value.into(), Ordering::SeqCst); - false // no high priority task woken - })?; - } - - unit.enable()?; - unit.start()?; - - Ok(Self { - unit, - _channels: [channel0, channel1], - approx_value, - }) - } - - pub fn get_value(&self) -> Result { - Ok(self.approx_value.load(Ordering::SeqCst) + self.unit.get_count()? as i64) - } - } -} diff --git a/src/lib.rs b/src/lib.rs index 9b7ce93fccb..573ed0ee973 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -44,20 +44,11 @@ pub mod ledc; pub mod mac; #[cfg(not(feature = "riscv-ulp-hal"))] pub mod modem; -#[cfg(all( - not(feature = "riscv-ulp-hal"), - any(feature = "pcnt4", esp_idf_version_major = "4") -))] +#[cfg(all(not(feature = "riscv-ulp-hal"), any(esp32, esp32s2, esp32s3)))] pub mod pcnt; pub mod peripheral; pub mod peripherals; pub mod prelude; -#[cfg(all( - not(feature = "riscv-ulp-hal"), - not(feature = "pcnt4"), - not(esp_idf_version_major = "4") -))] -pub mod pulse_cnt; #[cfg(not(feature = "riscv-ulp-hal"))] pub mod reset; #[cfg(not(feature = "riscv-ulp-hal"))] diff --git a/src/pcnt.rs b/src/pcnt.rs index 0f66696e159..3382bf8eaca 100644 --- a/src/pcnt.rs +++ b/src/pcnt.rs @@ -181,7 +181,7 @@ impl<'d> PcntDriver<'d> { /// returns /// - () /// - EspError - pub fn channel_config<'a>( + pub fn channel_config( &mut self, channel: PcntChannel, pulse_pin: PinIndex, @@ -539,7 +539,9 @@ fn enable_isr_service() -> Result<(), EspError> { } #[cfg(feature = "alloc")] -static mut ISR_HANDLERS: [Option>; pcnt_unit_t_PCNT_UNIT_MAX as usize] = [ +type IsrHandler = Option>; +#[cfg(feature = "alloc")] +static mut ISR_HANDLERS: [IsrHandler; pcnt_unit_t_PCNT_UNIT_MAX as usize] = [ None, None, None, diff --git a/src/peripherals.rs b/src/peripherals.rs index 6002f0c65be..33bec4b2a46 100644 --- a/src/peripherals.rs +++ b/src/peripherals.rs @@ -64,49 +64,21 @@ pub struct Peripherals { pub spi3: spi::SPI3, pub adc1: adc::ADC1, pub adc2: adc::ADC2, - #[cfg(all( - not(feature = "riscv-ulp-hal"), - any(feature = "pcnt4", esp_idf_version_major = "4") - ))] + #[cfg(all(not(feature = "riscv-ulp-hal"), any(esp32, esp32s2, esp32s3)))] pub pcnt0: pcnt::PCNT0, - #[cfg(all( - not(feature = "riscv-ulp-hal"), - any(feature = "pcnt4", esp_idf_version_major = "4") - ))] + #[cfg(all(not(feature = "riscv-ulp-hal"), any(esp32, esp32s2, esp32s3)))] pub pcnt1: pcnt::PCNT1, - #[cfg(all( - not(feature = "riscv-ulp-hal"), - any(feature = "pcnt4", esp_idf_version_major = "4") - ))] + #[cfg(all(not(feature = "riscv-ulp-hal"), any(esp32, esp32s2, esp32s3)))] pub pcnt2: pcnt::PCNT2, - #[cfg(all( - not(feature = "riscv-ulp-hal"), - any(feature = "pcnt4", esp_idf_version_major = "4") - ))] + #[cfg(all(not(feature = "riscv-ulp-hal"), any(esp32, esp32s2, esp32s3)))] pub pcnt3: pcnt::PCNT3, - #[cfg(all( - not(esp32s3), - not(feature = "riscv-ulp-hal"), - any(feature = "pcnt4", esp_idf_version_major = "4") - ))] + #[cfg(all(not(feature = "riscv-ulp-hal"), any(esp32)))] pub pcnt4: pcnt::PCNT4, - #[cfg(all( - not(esp32s3), - not(feature = "riscv-ulp-hal"), - any(feature = "pcnt4", esp_idf_version_major = "4") - ))] + #[cfg(all(not(feature = "riscv-ulp-hal"), any(esp32)))] pub pcnt5: pcnt::PCNT5, - #[cfg(all( - not(esp32s3), - not(feature = "riscv-ulp-hal"), - any(feature = "pcnt4", esp_idf_version_major = "4") - ))] + #[cfg(all(not(feature = "riscv-ulp-hal"), any(esp32)))] pub pcnt6: pcnt::PCNT6, - #[cfg(all( - not(esp32s3), - not(feature = "riscv-ulp-hal"), - any(feature = "pcnt4", esp_idf_version_major = "4") - ))] + #[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, diff --git a/src/pulse_cnt.rs b/src/pulse_cnt.rs deleted file mode 100644 index cb79a01b7d5..00000000000 --- a/src/pulse_cnt.rs +++ /dev/null @@ -1,594 +0,0 @@ -use crate::gpio::InputPin; -use crate::peripheral::Peripheral; -use esp_idf_sys::*; - -/// @brief PCNT glitch filter configuration -#[derive(Debug, Default, Copy, Clone)] -pub struct PcntFilterConfig { - ///< Pulse width smaller than this threshold will be treated as glitch and ignored, in the unit of ns - pub max_glitch_ns: u32, -} - -impl From<&PcntFilterConfig> for pcnt_glitch_filter_config_t { - fn from(value: &PcntFilterConfig) -> Self { - pcnt_glitch_filter_config_t { - max_glitch_ns: value.max_glitch_ns, - } - } -} - -/// @brief PCNT unit zero cross mode -#[derive(Debug, Copy, Clone, Eq, PartialEq)] -pub enum PcntZeroCrossMode { - ///< start from positive value, end to zero, i.e. +N->0 - PosZero, - ///< start from negative value, end to zero, i.e. -N->0 - NegZero, - ///< start from negative value, end to positive value, i.e. -N->+M - NegPos, - ///< start from positive value, end to negative value, i.e. +N->-M - PosNeg, -} - -impl From for PcntZeroCrossMode { - fn from(x: pcnt_unit_zero_cross_mode_t) -> Self { - match x { - esp_idf_sys::pcnt_unit_zero_cross_mode_t_PCNT_UNIT_ZERO_CROSS_POS_ZERO => { - PcntZeroCrossMode::PosZero - } - esp_idf_sys::pcnt_unit_zero_cross_mode_t_PCNT_UNIT_ZERO_CROSS_NEG_ZERO => { - PcntZeroCrossMode::NegZero - } - esp_idf_sys::pcnt_unit_zero_cross_mode_t_PCNT_UNIT_ZERO_CROSS_NEG_POS => { - PcntZeroCrossMode::NegPos - } - esp_idf_sys::pcnt_unit_zero_cross_mode_t_PCNT_UNIT_ZERO_CROSS_POS_NEG => { - PcntZeroCrossMode::PosNeg - } - _ => panic!("unknown pcnt_unit_zero_cross_mode_t value {}", x), - } - } -} - -/// @brief PCNT watch event data -#[derive(Debug, Copy, Clone)] -#[allow(dead_code)] -pub struct PcntWatchEventData { - ///< Watch point value that triggered the event - pub watch_point_value: i32, - ///< Zero cross mode - pub zero_cross_mode: PcntZeroCrossMode, -} - -impl From<*const pcnt_watch_event_data_t> for PcntWatchEventData { - fn from(x: *const pcnt_watch_event_data_t) -> Self { - unsafe { - Self { - watch_point_value: (*x).watch_point_value, - zero_cross_mode: (*x).zero_cross_mode.into(), - } - } - } -} - -#[derive(Debug, Copy, Clone, Eq, PartialEq)] -pub enum PcntEdgeAction { - ///< Hold current count value - Hold, - ///< Increase count value - Increase, - ///< Decrease count value - Decrease, -} - -impl From for pcnt_channel_edge_action_t { - fn from(value: PcntEdgeAction) -> Self { - match value { - PcntEdgeAction::Hold => { - esp_idf_sys::pcnt_channel_edge_action_t_PCNT_CHANNEL_EDGE_ACTION_HOLD - } - PcntEdgeAction::Increase => { - esp_idf_sys::pcnt_channel_edge_action_t_PCNT_CHANNEL_EDGE_ACTION_INCREASE - } - PcntEdgeAction::Decrease => { - esp_idf_sys::pcnt_channel_edge_action_t_PCNT_CHANNEL_EDGE_ACTION_DECREASE - } - } - } -} - -#[derive(Debug, Copy, Clone, Eq, PartialEq)] -pub enum PcntLevelAction { - ///< Keep current count mode - Keep, - ///< Invert current count mode (increase -> decrease, decrease -> increase) - Inverse, - ///< Hold current count value - Hold, -} - -impl From for pcnt_channel_level_action_t { - fn from(value: PcntLevelAction) -> Self { - match value { - PcntLevelAction::Keep => { - esp_idf_sys::pcnt_channel_level_action_t_PCNT_CHANNEL_LEVEL_ACTION_KEEP - } - PcntLevelAction::Inverse => { - esp_idf_sys::pcnt_channel_level_action_t_PCNT_CHANNEL_LEVEL_ACTION_INVERSE - } - PcntLevelAction::Hold => { - esp_idf_sys::pcnt_channel_level_action_t_PCNT_CHANNEL_LEVEL_ACTION_HOLD - } - } - } -} - -#[derive(Debug, Default, Copy, Clone)] -pub struct PcntChanFlags(pcnt_chan_config_t__bindgen_ty_1); - -impl PcntChanFlags { - pub const fn new() -> Self { - // uglyness due to bindgen bitfield generation - Self(pcnt_chan_config_t__bindgen_ty_1 { - _bitfield_align_1: [0u8; 0], - _bitfield_1: __BindgenBitfieldUnit::new([0u8; 1]), - __bindgen_padding_0: [0u8; 3], - }) - } - - #[inline] - pub fn invert_edge_input(&self) -> u32 { - self.0.invert_edge_input() - } - - #[inline] - pub fn set_invert_edge_input(&mut self, val: u32) { - self.0.set_invert_edge_input(val) - } - - #[inline] - pub fn invert_level_input(&self) -> u32 { - self.0.invert_level_input() - } - - #[inline] - pub fn set_invert_level_input(&mut self, val: u32) { - self.0.set_invert_level_input(val) - } - - #[inline] - pub fn virt_edge_io_level(&self) -> u32 { - self.0.virt_edge_io_level() - } - - #[inline] - pub fn set_virt_edge_io_level(&mut self, val: u32) { - self.0.set_virt_edge_io_level(val) - } - - #[inline] - pub fn virt_level_io_level(&self) -> u32 { - self.0.virt_level_io_level() - } - - #[inline] - pub fn set_virt_level_io_level(&mut self, val: u32) { - self.0.set_virt_level_io_level(val) - } - - #[inline] - pub fn io_loop_back(&self) -> u32 { - self.0.io_loop_back() - } - - #[inline] - pub fn set_io_loop_back(&mut self, val: u32) { - self.0.set_io_loop_back(val) - } -} - -/// @brief PCNT channel -#[derive(Debug)] -pub struct PcntChannel(pcnt_channel_handle_t); - -impl PcntChannel { - /// @brief Set channel actions when edge signal changes (e.g. falling or rising edge occurred). - /// The edge signal is input from the `edge_pin` configured in `PcntChanConfig`. - /// We use these actions to control when and how to change the counter value. - /// - /// @param[in] pos_act Action on posedge signal - /// @param[in] neg_act Action on negedge signal - /// @return - /// - (): on success - /// - EspError: on failure - pub fn set_edge_action( - &self, - pos_act: PcntEdgeAction, - neg_act: PcntEdgeAction, - ) -> Result<(), EspError> { - unsafe { - esp!(pcnt_channel_set_edge_action( - self.0, - pos_act.into(), - neg_act.into() - )) - } - } - - /// @brief Set channel actions when level signal changes (e.g. signal level goes from high to low). - /// The level signal is input from the `level_gpio_num` configured in `pcnt_chan_config_t`. - /// We use these actions to control when and how to change the counting mode. - /// - /// @param[in] chan PCNT channel handle created by `pcnt_new_channel()` - /// @param[in] high_act Action on high level signal - /// @param[in] low_act Action on low level signal - /// @return - /// - (): on success - /// - EspError: on failure - pub fn set_level_action( - &self, - high_act: PcntLevelAction, - low_act: PcntLevelAction, - ) -> Result<(), EspError> { - unsafe { - esp!(pcnt_channel_set_level_action( - self.0, - high_act.into(), - low_act.into() - )) - } - } -} - -impl Drop for PcntChannel { - fn drop(&mut self) { - unsafe { - esp!(pcnt_del_channel(self.0)).unwrap(); - } - } -} - -#[derive(Debug, Default, Copy, Clone)] -pub struct PcntUnitFlags(pcnt_unit_config_t__bindgen_ty_1); -impl PcntUnitFlags { - pub const fn new() -> Self { - // uglyness due to bindgen bitfield generation - Self(pcnt_unit_config_t__bindgen_ty_1 { - _bitfield_align_1: [0u8; 0], - _bitfield_1: __BindgenBitfieldUnit::new([0u8; 1]), - __bindgen_padding_0: [0u8; 3], - }) - } - - #[inline] - pub fn accum_count(&self) -> u32 { - self.0.accum_count() - } - - #[inline] - pub fn set_accum_count(&mut self, val: u32) -> Self { - self.0.set_accum_count(val); - *self - } -} - -/// @brief PCNT unit configuration -#[derive(Debug, Default, Clone)] -pub struct PcntUnitConfig { - /// @brief the low limit value, should be < 0 - pub low_limit: i32, - /// @brief the high limit value, should be > 0 - pub high_limit: i32, - pub flags: PcntUnitFlags, -} - -/// @brief PCNT unit -#[derive(Debug)] -pub struct PcntUnit { - unit: pcnt_unit_handle_t, - isr_id: Option, -} - -impl PcntUnit { - /// @brief Create a new PCNT unit - /// - /// @note The newly created PCNT unit is put in the init state. - /// - /// @param[in] config PCNT unit configuration - /// @return - /// - PcntUnit: on success - /// - EspError: on failure - pub fn new(config: &PcntUnitConfig) -> Result { - let config = pcnt_unit_config_t { - low_limit: config.low_limit, - high_limit: config.high_limit, - flags: config.flags.0, - }; - let mut unit: pcnt_unit_handle_t = std::ptr::null_mut(); - unsafe { - esp!(pcnt_new_unit(&config, &mut unit))?; - } - Ok(Self { unit, isr_id: None }) - } - - /// @brief Set glitch filter for PCNT unit - /// - /// @note The glitch filter module is clocked from APB, and APB frequency can be changed during DFS, which in return make the filter out of action. - /// So this function will lazy-install a PM lock internally when the power management is enabled. With this lock, the APB frequency won't be changed. - /// The PM lock can be uninstalled when the unit is dropped. - /// @note This function should be called when the PCNT unit is in the init state (i.e. before calling `enable()`) - /// - /// @param[in] config PCNT filter configuration, set config to None means disabling the filter function - /// @return - /// - (): on success - /// - EspError: on failure - pub fn set_filter(&self, config: Option<&PcntFilterConfig>) -> Result<(), EspError> { - unsafe { - esp!(pcnt_unit_set_glitch_filter( - self.unit, - match config { - Some(x) => &x.into(), - None => std::ptr::null(), - } - )) - } - } - - /// @brief Enable the PCNT unit - /// - /// @note This function will transit the unit state from init to enable. - /// @note This function will enable the interrupt service, if it's lazy installed in `subscribe()`. - /// @note This function will acquire the PM lock if it's lazy installed in `set_glitch_filter()`. - /// @note Enable a PCNT unit doesn't mean to start it. See also `start()` for how to start the PCNT counter. - /// - /// @return - /// - (): on success - /// - EspError: on failure - pub fn enable(&self) -> Result<(), EspError> { - unsafe { esp!(pcnt_unit_enable(self.unit)) } - } - - /// @brief Disable the PCNT unit - /// - /// @note This function will do the opposite work to the `enable()` - /// @note Disable a PCNT unit doesn't mean to stop it. See also `stop()` for how to stop the PCNT counter. - /// - /// @return - /// - (): on success - /// - EspError: on failure - pub fn disable(&self) -> Result<(), EspError> { - unsafe { esp!(pcnt_unit_disable(self.unit)) } - } - - /// @brief Start the PCNT unit, the counter will start to count according to the edge and/or level input signals - /// - /// @note This function should be called when the unit is in the enable state (i.e. after calling `pcnt_unit_enable()`) - /// @note This function is allowed to run within ISR context - /// @note This function will be placed into IRAM if `CONFIG_PCNT_CTRL_FUNC_IN_IRAM` is on, so that it's allowed to be executed when Cache is disabled - /// - /// @return - /// - (): on success - /// - EspError: on failure - pub fn start(&self) -> Result<(), EspError> { - unsafe { esp!(pcnt_unit_start(self.unit)) } - } - - /// @brief Stop PCNT from counting - /// - /// @note This function should be called when the unit is in the enable state (i.e. after calling `pcnt_unit_enable()`) - /// @note The stop operation won't clear the counter. Also see `pcnt_unit_clear_count()` for how to clear pulse count value. - /// @note This function is allowed to run within ISR context - /// @note This function will be placed into IRAM if `CONFIG_PCNT_CTRL_FUNC_IN_IRAM`, so that it is allowed to be executed when Cache is disabled - /// - /// @return - /// - (): on success - /// - EspError: on failure - pub fn stop(&self) -> Result<(), EspError> { - unsafe { esp!(pcnt_unit_stop(self.unit)) } - } - - /// @brief Clear PCNT pulse count value to zero - /// - /// @note It's recommended to call this function after adding a watch point by `pcnt_unit_add_watch_point()`, so that the newly added watch point is effective immediately. - /// @note This function is allowed to run within ISR context - /// @note This function will be placed into IRAM if `CONFIG_PCNT_CTRL_FUNC_IN_IRAM`, so that it's allowed to be executed when Cache is disabled - /// - /// @return - /// - (): on success - /// - EspError: on failure - pub fn clear_count(&self) -> Result<(), EspError> { - unsafe { esp!(pcnt_unit_clear_count(self.unit)) } - } - - /// @brief Get PCNT count value - /// - /// @note This function is allowed to run within ISR context - /// @note This function will be placed into IRAM if `CONFIG_PCNT_CTRL_FUNC_IN_IRAM`, so that it's allowed to be executed when Cache is disabled - /// - /// @param[out] value Returned count value - /// @return - /// - value: on success - /// - EspError: on failure - pub fn get_count(&self) -> Result { - let mut value: core::ffi::c_int = 0; - unsafe { - esp!(pcnt_unit_get_count(self.unit, &mut value))?; - } - Ok(value) - } - - /// @brief Add a watch point for PCNT unit, PCNT will generate an event when the counter value reaches the watch point value - /// - /// @param[in] watch_point Value to be watched - /// @return - /// - value: on success - /// - EspError: on failure - pub fn add_watch_point(&self, watch_point: i32) -> Result<(), EspError> { - unsafe { esp!(pcnt_unit_add_watch_point(self.unit, watch_point)) } - } - - /// @brief Remove a watch point for PCNT unit - /// - /// @param[in] watch_point Watch point value - /// @return - /// - value: on success - /// - EspError: on failure - pub fn remove_watch_point(&self, watch_point: i32) -> Result<(), EspError> { - unsafe { esp!(pcnt_unit_remove_watch_point(self.unit, watch_point)) } - } - - /// @brief subscribe to PCNT events - /// - /// @note User registered callbacks are expected to be runnable within ISR context - /// @note The first call to this function needs to be before the call to `pcnt_unit_enable` - /// @note User can deregister a previously registered callback by calling this function and passing None. - /// - /// @return - /// - value: on success - /// - EspError: on failure - pub fn subscribe(&mut self, callback: F) -> Result<(), EspError> - where - F: FnMut(PcntWatchEventData) -> bool + Send + 'static, - { - let id = match self.isr_id { - Some(x) => x, - None => { - let x = allocate_isr_id(callback); - self.isr_id = Some(x); - x - } - }; - let cbs = pcnt_event_callbacks_t { - on_reach: Some(Self::handle_isr), - }; - let data = id as *mut core::ffi::c_void; - unsafe { - esp!(pcnt_unit_register_event_callbacks( - self.unit, - &cbs, - data as *mut core::ffi::c_void - )) - } - } - - unsafe extern "C" fn handle_isr( - _unit: pcnt_unit_handle_t, - edata: *const pcnt_watch_event_data_t, - data: *mut core::ffi::c_void, - ) -> bool { - let id = data as usize; - match &mut ISR_HANDLERS[id] { - Some(f) => f(edata.into()), - None => panic!("isr handler called with no ISR!"), - } - } - - /// @brief Unsubscribe to PCNT events - /// - /// @param unit PCNT unit number - /// - /// @return - /// - value: on success - /// - EspError: on failure - pub fn unsubscribe(&mut self) -> Result<(), EspError> { - if let Some(id) = self.isr_id.take() { - unsafe { - esp!(pcnt_unit_register_event_callbacks( - self.unit, - &pcnt_event_callbacks_t { on_reach: None }, - 0 as *mut core::ffi::c_void - ))?; - } - free_isr_id(id); - } - Ok(()) - } - - /// @brief Create PCNT channel for specific unit, each PCNT has several channels associated with it - /// - /// @note This function should be called when the unit is in init state (i.e. before calling `pcnt_unit_enable()`) - /// - /// @param[in] config PCNT channel configuration - /// @return - /// - PcntChannel: on success - /// - EspError: on failure - pub fn channel<'a>( - &self, - edge_pin: Option + 'a>, - level_pin: Option + 'a>, - flags: PcntChanFlags, - ) -> Result { - let config = pcnt_chan_config_t { - edge_gpio_num: match edge_pin { - Some(pin) => { - crate::into_ref!(pin); - pin.pin() - } - None => -1, - }, - level_gpio_num: match level_pin { - Some(pin) => { - crate::into_ref!(pin); - pin.pin() - } - None => -1, - }, - flags: flags.0, - }; - let mut channel: pcnt_channel_handle_t = std::ptr::null_mut(); - unsafe { - esp!(pcnt_new_channel(self.unit, &config, &mut channel))?; - } - Ok(PcntChannel(channel)) - } -} - -impl Drop for PcntUnit { - fn drop(&mut self) { - unsafe { - esp!(pcnt_del_unit(self.unit)).unwrap(); - } - } -} - -static PCNT_CS: crate::task::CriticalSection = crate::task::CriticalSection::new(); - -#[cfg(esp32s3)] -const PCNT_UNIT_MAX: usize = 4; -#[cfg(not(esp32s3))] -const PCNT_UNIT_MAX: usize = 8; - -static mut ISR_HANDLERS: [Option bool>>; PCNT_UNIT_MAX] = [ - None, - None, - None, - None, - #[cfg(not(esp32s3))] - None, - #[cfg(not(esp32s3))] - None, - #[cfg(not(esp32s3))] - None, - #[cfg(not(esp32s3))] - None, -]; - -fn allocate_isr_id(callback: impl FnMut(PcntWatchEventData) -> bool + 'static) -> usize { - let _cs = PCNT_CS.enter(); - for i in 0..PCNT_UNIT_MAX { - unsafe { - if ISR_HANDLERS[i].is_none() { - ISR_HANDLERS[i] = Some(Box::new(callback)); - return i; - } - } - } - panic!("all ISR slots are full!"); -} - -fn free_isr_id(id: usize) { - let _cs = PCNT_CS.enter(); - unsafe { - ISR_HANDLERS[id] = None; - } -} From e5aed17cae8b63fd58add56cbe96bf75f55fe00f Mon Sep 17 00:00:00 2001 From: Christopher Liebman Date: Sun, 29 Jan 2023 09:27:44 -0800 Subject: [PATCH 42/48] fix #cfg's for when/where pcnt is available --- src/peripherals.rs | 46 +++++++++------------------------------------- 1 file changed, 9 insertions(+), 37 deletions(-) diff --git a/src/peripherals.rs b/src/peripherals.rs index 33bec4b2a46..a854078ab89 100644 --- a/src/peripherals.rs +++ b/src/peripherals.rs @@ -15,7 +15,7 @@ use crate::mac; use crate::modem; #[cfg(all( not(feature = "riscv-ulp-hal"), - any(feature = "pcnt4", esp_idf_version_major = "4") + //any(feature = "pcnt4", esp_idf_version_major = "4") ))] use crate::pcnt; #[cfg(not(feature = "riscv-ulp-hal"))] @@ -199,49 +199,21 @@ impl Peripherals { spi3: spi::SPI3::new(), adc1: adc::ADC1::new(), adc2: adc::ADC2::new(), - #[cfg(all( - not(feature = "riscv-ulp-hal"), - any(feature = "pcnt4", esp_idf_version_major = "4") - ))] + #[cfg(all(not(feature = "riscv-ulp-hal"), any(esp32, esp32s2, esp32s3)))] pcnt0: pcnt::PCNT0::new(), - #[cfg(all( - not(feature = "riscv-ulp-hal"), - any(feature = "pcnt4", esp_idf_version_major = "4") - ))] + #[cfg(all(not(feature = "riscv-ulp-hal"), any(esp32, esp32s2, esp32s3)))] pcnt1: pcnt::PCNT1::new(), - #[cfg(all( - not(feature = "riscv-ulp-hal"), - any(feature = "pcnt4", esp_idf_version_major = "4") - ))] + #[cfg(all(not(feature = "riscv-ulp-hal"), any(esp32, esp32s2, esp32s3)))] pcnt2: pcnt::PCNT2::new(), - #[cfg(all( - not(feature = "riscv-ulp-hal"), - any(feature = "pcnt4", esp_idf_version_major = "4") - ))] + #[cfg(all(not(feature = "riscv-ulp-hal"), any(esp32, esp32s2, esp32s3)))] pcnt3: pcnt::PCNT3::new(), - #[cfg(all( - not(esp32s3), - not(feature = "riscv-ulp-hal"), - any(feature = "pcnt4", esp_idf_version_major = "4") - ))] + #[cfg(all(not(feature = "riscv-ulp-hal"), any(esp32)))] pcnt4: pcnt::PCNT4::new(), - #[cfg(all( - not(esp32s3), - not(feature = "riscv-ulp-hal"), - any(feature = "pcnt4", esp_idf_version_major = "4") - ))] + #[cfg(all(not(feature = "riscv-ulp-hal"), any(esp32)))] pcnt5: pcnt::PCNT5::new(), - #[cfg(all( - not(esp32s3), - not(feature = "riscv-ulp-hal"), - any(feature = "pcnt4", esp_idf_version_major = "4") - ))] + #[cfg(all(not(feature = "riscv-ulp-hal"), any(esp32)))] pcnt6: pcnt::PCNT6::new(), - #[cfg(all( - not(esp32s3), - not(feature = "riscv-ulp-hal"), - any(feature = "pcnt4", esp_idf_version_major = "4") - ))] + #[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(), From 45a994a70262687a4e9cedeba0891f06cd6e38cd Mon Sep 17 00:00:00 2001 From: Christopher Liebman Date: Sun, 29 Jan 2023 10:15:10 -0800 Subject: [PATCH 43/48] fix #cfg's for when/where pcnt is available --- src/pcnt.rs | 16 ++++++++-------- src/peripherals.rs | 5 +---- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/src/pcnt.rs b/src/pcnt.rs index 3382bf8eaca..3821a1f0315 100644 --- a/src/pcnt.rs +++ b/src/pcnt.rs @@ -546,13 +546,13 @@ static mut ISR_HANDLERS: [IsrHandler; pcnt_unit_t_PCNT_UNIT_MAX as usize] = [ None, None, None, - #[cfg(not(esp32s3))] + #[cfg(esp32)] None, - #[cfg(not(esp32s3))] + #[cfg(esp32)] None, - #[cfg(not(esp32s3))] + #[cfg(esp32)] None, - #[cfg(not(esp32s3))] + #[cfg(esp32)] None, ]; @@ -577,11 +577,11 @@ 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(not(esp32s3))] +#[cfg(esp32)] impl_pcnt!(PCNT4: pcnt_unit_t_PCNT_UNIT_4); -#[cfg(not(esp32s3))] +#[cfg(esp32)] impl_pcnt!(PCNT5: pcnt_unit_t_PCNT_UNIT_5); -#[cfg(not(esp32s3))] +#[cfg(esp32)] impl_pcnt!(PCNT6: pcnt_unit_t_PCNT_UNIT_6); -#[cfg(not(esp32s3))] +#[cfg(esp32)] impl_pcnt!(PCNT7: pcnt_unit_t_PCNT_UNIT_7); diff --git a/src/peripherals.rs b/src/peripherals.rs index a854078ab89..09999d6b9b1 100644 --- a/src/peripherals.rs +++ b/src/peripherals.rs @@ -13,10 +13,7 @@ use crate::ledc; use crate::mac; #[cfg(not(feature = "riscv-ulp-hal"))] use crate::modem; -#[cfg(all( - not(feature = "riscv-ulp-hal"), - //any(feature = "pcnt4", esp_idf_version_major = "4") -))] +#[cfg(all(not(feature = "riscv-ulp-hal"), any(esp32, esp32s2, esp32s3)))] use crate::pcnt; #[cfg(not(feature = "riscv-ulp-hal"))] use crate::rmt; From bd9a9bb2f73648df53587470e020135525bdd5c8 Mon Sep 17 00:00:00 2001 From: Christopher Liebman Date: Mon, 30 Jan 2023 06:25:10 -0800 Subject: [PATCH 44/48] 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` --- src/pcnt.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/pcnt.rs b/src/pcnt.rs index 3821a1f0315..34444cee0e0 100644 --- a/src/pcnt.rs +++ b/src/pcnt.rs @@ -507,6 +507,7 @@ 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 @@ -514,12 +515,12 @@ impl Drop for PcntDriver<'_> { } } -#[cfg(all(not(feature = "riscv-ulp-hal"), feature = "alloc"))] +#[cfg(feature = "alloc")] static ISR_SERVICE_ENABLED: core::sync::atomic::AtomicBool = core::sync::atomic::AtomicBool::new(false); -#[cfg(all(not(feature = "riscv-ulp-hal"), feature = "alloc"))] -static PCNT_CS: crate::task::CriticalSection = crate::task::CriticalSection::new(); + #[cfg(feature = "alloc")] + static PCNT_CS: crate::task::CriticalSection = crate::task::CriticalSection::new(); #[cfg(feature = "alloc")] fn enable_isr_service() -> Result<(), EspError> { From 9217426aea532829bc3f25be5edb507f33e881a8 Mon Sep 17 00:00:00 2001 From: Christopher Liebman Date: Mon, 30 Jan 2023 06:43:14 -0800 Subject: [PATCH 45/48] cargo fmt / clippy --- src/pcnt.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pcnt.rs b/src/pcnt.rs index 34444cee0e0..d5049b178f5 100644 --- a/src/pcnt.rs +++ b/src/pcnt.rs @@ -519,8 +519,8 @@ impl Drop for PcntDriver<'_> { 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")] +static PCNT_CS: crate::task::CriticalSection = crate::task::CriticalSection::new(); #[cfg(feature = "alloc")] fn enable_isr_service() -> Result<(), EspError> { From 87b6c8d72c5a1b555a4068ea7c5796bd0c88c7cd Mon Sep 17 00:00:00 2001 From: Christopher Liebman Date: Thu, 2 Feb 2023 13:37:37 -0800 Subject: [PATCH 46/48] stub out the pcnt example on an unsuported device --- examples/pcnt_i64_encoder.rs | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/examples/pcnt_i64_encoder.rs b/examples/pcnt_i64_encoder.rs index 124513332e9..2c16ed34261 100644 --- a/examples/pcnt_i64_encoder.rs +++ b/examples/pcnt_i64_encoder.rs @@ -7,15 +7,15 @@ //! use anyhow; -use anyhow::Context; -use log::*; - use esp_idf_hal::delay::FreeRtos; -use esp_idf_hal::prelude::*; - -use encoder::Encoder; +use log::*; +#[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::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(); @@ -41,6 +41,15 @@ fn main() -> anyhow::Result<()> { } } +#[cfg(not(all(not(feature = "riscv-ulp-hal"), any(esp32, esp32s2, esp32s3))))] +fn main() -> anyhow::Result<()> { + error!("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; From 4f09bfd0ee1a08d73741553660024cc5b0144ce2 Mon Sep 17 00:00:00 2001 From: Christopher Liebman Date: Sat, 11 Feb 2023 10:56:52 -0800 Subject: [PATCH 47/48] - drop esp-idf-svc from dev-deps (compile error on esp-idf 5) - use println!() instead of log::info!() in pcnt example --- Cargo.toml | 1 - examples/pcnt_i64_encoder.rs | 10 +++------- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 57bc17d8cc1..ebfe33d10eb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,7 +38,6 @@ anyhow = "1" [dev-dependencies] anyhow = "1" -esp-idf-svc = { version = "0.45.0" } esp-idf-sys = { version = "0.32", features = ["native", "binstart"] } mipidsi = "0.5.0" display-interface-spi = "0.4.1" diff --git a/examples/pcnt_i64_encoder.rs b/examples/pcnt_i64_encoder.rs index 2c16ed34261..2611aafb2af 100644 --- a/examples/pcnt_i64_encoder.rs +++ b/examples/pcnt_i64_encoder.rs @@ -8,7 +8,6 @@ use anyhow; use esp_idf_hal::delay::FreeRtos; -use log::*; #[cfg(all(not(feature = "riscv-ulp-hal"), any(esp32, esp32s2, esp32s3)))] fn main() -> anyhow::Result<()> { @@ -20,21 +19,18 @@ fn main() -> anyhow::Result<()> { // or else some patches to the runtime implemented by esp-idf-sys might not link properly. esp_idf_sys::link_patches(); - // Bind the log crate to the ESP Logging facilities - esp_idf_svc::log::EspLogger::initialize_default(); - - info!("setup pins"); + 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; - info!("setup encoder"); + 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 { - info!("value: {value}"); + println!("value: {value}"); last_value = value; } FreeRtos::delay_ms(100u32); From 90e4640c6fff4662c912b7e13afb76d6ab27d3cc Mon Sep 17 00:00:00 2001 From: Christopher Liebman Date: Mon, 20 Feb 2023 13:52:37 -0800 Subject: [PATCH 48/48] fix ocnt example compile for riscv32imc-esp-espidf --- Cargo.toml | 1 - examples/pcnt_i64_encoder.rs | 9 ++++----- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ebfe33d10eb..005f67d68cd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -42,4 +42,3 @@ esp-idf-sys = { version = "0.32", features = ["native", "binstart"] } mipidsi = "0.5.0" display-interface-spi = "0.4.1" embedded-graphics = "0.7.1" -log = { version = "0.4" } diff --git a/examples/pcnt_i64_encoder.rs b/examples/pcnt_i64_encoder.rs index 2611aafb2af..01cded8e398 100644 --- a/examples/pcnt_i64_encoder.rs +++ b/examples/pcnt_i64_encoder.rs @@ -6,13 +6,11 @@ //! threshold and track how much that accounts for and provide an i64 value result //! -use anyhow; -use esp_idf_hal::delay::FreeRtos; - #[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, @@ -38,8 +36,9 @@ fn main() -> anyhow::Result<()> { } #[cfg(not(all(not(feature = "riscv-ulp-hal"), any(esp32, esp32s2, esp32s3))))] -fn main() -> anyhow::Result<()> { - error!("pcnt peripheral not supported on this device!"); +fn main() { + use esp_idf_hal::delay::FreeRtos; + println!("pcnt peripheral not supported on this device!"); loop { FreeRtos::delay_ms(100u32); }