esp-hal/esp32c6-hal/examples/pcnt_encoder.rs
Scott Mabin db409ffe7b
Unify the system peripheral (#832)
* Unify the system peripheral

Whilst the PCR, SYSTEM and DPORT peripherals are different, we currently
use them all in the same way. This PR unifies the peripheral name in the
hal to `SYSTEM`. The idea is that they all do the same sort of thing, so
we can collect them under the same name, and later down the line we can
being to expose differences under an extended API.

The benifits to this are imo quite big, the examples now are all identical,
which makes things easier for esp-wifi, and paves a path towards the
multichip hal.

Why not do this in the PAC? Imo the pac should be as close to the
hardware as possible, and the HAL is where we should abstractions such
as this.

* changelog
2023-09-29 08:14:50 -07:00

134 lines
3.8 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
#![no_std]
#![no_main]
use core::{
cell::RefCell,
cmp::min,
sync::atomic::{AtomicI32, Ordering},
};
use critical_section::Mutex;
use esp32c6_hal as esp_hal;
use esp_backtrace as _;
use esp_hal::{
clock::ClockControl,
interrupt,
pcnt::{channel, channel::PcntSource, unit, PCNT},
peripherals::{self, Peripherals},
prelude::*,
IO,
};
use esp_println::println;
static UNIT0: Mutex<RefCell<Option<unit::Unit>>> = Mutex::new(RefCell::new(None));
static VALUE: AtomicI32 = AtomicI32::new(0);
#[entry]
fn main() -> ! {
let peripherals = Peripherals::take();
let system = peripherals.SYSTEM.split();
let _clocks = ClockControl::boot_defaults(system.clock_control).freeze();
let unit_number = unit::Number::Unit1;
// setup a pulse couter
println!("setup pulse counter unit 0");
let pcnt = PCNT::new(peripherals.PCNT);
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.gpio5.into_pull_up_input();
let mut pin_b = io.pins.gpio6.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();
}
});
}