You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
MicroDexed/third-party/MD_REncoder/src/MD_REncoder.h

284 lines
8.9 KiB

/**
\mainpage Main Page
Rotary Encoder Library for Arduino
----------------------------------
This is an adaptation of Ben Buxton's excellent
[rotary library](http://www.buxtronix.net/2011/10/rotary-encoders-done-properly.html)
and implements additional features for encoder rotation speed.
Original library copyright 2011 Ben Buxton. Licensed under the GNU GPL Version 3.
Contact: bb@cactii.net
Features
--------
- Debounce handling with support for high rotation speeds
- Correctly handles direction changes mid-step
- Checks for valid state changes for more robust counting and noise immunity
- Interrupt based or polling in loop()
- Counts full-steps (default) or half-steps
- Calculates speed of rotation
If you like and use this library please consider making a small donation using [PayPal](https://paypal.me/MajicDesigns/4USD)
Topics
------
- \subpage pageBackground
- \subpage pageLibrary
Revision History
----------------
Jan 2020 - version 1.0.1
- Adjusted order of class initializers to fix errors in some compilers
April 2014 - version 1.0
- Initial implementation from Ben's code
- Cleaned up some compile issues and added begin() method
- Updated and documented
- Added speed functionality
Copyright
---------
This adaptation copyright (C) 2014 Marco Colli. All rights reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
\page pageBackground Detecting an Encoder's Rotation
_This great explanation is reproduced from Ben Buxton's example code._
A typical mechanical rotary encoder emits a two bit gray code on 3 output pins.
Every step in the output (often accompanied by a physical 'click') generates a
specific sequence of output codes on the pins.
There are 3 pins used for the rotary encoding - one common and two 'bit' pins
(called A and B).
The following is the typical sequence of codes output when moving from one
step to the next:
Position | Bit1| Bit2|
--------:|:---:|:---:|
Step1 | 0 | 0 |
1/4 | 1 | 0 |
1/2 | 1 | 1 |
3/4 | 0 | 1 |
Step2 | 0 | 0 |
From this table, we can see that when moving from one 'click' to the next,
there are 4 changes in the output code.
- From an initial 0 - 0, Bit1 goes high, Bit0 stays low.
- Then both bits are high, halfway through the step.
- Then Bit1 goes low, but Bit2 stays high.
- Finally at the end of the step, both bits return to 0.
Detecting the direction is easy - the table simply goes in the other direction
(read up instead of down).
To decode this, we use a simple state machine. Every time the output code changes,
it follows state, until finally a full steps worth of code is received (in the
correct order). At the final 0-0, it returns a value indicating a step in one
direction or the other.
It's also possible to use 'half-step' mode. This just emits an event at both the
0-0 and 1-1 positions. This might be useful for some encoders where you want to
detect all positions. In MD_REncoder.h set ENABLE_HALF_STEP to 1 to enable
half-step mode.
If an invalid state happens (for example we go from '0-1' straight to '1-0'), the
state machine resets to the start until 0-0 and the next valid codes occur.
The biggest advantage of using a state machine over other algorithms is that this
has inherent debounce built in. Other algorithms emit spurious output with switch
bounce, but this one will simply flip between sub-states until the bounce settles,
then continue along the state machine. A side effect of debounce is that fast
rotations can cause steps to be skipped. By not requiring debounce, fast rotations
can be accurately measured. Another advantage is the ability to properly handle bad
state, such as due to EMI, etc. It is also a lot simpler than others - a static
state table and less than 10 lines of logic.
\page pageLibrary The REncoder Library
Compile Time Switches
---------------------
ENABLE_HALF_STEP is 0 by default. Set this to 1 to emit codes when the rotary encoder
is at 11 as well as 00. The default is to emit codes only at 00.
ENABLE_PULLUPS is set to 1 by default. Set this 0 if internal pullup resistors on the
input pins are not required.
ENABLE_SPEED is set to 1 by default. Set this to 0 to disable the code and storage used to
calculate the speed of the encoder rotation.
Speed Calculation
-----------------
The number of clicks is accumulated during the period defined by setPeriod(). Once the time
has expired the velocity is calculated in clicks per second by multiplying the number of
clicks by the number of periods in a second.
speed = ClickCount * (1000 / period)
*/
#ifndef _MD_RENCODER_H
#define _MD_RENCODER_H
#include <Arduino.h>
/**
* \file
* \brief Main header file for the MD_Parola library
*/
// Library options
/**
\def ENABLE_HALF_STEP
Set this to 1 to emit codes when the rotary encoder is at 11 as well as 00.
The default is to emit codes only at 00.
*/
#define ENABLE_HALF_STEP 0
/**
\def ENABLE_PULLUPS
Set this 0 if internal pullup resistors on the input pins are not required.
*/
#define ENABLE_PULLUPS 1
/**
\def ENABLE_SPEED
Set this to 0 to disable the code and storage used to calculate the encoder rotation speed.
*/
#define ENABLE_SPEED 1
/**
Set the default sampling period for measuring the speed, in milliseconds. This works best as
a whole fraction of 1000 (ie 100, 200, 500, 1000). Longer periods provide some hysteresis
which is useful when the encoder is being turned by hand.
*/
#define DEFAULT_PERIOD 500
// Direction values returned by read() method
/**
\def DIR_NONE
read() return value - No complete step/movement
*/
#define DIR_NONE 0x00
/**
\def DIR_CW
read() return value - Clockwise step/movement
*/
#define DIR_CW 0x10
/**
\def DIR_CCW
read() return value - Counter-clockwise step/movement
*/
#define DIR_CCW 0x20
/**
* Core object for the MD_REncoder library
*/
class MD_REncoder
{
public:
/**
* Class Constructor.
*
* Instantiate a new instance of the class.
*
* \param pinA the pin number for the encoder A output
* \param pinB the pin number for the encoder B output
*/
MD_REncoder(uint8_t pinA, uint8_t pinB);
/**
* Initialize the object.
*
* Initialize the object data. This will be called to initialize
* new data for the class that cannot be done during the object creation.
*/
void begin(void);
/**
* Read the direction of rotation.
*
* Read the direction of rotation inferred from the previous state of the
* encoder the current state of the inputs. This method should be called
* on a frequent regular basis to ensure smooth encoder inputs.
*
* \return One of the DIR_NONE, DIR_CW or DIR_CCW.
*/
int32_t read_value(void);
int8_t read(void);
#if ENABLE_SPEED
/**
* Set the sampling period for the speed detection.
*
* Set the speed sampling interval in milliseconds. The period
* must be greater than 0 and less than 1000. This works best as
* a whole fraction of 1000 (ie 100, 200, 500, 1000). Longer
* periods provide some hysteresis which is useful when the
* encoder is being turned by hand.
*
* \param t time in millisecond between 0 and 1000 inclusive.
*/
inline void setPeriod(uint16_t t) { if ((t != 0) && (t <= 1000)) _period = t; };
/**
* Return the speed of the encoder.
*
* Calculate the speed (steps per second) for the encoder.
* The sampling period is set using the setPeriod() method.
* If the encoder is used to enter numbers or scan through menus, the speed
* can be used to accelerate the display (eg, skip larger values for each click).
*
* \return The speed in clicks per second.
*/
inline uint16_t speed(void)
{
if(_spd==0)
return(1);
if(_spd>10)
return(10/1.5+0.5);
return(float(_spd)/1.5+0.5);
};
#endif
void update(void);
void write(int32_t value);
private:
// Hardware data
uint8_t _pinA; // pin A number
uint8_t _pinB; // pin B number
// Encoder value
uint8_t _state; // latest state for the encoder
int32_t value;
int8_t last_dir;
#if ENABLE_SPEED
// Velocity data
uint16_t _period; // velocity calculation period
uint16_t _count; // running count of encoder clicks
uint16_t _spd; // last calculated speed (no sign) in clicks/second
uint32_t _timeLast; // last time read
#endif
};
#endif