domingo, 2 de marzo de 2025

Lectura de Codificador Rotatorio

Codificador Rotatorio con PIC

Fig1. Modelos de Encoder Rotativos

Bienvenido a mi blog que tiene por objetivo compartir el interés en la electrónica y en especial la programación de microcontroladores PIC. Antes de empezar quiero recordarte que estoy atento a cualquier sugerencia o critica constructiva relacionada con esta publicación. En la parte final dejo mis datos de contacto.

En esta ocasión veremos algo simple e interesante de usar como elemento de entrada para nuestro circuito PIC,  este dispositivo conocido como codificador rotatorio (Encoder) esta presente en gran cantidad de equipos electronicos y es un componente económico y fácil de conseguir, por lo veremos como implementar en el programa las rutinas necesarias para el uso adecuado.
La programación del microcontrolador PIC se lleva a cabo con el software  de diseño  MPLABX  y el compilador de lenguaje C para PIC XC8, ambos disponibles en la pagina de microchip de forma gratuita. Aquí dejo los enlaces de las versiones utilizadas para nuestro ejemplo:   
Recordarte que es importante contar con los conocimientos mínimos de programación en lenguaje C y el uso de microcontroladores PIC16F.

Introducción al Tema

Un codificador rotatorio, también llamado codificador del eje o generador de pulsos, suele ser un dispositivo electromecánico usado para convertir la posición angular de un eje a un código digital, esto lo convierte en una clase de transductor de entrada.
Por lo general la salida del codificador utiliza el código binario Gray, puesto que una ventaja importante para la construcción de este código es que la diferencia entre dos pasos adyacentes (distancia) es siempre un bit. La figura 2 muestra la secuencia del código generado por las dos salidas de codificador y en la parte inferior se observa la mecánica de rotación utilizado en su construcción.

Fig2. Operación del Codificador (fuente: wikipedia)
 

Modulo KY-040

Este modulo cuyo esquema de circuito se observa en la figura 3, dispone de un codificador rotativo que a diferencia de un potenciómetro este puede girar libremente sin topes mecánicos, además cuenta con un pulsador que resulta útil para la entrada de nuestro circuito. El modulo KY-040 trabaja con un voltaje de 5V y emite hasta 20 pasos de la secuencia (Gray) por revolución.

Fig3. Modulo Codificador KY-040
 

Esquema del Circuito PIC

Realizaremos la prueba del codificador rotativo modulo KY-040 montando el circuito que se muestra en la figura 4. El PIC16F887 utilizara el oscilador interno para una frecuencia de 8Mhz, la entrada RB0 tendrá configurado el resistor pull-up interno para validar los niveles lógicos cuando presionamos el botón central ESW1  (S1-S2) del codificador, en cuanto a los leds de salida todos se conectaran a una misma resistencia limitadora tomando en cuenta que solo un led permanecerá activo en el tiempo.

En cuanto a las señales de salida del codificador CK(A) y DT(B), en el esquema se conectan a la entrada RB1 (ECK1) y RB2 (EDT1)

Fig4. Esquema del circuito PIC16F

Programación del PIC16F

Vamos a crear un programa que en base a la señal del codificador se activara uno de los ocho leds conectados al PORTD del PIC,  de tal manera que se generara un desplazamiento del led activo según el sentido de rotación que se tenga en el codificador, adicionalmente el programa destellara el LED1 indicando el funcionamiento normal del programa, verificara si pulsador central ESW1 esta presionado para colocar en todos los pines del PORTD en nivel bajo (Apaga todos los leds). La definición de los pines junto al programa principal sera el siguiente código:

#define ECK1pin  PORTBbits.RB1    //Definición de pines
#define EDT1pin  PORTBbits.RB2
#define ESW1pin  PORTBbits.RB0

void main(void) //Rutina principal
{
    MCUSetup();
//Procedimiento para configurar el PIC
    while(1)
    {
        if(tickms)
//Se valida cada milisegundo
        {
            tickms = 0;
//Limpia bandera del temporizador
            taskLED1();
//Tarea para destellar LED1 a 1Hz
            taskSW1(); 
//Tarea para lectura del pulsador ESW1
            taskENC1();
//Tarea del Codificador
        }
    }  
}

Como notara en la estructura que tiene el programa principal, el proceso de ejecución de las tres tareas se realiza en cada milisegundo, donde cada tarea maneje su estado de ejecución de forma separada. Considera hacer lectura de la siguiente  entrada en la que explico con mas detalle la ejecución de tareas concurrentes con interrupción del temporizador. <Concurrencia de tareas con Temporizador>. No abordaremos detalles de las dos primeras tareas, ya que su operación no es algo relevante para la lectura del codificador rotatorio.

No enfocaremos en desarrollar la tercera tarea de la lista, taskENC1(). que es la encargada de hacer lectura del codificador y en base al sentido de rotación, efectuara el desplazamiento del led activo en el puerto de salida.

Para lograr esto, el primer paso sera crear la función para detectar el sentido de rotación del codificador, para lo cual utilizaremos como referencia el patrón de cambio de las señales CK / DT y un diagrama de Estados y Transiciones basado en el modelo Mealy para una Maquina de Estados Finitos (FSM). Observe el diagrama de la figura 5.

Fig5. Señalización(Izq) con Diagrama Estados y Transiciones(Der)

En base a la figura 5, vamos a describir dos métodos para crear la función de detección al cual llamaremos ENCGetSense(), describiendo las diferencias en cuanto a su aplicación.  

Método 1: Se utiliza una codificación basada en el diagrama de estados y transiciones donde la función deberá ejecutarse ante el cambio de estado en cualquiera de las señales CK o DT, el valor retornado indica el sentido de rotación. La ventaja de este método es la sensibilidad con la rotación, ya que la función responde con cada fase o paso del codificador. En el caso del modulo KY-040 notara esta sensibilidad con un sutil desplazamiento de rotación, lo que podría ser un inconveniente.

uint8_t ENCGetSense(void) //Debe ejecutarse con cada cambio en CK o DT
{
    static uint8_t state;
    uint8_t res = 0;
    switch(state)
    {
        case 0b00:
//Estado STA00
            if((ECK1pin == 1) && (EDT1pin == 0))
            {   res = 1;
                state = 0b10;
            }
            if((ECK1pin == 0) && (EDT1pin == 1))
                state = 0b01;
        break;
        case 0b01:
//Estado STA01
            if((ECK1pin == 0) && (EDT1pin == 0))
            {   res = 1;
                state = 0b00;
            }
            if((ECK1pin == 1) && (EDT1pin == 1))
                state = 0b11;
        break;
        case 0b10:
//Estado STA10
            if((ECK1pin == 1) && (EDT1pin == 1))
            {   res = 1;
                state = 0b11;
            }
            if((ECK1pin == 0) && (EDT1pin == 0))
                state = 0b00;
        break;
        case 0b11:
//Estado STA11
            if((ECK1pin == 0) && (EDT1pin == 1))
            {   res = 1;
                state = 0b01;
            }
            if((ECK1pin == 1) && (EDT1pin == 0))
                state = 0b10;
        break;
    }
    return res;
//retorna el sentido de rotación
}

Método 2: Esta codificación utiliza como referencia la transición en CK, por tanto la función deberá ejecutarse posterior a un cambio de nivel de esta señal. Observe la parte derecha de color azul en la figura 5 donde hay dos puntos de transición en CK, notara que posterior a cada transición si ambas señales son iguales el sentido es derecho y sera opuesto si las señales difieren.  El inconveniente de este método es que la función responde solo a los cambios en CK, por lo tanto no es sensible a todos las fases o pasos del codificador.

uint8_t ENCGetSense(void) //Debe ejecutarse con cada cambio en CK
{
    uint8_t res, val;
    res = ECK1pin;
    val = EDT1pin;
    res = (res ^ val); 
//XOR entre CK y DT
    return (res & 0x01);
//Retorna el sentido de giro

Este segundo método de codificación se adapta mejor a la lectura de nuestro modulo KY-040, por lo que en adelante consideraremos su uso en nuestro programa.
Como ya tenemos la función para detectar el sentido de giro del codificador ENCGetSense(). es momento de elaborar el procedimiento final taskENC1() que debe ejecutarse de manera concurrente cada milisegundo. Este procedimiento deberá llamar a la función de detección únicamente cuando cuando cambie la señal en CK, por esta razón observara que se realizar una enmascaramiento al pin con la finalidad de guardar un valor previo para realizar la comparativa con cada nuevo ciclo de ejecución, solo se enmascara el pin RB1 ya que utilizaremos para la detección el método 2.

void taskENC1(void) //Ejecución a cada milisegundo
{
    static uint8_t portbval = 0x02;
//Mascara inicial en CK
    static uint8_t portdval = 0x01;
    if((PORTB & 0x02) != portbval)
//Verifica modificación de la señal CK
    {
        portbval = PORTB & 0x02;
//Actualiza nuevo valor CK
        if(ENCGetSense())
//Si sentido es horario
        {
            if(portdval == 0) portdval = 0x80;
            else portdval >>= 1;
//Desplazamiento a la derecha
        }
        else
//Si sentido opuesto
        {
            if(portdval == 0) portdval = 0x01;
            else portdval <<= 1;
//Desplazamiento a la izquierda
        }
        PORTD = portdval;
    }    
}

Los procedimientos que se mostraron con antelación son parte del archivo principal main.c que contiene el proyecto, este archivo contiene código adicional que no se ha detallando en la presente publicación, por esta razón dejo el enlace para que puedas descargarlo y analizar con mas detalles desde el IDE MPLABX. <Proyecto MPLABX del Codificador Rotatorio>

Si quieres ver como compilar e implementar los proyectos de este blog, elaborados con MPLABX mira este vídeo.<Compilando proyectos MPLABX>

Conclusiones y recomendaciones

De manera general vimos como hacer uso del codificador rotatorio KY-040, la codificación utilizada se baso en un modelo de maquinas de estado finito aplicando el modelo Mealy para determinar el valor de salida en función de las transiciones de la señal CK y DT.  Mencionar que el ejemplo representa una de las varias maneras en que se puede crear un procedimiento para leer estos dispositivos codificadores, por lo que la viabilidad de cualquier método utilizado dependerá bastantes de los requisitos particulares de una aplicación.

Con relación al programa y la manera en que abordamos el procedimiento de lectura del codificador, es importante considerar los siguientes puntos:

  • La sensibilidad para la detección de cambios en las salidas del codificador, se limitan al periodo de un milisegundo correspondiente al tiempo de la interrupción del temporizador, por lo este ejemplo no funcionara si la frecuencia de cambios es mayor o igual a 1Khz.
  • Debido a que en la generación de niveles lógicos en la salida del codificador intervienen elementos mecánicos, es posible la presencia de rebotes, por lo que resulta una buena opción agregar al programa rutinas de validación, así como un divisor para controlar la relación de cambios.
Sin mas que mencionar agradezco tu visita al blog y espero que el ejemplo visto pueda ser útil en tu formación y el proyecto que desarrollas.

Atentamente, Pablo Zárate Arancibia  email: pablinza@me.com / pablinzte@gmail.com, @pablinzar

Santa Cruz de la Sierra - Bolivia


No hay comentarios.:

Publicar un comentario