Skip to content

Improve PCNT API (with HIL tests) #1765

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

Merged
merged 1 commit into from
Jul 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions esp-hal/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- `esp_wifi::initialize` no longer requires running maximum CPU clock, instead check it runs above 80MHz. (#1688)
- Move DMA descriptors from DMA Channel to each individual peripheral driver. (#1719)
- Improved interrupt latency on Xtensa based chips (#1735)
- Improve PCNT api (#1765)

### Removed
- uart: Removed `configure_pins` methods (#1592)
Expand Down
250 changes: 135 additions & 115 deletions esp-hal/src/pcnt/channel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
//! individual channels of the `PCNT` peripheral of pulse counting and signal
//! edge detection on ESP chips.

use super::unit;
use core::marker::PhantomData;

use crate::{
gpio::{InputPin, InputSignal, Pull, ONE_INPUT, ZERO_INPUT},
peripheral::Peripheral,
Expand All @@ -29,47 +30,13 @@ impl Default for PcntInputConfig {
}
}

/// Channel number
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
pub enum Number {
Channel0 = 0,
Channel1 = 1,
}

pub use crate::peripherals::pcnt::unit::conf0::{CTRL_MODE as CtrlMode, EDGE_MODE as EdgeMode};

/// Pulse Counter configuration for a single channel
#[derive(Debug, Copy, Clone)]
pub struct Config {
/// PCNT low control mode
pub lctrl_mode: CtrlMode,
/// PCNT high control mode
pub hctrl_mode: CtrlMode,
/// PCNT signal positive edge count mode
pub pos_edge: EdgeMode,
/// PCNT signal negative edge count mode
pub neg_edge: EdgeMode,
pub invert_ctrl: bool,
pub invert_sig: bool,
}

impl Default for Config {
fn default() -> Self {
Self {
lctrl_mode: CtrlMode::Reverse,
hctrl_mode: CtrlMode::Reverse,
pos_edge: EdgeMode::Increment,
neg_edge: EdgeMode::Increment,
invert_ctrl: false,
invert_sig: false,
}
}
}

/// PcntPin can be always high, always low, or an actual pin
#[derive(Clone, Copy)]
pub struct PcntSource {
source: u8,
inverted: bool,
}

impl PcntSource {
Expand All @@ -87,144 +54,197 @@ impl PcntSource {

Self {
source: pin.number(crate::private::Internal),
inverted: false,
}
}
pub fn always_high() -> Self {
Self { source: ONE_INPUT }
Self {
source: ONE_INPUT,
inverted: false,
}
}
pub fn always_low() -> Self {
Self { source: ZERO_INPUT }
Self {
source: ZERO_INPUT,
inverted: false,
}
}

pub fn invert(self) -> Self {
Self {
source: self.source,
inverted: !self.inverted,
}
}
}

pub struct Channel {
unit: unit::Number,
channel: Number,
pub struct Channel<'d, const UNIT: usize, const NUM: usize> {
_phantom: PhantomData<&'d ()>,
// Individual channels are not Send, since they share registers.
_not_send: PhantomData<*const ()>,
}

impl Channel {
impl<'d, const UNIT: usize, const NUM: usize> Channel<'d, UNIT, NUM> {
/// return a new Channel
pub(super) fn new(unit: unit::Number, channel: Number) -> Self {
Self { unit, channel }
pub(super) fn new() -> Self {
Self {
_phantom: PhantomData,
_not_send: PhantomData,
}
}

/// Configures how the channel behaves based on the level of the control
/// signal.
///
/// * `low` - The behaviour of the channel when the control signal is low.
/// * `high` - The behaviour of the channel when the control signal is high.
pub fn set_ctrl_mode(&self, low: CtrlMode, high: CtrlMode) {
let pcnt = unsafe { &*crate::peripherals::PCNT::ptr() };
let conf0 = pcnt.unit(UNIT).conf0();

conf0.modify(|_, w| {
w.ch_hctrl_mode(NUM as u8)
.variant(high)
.ch_lctrl_mode(NUM as u8)
.variant(low)
});
}

/// Configure the channel
pub fn configure(&mut self, ctrl_signal: PcntSource, edge_signal: PcntSource, config: Config) {
/// Configures how the channel affects the counter based on the transition
/// made by the input signal.
///
/// * `neg_edge` - The effect on the counter when the input signal goes 1 ->
/// 0.
/// * `pos_edge` - The effect on the counter when the input signal goes 0 ->
/// 1.
pub fn set_input_mode(&self, neg_edge: EdgeMode, pos_edge: EdgeMode) {
let pcnt = unsafe { &*crate::peripherals::PCNT::ptr() };
let conf0 = pcnt.unit(self.unit as usize).conf0();
let conf0 = pcnt.unit(UNIT).conf0();

conf0.modify(|_, w| {
w.ch_hctrl_mode(self.channel as u8)
.variant(config.hctrl_mode);
w.ch_lctrl_mode(self.channel as u8)
.variant(config.lctrl_mode);
w.ch_neg_mode(self.channel as u8).variant(config.neg_edge);
w.ch_pos_mode(self.channel as u8).variant(config.pos_edge)
w.ch_neg_mode(NUM as u8).variant(neg_edge);
w.ch_pos_mode(NUM as u8).variant(pos_edge)
});
self.set_ctrl_signal(ctrl_signal, config.invert_ctrl);
self.set_edge_signal(edge_signal, config.invert_sig);
}

/// Set the control signal (pin/high/low) for this channel
pub fn set_ctrl_signal(&self, source: PcntSource, invert: bool) -> &Self {
let signal = match self.unit {
unit::Number::Unit0 => match self.channel {
Number::Channel0 => InputSignal::PCNT0_CTRL_CH0,
Number::Channel1 => InputSignal::PCNT0_CTRL_CH1,
},
unit::Number::Unit1 => match self.channel {
Number::Channel0 => InputSignal::PCNT1_CTRL_CH0,
Number::Channel1 => InputSignal::PCNT1_CTRL_CH1,
},
unit::Number::Unit2 => match self.channel {
Number::Channel0 => InputSignal::PCNT2_CTRL_CH0,
Number::Channel1 => InputSignal::PCNT2_CTRL_CH1,
},
unit::Number::Unit3 => match self.channel {
Number::Channel0 => InputSignal::PCNT3_CTRL_CH0,
Number::Channel1 => InputSignal::PCNT3_CTRL_CH1,
pub fn set_ctrl_signal(&self, source: PcntSource) -> &Self {
let signal = match UNIT {
0 => match NUM {
0 => InputSignal::PCNT0_CTRL_CH0,
1 => InputSignal::PCNT0_CTRL_CH1,
_ => unreachable!(),
},
1 => match NUM {
0 => InputSignal::PCNT1_CTRL_CH0,
1 => InputSignal::PCNT1_CTRL_CH1,
_ => unreachable!(),
},
2 => match NUM {
0 => InputSignal::PCNT2_CTRL_CH0,
1 => InputSignal::PCNT2_CTRL_CH1,
_ => unreachable!(),
},
3 => match NUM {
0 => InputSignal::PCNT3_CTRL_CH0,
1 => InputSignal::PCNT3_CTRL_CH1,
_ => unreachable!(),
},
#[cfg(esp32)]
unit::Number::Unit4 => match self.channel {
Number::Channel0 => InputSignal::PCNT4_CTRL_CH0,
Number::Channel1 => InputSignal::PCNT4_CTRL_CH1,
4 => match NUM {
0 => InputSignal::PCNT4_CTRL_CH0,
1 => InputSignal::PCNT4_CTRL_CH1,
_ => unreachable!(),
},
#[cfg(esp32)]
unit::Number::Unit5 => match self.channel {
Number::Channel0 => InputSignal::PCNT5_CTRL_CH0,
Number::Channel1 => InputSignal::PCNT5_CTRL_CH1,
5 => match NUM {
0 => InputSignal::PCNT5_CTRL_CH0,
1 => InputSignal::PCNT5_CTRL_CH1,
_ => unreachable!(),
},
#[cfg(esp32)]
unit::Number::Unit6 => match self.channel {
Number::Channel0 => InputSignal::PCNT6_CTRL_CH0,
Number::Channel1 => InputSignal::PCNT6_CTRL_CH1,
6 => match NUM {
0 => InputSignal::PCNT6_CTRL_CH0,
1 => InputSignal::PCNT6_CTRL_CH1,
_ => unreachable!(),
},
#[cfg(esp32)]
unit::Number::Unit7 => match self.channel {
Number::Channel0 => InputSignal::PCNT7_CTRL_CH0,
Number::Channel1 => InputSignal::PCNT7_CTRL_CH1,
7 => match NUM {
0 => InputSignal::PCNT7_CTRL_CH0,
1 => InputSignal::PCNT7_CTRL_CH1,
_ => unreachable!(),
},
_ => unreachable!(),
};

if (signal as usize) <= crate::gpio::INPUT_SIGNAL_MAX as usize {
unsafe { &*GPIO::PTR }
.func_in_sel_cfg(signal as usize)
.modify(|_, w| unsafe {
w.sel().set_bit();
w.in_inv_sel().bit(invert);
w.in_inv_sel().bit(source.inverted);
w.in_sel().bits(source.source)
});
}
self
}

/// Set the edge signal (pin/high/low) for this channel
pub fn set_edge_signal(&self, source: PcntSource, invert: bool) -> &Self {
let signal = match self.unit {
unit::Number::Unit0 => match self.channel {
Number::Channel0 => InputSignal::PCNT0_SIG_CH0,
Number::Channel1 => InputSignal::PCNT0_SIG_CH1,
},
unit::Number::Unit1 => match self.channel {
Number::Channel0 => InputSignal::PCNT1_SIG_CH0,
Number::Channel1 => InputSignal::PCNT1_SIG_CH1,
},
unit::Number::Unit2 => match self.channel {
Number::Channel0 => InputSignal::PCNT2_SIG_CH0,
Number::Channel1 => InputSignal::PCNT2_SIG_CH1,
},
unit::Number::Unit3 => match self.channel {
Number::Channel0 => InputSignal::PCNT3_SIG_CH0,
Number::Channel1 => InputSignal::PCNT3_SIG_CH1,
pub fn set_edge_signal(&self, source: PcntSource) -> &Self {
let signal = match UNIT {
0 => match NUM {
0 => InputSignal::PCNT0_SIG_CH0,
1 => InputSignal::PCNT0_SIG_CH1,
_ => unreachable!(),
},
1 => match NUM {
0 => InputSignal::PCNT1_SIG_CH0,
1 => InputSignal::PCNT1_SIG_CH1,
_ => unreachable!(),
},
2 => match NUM {
0 => InputSignal::PCNT2_SIG_CH0,
1 => InputSignal::PCNT2_SIG_CH1,
_ => unreachable!(),
},
3 => match NUM {
0 => InputSignal::PCNT3_SIG_CH0,
1 => InputSignal::PCNT3_SIG_CH1,
_ => unreachable!(),
},
#[cfg(esp32)]
unit::Number::Unit4 => match self.channel {
Number::Channel0 => InputSignal::PCNT4_SIG_CH0,
Number::Channel1 => InputSignal::PCNT4_SIG_CH1,
4 => match NUM {
0 => InputSignal::PCNT4_SIG_CH0,
1 => InputSignal::PCNT4_SIG_CH1,
_ => unreachable!(),
},
#[cfg(esp32)]
unit::Number::Unit5 => match self.channel {
Number::Channel0 => InputSignal::PCNT5_SIG_CH0,
Number::Channel1 => InputSignal::PCNT5_SIG_CH1,
5 => match NUM {
0 => InputSignal::PCNT5_SIG_CH0,
1 => InputSignal::PCNT5_SIG_CH1,
_ => unreachable!(),
},
#[cfg(esp32)]
unit::Number::Unit6 => match self.channel {
Number::Channel0 => InputSignal::PCNT6_SIG_CH0,
Number::Channel1 => InputSignal::PCNT6_SIG_CH1,
6 => match NUM {
0 => InputSignal::PCNT6_SIG_CH0,
1 => InputSignal::PCNT6_SIG_CH1,
_ => unreachable!(),
},
#[cfg(esp32)]
unit::Number::Unit7 => match self.channel {
Number::Channel0 => InputSignal::PCNT7_SIG_CH0,
Number::Channel1 => InputSignal::PCNT7_SIG_CH1,
7 => match NUM {
0 => InputSignal::PCNT7_SIG_CH0,
1 => InputSignal::PCNT7_SIG_CH1,
_ => unreachable!(),
},
_ => unreachable!(),
};

if (signal as usize) <= crate::gpio::INPUT_SIGNAL_MAX as usize {
unsafe { &*GPIO::PTR }
.func_in_sel_cfg(signal as usize)
.modify(|_, w| unsafe {
w.sel().set_bit();
w.in_inv_sel().bit(invert);
w.in_inv_sel().bit(source.inverted);
w.in_sel().bits(source.source)
});
}
Expand Down
Loading