Skip to content

Commit

Permalink
Improve PCNT API (with HIL tests)
Browse files Browse the repository at this point in the history
  • Loading branch information
Dominic Fischer committed Jul 6, 2024
1 parent 967c478 commit c6f0a39
Show file tree
Hide file tree
Showing 6 changed files with 695 additions and 336 deletions.
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

0 comments on commit c6f0a39

Please sign in to comment.