1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105
//! An efficient `no_std`-compatible implementation of a quadrature decoder,
//! based on a finite-state-transducer with support for different step-modes.
#![warn(missing_docs)]
#![cfg_attr(not(test), no_std)]
mod decoder;
mod index_decoder;
mod state_transducer;
mod validator;
pub use self::decoder::{IncrementalDecoder, IndexedIncrementalDecoder};
use self::state_transducer::StateTransducer;
mod sealed {
pub trait Sealed {}
}
/// An error indicating an invalid quadrature signal sequence.
#[repr(u8)]
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
#[allow(non_camel_case_types)]
pub enum Error {
/// Invalid gray-code sequence [00, 11].
E00_11 = 0b_00_11,
/// Invalid gray-code sequence [11, 00].
E11_00 = 0b_11_00,
/// Invalid gray-code sequence [01, 10].
E01_10 = 0b_01_10,
/// Invalid gray-code sequence [10, 01].
E10_01 = 0b_10_01,
}
/// The change detected by a quadrature decoder.
#[repr(i8)]
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
pub enum Change {
/// Channel A leads channel B, commonly describing a forwards change.
Positive = 1,
/// Channel B leads channel A, commonly describing a backwards change.
Negative = -1,
}
/// A quadrature-based decoder's step mode.
pub trait StepMode: sealed::Sealed {
/// The step-mode's number of pulses per (quadrature) cycle (PPC).
const PULSES_PER_CYCLE: usize;
}
/// A step mode that is able to detect a "change" (e.g. movement)
/// for every stable full cycle (i.e. 1 change per quadrature cycle).
///
/// Full-step mode provides:
/// - high noise-resistance (factor 4× relative to naïve decoding)
/// - low resolution (factor 1× relative to native resolution)
pub struct FullStep;
impl sealed::Sealed for FullStep {}
impl StepMode for FullStep {
/// The number of pulses per (quadrature) cycle (PPC).
///
/// As an example, consider the effective pulses per revolution (PPR)
/// of a rotary encoder with 100 cycles per revolution (CPR): 100 PPR.
const PULSES_PER_CYCLE: usize = 1;
}
/// A step mode that is able to detect a "change" (e.g. movement) (e.g. movement)
/// for every stable half cycle (i.e. 2 changes per quadrature cycle),
/// resulting in an effective 2× resolution multiplication.
///
/// Half-step mode effectively doubles the resolution of the decoder.
///
/// Half-step mode provides:
/// - medium noise-resistance (factor 2× relative to naïve decoding)
/// - medium resolution (factor 1× relative to native resolution)
pub struct HalfStep;
impl sealed::Sealed for HalfStep {}
impl StepMode for HalfStep {
/// The number of pulses per (quadrature) cycle (PPC).
///
/// As an example, consider the effective pulses per revolution (PPR)
/// of a rotary encoder with 100 cycles per revolution (CPR): 200 PPR.
const PULSES_PER_CYCLE: usize = 2;
}
/// A step mode that is able to detect a "change" (e.g. movement)
/// for every stable quarter cycle (i.e. 4 change per quadrature cycle),
/// resulting in an effective 4× resolution multiplication.
///
/// Quad-step mode effectively quadruples the resolution of the decoder.
///
/// Quad-step mode provides:
/// - low noise-resistance (factor 1× relative to naïve decoding)
/// - high resolution (factor 1× relative to native resolution)
pub struct QuadStep;
impl sealed::Sealed for QuadStep {}
impl StepMode for QuadStep {
/// The number of pulses per (quadrature) cycle (PPC).
///
/// As an example, consider the effective pulses per revolution (PPR)
/// of a rotary encoder with 100 cycles per revolution (CPR): 400 PPR.
const PULSES_PER_CYCLE: usize = 4;
}