* Allow accessing signal list via ErasedPin * Replace AnyPin with more flexible signal config * Update tests and examples * Fix enable_from_gpio value * Access signals from pin drivers * DummyPin is not a pin * Remove redundant public fns * Various fixes, rename ErasedPin * Changelog * rustfmt * Update i8080 * Typos and endless recursion * Drop Pin suffix from traits * Extract AF conversion * Touch up changelog * Clean up spi tests * Refactor pull resistor handling * Don't disable configured output functionality * Clean up TODO * Tweak docs * Clean up examples
118 lines
3.4 KiB
Rust
118 lines
3.4 KiB
Rust
//! 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
|
|
//!
|
|
//! The following wiring is assumed:
|
|
//! - Control signal => GPIO4
|
|
//! - Edge signal => GPIO5
|
|
|
|
//% CHIPS: esp32 esp32c6 esp32h2 esp32s2 esp32s3
|
|
|
|
#![no_std]
|
|
#![no_main]
|
|
|
|
use core::{cell::RefCell, cmp::min, sync::atomic::Ordering};
|
|
|
|
use critical_section::Mutex;
|
|
use esp_backtrace as _;
|
|
use esp_hal::{
|
|
gpio::{Io, Pull},
|
|
interrupt::Priority,
|
|
pcnt::{
|
|
channel::{self, PcntInputConfig, PcntSource},
|
|
unit,
|
|
Pcnt,
|
|
},
|
|
prelude::*,
|
|
};
|
|
use esp_println::println;
|
|
use portable_atomic::AtomicI32;
|
|
|
|
static UNIT0: Mutex<RefCell<Option<unit::Unit<'static, 1>>>> = Mutex::new(RefCell::new(None));
|
|
static VALUE: AtomicI32 = AtomicI32::new(0);
|
|
|
|
#[entry]
|
|
fn main() -> ! {
|
|
let peripherals = esp_hal::init(esp_hal::Config::default());
|
|
|
|
let io = Io::new(peripherals.GPIO, peripherals.IO_MUX);
|
|
|
|
// Set up a pulse counter:
|
|
println!("setup pulse counter unit 0");
|
|
let mut pcnt = Pcnt::new(peripherals.PCNT);
|
|
pcnt.set_interrupt_handler(interrupt_handler);
|
|
let u0 = pcnt.unit1;
|
|
u0.set_low_limit(Some(-100)).unwrap();
|
|
u0.set_high_limit(Some(100)).unwrap();
|
|
u0.set_filter(Some(min(10u16 * 80, 1023u16))).unwrap();
|
|
u0.clear();
|
|
|
|
println!("setup channel 0");
|
|
let ch0 = &u0.channel0;
|
|
let pin_a = io.pins.gpio4;
|
|
let pin_b = io.pins.gpio5;
|
|
|
|
ch0.set_ctrl_signal(PcntSource::from(
|
|
pin_a.peripheral_input(),
|
|
PcntInputConfig { pull: Pull::Up },
|
|
));
|
|
ch0.set_edge_signal(PcntSource::from(
|
|
pin_b.peripheral_input(),
|
|
PcntInputConfig { pull: Pull::Up },
|
|
));
|
|
ch0.set_ctrl_mode(channel::CtrlMode::Reverse, channel::CtrlMode::Keep);
|
|
ch0.set_input_mode(channel::EdgeMode::Increment, channel::EdgeMode::Decrement);
|
|
|
|
println!("setup channel 1");
|
|
let ch1 = &u0.channel1;
|
|
ch1.set_ctrl_signal(PcntSource::from(
|
|
pin_b.peripheral_input(),
|
|
PcntInputConfig { pull: Pull::Up },
|
|
));
|
|
ch1.set_edge_signal(PcntSource::from(
|
|
pin_a.peripheral_input(),
|
|
PcntInputConfig { pull: Pull::Up },
|
|
));
|
|
ch1.set_ctrl_mode(channel::CtrlMode::Reverse, channel::CtrlMode::Keep);
|
|
ch1.set_input_mode(channel::EdgeMode::Decrement, channel::EdgeMode::Increment);
|
|
|
|
println!("enabling interrupts");
|
|
u0.listen();
|
|
println!("resume pulse counter unit 0");
|
|
u0.resume();
|
|
|
|
let counter = u0.counter.clone();
|
|
|
|
critical_section::with(|cs| UNIT0.borrow_ref_mut(cs).replace(u0));
|
|
|
|
let mut last_value: i32 = 0;
|
|
loop {
|
|
let value: i32 = counter.get() as i32 + VALUE.load(Ordering::SeqCst);
|
|
if value != last_value {
|
|
println!("value: {value}");
|
|
last_value = value;
|
|
}
|
|
}
|
|
}
|
|
|
|
#[handler(priority = Priority::Priority2)]
|
|
fn interrupt_handler() {
|
|
critical_section::with(|cs| {
|
|
let mut u0 = UNIT0.borrow_ref_mut(cs);
|
|
let u0 = u0.as_mut().unwrap();
|
|
if u0.interrupt_is_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();
|
|
}
|
|
});
|
|
}
|