From 2c2bb25262335fd9e7035d5eb2d1e43a1cdf6d12 Mon Sep 17 00:00:00 2001 From: Sergio Gasquez Arcos Date: Fri, 19 May 2023 09:27:11 +0200 Subject: [PATCH] Initial support for PCNT in ESP32-H2 (#551) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: ✨ Enable PCNT peripheral * feat: ✨ Rename PCNT GPIOs * feat: ✨ Add pcnt_encoder example * build: ⬆️ Update esp-pacs revision * docs: 📝 Update changelog --- CHANGELOG.md | 1 + esp-hal-common/Cargo.toml | 2 +- esp-hal-common/devices/esp32h2.toml | 2 +- esp-hal-common/src/soc/esp32h2/gpio.rs | 32 ++-- esp-hal-common/src/soc/esp32h2/peripherals.rs | 2 +- esp32h2-hal/examples/pcnt_encoder.rs | 157 ++++++++++++++++++ 6 files changed, 177 insertions(+), 19 deletions(-) create mode 100644 esp32h2-hal/examples/pcnt_encoder.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 58f1cb725..17ac91983 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added support for multicore async GPIO (#542) - Add initial support for MCPWM in ESP32-H2 (#544) - Add some miscellaneous examples for the ESP32-H2 (#548) +- Add initial support for PCNT in ESP32-H2 (#551) ### Fixed diff --git a/esp-hal-common/Cargo.toml b/esp-hal-common/Cargo.toml index 58298ec91..195980174 100644 --- a/esp-hal-common/Cargo.toml +++ b/esp-hal-common/Cargo.toml @@ -55,7 +55,7 @@ esp32 = { version = "0.23.0", features = ["critical-section"], optional = true esp32c2 = { version = "0.11.0", features = ["critical-section"], optional = true } esp32c3 = { version = "0.14.0", features = ["critical-section"], optional = true } esp32c6 = { version = "0.4.0", features = ["critical-section"], optional = true } -esp32h2 = { git = "https://github.com/esp-rs/esp-pacs", rev = "ff70333", package = "esp32h2", features = ["critical-section"], optional = true } +esp32h2 = { git = "https://github.com/esp-rs/esp-pacs", rev = "4fe0791", package = "esp32h2", features = ["critical-section"], optional = true } esp32s2 = { version = "0.14.0", features = ["critical-section"], optional = true } esp32s3 = { version = "0.18.0", features = ["critical-section"], optional = true } diff --git a/esp-hal-common/devices/esp32h2.toml b/esp-hal-common/devices/esp32h2.toml index 29e9fb967..0e03a7b23 100644 --- a/esp-hal-common/devices/esp32h2.toml +++ b/esp-hal-common/devices/esp32h2.toml @@ -36,7 +36,7 @@ peripherals = [ # "otp_debug", # "parl_io", # "pau", - # "pcnt", + "pcnt", "pcr", # "pmu", # "rmt", diff --git a/esp-hal-common/src/soc/esp32h2/gpio.rs b/esp-hal-common/src/soc/esp32h2/gpio.rs index a39c39165..e6f157a26 100644 --- a/esp-hal-common/src/soc/esp32h2/gpio.rs +++ b/esp-hal-common/src/soc/esp32h2/gpio.rs @@ -96,22 +96,22 @@ pub enum InputSignal { SIG_FUNC_98 = 98, SIG_FUNC_99 = 99, SIG_FUNC_100 = 100, - PCNT_SIG_CH00 = 101, - PCNT_SIG_CH10 = 102, - PCNT_CTRL_CH00 = 103, - PCNT_CTRL_CH10 = 104, - PCNT_SIG_CH01 = 105, - PCNT_SIG_CH11 = 106, - PCNT_CTRL_CH01 = 107, - PCNT_CTRL_CH11 = 108, - PCNT_SIG_CH02 = 109, - PCNT_SIG_CH12 = 110, - PCNT_CTRL_CH02 = 111, - PCNT_CTRL_CH12 = 112, - PCNT_SIG_CH03 = 113, - PCNT_SIG_CH13 = 114, - PCNT_CTRL_CH03 = 115, - PCNT_CTRL_CH13 = 116, + PCNT0_SIG_CH0 = 101, + PCNT0_SIG_CH1 = 102, + PCNT0_CTRL_CH0 = 103, + PCNT0_CTRL_CH1 = 104, + PCNT1_SIG_CH0 = 105, + PCNT1_SIG_CH1 = 106, + PCNT1_CTRL_CH0 = 107, + PCNT1_CTRL_CH1 = 108, + PCNT2_SIG_CH0 = 109, + PCNT2_SIG_CH1 = 110, + PCNT2_CTRL_CH0 = 111, + PCNT2_CTRL_CH1 = 112, + PCNT3_SIG_CH0 = 113, + PCNT3_SIG_CH1 = 114, + PCNT3_CTRL_CH0 = 115, + PCNT3_CTRL_CH1 = 116, SPIQ = 121, SPID = 122, SPIHD = 123, diff --git a/esp-hal-common/src/soc/esp32h2/peripherals.rs b/esp-hal-common/src/soc/esp32h2/peripherals.rs index 65e43bf18..c27e15040 100644 --- a/esp-hal-common/src/soc/esp32h2/peripherals.rs +++ b/esp-hal-common/src/soc/esp32h2/peripherals.rs @@ -38,7 +38,7 @@ crate::peripherals! { // OTP_DEBUG => true, // PARL_IO => true, // PAU => true, - // PCNT => true, + PCNT => true, PCR => true, // PMU => true, // RMT => true, diff --git a/esp32h2-hal/examples/pcnt_encoder.rs b/esp32h2-hal/examples/pcnt_encoder.rs new file mode 100644 index 000000000..946c8bf31 --- /dev/null +++ b/esp32h2-hal/examples/pcnt_encoder.rs @@ -0,0 +1,157 @@ +//! PCNT Encoder Demo +//! +//! This example decodes a quadrature encoder +//! +//! Since the PCNT units reset to zero when they reach their limits +//! we enable an interrupt on the upper and lower limits and +//! track the overflow in an AtomicI32 + +#![no_std] +#![no_main] +use core::{ + cell::RefCell, + cmp::min, + sync::atomic::{AtomicI32, Ordering}, +}; + +use critical_section::Mutex; +use esp32h2_hal as esp_hal; +use esp_backtrace as _; +use esp_hal::{ + clock::ClockControl, + interrupt, + pcnt::{channel, channel::PcntSource, unit, PCNT}, + peripherals::{self, Peripherals}, + prelude::*, + timer::TimerGroup, + Rtc, + IO, +}; +use esp_println::println; + +static UNIT0: Mutex>> = Mutex::new(RefCell::new(None)); +static VALUE: AtomicI32 = AtomicI32::new(0); + +#[entry] +fn main() -> ! { + let peripherals = Peripherals::take(); + let mut system = peripherals.PCR.split(); + let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + + // Disable the watchdog timers. For the ESP32-H2, this includes the Super WDT, + // and the TIMG WDTs. + let mut rtc = Rtc::new(peripherals.LP_CLKRST); + let timer_group0 = TimerGroup::new( + peripherals.TIMG0, + &clocks, + &mut system.peripheral_clock_control, + ); + let mut wdt0 = timer_group0.wdt; + let timer_group1 = TimerGroup::new( + peripherals.TIMG1, + &clocks, + &mut system.peripheral_clock_control, + ); + let mut wdt1 = timer_group1.wdt; + + // Disable watchdog timers + rtc.swd.disable(); + rtc.rwdt.disable(); + wdt0.disable(); + wdt1.disable(); + + let unit_number = unit::Number::Unit1; + + // setup a pulse couter + println!("setup pulse counter unit 0"); + let pcnt = PCNT::new(peripherals.PCNT, &mut system.peripheral_clock_control); + let mut u0 = pcnt.get_unit(unit_number); + u0.configure(unit::Config { + low_limit: -100, + high_limit: 100, + filter: Some(min(10u16 * 80, 1023u16)), + ..Default::default() + }) + .unwrap(); + + println!("setup channel 0"); + let mut ch0 = u0.get_channel(channel::Number::Channel0); + let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); + let mut pin_a = io.pins.gpio4.into_pull_up_input(); + let mut pin_b = io.pins.gpio5.into_pull_up_input(); + + ch0.configure( + PcntSource::from_pin(&mut pin_a), + PcntSource::from_pin(&mut pin_b), + channel::Config { + lctrl_mode: channel::CtrlMode::Reverse, + hctrl_mode: channel::CtrlMode::Keep, + pos_edge: channel::EdgeMode::Decrement, + neg_edge: channel::EdgeMode::Increment, + invert_ctrl: false, + invert_sig: false, + }, + ); + + println!("setup channel 1"); + let mut ch1 = u0.get_channel(channel::Number::Channel1); + ch1.configure( + PcntSource::from_pin(&mut pin_b), + PcntSource::from_pin(&mut pin_a), + channel::Config { + lctrl_mode: channel::CtrlMode::Reverse, + hctrl_mode: channel::CtrlMode::Keep, + pos_edge: channel::EdgeMode::Increment, + neg_edge: channel::EdgeMode::Decrement, + invert_ctrl: false, + invert_sig: false, + }, + ); + println!("subscribing to events"); + u0.events(unit::Events { + low_limit: true, + high_limit: true, + thresh0: false, + thresh1: false, + zero: false, + }); + + println!("enabling interrupts"); + u0.listen(); + println!("resume pulse counter unit 0"); + u0.resume(); + + critical_section::with(|cs| UNIT0.borrow_ref_mut(cs).replace(u0)); + + interrupt::enable(peripherals::Interrupt::PCNT, interrupt::Priority::Priority2).unwrap(); + + let mut last_value: i32 = 0; + loop { + critical_section::with(|cs| { + let mut u0 = UNIT0.borrow_ref_mut(cs); + let u0 = u0.as_mut().unwrap(); + let value: i32 = u0.get_value() as i32 + VALUE.load(Ordering::SeqCst); + if value != last_value { + println!("value: {value}"); + last_value = value; + } + }); + } +} + +#[interrupt] +fn PCNT() { + critical_section::with(|cs| { + let mut u0 = UNIT0.borrow_ref_mut(cs); + let u0 = u0.as_mut().unwrap(); + if u0.interrupt_set() { + let events = u0.get_events(); + if events.high_limit { + VALUE.fetch_add(100, Ordering::SeqCst); + } else if events.low_limit { + VALUE.fetch_add(-100, Ordering::SeqCst); + } + u0.reset_interrupt(); + } + }); +}