En este post desarrollaré un programa para leer las entradas de un tipo encoder incremental en cuadratura.
La señal en cuadratura se caracteriza por tener dos señales cuadradas que se encuentras desfasadas 90° una con respecto a la otra. Esto es mostrado en la figura “Señales del encoder”.
El movimiento rotacional puede ser medido contando los flancos que produce cada una de las señales. La relación de fase entre las dos señales determina la dirección de rotación. Cuando en giro es horario la señal QDPH0 (Comúnmente encontrada como A) adelanta a QDPH90 (B) en 90°. En el sentido antihorario ocurre exactamente lo contrario.
Fig. 1 Señales del encoder
Para nuestro ejemplo conectaremos las salidas CLK(A) y DT(B) de un encoder, como el mostrado en Fig.2, a las entradas PD2(INT0) y PD3(INT1) de un ATmega32. El encoder y el uC son alimentado con 5Volt.
Fig. 2 Encoder
El ejemplo es muy sencillo y puede funcionar con una lectura constante (en while) de las señales A y B, usando una interrupción para la señal A ó usando dos interrupciones para las señales A y B.
Primero, y luego de crear el proyecto, debemos declarar la velocidad del CPU (F_CPU es necesaria para las rutinas _delay…) y la del UART si fuera necesario. Aquí muestro una imagen de mi configuración.
Bueno, aquí el programa.
Includes:
Variables a usar:
14.- Se declara una variable global del tipo int32_t (cuenta) para que almacene las cuentas del encoder. Esta tiene que ser del tipo volatile para poder usarla sin problemas en las interrupciones.
15.- cambio las usaremos como un flag que nos indica que en valor de cuenta ha cambiado.
16.- str[30] almacenara un string que será enviado por el puesto serie.
18-26.- Se crea una nuevo tipo de dato(States_t) para almacenar el estado actual del encoder (S0) y el estado anterior (S1). 27.- Se crea la variable enc que es del tipo States_t.
35-38.- Se configura el UART del ATmega32.
40.- Se activa las resistencias pull-up de las entradas usadas para leer el encoder.
41.- Se configura la detección de cambio de estado en las interrupciones external INT0 e INT1.
42.- Se activa las interrupciones INT0 e INT1.
44-54.- Se activa las interrupciones globales y se espera en un cambio de los estados para enviar un mensaje por el UART.
Aquí la función para enviar los datos por el puerto serie:
En la siguiente imagen se muestran las interrupciones:
81.- Es la interrupción INT1, y no vemos código ya que el atributo ISR_ALIASOF le dice al compilador que en el vector de interrupción correspondiente a INT1 ponga un salto a INT0.
Esto lo podemos verificar viendo el manual y el programa desensamblado.
Vemos que el manual nos indica que INT0 esta asociada a la dirección 0x002 y INT1 a 0x004. En la segunda imagen vemos que el compilador generó un JMP a la dirección 0x00000049 en los dos casos. Es decir, que cuando se generen INT0 ó INT1, el programa ejecutará la misma rutina.
En INT0 primero pondremos un retardo para evitar algún rebote.
85.- Salvamos el estado anterior(enc.s1) antes de obtener el nuevo estado de las señales A y B.
86.- Leemos las entradas A y B. También los desplazamos para usar los 2 bits menos significativos. Si los pines usados hubieran sido PIN5 y PIN6 el corrimiento hubiera sido así: >>5.
Gracias a que la variable enc es una unión(como se muestra en figura inferior) podemos usar enc.s para hacer comparaciones del estado actual y el anterior en una sola variable.
Si consideramos la Fig. 1 Señales del encoder, podemos conocer los estados pasados y actuales para cada sentido de giro, es decir que en sentido horario tendremos enc.s0 = 0, 2, 3, 1 como posibles estados actuales y enc.s1 = 2, 3, 1, 0 como posibles estados anteriores. Si los combinamos tendremos enc.s = 0x02, 0x23, 0x31 y 0x10.
Algo similar ocurre en el sentido antihorario. Tendremos enc.s0 = 1, 3, 2, 0 como posibles estados actuales y enc.s1 = 3, 2, 0, 1 como posibles estados anteriores. Si los combinamos tendremos enc.s = 0x13, 0x32, 0x20 y 0x01.
Así que comparando solamente enc.s ,que contiene tanto el estado actual como el anterior, podremos conocer la dirección del giro.
De esta manera se consigue tener la más alta resolución del encoder, ya que se evalúan todos sus estados posibles.
Esta es la salida que se debe visualizar:
Ahora bien, si sólo disponemos de un pin de interrupción externo, debemos hacer algunos cambios en los estados, ya que no se podrían evaluar todos, es decir que perderíamos la mitad de los estados y con ello la resolución del encoder seria la mitad de la que obtuvimos usando INT0 e INT1.
El cambio solamente seria el siguiente:
Como se ve en la imagen, los estados se redujeron ya que, sólo tenemos un pin que nos genera interrupción.
Este código puede ser fácilmente adaptado para que funcione en un bucle, pero la idea de usar interrupciones es quitarle carga a la CPU.
Bueno, espero que les sea de utilidad, aquí les dejo el enlace para la descarga del programa.
Encoder_mega32
Encoder_mega32
Buena ayuda, gracias
ResponderEliminarMuy buena idea, gracias!q
ResponderEliminar