I have a rotary encoder KY-040 connected to a Digispark like this:
CLK go to p0 and and DT go to p2, SW is disconnected for now (it's just a button).
I did everything like the instructions here: http://henrysbench.capnfatz.com/henrys-bench/keyes-ky-040-arduino-rotary-encoder-user-manual/
#include <DigiKeyboard.h>
int pinA = 0; // Connected to CLK on KY-040
int pinB = 2; // Connected to DT on KY-040
int encoderPosCount = 0;
int pinALast;
int aVal;
boolean bCW;
void setup() {
pinMode (pinA,INPUT);
pinMode (pinB,INPUT);
/* Read Pin A
Whatever state it's in will reflect the last position
*/
pinALast = digitalRead(pinA);
Serial.begin (9600);
}
void loop() {
aVal = digitalRead(pinA);
if (aVal != pinALast){ // Means the knob is rotating
// if the knob is rotating, we need to determine direction
// We do that by reading pin B.
if (digitalRead(pinB) != aVal) { // Means pin A Changed first - We're Rotating Clockwise
encoderPosCount ++;
bCW = true;
} else {// Otherwise B changed first and we're moving CCW
bCW = false;
encoderPosCount--;
}
Serial.println("Rotated: ");
if (bCW){
//Serial.println ("clockwise");
DigiKeyboard.sendKeyStroke(KEY_V); //or DigiKeyboard.write("1");
}else{
//Serial.println("counterclockwise");
DigiKeyboard.println("cc");
}
Serial.print("Encoder Position: ");
Serial.println(encoderPosCount);
}
pinALast = aVal;
}
I only changed the pin numbers in the code and the Serial.println (...
to any of the following:
DigiKeyboard.write("1");
DigiKeyboard.println("1");
DigiKeyboard.sendKeyStroke(KEY_V);
All of them didn't do anything when rotating the knob...
Any ideas? Maybe the 10K resistors are too much?
-
This might help: github.com/Bluebie/volume_knob/blob/master/volume_knob.inoMikael Patel– Mikael Patel2015年12月29日 23:10:08 +00:00Commented Dec 29, 2015 at 23:10
2 Answers 2
Shown below is a KY-040 test program that in my tests doesn't lose any counts and is more accurate than some other software; it picks up the counts between detents as well as those at detents. You may be able to adapt it to your DigiKeyboard ATtiny system.
/* roto_jw4.ino -- JW, 29 September 2015 --
* A 4-state state-machine implementation of rotary
* encoding for KY-040 rotary knobs. The state-machine picture at
* https://e2e.ti.com/support/microcontrollers/hercules/f/312/t/318762
* in a Feb 4, 2014 7:40 PM post by Anthony Seely shows counts
* increasing on transitions 10 -> 11 -> 01 -> 00 -> 10 and
* decreasing on transitions the other way. Transitions between 00
* and 11 or 10 and 01 are invalid. This code detects valid
* transitions by (abOld xor abNew) equaling 1 or 2. It detects
* up-count events by the tri-bit value ABA' (where A' is the new
* reading on pin A) being equal to 1, 2, 5, or 6 (a bit mask of
* 0x66), and down-count events by ABA' being equal to 0, 3, 4, or 7
* (a bit mask of 0x99).
*
* On a KY-040 unit I tested, there are 30 detent positions per turn.
* With this unit the code generates 60 counts per turn, which can be
* seen individually as one turns the rotor slowly. Odd counts
* appear between detents, even counts at detents.
*
* Set quadrature-signal pin numbers, via PinA and PinB constants.
* Set IPINMODE to INPUT_PULLUP if there are no external pull-ups
* on encoder AB pins, else set IPINMODE to INPUT
*/
enum { PinA=2, PinB=3, IPINMODE=INPUT };
static byte abOld; // Initialize state
volatile int count; // current rotary count
int old_count; // old rotary count
void setup() {
pinMode(PinA, IPINMODE);
pinMode(PinB, IPINMODE);
attachInterrupt(0, pinChangeISR, CHANGE); // Set up pin-change interrupts
attachInterrupt(1, pinChangeISR, CHANGE);
abOld = count = old_count = 0;
Serial.begin(115200);
Serial.println("Starting Rotary Encoder Test");
}
// On interrupt, read input pins, compute new state, and adjust count
void pinChangeISR() {
enum { upMask = 0x66, downMask = 0x99 };
byte abNew = (digitalRead(PinA) << 1) | digitalRead(PinB);
byte criterion = abNew^abOld;
if (criterion==1 || criterion==2) {
if (upMask & (1 << (2*abOld + abNew/2)))
count++;
else count--; // upMask = ~downMask
}
abOld = abNew; // Save new state
}
void loop() {
if (old_count != count) {
Serial.print(millis());
Serial.print(" ");
Serial.println(count);
old_count = count;
}
}
Since some ATtiny's only have one external-interrupt pin, it may be necessary to use two pin-change interrupts instead of two external-interrupt pins. (Six of the pins on an ATtiny45, for example, support pin-change interrupts, and only one is an external-interrupt pin.) Change PinA and PinB to appropriate pin numbers, and replace
attachInterrupt(0, pinChangeISR, CHANGE); // Set up pin-change interrupts
attachInterrupt(1, pinChangeISR, CHANGE);
with
enableInterrupt(PinA, pinChangeISR, CHANGE); // Set up pin-change interrupts
enableInterrupt(PinB, pinChangeISR, CHANGE);
and add the following #include
near the beginning:
#include <EnableInterrupt.h>
To install the .h file for use via the Arduino IDE, unpack an EnableInterrupt
zip file (for example, enableinterrupt-0.8.2.zip
from bintray.com/greygnome) in the directory ~/sketchbook/libraries
. I think the package works with ATtiny's as well as more-standard Arduinos.
-
This is the only code so far worked for me. All the others caused erratic outputs at some encoder ticks. How to adjust this code so to have only one count output for each tick. I mean your code outputs two consecutive numbers for each tick like this for example for 5 ticks I get: 12 34 56 78 910 But I want to get 1 2 3 4 5. How can I do that? Thanks in advance.user16307– user163072017年01月26日 09:40:12 +00:00Commented Jan 26, 2017 at 9:40
-
@user16307, There are several ways; which is best depends on intended use. You can: • Divide count by 2 before using it • Use the count only if (count&1), or !(count&1), ie, only when odd (or even) • In ISR, instead of count++ say icount++, where icount is a
static
internal counter, and then say,count += icount&1
, orcount += 1-(icount&1)
to incrementcount
only when icount is odd (or even); and similarly for thecount--
case. Please let me know if you find bugs in any of these approaches if you try them. Thanks!James Waldby - jwpat7– James Waldby - jwpat72017年01月26日 19:01:51 +00:00Commented Jan 26, 2017 at 19:01 -
@JamesWaldby-jwpat7, your answer is so great! It helped me with proper values from the encoder. Please, could you explain
pinChangeISR
function? I am new to cpp and I would like to understand it. I understand that there are bit operations and shifts, but explanation would be great. thank youDawe– Dawe2023年02月06日 22:04:50 +00:00Commented Feb 6, 2023 at 22:04 -
@Dawe, I will edit the answer next week to add some detail re
pinChangeISR
. For the moment, refer to the comments in the first paragraph of code and to the referenced state machine diagramJames Waldby - jwpat7– James Waldby - jwpat72023年03月13日 15:45:47 +00:00Commented Mar 13, 2023 at 15:45
int pinA = 0; // Connected to CLK on KY-040
...
Serial.begin (9600);
Hardware Serial uses pins 0 and 1. I would try other pins. (Oh, it's an ATtiny - well I'm not sure about HardwareSerial on that, but it looks suspicious).
See my page about rotary encoders for more ideas.
I only changed the pin numbers in the code and the Serial.println (... to any of the following:
Maybe post the code you actually used, that would be helpful. Not some code you didn't use.
Maybe the 10K resistors are too much?
No, that looks fine.
-
This is the code I used, the only difference was with changing the
Serial.println (...
to other things as I mentioned. On the digispark p3,p4 are used for usb communication, p5 is the reset and p1 is connected to the led so I'd have to add a resistor, so p0 and p2 are only really free ones to use.shinzou– shinzou2015年09月28日 08:19:57 +00:00Commented Sep 28, 2015 at 8:19 -
So you left the
Serial.begin
in? I can't help you without seeing your code, maybe someone else can.2015年09月28日 22:00:45 +00:00Commented Sep 28, 2015 at 22:00 -
Yes, I edited the question.shinzou– shinzou2015年09月29日 08:52:30 +00:00Commented Sep 29, 2015 at 8:52
-
... the only difference was with changing the Serial.println (... to other things as I mentioned.
- not really:Serial.println(encoderPosCount);
2015年09月29日 09:23:18 +00:00Commented Sep 29, 2015 at 9:23 -
That was in the original code too.shinzou– shinzou2015年09月29日 10:19:28 +00:00Commented Sep 29, 2015 at 10:19