
Los encoders rotativos o quadrature encoders son unos dispositivos que se utilizan como sensores de posición, ya que permiten medir con exactitud tanto la dirección como la magnitud de un desplazamiento de su eje.
El módulo QEI de nuestros queridos dsPIC33F gestiona la información de un encoder rotativo tal y como vemos a continuación.
Encoders industriales como los de la foto se suelen poner como realimentación de la posición de cualquier dispositivo mecánico. Son de alto rendimiento, gran precisión y bastante caros, por cierto.
Otro tipo de encoders son los utilizados como mando de control rotativo en equipos de música, de instrumentación, …
El que yo tengo es un 3315Y como el de la foto.

En su interior estos encoders llevan un disco con marcas y dos sensores ópticos:

Las marcas del disco, o la posición de los sensores respecto a la misma, está desfasada 90º, como en la imagen siguiente:

Esto permite que la señal que los encoders generan al girar el disco sea distinta según se gire en un sentido o en el contrario:

Módulo QEI
- contar pulsos ascendentes o descendentes hasta una palabra de 16 bits
- contar en resolución simple (x2) o doble (x4)
- filtrar la señal de entrada con filtros digitales programables
- generar una interrupción según determinados eventos


Modos de funcionamiento
- Phase-A: cada flanco de subida o bajada incrementa/decrementa el contador
- Phase-B: permite determinar la dirección del movimiento. Además, en modo de alta resolución (4x) incrementa/decrementa el contador.
- Index: se recibe un pulso por cada revolución (si el encoder tiene esta salida). El módulo puede resetear el contador de posición al recibir dicho pulso.
Interrupciones
- cuando el contador de 16 bits pasa de 0x0000 o de 0xFFFF
- detección de un pulso de Reset o un nuevo error
- fin de periodo del Timer, cuando funciona como timer
- evento de acumulación Gate
Configuración
- 111: resolución 4x, puesta a 0 del contador POSCNT cuando se alcanza MAXCNT
- 110: resolución 4x, puesta a 0 del contador POSCNT al recibir un pulso en Index.
- 101: resolución 2x, puesta a 0 del contador POSCNT cuando se alcanza MAXCNT
- 100: resolución 2x, puesta a 0 del contador POSCNT al recibir un pulso en Index.
- 001: uso como Timer
- en el resto de combinaciones, el módulo QEI estará apagado.
- 00 prescaler 1:1
- 01 prescaler 1:8
- 10 prescaler 1:64
- 11 prescaler 1:256
- 1: la dirección del contador del Timer vendrá dada por el estado del pin Phase-B
- 0: la dirección del contador del Timer vendrá dada por el bit UPDN.
- a 4x: indicaremos cómo tienen que estar las señales Phase-A (IMV1) y Phase-B (IMV0) cuando se reciba un pulso de Reset.
- a 2x: con el primer bit seleccionamos cuál de las dos señales será crítica al recibir un pulso Index (0 para Phase-A y 1 para Phase-B). Con el segundo bit indicaremos el estado al que tendrá que estar la señal seleccionada al recibir el Index.
- 111 = 1:256
- 110 = 1:128
- 101 = 1:64
- 100 = 1:32
- 011 = 1:16
- 010 = 1:4
- 001 = 1:2
- 000 = 1:1
Ejemplo
A continuación pego un programa de ejemplo y un vídeo con su ejecución.
#include <p33FJ64MC706.h>
#define __33FJ64MC706_H
_FOSCSEL(3);
_FOSC (194);
_FICD (223);
#include «LCD_HD44780.h»
#include <stdio.h>
char yo[] = {‘N’,’o’,’c’,’t’,’u’,’r’,’n’,’o’,’ ‘,’-‘,’ ‘,’2′,’0′,’0′,’6’,»};
const elrebujito[] = {‘Q’,’U’,’A’,’D’,’R’,’A’,’T’,’U’,’R’,’E’,’ ‘,’E’,’N’,’C’,’.’,»};
const c1[] = {24,24,24,24,24,24,24,24};
const c2[] = {12,12,12,12,12,12,12,12};
const c3[] = {6,6,6,6,6,6,6,6};
const c4[] = {3,3,3,3,3,3,3,3};
const c5[] = {1,1,1,1,1,1,1,1};
void barrita (int posicion) {
char x,col;
x = ((posicion % 80) / 5);
col = (posicion % 5);
if (x==0)
LCD_Goto (16,2);
else
LCD_Goto (x,2);
printf (» «);
LCD_Goto (x+1,2);
printf («%c»,col+1);
if (x==15)
LCD_Goto (1,2);
else
LCD_Goto (x+2,2);
printf (» «);
}
int main(void){
int i=1;
CLKDIVbits.PLLPRE=10;
CLKDIVbits.PLLPOST=1;
PLLFBD=238;
AD1PCFGL=0xFFFF;
AD2PCFGL=0xFFFF;
TRISB=0xFFFF;
LCD_Inicializacion();
LCD_Goto (1,1);
LCD_EscribeCadena(yo);
LCD_Goto (2,2);
LCD_EscribeConstante(elrebujito);
delay_ms(300);
LCD_DefineCaracter (8,c1);
LCD_DefineCaracter (16,c2);
LCD_DefineCaracter (24,c3);
LCD_DefineCaracter (32,c4);
LCD_DefineCaracter (40,c5);
POSCNT = 120;
QEICONbits.QEIM = 0b100; // resolución 4x, modo de Reset por MAXCNT
QEICONbits.QEISIDL = 0; // continuar en modo idle (0)
QEICONbits.SWPAB = 1; // Phase-A y Phase-B intercambiados
QEICONbits.PCDOUT = 1; // activado el pin de Position Counter Status
QEICONbits.TQGATE = 0; // Timer gate apagado
QEICONbits.TQCKPS = 0; // Prescaler 1:1
QEICONbits.POSRES = 0; // Un pulso en INDEX no hace un reset
QEICONbits.TQCS =0; // Usamos clock interno para el timer
QEICONbits.UPDN_SRC=1; // Phase-B indica dirección
MAXCNT=160;
DFLTCON = 0x00F0;
LCD_BorraPantalla();
while(1) {
LCD_Goto (1,1);
printf («Pos:%3u Dir:»,POSCNT);
if (QEICON & 0x0800)
printf («DER.»);
else
printf («IZQ.»);
barrita (POSCNT);
};
}
