I would like to share my personal HW/SW solution that allow to decode 4 quadrature encoders using only 3 processor’s I/O: one external interrupt, one analog input and one GPIO.
It uses only a simple external HW circuit and it’s based on external interrupt routine (no polling), using only one external interrupt input.
This solution could be useful in case your project needs:
- Small form factor;
- Low budget;
- I/O saving;
- Computing resource saving;
The solution doesn’t use external library, serial communication protocols or complex data manipulation so it’s a valid alternative to the use of more complex solutions and save space and processor’s resources for integration of further functionalities.
External HW schematic: HERE
Complete breadboard based project: HERE
Here's the sketch:
/* This sketch, in combination with a few external components, reads 4 encoders using only 3 microprocessor inputs: one interrupt input,
* one analog input and one GPIO (in this example digital pin n.4 is used, but an analog input could be used indifferently).
*
* The encoders are connected in parallel through a voltage divider that provides interrupt signals at a specific voltage for each encoder.
* Depending on the interrupt signal voltage the microprocessor identifies the "active" encoder.
* After the voltage divider the signal is normalized again to 0-5V to be used as interrupt signal.
* The system works only with encoders with detent (both encoder contacts normally open or closed).
* For further details on the external circuit and system working limitations please refer to: https://massimoriggi.wordpress.com/2016/12/12/4-rotary-encoders/
*/
#define intpin 2 //rotary encoder interrupt pin
#define dirpin 4 //rotary encoder direction pin
#define analogreadpin A0 //pin for interrupt signal analog reading
/* The following lines declare:
* - variables for Rotary encoders values/position, from rotary encoder n.1 to rotary encoder n.4 (pot1 to pot4);
* - variable for analog reading of the interrupt signal (analogvalue);
* - variable for encoders direction reading (dir).
*/
volatile int pot1=500;
volatile int pot2=500;
volatile int pot3=500;
volatile int pot4=500;
volatile int analogvalue;
volatile int dir;
void setup() {
pinMode(intpin,INPUT);
pinMode(dirpin,INPUT);
pinMode(analogreadpin,INPUT);
attachInterrupt(digitalPinToInterrupt(intpin),readenc,FALLING);
Serial.begin(250000);
}
/* The main loop only prints encoder positions on terminal window. The ISR (readenc function) does all the job! */
void loop() {
Serial.print(pot4);
Serial.print("\t");
Serial.print(pot3);
Serial.print("\t");
Serial.print(pot2);
Serial.print("\t");
Serial.println(pot1);
}
void readenc(){
dir=digitalRead(dirpin); //reads the direction pin state
analogvalue=analogRead(analogreadpin); //reads the voltage of the interrupt
/* the following lines split the analog reading in 4 different voltage ranges and update (increment or decrement) only the variable of the corresponding encoder */
if(analogvalue>300&&analogvalue<400){
if(dir==HIGH){
pot1++;
}
else{
pot1--;
}
}
if(analogvalue>500&&analogvalue<600){
if(dir==HIGH){
pot2++;
}
else{
pot2--;
}
}
if(analogvalue>700&&analogvalue<800){
if(dir==HIGH){
pot3++;
}
else{
pot3--;
}
}
if(analogvalue>900&&analogvalue<1023){
if(dir==HIGH){
pot4++;
}
else{
pot4--;
}
}
}
This is a video demo of the project: HERE
Comments, suggestions, remarks or corrections are really appreciated
(sorry for my english, for my programming skills and sorry if I have posted it in the wrong section)
Regards.
Massimo