use embedded_hal::digital::InputPin;
use crate::{Direction, RotaryEncoder};
const QUAD_TABLE: [i8; 16] = [
0, 1, -1, 0, -1, 0, 0, 1, 1, 0, 0, -1, 0, -1, 1, 0, ];
impl<DT, CLK> RotaryEncoder<QuadratureTableMode, DT, CLK>
where
DT: InputPin,
CLK: InputPin,
{
pub fn update(&mut self) -> Direction {
self.mode.update(
self.pin_dt.is_high().unwrap_or_default(),
self.pin_clk.is_high().unwrap_or_default(),
)
}
}
impl<LOGIC, DT, CLK> RotaryEncoder<LOGIC, DT, CLK>
where
DT: InputPin,
CLK: InputPin,
{
pub fn into_quadrature_table_mode(
self,
threshold: u8,
) -> RotaryEncoder<QuadratureTableMode, DT, CLK> {
RotaryEncoder {
pin_dt: self.pin_dt,
pin_clk: self.pin_clk,
mode: QuadratureTableMode::new(threshold),
}
}
}
impl Default for QuadratureTableMode {
fn default() -> Self {
Self::new(1)
}
}
pub struct QuadratureTableMode {
prev_state: u8, threshold: u8, count: i8, }
impl QuadratureTableMode {
pub fn new(threshold: u8) -> Self {
Self {
prev_state: 0,
count: 0,
threshold,
}
}
pub fn update(&mut self, dt: bool, clk: bool) -> Direction {
let curr = (dt as u8) | ((clk as u8) << 1);
let idx = ((self.prev_state << 2) | curr) as usize;
let delta = QUAD_TABLE[idx];
self.prev_state = curr;
self.count += delta;
if self.count.unsigned_abs() >= self.threshold {
let dir = if self.count > 0 {
Direction::Clockwise
} else {
Direction::Anticlockwise
};
self.count = 0; return dir;
}
Direction::None
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::Direction;
fn drive_sequence(mode: &mut QuadratureTableMode, seq: &[(bool, bool)]) -> Vec<Direction> {
seq.iter().map(|&(dt, clk)| mode.update(dt, clk)).collect()
}
#[test]
fn single_cw_step_threshold_1() {
let mut mode = QuadratureTableMode::new(1);
assert_eq!(mode.update(true, false), Direction::Clockwise);
}
#[test]
fn single_ccw_step_threshold_1() {
let mut mode = QuadratureTableMode::new(1);
assert_eq!(mode.update(false, true), Direction::Anticlockwise);
}
#[test]
fn aggregation_threshold_2_requires_two_valid_pulses() {
let mut mode = QuadratureTableMode::new(2);
assert_eq!(mode.update(true, false), Direction::None);
assert_eq!(mode.update(true, true), Direction::Clockwise);
assert_eq!(mode.update(false, true), Direction::None);
}
#[test]
fn no_movement_on_constant_state() {
let mut mode = QuadratureTableMode::new(1);
for _ in 0..5 {
assert_eq!(mode.update(false, false), Direction::None);
}
}
#[test]
fn invalid_transition_skipped_state() {
let mut mode = QuadratureTableMode::new(1);
assert_eq!(mode.update(true, true), Direction::None);
assert_eq!(mode.update(false, false), Direction::None);
}
#[test]
fn full_cw_cycle_threshold_1() {
let mut mode = QuadratureTableMode::new(1);
let seq = [
(false, false), (true, false), (true, true), (false, true), (false, false), ];
let results = drive_sequence(&mut mode, &seq);
assert_eq!(
results,
vec![
Direction::None,
Direction::Clockwise,
Direction::Clockwise,
Direction::Clockwise,
Direction::Clockwise,
]
);
}
}