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.
146 lines
4.3 KiB
146 lines
4.3 KiB
/*
|
|
Copyright 2021 Peter Harrison
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
https://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
|
|
------------------------------------------------------------------------------
|
|
|
|
BasicEncoder provides a class for reading a rotary encoder knob.
|
|
It is not suited to motor encoders.
|
|
The switch, if present, needs a deparate button library .
|
|
|
|
Decoding logic based on https://www.mikrocontroller.net/articles/Drehgeber
|
|
*/
|
|
|
|
#ifndef BASIC_ENCODER_H_
|
|
#define BASIC_ENCODER_H_
|
|
|
|
#include <Arduino.h>
|
|
|
|
uint8_t SREG;
|
|
|
|
class BasicEncoder
|
|
{
|
|
public:
|
|
BasicEncoder(int8_t pinA, int8_t pinB, uint8_t active_state = LOW, uint8_t steps = 4)
|
|
: m_pin_a(pinA), m_pin_b(pinB), m_pin_active(active_state), m_steps_per_count(steps) {
|
|
pinMode(pinA, INPUT_PULLUP);
|
|
pinMode(pinB, INPUT_PULLUP);
|
|
m_previous_state - pin_state();
|
|
m_change = 0;
|
|
}
|
|
~BasicEncoder() {}
|
|
|
|
void begin() { reset(); }
|
|
|
|
int8_t pin_state() {
|
|
int8_t state_now = 0;
|
|
if (digitalRead(m_pin_a) == m_pin_active) {
|
|
state_now |= 2;
|
|
}
|
|
if (digitalRead(m_pin_b) == m_pin_active) {
|
|
state_now |= 1;
|
|
}
|
|
return state_now;
|
|
}
|
|
|
|
// to update the encoder changes
|
|
// call this method in a timmer interrupt for best performance
|
|
// it could also be called in the main loop
|
|
// this takes about 10-15us using digitalRead on Arduino Nano
|
|
void service() {
|
|
int8_t state_now = pin_state();
|
|
state_now ^= state_now >> 1; // two bit gray-to-binary
|
|
int8_t difference = m_previous_state - state_now;
|
|
// bit 1 has the direction, bit 0 is set if changeed
|
|
if (difference & 1) {
|
|
m_previous_state = state_now;
|
|
int delta = (difference & 2) - 1;
|
|
if (m_reversed) {
|
|
delta = -delta;
|
|
}
|
|
m_change += delta;
|
|
m_steps += delta;
|
|
}
|
|
}
|
|
|
|
/****************************************************************
|
|
|
|
// try this if there are unwanted transitions at the detents
|
|
// it is half resolution so counts are doubled
|
|
const int8_t decode_table[16] PROGMEM = {0, 0, -2, 0, 0, 0, 0, 2, 2, 0, 0, 0, 0, -2, 0, 0};
|
|
void flaky_encoder_service(void) {
|
|
// just get the bits - no gray-to-binary conversion needed
|
|
int8_t state_now = pin_state();
|
|
static int8_t encoder_state = state_now;
|
|
encoder_state = ((encoder_state << 2) | state_now) & 0x0f;
|
|
encoder_change += (int8_t)pgm_read_byte(&decode_table[encoder_state]);
|
|
}
|
|
|
|
****************************************************************/
|
|
|
|
// Read changes frequently enough that overflows cannot happen.
|
|
int8_t get_change() {
|
|
uint8_t sreg = SREG; // save the current interrupt enable flag
|
|
noInterrupts();
|
|
int8_t change = m_change;
|
|
// the switch statement can make better code because only optimised
|
|
// operations are used instead of generic division
|
|
switch (m_steps_per_count) {
|
|
case 4:
|
|
m_change %= 4;
|
|
change /= 4;
|
|
break;
|
|
case 2:
|
|
m_change %= 2;
|
|
change /= 2;
|
|
break;
|
|
default:
|
|
m_change = 0;
|
|
break;
|
|
}
|
|
SREG = sreg; // restore the previous interrupt enable flag state
|
|
return change;
|
|
}
|
|
|
|
int get_count() {
|
|
uint8_t sreg = SREG; // save the current interrupt enable flag
|
|
noInterrupts();
|
|
int count = m_steps / m_steps_per_count;
|
|
SREG = sreg; // restore the previous interrupt enable flag state
|
|
return count;
|
|
}
|
|
|
|
void reset() {
|
|
uint8_t sreg = SREG; // save the current interrupt enable flag
|
|
noInterrupts();
|
|
m_steps = 0;
|
|
m_change = 0;
|
|
SREG = sreg; // restore the previous interrupt enable flag state
|
|
}
|
|
|
|
void set_reverse() { m_reversed = true; }
|
|
|
|
void set_forward() { m_reversed = false; }
|
|
|
|
private:
|
|
int8_t m_pin_a = 0;
|
|
int8_t m_pin_b = 0;
|
|
uint8_t m_pin_active = LOW;
|
|
uint8_t m_steps_per_count = 4;
|
|
bool m_reversed = false;
|
|
volatile int m_change = 0;
|
|
int8_t m_previous_state = 0;
|
|
int m_steps = 0;
|
|
};
|
|
#endif // BASIC_ENCODER_H_
|