Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 53 commits into from
Feb 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
123902e
WIP - PCNT implementation for idf v4
liebman Nov 6, 2022
e2ec50a
use from core not std
liebman Nov 7, 2022
4b129c8
try a allocate/deallocate stratagy for PCNT UNITS (like esp-idf v5)
liebman Nov 8, 2022
8c7180e
update pcnt example
liebman Nov 8, 2022
510939d
WIP - PCNT implementation for idf v4
liebman Nov 6, 2022
c10cb63
use from core not std
liebman Nov 7, 2022
bbc1714
try a allocate/deallocate stratagy for PCNT UNITS (like esp-idf v5)
liebman Nov 8, 2022
da9b29c
update pcnt example
liebman Nov 8, 2022
9b904bf
Merge branch 'pcnt_idf_v4' of https://github.com/liebman/esp-idf-hal …
liebman Nov 18, 2022
f47a77a
use Arc/Mutex for PcntPin so Pnct can be Send
liebman Nov 20, 2022
08b161e
update pcnt encoder example
liebman Nov 20, 2022
bc4210f
Merge branch 'master' into pcnt_idf_v4
liebman Nov 27, 2022
82dff57
add partial esp-idf 5 pulse_cnt support (no watches/events)
liebman Dec 4, 2022
8e3b8ea
fix pcnt compilation on esp-idf 4
liebman Dec 4, 2022
bdfc2da
implement interrupt service
liebman Dec 6, 2022
6582fc4
added some documentation
liebman Dec 6, 2022
e40e6c5
Merge branch 'esp-rs:master' into pcnt_idf_v4
liebman Dec 7, 2022
2fa6de3
fix subscribe - trampoline did not work with captured values.
liebman Dec 7, 2022
17de23e
no need to initialize logging in example
liebman Dec 7, 2022
b124a92
use esp_idf_sys::*
liebman Dec 23, 2022
af48ff0
Merge branch 'master' into pcnt_idf_v4
liebman Dec 23, 2022
22faf31
Implement From not Into
liebman Dec 23, 2022
a021f5d
switch from bitflags to enumset
liebman Dec 23, 2022
62cca6b
typo
liebman Dec 23, 2022
d4ddf2f
switch #[doc] to /// style
liebman Dec 23, 2022
92d2304
gate uses of Box with alloc feature and use alloc::boxed::Box
liebman Dec 23, 2022
94867cc
rename to PcntDriver and make Pcnt Peripherals
liebman Dec 23, 2022
9ce3bbd
use `impl Peripheral<P = impl InputPin>` instead of `AnyInputPin`
liebman Dec 23, 2022
f638198
small cleanup
liebman Dec 23, 2022
87556ba
rename config() -> channel_config()
liebman Dec 23, 2022
4a1c611
some cleanup on idf v5 pulse_ctr
liebman Dec 25, 2022
1a02770
no need for PcntChanConfig, just use flags
liebman Dec 25, 2022
10b02f8
use comment style documentation not `#[doc=""]`
liebman Dec 26, 2022
667dd21
Merge branch 'master' into pcnt_idf_v4
liebman Dec 26, 2022
499aac6
small tweak to pnct example
liebman Dec 29, 2022
bc735a7
review fixes (the easy ones)
liebman Jan 24, 2023
527dfb6
added deps for the pcnt example (dev-dependencies)
liebman Jan 24, 2023
e8804cf
cargo fmt
liebman Jan 24, 2023
ec63abd
pub const fn new for PcntChanFlags
liebman Jan 24, 2023
e6f235b
pub const fn new for PcntUnitFlags
liebman Jan 24, 2023
8bbedb7
fix compilation for esp-idf 4
liebman Jan 24, 2023
82b118f
pass in all pins in PcntDriver::new
liebman Jan 25, 2023
5fa2f06
remove unused lifetime
liebman Jan 25, 2023
a380f72
use a simple macro to convert Option<> pin to a pin number
liebman Jan 25, 2023
82285fd
cargo fmt
liebman Jan 28, 2023
024aaf4
remove V5 for now
liebman Jan 29, 2023
e5aed17
fix #cfg's for when/where pcnt is available
liebman Jan 29, 2023
45a994a
fix #cfg's for when/where pcnt is available
liebman Jan 29, 2023
bd9a9bb
gate access to ISR_HANDLERS in drop with `alloc`
liebman Jan 30, 2023
9217426
cargo fmt / clippy
liebman Jan 30, 2023
87b6c8d
stub out the pcnt example on an unsuported device
liebman Feb 2, 2023
4f09bfd
- drop esp-idf-svc from dev-deps (compile error on esp-idf 5)
liebman Feb 11, 2023
90e4640
fix ocnt example compile for riscv32imc-esp-espidf
liebman Feb 20, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
143 changes: 143 additions & 0 deletions examples/pcnt_i64_encoder.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
//! PCNT decoding a rotary encoder
//!
//! To try this out, connect a rotary encoder to pins 5 and 6, the common should be grounded
//!
//! Note that PCNT only track a singed 16bit value. We use interrupts to detect a LOW and HIGH
//! threshold and track how much that accounts for and provide an i64 value result
//!

#[cfg(all(not(feature = "riscv-ulp-hal"), any(esp32, esp32s2, esp32s3)))]
fn main() -> anyhow::Result<()> {
use anyhow::Context;
use encoder::Encoder;
use esp_idf_hal::delay::FreeRtos;
use esp_idf_hal::prelude::*;

// Temporary. Will disappear once ESP-IDF 4.4 is released, but for now it is necessary to call this function once,
// or else some patches to the runtime implemented by esp-idf-sys might not link properly.
esp_idf_sys::link_patches();

println!("setup pins");
let peripherals = Peripherals::take().context("failed to take Peripherals")?;
let mut pin_a = peripherals.pins.gpio5;
let mut pin_b = peripherals.pins.gpio6;
println!("setup encoder");
let encoder = Encoder::new(peripherals.pcnt0, &mut pin_a, &mut pin_b)?;

let mut last_value = 0i64;
loop {
let value = encoder.get_value()?;
if value != last_value {
println!("value: {value}");
last_value = value;
}
FreeRtos::delay_ms(100u32);
}
}

#[cfg(not(all(not(feature = "riscv-ulp-hal"), any(esp32, esp32s2, esp32s3))))]
fn main() {
use esp_idf_hal::delay::FreeRtos;
println!("pcnt peripheral not supported on this device!");
loop {
FreeRtos::delay_ms(100u32);
}
}

#[cfg(all(not(feature = "riscv-ulp-hal"), any(esp32, esp32s2, esp32s3)))]
// esp-idf encoder implementation using v4 pcnt api
mod encoder {
use std::cmp::min;
use std::sync::atomic::AtomicI64;
use std::sync::atomic::Ordering;
use std::sync::Arc;

use esp_idf_hal::gpio::AnyInputPin;
use esp_idf_hal::gpio::InputPin;
use esp_idf_hal::pcnt::*;
use esp_idf_hal::peripheral::Peripheral;
use esp_idf_sys::EspError;

const LOW_LIMIT: i16 = -100;
const HIGH_LIMIT: i16 = 100;

pub struct Encoder<'d> {
unit: PcntDriver<'d>,
approx_value: Arc<AtomicI64>,
}

impl<'d> Encoder<'d> {
pub fn new<PCNT: Pcnt>(
pcnt: impl Peripheral<P = PCNT> + 'd,
pin_a: impl Peripheral<P = impl InputPin> + 'd,
pin_b: impl Peripheral<P = impl InputPin> + 'd,
) -> Result<Self, EspError> {
let mut unit = PcntDriver::new(
pcnt,
Some(pin_a),
Some(pin_b),
Option::<AnyInputPin>::None,
Option::<AnyInputPin>::None,
)?;
unit.channel_config(
PcntChannel::Channel0,
PinIndex::Pin0,
PinIndex::Pin1,
&mut PcntChannelConfig {
lctrl_mode: PcntControlMode::Reverse,
hctrl_mode: PcntControlMode::Keep,
pos_mode: PcntCountMode::Decrement,
neg_mode: PcntCountMode::Increment,
counter_h_lim: HIGH_LIMIT,
counter_l_lim: LOW_LIMIT,
},
)?;
unit.channel_config(
PcntChannel::Channel1,
PinIndex::Pin1,
PinIndex::Pin0,
&mut PcntChannelConfig {
lctrl_mode: PcntControlMode::Reverse,
hctrl_mode: PcntControlMode::Keep,
pos_mode: PcntCountMode::Increment,
neg_mode: PcntCountMode::Decrement,
counter_h_lim: HIGH_LIMIT,
counter_l_lim: LOW_LIMIT,
},
)?;

unit.set_filter_value(min(10 * 80, 1023))?;
unit.filter_enable()?;

let approx_value = Arc::new(AtomicI64::new(0));
// unsafe interrupt code to catch the upper and lower limits from the encoder
// and track the overflow in `value: Arc<AtomicI64>` - I plan to use this for
// a wheeled robot's odomerty
unsafe {
let approx_value = approx_value.clone();
unit.subscribe(move |status| {
let status = PcntEventType::from_repr_truncated(status);
if status.contains(PcntEvent::HighLimit) {
approx_value.fetch_add(HIGH_LIMIT as i64, Ordering::SeqCst);
}
if status.contains(PcntEvent::LowLimit) {
approx_value.fetch_add(LOW_LIMIT as i64, Ordering::SeqCst);
}
})?;
}
unit.event_enable(PcntEvent::HighLimit)?;
unit.event_enable(PcntEvent::LowLimit)?;
unit.counter_pause()?;
unit.counter_clear()?;
unit.counter_resume()?;

Ok(Self { unit, approx_value })
}

pub fn get_value(&self) -> Result<i64, EspError> {
let value =
self.approx_value.load(Ordering::Relaxed) + self.unit.get_counter_value()? as i64;
Ok(value)
}
}
}
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ pub mod ledc;
pub mod mac;
#[cfg(not(feature = "riscv-ulp-hal"))]
pub mod modem;
#[cfg(all(not(feature = "riscv-ulp-hal"), any(esp32, esp32s2, esp32s3)))]
pub mod pcnt;
pub mod peripheral;
pub mod peripherals;
pub mod prelude;
Expand Down
Loading