Hola!
Este es un antiguo proyecto (http://www.todopic.com.ar/foros/index.php?topic=18873.msg132655#msg132655) que he retomado ahora. La razón de no continuar con el antiguo hilo es pq hace 6 meses de ese hilo por eso he decidido crear uno nuevo.
La definición del proyecto es simple: sacar toda la informacion posible de un encoder de 2 canales. Dicha información es: posición actual del encoder (pasos), sentido, velocidad actual, velocidad media y velocidad de transferencia de los paquetes USB.
El funcionamiento de este encoder esta explicado en el antiguo hilo (http://www.todopic.com.ar/foros/index.php?topic=18873.msg132655#msg132655) pero voy a volver a exponerlo para tenerlo todo centralizado en este hilo, además de miedo a que se borre debido al tiempo q ha pasado.
ENCODER INCREMENTAL
Un encoder incremental es un dispositivo que transforma un movimiento rotatorio en pulsos digitales. Estos pulsos pueden ser usados para controlar desplazamientos de tipo angular o de tipo lineal si se asocia a una cremallera (http://upload.wikimedia.org/wikipedia/commons/6/68/Rack_and_pinion_animation.gif) o un husillo (http://www.iesmarenostrum.com/departamentos/tecnologia/mecaneso/mecanica_basica/mecanismos/imagenes/mec_sinfin-pinon02.gif)
(http://s1.subirimagenes.com/imagen/previo/thump_32204920070821klpinginf48.png)
El encoder se compone de 3 partes:
- Un disco ranurado formado con lineas opacas y transparentes
- Un diodo LED
- Uno o varios Fototransistores
El funcionamiento es el siguiente: cuando el sistema comienza a funcionar, el led crea un haz de luz que incide en el foto transistor; a medida que el eje vaya girando, se producirán una serie de pulsos de luz en el receptor, correspondientes a la luz que atarviesa los huecos entre las marcas. Llevando una cuenta de esos pulsos es posible conocer la posición del eje.
METODOS DE CONTROL
El encoder que voy a usar es un encoder incremental de 2 canales desfasados 90º hay algunos pic que incorporan el modulo QEI que es especifico para el control de estos encoders, por desgracia, ningun picUSB los incorpora.
Este tipo de encoder se puede usar de 3 maneras (sin modulo QEI):
- Modo 1X: Usando solo un canal para el conteo y el otro obtener el sentido
- Modo 2X: Atendiendo a los cambios de nivel para el conteo y para el sentido
- Modo 4X: Atendiendo a los flancos para el conteo y para el sentido
(http://img522.imageshack.us/img522/4706/dibujofn0.jpg)
Modo 1X: Usando solo un canal para el conteo y el otro obtener el sentido
Este modo es el mas sencillo de hacer solo se tiene que conectar el Canal A a la interrupción externa del pic, configuramos la interrupción por flanco de subida (aunque se puede hacer por flanco de bajada). El otro canal lo podemos conectar a cualquier patilla de configurada como entrada en el pic, esta patilla nos dara el sentido.
El funcionamiento de este método es el siguiente: Al girar el encoder de izquierda-derecha los flancos de subida son los flancos izquierdos del Canal A, fijandose en la imagen anterior, el nivel del Canal B en todos los flancos de subida del Canal A es nivel alto. Si el encoder gira derecha-izquierda los flancos de subida son los flancos derechos del Canal A, que coinciden con el nivel bajo del Canal B.
int16 pasos=0;
int sentido=1;
#int_EXT
void EXT_isr(void)
{
if(input(PIN_B1)) //Miro el estado de el otro canal q me indica el sentido
{
sentido=1;
pasos=pasos+1;
}
else
{
sentido=0;
pasos=pasos-1;
}
}
Modo 2X: Atendiendo a los cambios de nivel para el conteo y para el sentido
Por Hacer....
Modo 4X: Atendiendo a los flancos para el conteo y para el sentido
Por Hacer....
PROGRAMACIÓN DEL FIRMWARE
El pic que uso es el 18F2550. El firmware lo tengo totalmente verde es un clon del proyecto de J1M (http://www.todopic.com.ar/foros/index.php?topic=2260.0) pero en vez de hacer la suma manda el valor del encoder. Me da verguenza de ponerlo. :P En cuanto tenga algo mas personal actualizaré este apartado.
PROGRAMACION DE APLICACIÓN
(http://s1.subirimagenes.com/otros/322667encoder.jpg)
El lenguaje que uso para la aplicacón es el Visual C# 2005 express. La aplicación hace un puleo al USB pidiendo el estado del encoder al pulsar el boton de Muestreo, mostrando el numero de pasos del encoder en el Label. Para ello he hecho 2 métodos nuevos en la clase PicUSBAPI:
public void Pulea(Label pasos)
{
poolling = true;
while (poolling)
{
Application.DoEvents();
SumaPIC(10, 10);
pasos.Text = (ResultadoPIC()).ToString();
}
}
public void PararPuleo()
{
poolling = false;
}
Descargate el código (http://jjcorps.diinoweb.com/files/ENCODER%20v1.rar)
AYUDA
Despues de este rollo queria pediros vuestra ayuda en como implementar los modos 2X y 4X de los metodos de control, de la manera mas rápida posible ya que cuanta mas precision tiene el metodo mas veces va interrumpir al pic.
Gracias
Propongo un encoder con el PIC 16F876A, pero en principio puede ser cualquier PIC que tenga Int/RB0.
Pongo dos códigos, uno para CCS y otro para Proton IDE, antes de nada el esquema:
(http://sites.google.com/site/proyectosroboticos/_/rsrc/1281698939189/encoder/encoder-por-software/Encoder16F876FotoBarrera.PNG)
Funciona por la interrupción RB0/INT y cada vez que detecta un flanco (sea de subida o de bajada) hace cambiar el registro INTEDG. Así se consigue no perder pulsos, no cuenta de más ni de menos y es muy rápido. Uso un disco de 5 aspas (o dientes) en el eje de un motorcillo de juguete, lo alimento a 12 (es de 3v.) y pese a la velocidad que coge no pierde pulsos. Probar este código sólo te llevará un momento. Es esencial poner como acondicionador de señal del encoder un disparador Schmitt tipo 7414 ó 74LS14 (ver circuito de arriba); son puertas NOT con tecnología trigger Schmitt y TTL, funcionan la mar de bien. Evita usar como acondicionador de señal tecnología CMOS, como por ejemplo el 74HC14, porque al menos a mi me ha dado problemas intermitentes con esta tecnología. Para más info haz clic en el enlace final de este post.
Código para CCS
#include <16F876A.h> //Usamos el PIC 16F876A.
#FUSES NOWDT, XT, PUT, NOPROTECT, NODEBUG, NOBROWNOUT, NOLVP, NOCPD, NOWRT
#use delay(clock=4000000) // Usamos un cristal de 4MHz.
#ZERO_RAM // Todo a cero.
#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).
// ------ Variables Globales ------
int8 x=0; //Declaramos el valor de X como byte, es decir, 8 bits.
// ---------- Interrupción ----------
#INT_EXT
void IntRB0()
{
//disable_interrupts(int_EXT);
if (bit_test(portb,0))
{
ext_int_edge(H_TO_L);
if ((bit_test(portb,1)) == 1)
{
x++;
}
}
else
{
ext_int_edge(L_TO_H);
if ((bit_test(portb,1)) == 1)
{
x--;
}
}
//enable_interrupts(int_EXT);
}
// ---------- 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 configuración del 16F876A ----
enable_interrupts(int_ext); //Activar Interrupcion Externa.
ext_int_edge(L_TO_H); //Inicialmente Interrupción 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 (sólo 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, los 8 LED de salida.
}
}
Código para Proton IDE
Device=16F876A
REMINDERS = FALSE
Config XT_OSC, PWRTE_ON, CPD_OFF, WDT_OFF, BODEN_OFF, LVP_OFF
REMINDERS = TRUE
Symbol INTF = INTCON.1 ' RB0 External Interrupt Flag
Symbol INTE = INTCON.4 ' RB0 External Interrupt Enable
Symbol GIE = INTCON.7 ' Global Interrupt Enable
Symbol INTEDG = OPTION_REG.6 ' Flag = 0 Flanco bajada. Flag = 1 Flanco subida.
On_INTERRUPT GoTo Interrupcion ' Interrupción por Hardware (es la más rápida).
GIE = 1 ' Activa interrupciones generales.
INTE = 1 ' Activa la interrupción externa RB0/INT.
INTEDG = 1 ' Hace que inicialmente la interrupción se habilite
' para flanco de subida.
ALL_DIGITAL = TRUE ' Todas las entradas y salidas son digitales.
TRISA = %111111
TRISB = %11111111 ' Puerto A y B todo entradas.
TRISC = %00000000 ' Puerto C como salida para visualizar a través de los LED.
Dim x As Byte ' Variable X ---> contador de posición actual.
x=0
While 1=1 ' |------ Programa Principal ------|
PORTC = x ' El contenido de X se visualiza en el Puerto C a través de los LED.
Wend ' |--------------------------------|
End
Interrupcion: '-------- Decodificador de Encoder --------------
Context SAVE ' Salva en contexto de los registros antes de operar con la interrupción.
If PORTB.0 = 1 Then ' Si RB0 se ha puesto a 1 (flanco de subida),
INTEDG = 0 ' entonces activar la siguiente interrupción para flanco de bajada.
If PORTB.1 = 1 Then ' Si RB1 está a 1
Inc x ' entonces incrementar el contador X.
EndIf
EndIf
If PORTB.0 = 0 Then ' Si RB0 se ha puesto a 0 (flanco de bajada),
INTEDG = 1 ' entonces activar la siguiente interrupción para flanco de subida.
If PORTB.1 = 1 Then ' Si RB1 está 1
Dec x ' entonces decrementar el contador X.
EndIf
EndIf
INTF = 0 ' Borra el "flag" de la interrupción RB0/INT para poder permitir la
' siguiente interrupción, cuando ocurra.
Context Restore ' Restablece el contexto de los registros tal como estaban antes de la
' interrupción.
Información de este proyecto aquí: http://sites.google.com/site/proyectosroboticos/encoder/encoder-por-software
Información sobre encoder con sensor Hall aquí: http://sites.google.com/site/proyectosroboticos/encoder/encoder-con-hall
Información sobre encoder electrónico puro aquí: http://sites.google.com/site/proyectosroboticos/encoder/encoder-por-hardware
Pic Encoder USB: http://sites.google.com/site/proyectosroboticos/encoder/pic-encoder-usb