#encoder #rotary #real-time #driver

no-std sb-rotary-encoder

Rotary encoder processor for embedded systems

1 unstable release

0.1.0 Apr 15, 2023

#1727 in Embedded development

MIT license

15KB
255 lines

sb-rotary-encoder

no_std Rust crate for processing rotary encoder signals.

Features

  • Platform independent with minimum dependencies.
  • Pulse division for handling of encoders with detents.
  • Velocity calculation via timestamps for optional acceleration.
  • Integer processing for maximum performance.
  • Automatic realignment for encoders with detents (see explanation below).

Encoders with Detents

There are two types of rotary encoders available on the market: ones with detents and ones without. Having detents gives the user an experience of a stepped control, but has some implications that need to be taken care of:

Number of Pulses

The resolution of the detents is often lower than the resolution of pulses generated. So several pulses have to occur before a single step is reached and a corresponding event has to be emitted. In practice, these encoder models typically use 2 or 4 pulses per detent.

Switching Point

For best user experience, the encoder should emit an event when the next step is hit, i.e. when the number of pulses per detent has been reached. Unfortunately, detection of pulses can sometimes be missed either to turning the encoder very fast or due to natural wearout of the component itself. In this case, the noticable switching point will shift, something that is noticeable when doing further movements.

Usage Example

A processing interval of 1ms is recommended for most use cases and fits into the common 1kHz frequency setting of an RTOS or a SysTick counter.

// Number of pulses required for one step. 4 is a typical value for encoders with detents.
const PULSE_DIVIDER: i32 = 4;

// Update frequency in Hz, used for velocity calculation
const UPDATE_FREQUENCY: i32 = 1000;

// Create a new instance of an encoder.
let mut encoder = sb_rotary_encoder::RotaryEncoder::new();

// Some dummy input signals to be replaced by the real pin states.
let input_a = true;
let input_b = false;

// Some dummy tick value, can also be `None` if you don't want to use the velocity feature.
let tick = Some(42);

// Process the input and get an event if a step was recognized.
if let Some(event) = encoder.update(input_a, input_b, tick, PULSE_DIVIDER) {
    println!("{:?}", event);

    // If timestamps were used, the velocity can be calculated.
    // This is done as an additional step because it requires some calculations
    // which may not be always needed.
    if let Some(velocity) = event.velocity(UPDATE_FREQUENCY) {
        println!("{:?}", velocity);

        // The velocity allows to calculate a dynamic step value to accelerate the encoder
        // when moved quickly.
        let acceleration = velocity >> 4;
        let step = event.step() + (event.step() * acceleration);
    }
}

Tests

Run cargo test for the unit tests.

License

Published under the MIT license. Any contribution to this project must be provided under the same license conditions.

Author: Oliver Rockstedt info@sourcebox.de

No runtime deps