http://www.jmnlab.com/receptorir/receptorir.html
Receptor IR.
Buscando programas en el pc he encontrado uno de los primeros que hice
para un pic en ensamblador para parte de un robot que nunca
llegó a andar.
Una forma sencilla y barata de controlar un robot o circuito a
distancia es usar un mando infrarrojo, como puede ser el de la
televisión, cadena etc.. de los muchos que hay por casa. Barato
porque sólo necesitaremos un receptor y una resistencia que
conectar al microcontrolador, es decir entre uno y dos euros. Como
receptor he utilizado un TSOP1738, que se encarga de demodular la
señal enviada por el mando y nos la da en una línea en
serie. Tiene 3 patas, dos de alimentación y la salida, en la que
debemos colocar una resistencia de pull-up y conectarla al
microcontrolador. Su montaje es el siguiente:
Circuito TSOP1738
Cada fabricante suele usar sus propios protocolos, por lo que primero
tenemos que saber que es lo que buscamos en esa salida que nos
proporciona el TSOP. Para esto yo he usado un analizador
lógico de pc, que nos permite ver los unos y ceros que
tienen lugar durante un tiempo, su precio está sobre los 150,
200 euros y nos será útil a la hora de trabajar con
microcontroladores para comprobar su correcto funcionamiento. Es
recomendable hacerse con uno que su soft tenga un analizador de
protocolos. El que yo uso de 8 canales lo compre por unos 180 euros (lo
más barato que encontre en su día) .
Analizador lógico.
Para ver los datos que recibimos del mando conectamos un canal del
analizador lógico a la salida del TSOP, y procedemos a
pulsar botones. En este caso se está usando un mando de
JVC y al pulsar un botón nos manda lo siguiente.
Mando JVC
Esa señal es la que sale del mando cuando pulsamos un botón.
Como se puede ver la señal comienza cuando baja la línea, con un pulso de
8.4 ms seguido de uno de 4.2 ms, esto sólo se realiza una vez para
indicar que se ha pulsado el botón y diferenciar de cuando se mantiene
pulsado, la señal se repite cada 50 ms esta vez sin el pulso de inicio
de 8 y 4 ms.
Para diferenciar entre 1 y 0 se mide el
tiempo que pasa entre dos
flancos de bajada, 1 ms corresponde a un 0 y 2.1 ms a un 1, en este
ejemplo se mandan 16 bits (8 de dirección y 8 de dato
según el protocolo de JVC). En este caso tenemos 11000101
(197) y 01111000 (120).
Por cada botón nuestro micro va a recibir dos bytes, el primer
byte es de dirección, dice para que componente del equipo
receptor es el segundo byte que sale del mando, por lo que en el mando
nos encontraremos con unos cuántos botones que mandan el mismo
primer byte. En este caso en el mando sólo había dos
bytes de dirección, es decir todos los botones mandaban uno u
otro.
Una vez que sabemos lo que sale del mando tenemos que capturarlo, interpretarlo y usarlo.
Para capturarlo se puede hacer de varias maneras, usando algún
periférico del micro para protocolos serie, o en mi caso de la
forma larga mediante una interrupción activada por el flanco de
bajada de la línea del TSOP y contando tiempos entre flancos de
bajada. La ventaja de programar en ensamblador es que en todo momento
sabemos lo que está haciendo el microcontrolador y podemos tener
un control del tiempo exacto.
Cada instrucción de nuestro programa en ensamblador del
microcontrolador tarda una cantidad de ciclos de reloj en ejecutarse,
el reloj es el cristal que conectamos externo que puede ser de varios
KHz, MHz, o un oscilador interno propio del microcontrolador del orden
de KHz a MHz.
Por ejemplo si tenemos el siguiente código para pic:
***************************************************
***************************************************
org 0
goto Start
org 5
Start
bsf STATUS,RP0 ; select Register Page 1
bcf TRISC,0 ; make IO Pin C.0 an output
bcf STATUS,RP0 ; back to Register Page 0
MainLoop
bcf PORTC,0 ; turn on LED C0
bsf PORTC,0 ; Turn off LED C0
goto MainLoop ; Do it again...
end
***************************************************
***************************************************
Nuestro programa hace lo que tenemos dentro del Mainloop de forma
repetida, es decir haríamos 3 instrucciones en bucle: "bcf PORTC,0" y "
bsf PORTC,0" instrucciones de 4 ciclos de reloj, y "goto
MainLoop" instrucción de 8 ciclos de reloj. Es decir cada
iteracción del bucle serían 4+4+8 = 16 ciclos de
reloj.
Si colocamos el osciloscopio en el puerto C0 tendremos una señal
cuyo período es función del valor del oscilador. Por
ejemplo si nuestro oscilado funciona a 16 MHz, en el port c tendremos
una señal de 16 MHz/ 16 ciclos de reloj, es decir 1 MHz. Nuestro
bucle de 3 instrucciones se realizaría en 1 microsegundo.
Cristal de 16 MHz
Primero se realiza la instrucción que pone a cero el pin C0,
0.25 uS a cero la salida. Después ponemos el pin del puerto a 1
0.25 uS y luego con este pin a 1 volvemos a la primera
instrucción, esto tarda 0.5 uS, por lo que nos encontramos con
una señal con un duty del 75%.
Si por ejemplo utilizamos el oscilador interno del pic a 4 MHz, tendríamos la siguiente salida:
Oscilador 4 MHz
La frecuencia de la señal es 4 MHz / 16 ciclos de reloj = 250
Khz, es decir el bucle de 3 instrucciones se realizaría en
1/250KHz = 4 microsegundos. Luego la primera instrucción tarda 1
uS, la siguiente otro uS y el goto 2 uS, en todal los 4 uS.
De esta forma podemos calcular lo que tarda en realizarse un trozo de
código de nuestro programa, que usaremos para capturar los unos
y los ceros mandados por el TSOP17.
Nuestro código para capturar la señal salta mediante una
interrupción en RB0, cuando se detecta un flanco de bajada en
este puerto salta nuestra interrupción y pasamos a ejecutar su
código (repito que hay formas más adecuadas de hacerlo),
por lo que tenemos que medir el tiempo que pasa entre flancos de bajada
para saber si nos llega un uno y un cero, y esto lo hacemos
incrementado un contador cada 0.1 ms en un registro del pic, para ello
debemos conocer cuánto tarda en ejecutarse cada
instrucción.
Si entre dos flancos de bajada nuestro contador vale menos de un valor
mínimo o más de uno máximo nos salimos de la
interrupción con una condición de error, ya que
ningún valor de 1 o 0 se corresponde con estos tiempos, estamos
ante un ruido. Si vale más de 0.8 ms estamos ante un 0, y si
vale más de 1.7 ms ante un uno. De esta forma capturamos los 1 y
0 y los almacenamos en dos registros del pic.
Una vez que tenemos un montón de unos y ceros debemos de saber
que hacer con ellos. Si queremos que nuestro circuito realice una
función con cada botón, sólo debemos comparar el
valor de cada registro con un valor en otro registro perteneciente a
ese botón, y ejecutar cierta acción cuando los registros
sean iguales.
En este caso lo que se ha hecho es convertir esos registros en binario
a decimal, y sacar este valor convertido por un LCD para comprobar que
el valor leído es el mismo que el visto en el analizador
lógico, y de esta forma comprobar que nuestro programa funciona.
Sobre el tema del LCD pongo un link en español de ionitron donde
explica bien el tema http://www.x-robotics.com/rutinas.htm#LCD.
Se podía haber sacado en unos y ceros sin convertir a decimal,
pero un valor decimal siempre es más fácil de identificar
que 16 unos y ceros seguidos.
En el analizador lógico teníamos el 197 y el 120 (tecla +
del mando) recibidos por el TSOP1738, se puede ver como esos unos y
ceros son capturados, convertidos a decimal y llevados al LCD.
Foto conjunto.
El circuito del pic es un circuito de pruebas con un 16F877A orientado
hacía robótica, se puede ver un L298 y algunos
componentes más. A la izquierda el receptor de infrarrojos y
abajo el LCD con los valores recibidos por el receptor.
Agrego todo el programa al final, fue uno de los primero que hice en
ensamblador para probar las interrupciones y tiempos del pic, todos los
botones vistos en el analizador lógico se corresponden con los
leídos en el LCD, así que supongo que funciona. De todas
formas para implementar esto lo mejor es usar un periférico del
microcontrolador para tal fin.
Video de funcionamiento. El color azul del led indica que recibe dato.
Para cualquier duda, comentario, corrección etc...
Gracias por pasar por aquí. Saludos.
Código Pic.
; Designed to run at 20MHz
list p=16F877a
#include p16F877a.inc
__CONFIG _CP_OFF & _WDT_OFF & _HS_OSC & _LVP_OFF & _BODEN_OFF
;******************************* DEFINICIONES***************************************************
#DEFINE LED1 PORTA,3 ;Rojo
#DEFINE LED2 PORTA,5 ;Azul
#DEFINE LED3 PORTC,5 ;Verde
#DEFINE PULSADOR2 PORTA,1
#DEFINE DATO0 PORTD,0
#DEFINE DATO1 PORTD,1
#DEFINE DATO2 PORTD,2
#DEFINE DATO3 PORTD,3
#DEFINE DATO4 PORTD,4
#DEFINE DATO5 PORTD,5
#DEFINE DATO6 PORTD,6
#DEFINE DATO7 PORTD,7
#DEFINE ENABLE PORTE,2
#DEFINE RS PORTE,0
#DEFINE W_R PORTE,1
#DEFINE LCD_DATOS PORTD
#DEFINE LCD_CTRL PORTE
#DEFINE IR PORTB,0
#DEFINE PIN_PRUEBA PORTA,0
#DEFINE PIN_PRUEBA2 PORTA,2
#DEFINE PIN_PRUEBA3 PORTC,0
;****************************** REGISTROS RAM ***************************************************
cblock 0x20
Delay0
; Assign an address to label
Delay1
Delay1
Delay2
Delay3
DELAY4
DELAY5
W_TEMP
STATUS_TEMP
CUENTA
UNIDADES
DECENAS
CENTENAS
DATO1IR
DATO2IR
TICTAC
INT_CONTROL
INT_CONTROL_OLD
CONTADOR1
endc
;**************************** POSICIONES *********************************************************
ORG 0
; Posición de memoria
donde se coloca la instrucción.
GOTO Start
; Salto a Start, se ejecutará la
instrucción debajo de Start.
ORG 4
GOTO ISR
ORG 5
; Salta el vector de
interrupción
;***************************** CONFIGURAR REGISTROS ************************************************
Start ; Inicio del programa
; ****************** BANCO 0 **************************************************************
BCF STATUS, RP0
BCF STATUS, RP1 ; Seleciona el Bank0
CLRF PORTA
; Initialize poerts by clearing
output
CLRF PORTB ; data latches
CLRF PORTC
CLRF PORTD
CLRF PORTE
MOVLW B'10010000'
MOVWF INTCON ;GIE e INTE, bit 1 flag de INTE
; ******************** BANCO 1
**************************************************************
BSF STATUS,RP0 ; select Register Page 1
BCF OPTION_REG,INTEDG ;Flanco de bajada
MOVLW B'00000000' ;Interruptores son entradas
MOVWF TRISA
MOVLW B'00000001'
MOVWF TRISB
CLRF TRISC
CLRF TRISD
CLRF TRISE
MOVLW 0X06 ;Configurar los pin de A
MOVWF ADCON1 ;como entradas digitales.
BCF STATUS, RP0 ;Bank0
;************************ INICIO PROGRAMA ****************************************************************
BSF LED2 ;Parpadeo de Reset
CALL Delay_2
BCF LED2
CALL INICIALIZA_LCD
MOVLW B'00000001' ;Clear Display
CALL LCD_COMANDO
MOVLW B'00000110' ;Entry mode set
CALL LCD_COMANDO
MOVLW B'00001110' ;Activa lcd y cursor
CALL LCD_COMANDO
BCF PIN_PRUEBA2
BCF PIN_PRUEBA
;************************** PROGRAMA PRINCIPAL ********************************************************
MAIN_LOOP
BSF LED1
CALL Delay_2
BCF LED1
CALL Delay_2
GOTO MAIN_LOOP
;*************************** INTERRUPCIONES*************************************************************
ISR
MOVWF W_TEMP ;Salva W y Status
SWAPF STATUS,0
MOVWF STATUS_TEMP
BSF LED2
BCF LED1
BCF LED3
CLRF TICTAC
CLRF DATO1IR
CLRF DATO2IR
CLRF CONTADOR1
CLRF INT_CONTROL
;bit 7 entrada IR, bit 3 y 4 cambio de byte y
salida, bit 0:2 contador
CLRF INT_CONTROL_OLD
BCF PIN_PRUEBA
BCF PIN_PRUEBA2
BCF PIN_PRUEBA3
ISR_LOOP
; ********* Se
mira el pin de entrada, si no cambia se va a SUBIR_TIC
************************
BTFSS IR
;Si es cero se ejecuta la
siguiente,PORTB,0 ;0.2
GOTO PONER_A_CERO
;---->0.6
BSF INT_CONTROL,7
;0.6
GOTO COMP_ENTRADA
;---->1
PONER_A_CERO
;<----0.6
NOP
;0.8
BCF INT_CONTROL,7
;1
COMP_ENTRADA
;<----1
MOVFW INT_CONTROL_OLD
;1.2
XORWF INT_CONTROL,W
;Si son iguales Z=1
;1.4
BTFSS STATUS,Z
;Si es 0 se ejecuta la siguiente
;1.6
GOTO COMPARAR_TIC
;--->2
;************************** Incrementar el TIC **********************************************************
NOP
;2
NOP
;2.2
NOP
;2.4
NOP
;2.6
SUBIR_TIC
;<--2.6
NOP
;2.8
MOVLW D'140'
;1 Tics = 0.1 mS
;3
SUBWF TICTAC,W
;3.2
BTFSC STATUS,C
;Si TICTAC-W mayor que 0, ejecuta la
siguiente
;3.6
GOTO ERROR_IR
;
INCF TICTAC,F
;3.8
;
MOVLW D'158'
;4
MOVWF DELAY5
;4.2
Delay_5loop
;3*0.2*158=94.8
DECFSZ DELAY5,f
;
GOTO Delay_5loop
;
MOVFW INT_CONTROL
;99.2
MOVWF INT_CONTROL_OLD
;99.4
NOP
;99.6
GOTO ISR_LOOP
;100
; ********* El
pin de entrada a cambiado, en función de los tics damos 1 o 0
*****************
; ********* TIC
menos 0.8 mS error, mayor de 0.8 mS escribir cero, mayor 1.7 mS
escribir uno *
;
********** Si es el flanco de subida no comparamos
***************************************
COMPARAR_TIC
BTFSC IR
GOTO SUBIR_TIC
MOVLW D'26'
;1 Tics = 0.1
mS
SUBWF TICTAC,W
BTFSC STATUS,C
;Si TICTAC-W mayor que 0, ejecuta la
siguiente
GOTO COMPARAR_SALIDA
MOVLW D'17'
;1 Tics = 0.1
mS
SUBWF TICTAC,W
BTFSC STATUS,C
;Si TICTAC-W mayor que 0, ejecuta la
siguiente
GOTO ALMACENAR_UNO
MOVLW D'8'
;1 Tics = 0.1
mS
SUBWF TICTAC,W
BTFSC STATUS,C
;Si TICTAC-W mayor que 0, ejecuta la
siguiente
GOTO ALMACENAR_CERO
ERROR_IR
BCF LED2
GOTO POP
COMPARAR_SALIDA
MOVFW INT_CONTROL
MOVWF INT_CONTROL_OLD
CLRF TICTAC
GOTO ISR_LOOP
ALMACENAR_CERO
BTFSC INT_CONTROL,3 ;Si el bit 3
es uno se ejecuta la siguiente, vale 8 o mas.
GOTO ALMACENAR_DATO2
BCF STATUS,C
RLF DATO1IR,F
GOTO ALMACENAR_SALIDA
ALMACENAR_DATO2
BCF STATUS,C
RLF DATO2IR,F
GOTO ALMACENAR_SALIDA
ALMACENAR_UNO
BTFSC INT_CONTROL,3 ;Si el bit 3
es uno se ejecuta la siguiente, vale 8 o mas.
GOTO ALMACENAR_DATO2_1
BCF STATUS,C
RLF DATO1IR,F
INCF DATO1IR,F
GOTO ALMACENAR_SALIDA
ALMACENAR_DATO2_1
BCF STATUS,C
RLF DATO2IR,F
INCF DATO2IR,F
GOTO ALMACENAR_SALIDA
ALMACENAR_SALIDA
INCF INT_CONTROL,F
BTFSC INT_CONTROL,4 ;Si el bit 4
es uno se ejecuta la siguiente, vale 16 o mas.
GOTO DATOS_A_LCD
GOTO COMPARAR_SALIDA
DATOS_A_LCD
MOVLW B'10000000'
CALL LCD_COMANDO
MOVFW DATO1IR
CALL BINARIO_LCD
MOVFW CENTENAS
CALL LCD_ESCRIBIR
MOVFW DECENAS
CALL LCD_ESCRIBIR
MOVFW UNIDADES
CALL LCD_ESCRIBIR
MOVLW B'11000000'
CALL LCD_COMANDO
MOVFW DATO2IR
CALL BINARIO_LCD
MOVFW CENTENAS
CALL LCD_ESCRIBIR
MOVFW DECENAS
CALL LCD_ESCRIBIR
MOVFW UNIDADES
CALL LCD_ESCRIBIR
POP
SWAPF STATUS_TEMP,0
MOVWF STATUS
MOVFW W_TEMP
BCF INTCON,INTF
BCF LED2
RETFIE
;********************************** LCD ****************************************************************
INICIALIZA_LCD
CALL Delay
CLRF LCD_CTRL
MOVLW B'00111000' ;8 bits, 2 líneas y 5x8 puntos
MOVWF LCD_DATOS
CALL LCD_E
CALL Delay
RETURN
LCD_E
BSF ENABLE
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
NOP
BCF ENABLE
RETURN
LCD_COMANDO
CLRF LCD_CTRL
MOVWF LCD_DATOS
CALL LCD_E
CALL Delay
RETURN
LCD_ESCRIBIR
CLRF LCD_CTRL
BSF RS
MOVWF LCD_DATOS
CALL LCD_E
CALL Delay
RETURN
;********************************** BINARIO A LCD *****************************************************
BINARIO_LCD
;Para registros de 8 bits, convierte a centenas, decenas y unidades.
MOVWF CUENTA
CLRF UNIDADES
CLRF DECENAS
CLRF CENTENAS
CENT_LOOP
MOVLW D'100'
SUBWF CUENTA,W
BTFSS STATUS,C
GOTO DEC_LOOP
INCF CENTENAS,F
MOVWF CUENTA
GOTO CENT_LOOP
DEC_LOOP
MOVLW D'10'
SUBWF CUENTA,W
BTFSS STATUS,C
GOTO UNIDAD
INCF DECENAS,F
MOVWF CUENTA
GOTO DEC_LOOP
UNIDAD
MOVF CUENTA,W
MOVWF UNIDADES
MOVLW B'00110000'
ADDWF CENTENAS,1
ADDWF DECENAS,1
ADDWF UNIDADES,1
RETURN
;********************************** DELAYS *************************************************************
Delay ;20 mS
MOVLW D'65'
MOVWF Delay1
Delay_loop
decfsz Delay0,f ; Waste time.
goto Delay_loop
; The Inner loop takes 3 instructions per loop * 256 loopss = 768
instructions
decfsz Delay1,f
; The outer loop takes and additional 3 instructions per lap * 256 loops
goto Delay_loop
; (768+3) * 256 = 197376 instructions / 1M instructions per second =
0.197 sec.
; call it a
two-tenths of a second.
RETURN
Delay_2 ;0.86 s
MOVLW D'22'
MOVWF Delay2
Delay_2loop
decfsz Delay0,f ; Waste time.
goto Delay_2loop
; The Inner loop takes 3 instructions per loop * 256 loopss = 768
instructions
decfsz Delay1,f
; The outer loop takes and additional 3 instructions per lap * 256 loops
goto Delay_2loop
; (768+3) * 256 = 197376 instructions / 1M instructions per second =
0.197 sec.
; call it a
two-tenths of a second.
decfsz Delay2,f
goto Delay_2loop
return
Delay_3 ;50.2 uS
MOVLW D'82'
MOVWF Delay3
Delay_3loop
DECFSZ Delay3,f
GOTO Delay_3loop
RETURN
end
No hay comentarios:
Publicar un comentario