Well, this has been quite an odyssey, but it has a most pleasant conclusion. As you may know, rotary encoders are particularly "bouncy," or at least the cheap ones are. A quick Google search will convince you that these guys are giving others no end of trouble. I spent a week seeing all of these woes first-hand and was just about to give up. Balancing speed versus reliability is the crux of the matter.
I'm pleased to share the fruits of much scurrilous language at the workbench. Attached in the next post is a header file that makes reliable use of rotary encoders a snap. I'm using a particularly cheap one from China (I got it through Amazon, the KY-040) and am happy to report that it performs like a champ now: no bounce, excellent speed and exceedingly accurate. Best of all, there's no calling on touchy debouncing code, or delay loops.
Moreover, the subroutine let's you set the maximum value (0 to 10, 0 to 16, 0 to 100, whatever you want) and also incorporates the push-switch on the encoder to lock in a value. As usual, the source code is completely documented throughout. Here's a demo program you might want to try:
;Demooftherotaryencoderincludefile.Thiswillshowthe;currentvalueandthelatchedvalueoftheencoder;onanLCD.;ThomasHenry---6/7/2014;-----Configuration#chip 16F88, 8 ;PIC16F88 running at 8 MHz#config mclr=off ;reset handled internally#config osc=int ;use internal clock#include"Encoder.h";-----Constants#define LCD_IO 4 ;4-bit mode#define LCD_RS PortB.2 ;LCD Register Select on pin 8#define LCD_Enable PortB.3 ;LCD Enable on pin 9#define LCD_DB4 PortB.4 ;DB4 on pin 10#define LCD_DB5 PortB.5 ;DB5 on pin 11#define LCD_DB6 PortB.6 ;DB6 on pin 12#define LCD_DB7 PortB.7 ;DB7 on pin 13#define LCD_NO_RW 1 ;ground the RW line on LCD#define ENCODER_A PortB.0 ;sometimes called CLK#define ENCODER_B PortB.1 ;sometimes called DT#define ENCODER_SW PortA.0 ;the central switch;-----Variablesdimvalue,latchedasinteger;-----Programlcdcmd40;2-lineLCDmodevalue=0;beginencoderat0latched=0doencoder(value,latched,100);between0and100,inclusivelocate0,0;printcurrentvalueprint"Current: "printvalueprint" "locate1,0;printthelatchedvalueprint"Latched: "printlatchedprint" "loop
We often think of switches as trivial, but believe me when I say, this include file is anything but trivial. It took dozens of revisions to make it reliable. There's more to a rotary encoder than meets the eye...
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
One change I'd suggest is to turn it into a function that returns -1, 0, or +1 depending on whether the state has decremented, incremented or is unchanged. This way, you can use it in a more generalized way while preserving its present volume-control-like functionality.
Joe
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Nice.
So the switch pin should be held high if no switch is used right ?
Also, how precise does the 10k resistor has to be ? don't have those at the moment so i tried with 1K but it does not seem to work...
What I don't get is why do you have to set the maximum value every time you read the encoder ? shouldn't we just set it once ?
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Rotary encoders use the Gray code. So XORing consecutive values gives the direction of rotation. Here's a rather poor example (but it works) for setting a temperature for a brewing thermostat I made. The two encoder pins are on a.2 and a.3 and b.7 is a push button that exits and enters the adjustment subroutine. I've had no issues with "bounce".
subqadj'adjustment subclsk=0:kk1=99wait 1 seclocate 1,0print " Target Temp."d2:locate 0,0print" "k.1=porta.2k.2=porta.3if kk1=99 then kk1=kif kk1<>k thenkk2=k.1 # kk1.2if kk2=1 then k3=k3+1if kk2=0 then k3=k3-1end ifprint k3kk1=kif portb.7=1 then goto d1goto d2d1: end sub
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
I'm new to GCB, and if I may, could you clarify a bit of your code for me? The documentation is pretty sparse with regard to data types. In your code above, I'm assuming that one would have to declare the variable k and kk1 as bytes using a dim statement. Also is the notation "kk1.2" referencing the 2nd bit of of kk1, i.e. GCB is using an index starting with 1?
Many thanks,
Kenn
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
@David... as keeper of the documentation. Can you me?
To answer your question first. You do not have to declare byte variables with Dim. And the notation of kk1.0 to kk1.7 relates to the 8 bits of the byte variable kk1. The notation therefore relates to the 0 to 7 bits of the variable.
My question: Where do you look for variables details? And, what more can we add to make the information more appropriate?
Thank you.
P.S.
Variables:
•Life cycle - once a variable is defined as used it persists.
•Aliasing - You can use aliasing to reduce memory usage.
•General guide though - it is best to define any shared variables near the start of the program for easier readability.
•GCB does not currently support local variables (within Subs and Functions), therefore, all variables are in the same name space into the same RAM location.
•Scope - every variable is global. If a variable is defined inside a particular subroutine then the type of variable is not.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
At the risk of completely hijacking this thread:
it turns out my question about about dim-ing byte variable types was answered on page 21 of the manual and I completely overlooked it.
So bytes don't have to be explicitly declared, but is it allowed?
Personally, in the type table on page 21 of the programmers manual, I would clarify the byte.bit notation, stating a 0 based index, along with a note that states whether or not that dim-ing a byte is allowed/not required as appropriate.
And, believe me, I'm not complaining about the manual. Compared to other projects of this type, GCB is extraordinarily well documented.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
I've looked at the include file and it confuses me even more. The code should go
00, 01, 11, 10 .. there should be no dead spots in gray code as only one bit is changing.
My encoder is also a cheap mechanical. I hold the pins at Vcc with a 100k and the on switches a 1k from Vdd.
I have attached the circuit diagram (drawn in expressSCH).
Well, this has been quite an odyssey, but it has a most pleasant conclusion. As you may know, rotary encoders are particularly "bouncy," or at least the cheap ones are. A quick Google search will convince you that these guys are giving others no end of trouble. I spent a week seeing all of these woes first-hand and was just about to give up. Balancing speed versus reliability is the crux of the matter.
I'm pleased to share the fruits of much scurrilous language at the workbench. Attached in the next post is a header file that makes reliable use of rotary encoders a snap. I'm using a particularly cheap one from China (I got it through Amazon, the KY-040) and am happy to report that it performs like a champ now: no bounce, excellent speed and exceedingly accurate. Best of all, there's no calling on touchy debouncing code, or delay loops.
Moreover, the subroutine let's you set the maximum value (0 to 10, 0 to 16, 0 to 100, whatever you want) and also incorporates the push-switch on the encoder to lock in a value. As usual, the source code is completely documented throughout. Here's a demo program you might want to try:
We often think of switches as trivial, but believe me when I say, this include file is anything but trivial. It took dozens of revisions to make it reliable. There's more to a rotary encoder than meets the eye...
Here's the include file for the rotary encoder.
Nice work Thomas!
One change I'd suggest is to turn it into a function that returns -1, 0, or +1 depending on whether the state has decremented, incremented or is unchanged. This way, you can use it in a more generalized way while preserving its present volume-control-like functionality.
Joe
Nice.
So the switch pin should be held high if no switch is used right ?
Also, how precise does the 10k resistor has to be ? don't have those at the moment so i tried with 1K but it does not seem to work...
What I don't get is why do you have to set the maximum value every time you read the encoder ? shouldn't we just set it once ?
Rotary encoders use the Gray code. So XORing consecutive values gives the direction of rotation. Here's a rather poor example (but it works) for setting a temperature for a brewing thermostat I made. The two encoder pins are on a.2 and a.3 and b.7 is a push button that exits and enters the adjustment subroutine. I've had no issues with "bounce".
David,
I'm new to GCB, and if I may, could you clarify a bit of your code for me? The documentation is pretty sparse with regard to data types. In your code above, I'm assuming that one would have to declare the variable k and kk1 as bytes using a dim statement. Also is the notation "kk1.2" referencing the 2nd bit of of kk1, i.e. GCB is using an index starting with 1?
Many thanks,
Kenn
@David... as keeper of the documentation. Can you me?
To answer your question first. You do not have to declare byte variables with Dim. And the notation of kk1.0 to kk1.7 relates to the 8 bits of the byte variable kk1. The notation therefore relates to the 0 to 7 bits of the variable.
My question: Where do you look for variables details? And, what more can we add to make the information more appropriate?
Thank you.
P.S.
Variables:
•Life cycle - once a variable is defined as used it persists.
•Aliasing - You can use aliasing to reduce memory usage.
•General guide though - it is best to define any shared variables near the start of the program for easier readability.
•GCB does not currently support local variables (within Subs and Functions), therefore, all variables are in the same name space into the same RAM location.
•Scope - every variable is global. If a variable is defined inside a particular subroutine then the type of variable is not.
Thanks, Anobium.
At the risk of completely hijacking this thread:
it turns out my question about about dim-ing byte variable types was answered on page 21 of the manual and I completely overlooked it.
So bytes don't have to be explicitly declared, but is it allowed?
Personally, in the type table on page 21 of the programmers manual, I would clarify the byte.bit notation, stating a 0 based index, along with a note that states whether or not that dim-ing a byte is allowed/not required as appropriate.
And, believe me, I'm not complaining about the manual. Compared to other projects of this type, GCB is extraordinarily well documented.
@Kendal :-) I am tidying up some of the documentation at the moment. May I ask. Page 21 of which manual?
@Anobium,
I was using this one:
http://www.greatcowbasic.com/uploads/9/2/9/8/9298268/greatcowbasic_programming_manual_v1_2.pdf
Many thanks,
KG
Thank you. That makes sense.
I've looked at the include file and it confuses me even more. The code should go
00, 01, 11, 10 .. there should be no dead spots in gray code as only one bit is changing.
My encoder is also a cheap mechanical. I hold the pins at Vcc with a 100k and the on switches a 1k from Vdd.
I have attached the circuit diagram (drawn in expressSCH).