-
Notifications
You must be signed in to change notification settings - Fork 23
Encoder using interrupts; faceplate for bigger screen; hat for smaller encoder #5
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
grzesiek2201
wants to merge
2
commits into
UrbanCircles:main
Choose a base branch
from
grzesiek2201:encoder-interrupts
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Binary file not shown.
Binary file not shown.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,246 @@ | ||
| /** | ||
| * Class: KY040 | ||
| * | ||
| * Description: | ||
| * Class for KY-040 rotary encoders. Without builtin pull up resistors for | ||
| * CLK/DT you have to set pinMode( ,INPUT_PULLUP) before using the class. | ||
| * The class works with or without interrupts and prevents bounces | ||
| * by ignoring invalid CLK/DT sequences | ||
| * | ||
| * License: 2-Clause BSD License | ||
| * Copyright (c) 2024 codingABI | ||
| * For details see: LICENSE.txt | ||
| * | ||
| * Valid clockwise sequence for CLK/DT: Low/High->Low/Low->High/Low->High/High | ||
| * | ||
| * @code | ||
| * 0 1 2 3 | ||
| * --+ +---- High | ||
| * CLK | | | ||
| * +---+ Low | ||
| * | ||
| * ----+ +-- High | ||
| * DT | | | ||
| * +---+ Low | ||
| * @endcode | ||
| * | ||
| * Valid counter-clockwise sequence for CLK/DT: High/Low->Low/Low->Low/High->High/High | ||
| * | ||
| * @code | ||
| * 0 1 2 3 | ||
| * ----+ +--- High | ||
| * CLK | | | ||
| * +---+ Low | ||
| * | ||
| * --+ +----- High | ||
| * DT | | | ||
| * +---+ Low | ||
| * @endcode | ||
| * | ||
| * Home: https://github.com/codingABI/KY040 | ||
| * | ||
| * @author codingABI https://github.com/codingABI/ | ||
| * @copyright 2-Clause BSD License | ||
| * @file KY040.h | ||
| * @version 1.0.1 | ||
| */ | ||
| #pragma once | ||
|
|
||
| /** Library version */ | ||
| #define KY040_VERSION "1.0.1" | ||
|
|
||
| #include <arduino.h> | ||
|
|
||
| /** When using sleep modes wait X milliseconds for next sleep after a CLK/DT sequence start do prevent missing signals */ | ||
| #define PREVENTSLEEPMS 150 | ||
| // Pin idle state | ||
| #define INITSTEP 0b11 | ||
| // Max steps for a signal sequence | ||
| #define MAXSEQUENCESTEPS 4 | ||
|
|
||
| /** Class for a KY-040 rotary encoder */ | ||
| class KY040 { | ||
| public: | ||
| /** Rotation states */ | ||
| enum directions | ||
| { | ||
| IDLE, /**< Rotary encoder is idle */ | ||
| ACTIVE, /**< Rotary encoder is rotating, but the CLK/DT sequence has not finished */ | ||
| CLOCKWISE, /**< CLK/DT sequence for one step clockwise rotation has finished */ | ||
| COUNTERCLOCKWISE /**< CLK/DT sequence for one step counter-clockwise rotation has finished */ | ||
| }; | ||
|
|
||
| /**@brief | ||
| * Constructor of a the KY-040 rotary encoder | ||
| * | ||
| * @param[in] clk_pin Digital input pin connected to CLK aka. A | ||
| * @param[in] dt_pin Digital input pin connected to DT aka. B | ||
| */ | ||
| KY040(byte clk_pin, byte dt_pin) | ||
| { | ||
| m_clk_pin = clk_pin; // aka. A | ||
| m_dt_pin = dt_pin; // aka. B | ||
| v_state = 255; | ||
| v_lastResult = IDLE; | ||
| v_lastSequenceStartMillis = millis(); | ||
| v_sequenceStep = 0; | ||
| v_direction = IDLE; | ||
| v_oldState = INITSTEP; | ||
| } | ||
|
|
||
| /**@brief | ||
| * Returns current rotation state from stored pin state. | ||
| * | ||
| * If you do not use interrupts, you have to start setState() and checkRotation() or a function using | ||
| * these (for example getRotation()) very frequently in your loop to prevent missing signals | ||
| * | ||
| * @retval KY040::CLOCKWISE CLK/DT sequence for one step clockwise rotation has finished | ||
| * @retval KY040::COUNTERCLOCKWISE CLK/DT sequence for one step counter-clockwise rotation has finished | ||
| * @retval KY040::IDLE Rotary encoder is idle | ||
| * @retval KY040::ACTIVE Rotary encoder is rotating, but the CLK/DT sequence has not finished | ||
| */ | ||
| byte checkRotation() | ||
| { | ||
| byte result = IDLE; | ||
|
|
||
| if (v_state != v_oldState) { // State changed? | ||
| if (v_sequenceStep == 0) { // Check for begin of rotation | ||
| if (v_state == c_signalSequenceCW[0]) { // Begin of CW | ||
| v_direction=CLOCKWISE; | ||
| v_sequenceStep = 1; | ||
| v_lastSequenceStartMillis = millis(); | ||
| } | ||
| if (v_state == c_signalSequenceCCW[0]) { // Begin of CCW | ||
| v_direction=COUNTERCLOCKWISE; | ||
| v_sequenceStep = 1; | ||
| v_lastSequenceStartMillis = millis(); | ||
| } | ||
| } else { | ||
| switch (v_direction) { | ||
| case CLOCKWISE: | ||
| if (v_state == c_signalSequenceCW[v_sequenceStep]) { | ||
| v_sequenceStep++; | ||
| if (v_sequenceStep >= MAXSEQUENCESTEPS) { // Sequence has finished | ||
| result=v_direction; | ||
| v_lastResult=result; | ||
| v_direction=IDLE; | ||
| v_sequenceStep=0; | ||
| } else result=ACTIVE; | ||
| } else { | ||
| // Invalid sequence | ||
| if (v_state == INITSTEP) { // Reset sequence in init state | ||
| v_direction=IDLE; | ||
| v_sequenceStep=0; | ||
| } | ||
| } | ||
| break; | ||
| case COUNTERCLOCKWISE: | ||
| if (v_state == c_signalSequenceCCW[v_sequenceStep]) { | ||
| v_sequenceStep++; | ||
| if (v_sequenceStep >= MAXSEQUENCESTEPS) { // Sequence has finished | ||
| result=v_direction; | ||
| v_lastResult=result; | ||
| v_direction=IDLE; | ||
| v_sequenceStep=0; | ||
| } else result=ACTIVE; | ||
| } else { | ||
| // Invalid sequence | ||
| if (v_state == INITSTEP) { // Reset sequence in init state | ||
| v_direction=IDLE; | ||
| v_sequenceStep=0; | ||
| } | ||
| } | ||
| break; | ||
| } | ||
| } | ||
| v_oldState = v_state; | ||
| } | ||
| // Prevent unsigned long overrun | ||
| if (millis() - v_lastSequenceStartMillis > PREVENTSLEEPMS) { | ||
| v_lastSequenceStartMillis = millis() - PREVENTSLEEPMS - 1; | ||
| } | ||
| return result; | ||
| } | ||
|
|
||
| /**@brief | ||
| * Get and reset last finished rotation step (Do not use inside ISR) | ||
| * | ||
| * @retval KY040::CLOCKWISE CLK/DT sequence for one step clockwise rotation has finished | ||
| * @retval KY040::COUNTERCLOCKWISE CLK/DT sequence for one step counter-clockwise rotation has finished | ||
| * @retval KY040::IDLE Rotary encoder is idle | ||
| */ | ||
| byte getAndResetLastRotation() | ||
| { | ||
| cli(); | ||
| byte result = v_lastResult; | ||
| v_lastResult = IDLE; | ||
| sei(); | ||
| return result; | ||
| } | ||
|
|
||
| /**@brief | ||
| * Read and stores current pin state for CLK and DT and returns the current rotation state. | ||
| * | ||
| * Reads pin state for CLK and DT with DigitalRead() and checks current rotation state by calling checkRotation() | ||
| * | ||
| * @retval KY040::CLOCKWISE CLK/DT sequence for one step clockwise rotation has finished | ||
| * @retval KY040::COUNTERCLOCKWISE CLK/DT sequence for one step counter-clockwise rotation has finished | ||
| * @retval KY040::IDLE Rotary encoder is idle | ||
| * @retval KY040::ACTIVE Rotary encoder is rotating, but the CLK/DT sequence has not finished | ||
| */ | ||
| byte getRotation() | ||
| { | ||
| setState((digitalRead(m_clk_pin)<<1)+digitalRead(m_dt_pin)); | ||
| return checkRotation(); | ||
| } | ||
|
|
||
| /**@brief | ||
| * Get stored pin states for CLK and DT (Left bit is for CLK, right bit is for DT). Should be called from ISR, when needed | ||
| * | ||
| * @returns Stored pin states for CLK and DT in two bits (Left bit is for CLK, right bit is for DT) | ||
| */ | ||
| byte getState() | ||
| { | ||
| return v_state; | ||
| } | ||
|
|
||
| /**@brief | ||
| * Checks, if it save to go to sleep | ||
| * | ||
| * Returns true, if device was running long enough to get a full sequence (Do not use inside ISR) | ||
| * | ||
| * @retval true Yes, it is save to go to sleep | ||
| * @retval false No, it is not save and you could miss signals, if you go to sleep anyway | ||
| */ | ||
| bool readyForSleep() | ||
| { | ||
| cli(); | ||
| unsigned long lastStepMillis = v_lastSequenceStartMillis; | ||
| sei(); | ||
| return (millis()-lastStepMillis > PREVENTSLEEPMS); | ||
| } | ||
|
|
||
| /**@brief | ||
| * Stores pin states for CLK and DT (Left bit is for CLK, right bit is for DT). Should be called from ISR, when needed. | ||
| * | ||
| * @param[in] state Pin state for CLK and DT in two bits (Left bit is for CLK, right bit is for DT) | ||
| */ | ||
| void setState(byte state) | ||
| { | ||
| v_state = state; | ||
| } | ||
| private: | ||
| byte m_clk_pin; // aka. A | ||
| byte m_dt_pin; // aka. B | ||
| volatile byte v_state; | ||
| volatile byte v_lastResult; | ||
| volatile unsigned long v_lastSequenceStartMillis; | ||
| volatile byte v_sequenceStep; | ||
| volatile byte v_direction; | ||
| volatile byte v_oldState; | ||
| // CLK/DT sequence for a clockwise rotation (One byte instead of a byte array would be enough for the four 2-bit values, but are harder to read) | ||
| const byte c_signalSequenceCW[MAXSEQUENCESTEPS] = {0b01,0b00,0b10,INITSTEP}; | ||
| // CLK/DT sequence for a counter-clockwise rotation (One byte instead of a byte array would be enough for the four 2-bit values, but are harder to read) | ||
| const byte c_signalSequenceCCW[MAXSEQUENCESTEPS] = {0b10,0b00,0b01,INITSTEP}; | ||
|
|
||
| }; | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This caused an error for me in Arduino IDE until I changed arduino.h to Arduino.h