dsPIC33F – Capítulo 3: Quadrature Encoder Interface (QEIM)

1 febrero, 2007

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:

Ahora que ya sabemos cómo funcionan los quadrature encoders, vamos a ver cómo los gestiona nuestro formidable dsPIC33F.

Módulo QEI

El módulo QEI de los dsPIC está compuesto por 3 pines de entrada, las dos fases + pulso Index, lo que nos permitirá gestionar sólo un quadrature encoder. Así como de otro tipo de módulos viene bien despachado, en este tema se han quedado un poco cortitos los señores de Microchip.
Pero nuestro solitario módulo es capaz de:
  • 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

Para colmo, también podemos utilizar la entrada Phase-A como entrada de Timer si necesitamos alguno más, además de los 9 que trae.
Este es el diagrama del módulo QEI:

Modos de funcionamiento

A medida que nuestro encoder va girando se van sucediendo pulsos en las entradas Phase-A, Phase-B e Index como hemos descrito anteriormente. El módulo QEI las utilizará según esté configurado:
  • 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.
El contador de posición se almacena en el registro POSCNT que es registro de lectura y escritura.
El registro MAXCNT nos permitirá determinar el límite máximo al que queremos que llegue. Una vez superado la cuenta vuelve a empezar por 0.
El único pin de salida de nuestro módulo es el UPDN que se pondrá a 0 en un sentido de giro y a 1 en el otro. Este pin puede activarse o desactivarse según se establezca en la configuración.
También se puede programar un modo de control de errores. Nuestro QEI puede estar esperando un pulso Index en el momento programado como recuento máximo y si no lo recibe generará un error.
El módulo QEI también puede utilizarse como un Timer estándar con las mismas características que el resto de los timer, a las que hay que añadir la posibilidad de cuenta descendente, que se elige en la configuración.
A todas estas prestaciones, el módulo QEI añade una serie de filtros digitales programables que asegurarán la calidad de la señal recibida incluso en ambientes adversos. 

Interrupciones

El módulo QEI puede generar interrupciones por cualquiera de estos eventos:
  • 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

El módulo QEI se configura mediante los siguientes bits de configuración:
QEICON.QEIM<2:0>: admite los siguientes valores
  • 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.
QEICON.QEISIDL: si lo ponemos a 0, el módulo seguirá funcionando cuando el micro entre en modo Idle.
QEICON.SWPAB: podemos seleccionar si queremos las entradas Phase-A / Phase-B en sus pines originales (0) o intercambiadas entre sí (1).
QEICON.PCDOUT: si colocamos un 1, activará el uso del pin UPDN para informar del sentido de giro del encoder. Si ponemos un 0, el pin se dejará como un I/O más.
QEICON.TQGATE: activa o desactiva el modo de acumulación de Gate.
QEICON.TQCKPS<1:0>: configuración del prescaler cuando actúa como Timer
  • 00 prescaler 1:1
  • 01 prescaler 1:8
  • 10 prescaler 1:64
  • 11 prescaler 1:256
QEICON.POSRES: hay que poner a 1 este bit si queremos el un pulso en Index ponga a 0 el contador POSCNT.
QEICON.TQCS: seleccionamos la fuente para el timer. Con un 1 serán los pulsos recibidos en Phase-A y con un 0 será la frecuencia de instrucciones.
QEICON.UPDN_SRC: sólo funciona en modo Timer. Admite las siguientes dos opciones:
  • 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.
DFLTCON.IMV<2:0>: este par de bits tienen funciones distintas, según tengamos configurado el módulo a 2x o 4x
  • 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.
DFLTCON.CEID: lo ponemos a 0 si queremos activar la interrupción por recuento de errores.
DFLTCON.QEOUT: se activa el filtro digital poniendo este bit a 1.
DFLTCON.QECK<2:0>: seleccionaremos el divisor de clock para el filtro digital, según la siguiente relación:
  • 111 = 1:256
  • 110 = 1:128
  • 101 = 1:64
  • 100 = 1:32
  • 011 = 1:16
  • 010 = 1:4
  • 001 = 1:2
  • 000 = 1:1
Además del registro POSCNT, el módulo nos informará de su funcionamiento en los siguientes bits:
QEICON.INDEX: indicará el estado de la entrada Index
QEICON.UPDN: indicará el sentido del movimiento

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);
};
}

Share

Etiquetas: , , , ,