Encoder x4 con interrupción hardware para Arduino

Antes de nada y para ahorrarme explicaciones te recomiendo ir a esta página para empaparte sobre las interrupciones con Arduino. Es la mejor web que he encontrado y lo hace con todo lujo de detalles. Los dsPIC y los 18Fxx5x tienen más de una interrupción externa y la filosofía del programa sería parecida.

Me ha sorprendido mucho no necesitar acondicionar las señales del encoder óptico porque las entradas digitales del Arduino tienen disparadores Schmitt de 0,5 voltios de histéresis cuando el microcontrolador es alimentado con 5 voltios; los modelos de Arduino que funcionan a 3,3v esa histéresis sería menor.

Las interrupciones no devuelven valores y las variables generales que usemos dentro y fuera de ellas se han de declarar como 'volatile'. En la página que cito arriba explica la razón de ello y cómo hacerlo todo correctamente.

Los pines 2 y 3 son los que utilizo para conectar las señales A y B del encoder, esas dos patillas son las de interrupción externa en el Arduino UNO/Nano.

volatile int contador=0; // Declaramos como 'volatile' las variables que participan dentro y fuera

de la interrupción.

byte ant=0, act=0; // Sólo se utiliza los dos primeros bits de estas variables.

ant=anterior, act=actual.

int n=0; // Variable auxiliar para notar cambios en el contador.


void setup()

{

Serial.begin(115200); // Dará la posición por el terminal serie.

/*

pinMode(2, INPUT); // No es necesario declarar pinMode, "attachInterrupt" se encarga de

convertir en entrada, pero

pinMode(3, INPUT); // los puedes poner si quieres. Los pines 2 y 3 es donde va conectado el

encoder incremental.

*/

attachInterrupt(digitalPinToInterrupt(2), encoder, CHANGE); // En cualquier flanco ascendente o

descendente

attachInterrupt(digitalPinToInterrupt(3), encoder, CHANGE); // en los pines 2 y 3 actúa la

interrupción.

}


void loop()

{

if(n != contador) // Sólo cuando la variable contador cambie de valor se

transmite dicho valor.

{

n=contador;

Serial.println(contador);

}

}

void encoder()

{

ant=act; // Guardamos 'act' para convertirlo

en pasado.

if(digitalRead(2)==1) bitSet(act,0); else bitClear(act,0); // Seteamos los dos primeros bits

de la variable 'act' con

if(digitalRead(3)==1) bitSet(act,1); else bitClear(act,1); // el valor de este instante, como

un número de dos bits.

if(ant==3 && act==1) contador++;

if(ant==1 && act==0) contador++;

if(ant==0 && act==2) contador++;

if(ant==2 && act==3) contador++;

if(ant==1 && act==3) contador--;

if(ant==0 && act==1) contador--;

if(ant==2 && act==0) contador--;

if(ant==3 && act==2) contador--;

}

Una manera de leer el encoder de forma más eficiente sería hacerlo en paralelo en vez de comprobar bit a bit. La función/interrupción encoder() quedaría así:

void encoder()

{

ant=act; // Guardamos el valor 'act' en 'ant' para convertirlo en

pasado.

act=PIND & 12; // Guardamos en 'act' el valor que hay en ese instante en

el encoder y hacemos un

// enmascaramiento para aislar los dos únicos bits que

utilizamos para esta finalidad.

if(ant==12 && act==4) contador++;

if(ant==4 && act==0) contador++;

if(ant==0 && act==8) contador++;

if(ant==8 && act==12) contador++;

if(ant==4 && act==12) contador--;

if(ant==0 && act==4) contador--;

if(ant==8 && act==0) contador--;

if(ant==12 && act==8) contador--;

}

'PIND' es una instrucción para leer el puerto D del Arduino, se corresponde con los pines 0 hasta el 7. Haz clic en este enlace para más información.