Introduction: ROTARY ENCODER WITHOUT INTERRUPTS
Hi!
This is my contribution to the wast amount of solutions to read the ”Rotary-Encoder-switch”. I have an early published Instr. for how to eliminate the ”switch debouncing” by using a Quad NAND-gate chip 4011 (two gates/ switch, so two separately switches can be used with 1 chip) That works right as is for many purposes.
At some point I wanted to use this Rot_Count to something by using a processor, in my case it would be the ATtiny-84 (the 14-pins). I searched for solutions for this function.
Oh…Boy… there seemed to be ton’s of problems and as many solutions as well.
There are these that require external components like a capacitor + resistor
Then there’s lot of software solutions. In them almost all You have to work with Timer-Interrupts Or with Input- interrupts. They all have their Cons. and Pros.
Naah.. not for me. You know? Them interrupts are dynamic, that is: they appear when-ewer they need to, no matter what you are doing at the moment
This is a never-ending subject for discussions (let’s not start on that here now).
Gee… so, what to do? Then I got this brilliant idea :) !! Why not to use an external unit such as mine”Debounce eliminator”. Of course you now need some external components with my solution, but again, so you do need with them rc-filter allso?
So I made a test-unit on a breadboard. And then I made a rough prototype on a perf-board.
Step 1: The Princips of the Rotary
There are 3 terminals on the rotator (+2 for the push-switch, if available) see the PDF here by
they are named A, C, B. The C-terminal is the Common that connects to A & B in a sequence depending of the direction of the knob.
When turning the knob CW (Clock Wise) the A-terminal connects first and then the B-terminal connects with a phase shift of 90deg.
When turning the knob CCW (Counter Clock Wise) the B-terminal connects first and then the A-terminal connects with a phase shift of 90deg.
Well.. the basic functions were OK.
On break-out boards the terminals are often named as: CLOCK, DATA, SW and some
I’m gonna use the A-terminal as the Clock and the B-terminal as Data
Never-the-less in my setup I’ll call them for A = Count and B = Up/Down.
The Rotator I used is so that when at ”detent” position (not mowing) neither of them terminals A- or B- is connected to C . However them breakout units also usually have pull-up or pull-down resistors at A- and B- terminals
Attachments
Step 2: The Problems Begin... But There's Also Solutions
And here starts the problems for me. Had to make a program to run it.
”Peace-of-cake” you might say? Let’s see.
The Rotator (i call it so for the simplycity) works in a very simple matter (see the pics.)
But actually it’s a ”pain in the ass” from a programming point of view. Why?
There’s 4 phases in one ”click” and it does it in a blink of an eye.
Actually the ”click” comes from when the knob comes to a ”detent” position and by then the electrical connections have already been done
In this example we go CW. It applyes the same for CCW also, but in reversed direction This is the principal function of the Rotator. (Then see my solutions.)
The manufacturer of my Rotator says the max. debounce to be 2ms.
1. First I trig the A-terminal. I use the ”falling edge” to trig the Flip-Flops. This negative trig is required because of the nature of the NAND- Flip-Flop, the input goes HIGH-LOW-HIGH-LOW for .~2ms but the output Q have gone HIGH by the very 1'st LOW at the input and via the output !Q it locks itself in that state
2. Next: we trig the B-terminal, and here we got the same problem as in 1.
3. Next: the A- goes goes back to high thus again having the problems as we had at 1.
4. Next: the B- goes back to high thus again having the problems as we had at 1.
And all of this happens in a blink of an eye. Therefore all time-based solutions are a bit spurious.
Let’s have a look at MY solution.
1. The C-terminal is connected to the output Q of the Count F-F and is for now LOW.
2. The falling edge on A-terminal trigers the F-F thus resulting the Count-output Q of the F-F go HIGH thus preventing future actions until the F-F is Resetted by the program.
3. Now, if the B-terminal (Up/Down) have preceded the A-terminal, it all-ready have triggered the Up/ Down F-F thus Setting the output Q to be HIGH . Here applys the same as in 2.
Yet, when A-triggers it will inhibit them both A- and B- to trig again because the Count-output Q is HIGH
This results in that now both Count F-F and Up/Down F-F are in a locked position and just ”waiting” for to be
Read and then Resetted. They stay there as long as you wish.
”Easy-Do” you might think? But it isn’t. Why? Well.. even the contact –time is like a blink of an eye it still matters, because the processor is sooo.. fast so it could read the inputs hundreds of times during them contacts still ”going on”. So I’m first reading them inputs and put them on ”hold” in a temporary register and continue to scan (by the means of to reset/ read/reset) until BOTH inputs go LOW again.
WHEN THIS state OCCURE , ( both are LOW), I’ll do a Count: Up or Down.
Funny isn’t it? All this ”fuzz” for to count just ONE pulse.
Anyway this works super. I have tried to turn the knob as fast as possibly I can do manualy, first to make many revolutions in one direction and then back the same amount and voi’la it works spot on (or should I say ”click on”) it want ”miss a click”. Yet running my ATtiny with a speed of 8MHz and some program (the LED's) the same time. I also tested it to count up to 255 (12rev+16 ”clicks”) and then back. It was ”click on” back to zero.
Step 3: The Program
Three ( 3 ) GPIO-lines are required: Count, Up/Down and Reset, (+ one option line for the Push-button).
Here is my program that I run, and as showed in the video, it runs ”Click On”.
Keep in mind: This is NOT a HIGH-speed evaluation. I’ve tested it with my human top-speed only.
O.K. I’m 68+ to age so my speed isn’t a good reference In the future I’m gonna perform some kind’f mechanical test to see the real top-speed. (for now I’m running this with internal 8MHz).
On the board I have the outputs of them F-F’s Q's to go via FET-transistors and thus enhance the output source current (the 0Volt/ GND). This is good to have if the Rotator-unit comes to a distance away from the main unit (as it will be in almost all apps). I also have a Pull-Up resistor to +Vc there in the output for the same reason. The Processor INPUT_PULLUP may not be good enough, it’s quite a HIGH-impedance
The inputs will be INVERTED (actually they don’t need to be but it’s more Logical to work with Positive Logic's).
Count = (!digitalRead(Count_in)); //FET out = Active LOW in
Up_Down = (!digitalRead(Up_Do_in));//FET out = Active LOW in
IMPORTANT NOTE! The Counter() subroutine can be called from anywhere in Your program when it is desired.Here I have it in the main void loop() only
/* ROTARY UP/DOWN COUNTER SWITCH 6.6.2020
* For more info: kimmo.selin@pp.inet.fi
* or my homepage: www.teksel.net there You can find more contact info */
// Inputs
const int SW_in = 7; // The Rotator Push-button Switch
const int Count_in = 6; // Count_in
const int Up_Do_in = 5; // Up_Down_in
// Outputs
const int RST_Out = 8; // RESET Count and Up/Down
const int data_1 = 0; // This is for the LED only
const int latch_1 = 1; // This is for the LED only
const int clock_1 = 2; // This is for the LED only
//------------ VARIABLES (will change)---------------------------
int count_led = 0 ; //
int data_led = 0 ; // LED’s
int Up_Down = 0 ; // For Up_Down_in inputs
int U_D_help = 0 ; // Help for the Up_Down
int Count = 0 ; // For Count_in inputs
int Count_help = 0 ; // Help for the Count
int Dummy = 0 ; // For LED's to bypass the 1'st 595-driver in series
//--------------------------------
void setup() {
//-------- INPUTS -----------------
pinMode(SW_in,INPUT_PULLUP); // Switch In
pinMode(Up_Do_in,INPUT_PULLUP); // Up_Down_in
pinMode(Count_in,INPUT_PULLUP); // Count
//--------- OUTPUTS ---------------
pinMode(RST_Out, OUTPUT); // RESET_Out for the F-F’s
pinMode(data_1, OUTPUT); // Data This is for the LED only
pinMode(latch_1, OUTPUT); // Latch This is for the LED only
pinMode(clock_1, OUTPUT); // Clock This is for the LED only
}
//========= THE MAIN LOOP ======================
void loop() {
Counter(); // Up-Down Counter
ledit(); // To display the Count
}
//============== END of LOOP =============
void Counter(){
// The (!digitalRead) has the excl. ”!” for to invert the input
Count = (!digitalRead(Count_in)); // FET out = Active LOW in
Up_Down = (!digitalRead(Up_Do_in));// FET out = Active LOW in
if (Count_help == LOW) // If Temporary Count is NOT active
{if (Count == HIGH) // If Count input IS active
{Count_help = HIGH; // Set Temporary Count
if(Up_Down == HIGH) // To Count Up or Down?
{U_D_help = HIGH; // Set Temporary Up-count
}else{
U_D_help = LOW;} // Set Temporary Down-count
}
}else{
digitalWrite(RST_Out,LOW); // Latch the Reset to read the
digitalWrite(RST_Out,HIGH); // status of the Flip-Flop
if (Count== LOW && Up_Down== LOW) // BOTH MUST go LOW
{if(U_D_help == HIGH) // before the Count
{count_led++ ; // The count_led is the register
}else{ // to be displayed showing the count
count_led-- ; // Count Down }
Count_help = LOW; // End of Count Clear the help
U_D_help = LOW; // End of Count Clear the help }
}
//-----------------------------------------------
if (digitalRead(SW_in)== LOW) // Clear the Count if the
count_led = 0 // button is Pushed
}
//------------- LED --------------------------------
void ledit () { // /
* This routine refers to my own PCB setup only.
* Use your own routines to whatsoewer you wanna the count to do */
data_led = count_led; // The Count LED's are in the 2'nd '595-driver in series
shiftOut (data_1,clock_1,MSBFIRST,data_led);//
//Push the 2’nd ’595 forward with the 1'st serial '595 //that is for other functions
shiftOut (data_1,clock_1,MSBFIRST,Dummy);//
digitalWrite (latch_1, HIGH);
digitalWrite (latch_1, LOW); }
Step 4: The Hardware
As said, I made a ”rough” prototype on a perf-board (the pictures)
In the simplest way You would only need a 4011 NAND chip and three resistors.
But Hey… com’n.
Why not to put them FET's there ( 5cent/pcs.) + 2 Pull-Up resistors?
And if you put them FET’s there, why not to put them LED’s there also ( 5cent/pcs.)
The physical footprint of the PCB gives you the free footprint- area for these components.
I’ve made a layout of the PCB (not ordered them yet, but will do so soon).
The layout is double-sided but as you can see it can also be single-sided.
Having it to be double-sided gives you the freedom of to place all the components on the side of you’r desire, except the 4011 chip that must be on the component side.
Therefore I recommend You to place the Rotator and the LED’s and if You use them selector-switches put them on the Solder-side That is: The Rotator will be panel-mounted with the solder-side towards the panel and so will the LED’s be (if you use them) and the rest ( the Component-side) would be facing away from the panel.
I also did put there some ”selector switches”, why? Because I could do it easy like:
The F-F’s have two outputs Q and !Q each and they are always the opposite to each other respectively, so now I can choice the outputs to be ”active” HIGH or LOW. There’s the J2 to connect an external switch to select the Up or Down count ( but now the Rotator B- is disabled). The Switch2 selects if you want to Reset the F-F’s with the push-button but then you can’t Reset via the program.
The board connects via J1 to the processor/ something else
This PCB can be used completely as stand alone in many applications.
I haven’t thought about futher more configurations of this, yet??
I will update this Instr. with them Gerber-files for the PCB later on
Please, please, comment on this and/or make some suggestions of apps.
Or contact me:kimmo.selin@pp.inet.fi or www.teksel.net