Sei sulla pagina 1di 7

PIC Encoder con un 16F628A.

El funcionamiento de este lector de encoder.- Un bucle se encarga de comprobar el estado de las


entrada RA0 y RA1 (A y B respectivamente en el grfico de abajo) en el menor tiempo posible. En
condiciones normales la velocidad de lectura suele rondar unas 25.000 veces por segundo para un
cristal de 4MHz dependiendo de la longitud del programa. Esto es ms que suficiente para poner el
encoder directo al eje de la mayora de los motores, para un mximo de 12 pulsos por vuelta del motor
que es hasta donde yo he probado. Aunque con 4MHz funciona perfecto, aumentando los MHz del cristal
conseguirs velocidades de lectura mayores. Siempre y cuando el PIC permita frecuencias mayores, con
8MHz doblars la velocidad de lectura, con 12MHz la triplicars, con 20MHz la quintuplicars. Cada
aumento de 4MHz incrementas una vez el factor de multiplicacin y esto es debido a que una instruccin
en ensamblador necesita 4 ciclos de reloj para ejecutar.

Como puedes ver en la imagen, hemos de


detectar cundo hay un flanco de subida o de
bajada en la entrada 'A' (un cambio de estado)
siempre que 'B' est a '1'. Si A es el bit bajo
(LSB) y B es el bit alto (MSB), al considerar los
dos bits como un nmero tenemos que si hubo
un cambio de 2 (10 en binario) a 3 (11 en
binario), se ha producido un flanco de subida,
por tanto hemos de incrementar el contador. Y
al revs, cuando el PIC detectar que en las
entradas hubo un 3 (11 en binario) y pasa a 2
(10 en binario), es decir, un flaco de bajada, ha
de decrementar el contador. En el programa
para el PIC el valor 'anterior' se almacena en la
variable AUX, y el valor 'actual' se almacena en
la variable ENC; la variable que hace de
contador se llama X.
#include <16F628A.h>
#FUSES NOWDT, XT, PUT, NOPROTECT, NOBROWNOUT, NOLVP, NOCPD
#use delay(clock=4000000)

#byte porta = 0x05 // Asignamos PortA.


#byte portb = 0x06 // Asignamos PortB.

// ---------- Programa Principial ----------


void main()
{
port_b_pullups(FALSE); // Sin resistencias pull ups a las salidas del puerto B.
setup_timer_0(RTCC_INTERNAL|RTCC_DIV_1); // Al no usar el TIMER configuramos lo ms bsico.
setup_comparator(NC_NC_NC_NC); // Sin comparadores.
setup_vref(FALSE); // Al no usar comparadores no necesitamos Vref.

//---- Fin de la configuracin del 16F628A ----

int8 x; // Declaramos el valor de X como byte, se corresponder con los 8 LEDs de salida.
int8 enc; // Se almacenar el valor actual de RA0 y RA1, hasta la siguiente comparacin.
int8 aux; // Se almacenar el valor anterior de RA0 y RA1, hasta la siguiente comparacin.

set_tris_a(0b11111); // Puerto A como entradas. Slo usamos RA0 y RA1.


set_tris_b(0b00000000); // Puerto B como salidas, para los 8 LEDs.

portb=0; // Inicialmente ponemos a cero el puerto B.


x=0; // Inicialmente ponemos a cero la variable que se usa para contar.
enc=0; // Inicialmente ponemos a cero la variable que tomar los valores de RA0 y RA1.

while (true)
{
aux=enc; // Igualamos 'AUX' y 'ENC' para luego comparar cuando cambie 'ENC'.
enc=porta & 3; // Aislamos RA0 y RA1 como un nmero de 2 bits y se carga en la variable 'ENC'.

if ((aux==2)&&(enc==3)) // Si en la comparacin hay flanco de subida,


{
x++; // entonces incrementar una unidad el valor de X.
}
if ((aux==3)&&(enc==2)) // Si en la comparacin hay flanco de bajada,
{
x--; // entonces decrementar una unidad el valor de X.
}
portb = x; // El valor de X sale por el puerto B, los 8 LED de salida.
}
}
PIC Encoder con un 16F876.

Este lector de encoder usa la interrupcin externa RB0/INT. Cuando se produce un cambio de
estado en la patilla RB0/INT, el micro deja lo que estaba haciendo y saltar a un procedimiento de
forma inmediata. Terminado ese procedimiento vuelve al programa principal.

Funciona de la siguiente manera: Podemos hacer que la interrupcin se active con un flanco de
subida o de bajada, esto es programable modificando el registro INTEDG. Cuando ocurre una
interrupcin por flanco de subida hemos de programar la siguiente interrupcin para que se active
por flanco de bajada e incrementamos el contador si RB1 est a 1. Y viceversa, si ocurre una
interrupcin por flanco de bajada hemos de programar la siguiente interrupcin por flanco de
subida y decrementamos el contador si RB1 est a 1.

Una cosa muy buena que tiene esta forma de funcionar es que mientras el PIC no recibe
interrupciones (RB0/INT en este caso) puede dedicarse a hacer otra cosa, como por ejemplo,
calcular velocidades y cosas as. Como contrapartida, las interrupciones tienen un problema, y es
que si intentas hacer comunicaciones por el puerto serie, podra perturbar los tiempos de
temporizacin que rige la comunicacin y hacer que falle. Adems, recomiendo que cuando se
use la interrupcin RB0/INT no se use otras interrupciones (sea internas o externas), porque si ha
de contar, esas otras interrupciones puede impedir el contaje.
#include <16F876A.h>
#FUSES NOWDT, XT, PUT, NOPROTECT, NODEBUG, NOBROWNOUT, NOLVP, NOCPD, NOWRT
#use delay(clock=4000000)
#ZERO_RAM
#byte porta = 0x05 // Asignamos PortA (No lo usamos).
#byte portb = 0x06 // Asignamos PortB (Usamos RB0 y RB1).
#byte portc = 0x07 // Asignamos PortC (8 salidas a LED).
// ------ Variable Global ------
int8 x=0; //Declaramos el valor de X como byte, es decir, 8 bits.
// ---------- Interrupcin ----------
#INT_EXT
void IntRB0()
{
if (bit_test(portb,0)) // Si RB0 se ha puesto a 1 (flanco de subida),
{
ext_int_edge(H_TO_L); //entonces activar la interrupcin flanco bajada.
if (bit_test(portb,1)) // Si RB1 est 1,
{
x++; // entonces incrementar el contador X.
}
}
else // Si RB0 se ha puesto a 0 (flanco de bajada),
{
ext_int_edge(L_TO_H); //entonces activar la interrupcin flanco subida.
if (bit_test(portb,1)) // Si RB1 est 1,
{
x--; // entonces decrementar el contador X.
}
}
}
// ---------- Programa Principial ----------
void main()
{
port_b_pullups(FALSE);
setup_adc_ports(NO_ANALOGS);
setup_adc(ADC_CLOCK_DIV_2);
setup_spi(SPI_SS_DISABLED);
setup_timer_0(RTCC_INTERNAL|RTCC_DIV_1);
setup_timer_1(T1_DISABLED);
setup_timer_2(T2_DISABLED,0,1);
setup_comparator(NC_NC_NC_NC);
setup_vref(FALSE);

//---- Fin de la configuracin del 16F876A ----

enable_interrupts(int_ext); //Activar Interrupcin Externa.


ext_int_edge(L_TO_H); //Inicialmente Interrupcin por Flaco de Subida.
enable_interrupts(GLOBAL); //Interrupciones Generales Activadas.

set_tris_a(0b111111); //Puerto A como entrada (No usado).


set_tris_b(0b11111111); //Puerto B como entrada (slo usamos RB0 y RB1).
set_tris_c(0b00000000); //Puerto C todo como salida (Salida a LED, 8 bits).

While (true)
{
portc = x; //El valor de X sale por el puerto C, 8 LED de salida.
}
}
PIC Encoder con un 18F4550.

Funciona de la siguiente manera: Podemos hacer que la interrupcin se active con un flanco de
subida o de bajada, esto es programable modificando el registro INTEDG. Cuando ocurre una
interrupcin por flanco de subida hemos de programar la siguiente interrupcin para que se
active por flanco de bajada e incrementamos el contador si RB1 est a 1. Y viceversa, si ocurre
una interrupcin por flanco de bajada hemos de programar la siguiente interrupcin por flanco
de subida y decrementamos el contador si RB1 est a 1. cuenta los pulsos de forma muy
eficiente.

Los PICs 18F2455 y 18F2550 no tienen puerto D, tendras que hacer la salida a los LED a
travs del puerto A (sin olvidar poner una resistencia de RA4 a positivo porque es salida con
colector abierto). En este caso slo tendramos 6 bits en vez de 8 pero para ver el
funcionamiento de conteo es suficiente.

En principio el programa est configurado para un cristal de 4MHz pero como en este ejercicio
no usamos el USB puedes usar el cristal que desees mientras est en el rango de 4 a 20MHz,
porque no se utiliza temporizaciones ni nada parecido.

En CCS la direccin de los puertos A B C D E cambia de lugar.


Include <18F4550.h> // Usamos el PIC 18F4550, puedes declarar
cualquiera de los 18Fxx5x.

#Fuses XT, NOWDT, NOPROTECT, NOLVP, BROWNOUT


#use Delay( Clock = 4000000 )

#Byte PortA = 0xF80 // Direccin del puerto A para la familia 18Fxx5x.


#Byte PortB = 0xF81 // Direccin del puerto B para la familia 18Fxx5x.
#Byte PortC = 0xF82 // Direccin del puerto C para la familia 18Fxx5x.
#Byte PortD = 0xF83 //Direccin puerto D para familia 18Fxx5x (Slo 40/44 pines)
#Byte PortE = 0xF84 // Direccin del puerto E para la familia 18Fxx5x.

// ------ Variable Global ------


Int8 x = 0; // Declaramos el valor de X como Byte, es decir, 8 bits.
// Esta variable ha de ser global porque su valor lo
// usaremos en la interrupcin y en el programa principal.
// Por tanto declaramos esta variable antes la interrupcin
// y de "void main".

// --------- Interrupcin ---------


#INT_EXT // Interrupcin Externa por RB0: Decodificacin de Encoder.
Void IntRB0()
{
// CCS se encarga de desactiva automticamente cualquier interrupcin.
// No hace falta guardar contextos de registros.

If (Bit_Test(PortB, 0)) // Si RB0 se ha puesto a 1 (flanco de subida),


{
Ext_Int_Edge(H_TO_L); // entonces activar la siguiente interrupcin
por flanco de bajada.
If (Bit_Test(PortB, 1)) // Si RB1 est a 1,
{
x++; // entonces incrementar una unidad el valor de X.
}
}
Else // Si RB0 se ha puesto a 0 (flanco de bajada),
{
Ext_Int_Edge(L_TO_H); // entonces activar la siguiente interrupcin por
flanco de subida.
If (Bit_Test(PortB, 1)) // Si RB1 est 1,
{
x--; // entonces decrementar una unidad el valor de X.
}
}
// Al finalizar la interrupcin CCS se encarga de volver a poner automticamente
// la bandera INTF =0 ---> borra la interrupcin para poder permitir la sgte;
// no hemos de hacer nada por nuestra parte.
}

Void Main() // Inicio y configuracin.


{
Port_B_Pullups(FALSE); // Configuracin para el PIC 18F4550.
Setup_ADC_Ports(NO_ANALOGS); // Sin comparadores ni ADCs, todo digital
Setup_adc(ADC_CLOCK_DIV_2);
Setup_spi(SPI_SS_DISABLED);
Setup_timer_0(RTCC_INTERNAL|RTCC_DIV_1);
Setup_timer_1(T1_DISABLED);
Setup_timer_2(T2_DISABLED,0,1);
Setup_comparator(NC_NC_NC_NC);
Setup_vref(FALSE);

Enable_Interrupts(Int_Ext); // Activar Interrupcin Externa a travs de RB0.


Ext_Int_Edge(L_TO_H); // Inicialmente detectar interrupcin por flanco
de subida.
Enable_Interrupts(GLOBAL); // Interrupciones Generales Activadas.

Set_Tris_A(0b11111111); // Puerto A todo entradas, en este caso no usamos


el Puerto A.
Set_Tris_B(0b11111111); // Puerto B todo entradas, slo usamos las
entradas RB0 y RB1.
Set_Tris_C(0b11111111); // Puerto C todo entradas, en este caso no usamos
el Puerto C.
Set_Tris_D(0b00000000); // Puerto D todo salidas (8 bits que irn a los
LEDs).
Set_Tris_E(0b11111111); // Puerto E todo entradas, en este caso no usamos
el Puerto E.

// ---------- Programa Principial ----------

While (True)
{
PortD = x; // El valor de X sale por el Puerto D a los 8 LED.
}
}

Potrebbero piacerti anche