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