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.
285 lines
8.9 KiB
285 lines
8.9 KiB
5 years ago
|
/**
|
||
|
\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
|
||
|
|