Documentos de Académico
Documentos de Profesional
Documentos de Cultura
Introducción Al PIC
Introducción Al PIC
Introducción
• RB0 a RB7
• VSS y VDD
• OSC1/CLK IN y OSC2/CLKOUT
• MCLR
Este pin se utiliza para borrar las posiciones de memoria dentro del
PIC (p.ej. cuando quiero reprogramarlo). Durante el funcionamiento
normal está conectado a la alimentación positiva.
• INT
Este es un pin de entrada que puede ser monitorizado. Si el pin se
pone a nivel alto, podemos hacer que el programa se reinicie, se pare
o cualquier otra función de deseemos. No lo utilizaremos mucho.
• TOCK1
Si quieres ir por la vía fácil, echa un vistazo a este sitio: (falta el sitio,
el del texto original no funciona).
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Autor:
;
; Fecha:
;
; Versión:
;
; Titulo:
;
;
;
; Descripción:
;
;
;
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Date cuenta de que hemos hecho una especie de caja utilizando
puntos y comas. Esto es simplemente para hacerlo más pulcro.
Los Registros
Un registro es un lugar dentro del PIC que puede ser escrito, leído o
ambas cosas. Piensa en un registro como si fuese un trozo de papel
donde tu puedes ver la información o escribirla.
• STATUS
• TRISA y TRISB
Numero de bit 4 3 2 1 0
Valor Binario 0 0 1 1 0
• W
Un ejemplo de código
BSF 03h,5
MOVLW b'00110'
Estamos poniendo el valor binario 00110 (la letra 'b' significa que el
número está en binario) en nuestro registro de propósito general W.
Podríamos haber hecho esto en hexadecimal, en cuyo caso nuestra
instrucción hubiese sido:
MOVLW 06h
MOVWF 85h
Valor Binario 0 0 1 1 0
Entrada/Salida S S E E S
Ahora tenemos que configurar los pines del Puerto A, y para ello
necesitamos volver al banco 0 para manipular cualquier dato.
BCF 03h,5
Así que lo que hemos hecho ha sido encender y apagar el LED una
vez.
;
bsf 03h,5
movlw 00h
movwf 85h
bcf 03h,5
Inicio movlw 02h
movwf 05h
movlw 00h
movwf 05h
goto Inicio
Seguro que ahora puedes ver que las constantes hacen el programa
un poco más sencillo, aunque todavía no hemos puesto los
comentarios. Sin embargo, no hemos terminado todavía.
Bucles de Retardo
decfsz CONTADOR,1
Esta instrucción dice "resta 1 al registro (en esta caso CONTADOR). Si
llegamos a cero, salta 2 lugares hacia delante"[Nota de la traducción:
El valor que le sigue a la coma, indica donde debe almacenarse el
resultado de la operación. Si es 1, como en el ejemplo anterior, el
resultado se almacena en el mismo registro indicado en la
instrucción, y si es 0 el resultado se almacena en el registro w.] . Un
montón de palabras para una sola instrucción. Veamosla en acción
antes, después la pondremos en nuestro programa.
; ...de nuevo.
;****Termina el Programa****
end ; Algunos compiladores necesitan esta
instrucción.
; y también por si acaso olvidamos poner...
; ... la instrucción 'goto'.
¿ Por qué no intentas modificar los bucles de retardo para hacer que
el LED parpadee mas rápido ? Cual es el valor mínimo de CONTADOR
para poder ver el LED parpadear ? ¿ Por qué no añades un tercer
bucle o incluso más bucles de retardo después del primero para hacer
más lento el apagado mas lento ? Necesitarás una constante para
cada bucle de retardo. Podrías incluso ajustar tus bucles de retardo
para hacer que el LED parpadease con un ritmo definido, por ejemplo
una vez por segundo.
Subrutinas
; ...de nuevo.
;
;**** Aquí está nuestra Subrutina
Retardo
Bucle1 decfsz CONTADOR1,1 ; Este segundo bucle mantiene
el LED...
goto Bucle1 ; ...apagado el tiempo suficiente...
decfsz CONTADOR2,1 ; ...para que lo veamos
goto Bucle1 ;
return
;
;**** Fin del Programa****
end ; Algunos compiladores necesitan esta
instrucción,
; y también por si acaso olvidamos poner...
; ... la instrucción 'goto'.
Ahora, para configurar el pin de un puerto para que sea una salida,
enviamos un 0 al registro TRISA. Para poner el pin como entrada,
ponemos un 1 en el registro TRISA.
;Aquí el código
:
BTFSS PortA,0
Goto Inicio
;Continua por aquí
:
:
; ...de nuevo.
;
;**** Aquí está nuestra Subrutina
Retardo
Bucle1 decfsz CONTADOR1,1 ; Este segundo bucle mantiene
el LED...
goto Bucle1 ; ...apagado el tiempo suficiente...
decfsz CONTADOR2,1 ; ...para que lo veamos
goto Bucle1 ;
return
;
;**** Fin del Programa****
end ; Algunos compiladores necesitan esta
instrucción,
; y también por si acaso olvidamos poner...
; ... la instrucción 'goto'.
movlw 02h
movwf PORTA
movlw 00h
movlw PORTA
Así que, ¿ cómo podemos hacer esto de una manera mas eficiente ?
Sencillo. Utilizamos otra instrucción llamada XORWF.
Si tenemos dos entradas, y una salida, la salida solo será 1 si, y solo
si, las dos entradas son diferentes. Si son iguales, la salida será 0.
Aquí está la tabla de verdad, para aquellos que prefieran verlo en una
tabla:
A B Salida
0 0 0
0 1 1
1 0 1
1 1 0
A B Salida
0 0 0
1 0 1
1 1 0
1 0 1
Así que ahora, para encender y apagar nuestro LED, necesitamos solo
dos lineas:
MOVLW 02h
XORWF PORTA,1
PORTA
00010
xor
00000
wf
xor
00010
wf
xor
00000
wf
xor
00010
wf
LED Parpadeante:
Tamaño (en
Programa Cambio
bytes)
Utilizando función
LED Parpadeante 91
XOR
LED con
Original 132
conmutador
Operadores Lógicos
• AND
1100101
1
1011001
AND
1
Igual 1000001
a 1
Resultado de
A B
AND
0 0 1
0 1 0
1 0 0
1 1 1
El PIC nos da dos modalidades para AND. Estas son ANDLW y ANDWF.
ANDLW nos permite hace una función AND con los contenidos del
registro W, y un número que nosotros especifiquemos. Las sintaxis
es:
ANDWF nos permite hacer una función AND con los contenidos del
registro W y otro registro, como por ejemplo un puerto. Las sintaxis
es:
movlw 1100
ANDWF 05h,0
ANDLW 1100
• OR
Resultado de
A B
OR
0 0 0
0 1 1
1 0 1
1 1 1
Operadores Aritméticos
• ADD
ADDLW <número>
• SUB
Ahora, ¡apuesto a que sabes que hace esta función! Sí, lo que
suponías, esta función sustrae(o resta) un bit a otro ['Nota de la
traducción: SUB, viene del ingles "substract" que significa "sustraer"
en castellano]]. Una vez más el PIC da dos modalidades: SUBLW y
SUBWF. La sintaxis es exactamente la misma que para la función
ADD, excepto por supuesto ¡que tienes que escribir SUB en lugar de
ADD!
• Incremento
movlw 01
addwf 0Ch,1
• Decremento
• Complemento
0Ch = 11001100
COMF 0Ch,1
0Ch = 00110011
Esto se puede usar, por ejemplo, para cambiar rápidamente todos los
bits de un puerto de salida a entra y viceversa.
• BCF
BCF <registro>,<bit>
BCF 0Ch,2
8º 7º 6º 5º 4º 3er 2º 1er
Posición
bit bit bit bit bit bit bit bit
Número de bit 7 6 5 4 3 2 1 0
Nuestro ejemplo 1 1 0 0 1 1 0 1
Después de "BCF
1 1 1 0 0 0 0 1
0Ch,2"
]
• BSF
• BTFSC
;
BTFSC 03h,0
<instrucción x> ; continua por aquí si CARRY está a 1.
<instrucción y> ; o por aquí si está a 0.
Bucle :
:
:
BTFSC 03,0
Goto Bucle
• CLRF
CLRF <registro>
• CLRW
CLRW
• RLF y RRF
7654321
C
0
0000000
0
1
0000001
RLF 0
0
0000010
RLF 0
0
0000100
RLF 0
0
0001000
RLF 0
0
0010000
RLF 0
0
0100000
RLF 0
0
1000000
RLF 0
0
0000000
RLF 1
0
0000000
RLF 0
1
RLF <registro>,d
RRF <registro>,d
Programa de ejemplo
Ahora vamos a darte un ejemplo de código que puedes compilar y
ejecutar. Este hará que una luz que se desplace, comenzando en el
bit 0 del puerto B hasta el bit 8 del mismo, siguiendo al bit 0 del
puerto A hasta el bit 5, y después haciendo todo el camino inverso
hasta al principio. Conecta LEDs a todos los pines de los puertos A y
B. Aquí comprenderás algunas de las operaciones de bit mencionadas
en este capítulo:
Tablas de Datos
b'01000001' =
1
41h
b'00111011' =
2
3Bh
b'01101011' =
3
6Bh
b'01001101' =
4
4Dh
b'01101110' =
5
6Eh
b'01111110' =
6
7Eh
b'01000011' =
7
43h
b'01111111' =
8
7Fh
b'01101111' =
9
6Fh
b'01110111' =
0
77h
10 LET K=0
11 K=K+1
12 IF K>10 THEN GOTO 20 ELSE GOTO 11
20 PRINT K
21 END
PC Instrucción
-----------------------------
0000 movlw 03h
0001 movwf 0Ch
0002 Bucle decfsc 0Ch
0003 goto Bucle
0004 end
PC equ 02h
;
movlw 03h
call tabla
:
:
:
tabla addwf PC
retlw 0Ah
retlw 0Bh
retlw 0Ch
retlw 0Dh
retlw 0Eh
retlw 0Fh
retlw 10h
;
return
PC equ 02h
:
:
tabla addwf PC
retlw 41h
retlw 3Bh
retlw 6Bh
retlw 4Dh
retlw 6Eh
retlw 7Eh
retlw 43h
retlw 7Fh
retlw 6Fh
retlw 77h
return
Si observas los pines del PIC, verás que el pin 6 se muestra como
RB0/INT. RB0 es obviamente el bit 0 del Puerto B. El INT simboliza que
también puede ser configurado como un pin de interrupción.
También, los bits del 4 al 7 del puerto B (pines 10 al 13) pueden ser
utilizados para interrupciones. Antes de que usemos INT u otros pines
del puerto B, necesitamos hacer dos cosas. La primera necesitamos
decirle al PIC que vamos a usar las interrupciones. Segunda,
necesitamos especificar que pin del puerto B usaremos como
interrupción y no como un pin de Entrada/Salida.
Bien, así que ahora que le hemos dicho al PIC qué pin va a ser para la
interrupción, y en qué flanco se va a disparar la misma, ¿qué ocurre
en el programa y en el PIC cuando sucede la interrupción ?
Ocurren dos cosas. Primero, se activa un flag. Este le dice al
procesador interno del PIC que ha ocurrido una interrupción.
Segundo, el Contador de Programa (que hemos mencionado en el
capítulo anterior) apunta a una dirección particular dentro del PIC.
Vamos a ver cada cosa separadamente.
El Flag de Interrupción
La Posición de Memoria
;
ORG 0000h ; El PIC comienza aquí si se enciende o hay un
reset.
GOTO Inicio ; Ve al programa principal.
ORG 0004h ; El PIC vendrá aquí si ocurre una interrupción.
: ; Esta es nuestra rutina de interrupción
: ; con lo que queremos que haga el PIC
: ; cuando reciba una interrupción.
RETFIE ; Fin de la rutina de interrupción.
Inicio ; Este es el comienzo de nuestro programa principal
Hay dos cosas que tienes que tener en cuenta cuando utilices
interrupciones. La primera que si estás usando el mismo registro en
tu programa principal y en la rutina de interrupción, recuerda que los
contenidos del registro, probablemente cambien cuando ocurra la
interrupción. Por ejemplo, estás utilizando el registro W para enviar
datos al puerto A en el programa principal, y también vas a utilizar el
registro W en tu rutina de interrupción para mover datos de una
posición a otra. Si no tienes cuidado, el registro W contendrá el ultimo
valor que tenía cuando estaba en la rutina de interrupción, y cuando
vuelvas de la interrupción, este dato se enviará al puerto A en lugar
del valor que tenías antes de que ocurriese la interrupción. Para
evitar esto, dentro de la rutina de interrupción, se tienen que
almacenar los contenidos del registro W, antes de que lo uses . Lo
segundo es que tiene que existir un retardo entre que ocurre una
interrupción y que pueda ocurrir la siguiente. Como sabes, el PIC
tiene un reloj externo que puede ser, o bien un Cristal, o bien una red
RC (resistencia-condensador). Independientemente de la frecuencia
de reloj, el PIC la divide entre 4 y la utiliza para su reloj interno. Por
ejemplo, si usas un cristal de 4MHz, el PIC llevará a cabo las
instrucciones a 1MHz. A este tiempo interno se le llama Ciclo de
Instrucción. Bien, la hoja de características (datasheet), aunque en
letra pequeña, dice que debes dejar que pasen de 3 a 4 ciclos de
instrucción entre interrupciones. Mi consejo es que dejes 4 ciclos. La
razón para este retardo es que el PIC necesita tiempo para saltar a la
dirección de interrupción, poner el flag, y volver de la rutina de
interrupción. Así que ten esto en mente si estas utilizando otro
circuito que genere una interrupción en el PIC.
La primera cosa que tenemos que hacer es decirle al PIC que salte la
dirección donde el Contador de Programa apuntará cuando ocurra
una interrupción. Aquí notarás que estamos utilizando una forma
distinta de expresar los número hexadecimales. Antes usábamos
"F9h", donde "h" denotaba que era hexadecimal. Podemos escribir
esto como 0xF9, y ese será el formato que vamos a utilizar a partir de
ahora.
;
org 0x00 ; Aquí es donde apunta el PC si energiza el
PIC o en caso de reset
goto Principal ; Ir a nuestro programa principal
org 0x04 ; Aquí es donde comienza nuestra rutina de
interrupción.
retfie ; Esto le dice al PIC que la rutina de
interrupción
; ha terminado y el PC volverá a apuntar al
programa principal
Principal ; Este es el comienzo de nuestro programa
principal.
;
bsf INTCON,7 ; GIE – Global interrupt enable
(1=habilitado)
bsf INTCON,4 ; INTE - RB0 interrupt enable (1=habilitado)
;
bcf INTCON,1 ; INTF - A 0 por si acaso.
;
bsf STATUS,5 ; Cambia al banco 1.
movw 0x01 ;
movwf TRISB ; Establece RB0 como entrada
movlw 0x10 ;
movwf TRISA ; Pone los 4 primeros pines del puerto A
como salida
bcf STATUS,5 ; Vuelve al banco 0.
Bucle
movf CONTADOR,0 ; Movemos los contenidos de
CONTADOR a W.
movwf PORTA ; Ahora lo movemos al puerto A.
goto Bucle ; Continuamos haciendo esto.
end ; Fin de nuestro programa.
;
movwf TEMPORAL ; Almacenamos w en una posición
temporal.
;
incf CONTADOR,1 ; Incrementamos CONTADOR en 1 y
ponemos
; el resultado de vuelta en CONTADOR.
;
movlw 0x0A ; Ponemos 10 en W.
subwf CONTADOR,0 ; Restamos W a CONTADOR y ponemos
; el resultado en W.
;
btfss STATUS,0 ; Comprueba el flag CARRY. Se activará si
; CONTADOR es igual o mayor que w,
; y se activará como resultado de la instrucción
subwf
;
org 0x00 ; Aquí es donde apunta el PC si energiza el
PIC o en caso de reset
;
;*****************DEFINICIÓN DE
CONSTANTES********************************
INTCON EQU 0x0B ; Registro de Control de Interrupciones
PORTB EQU 0x06 ; Dirección del registro PORTB
PORTA EQU 0x05 ; Dirección del registro PORTA
TRISA EQU 0x85 ; Dirección del registro TRISA
TRISB EQU 0x86 ; Dirección del registro TRISB
STATUS EQU 0X03 ; Dirección del registro STATUS
CONTADOR EQU 0x0c ; Esta será nuestra variable contador
TEMPORAL EQU 0x0d ; Almacén temporal para el registro W
goto Principal ; Ir a nuestro programa principal saltando
se la dirección de interrupción.
;
;***************RUTINA DE
INTERRUPCION*********************************
org 0x04 ; Aquí es donde comienza nuestra rutina de
interrupción.
movwf TEMPORAL ; Almacenamos w en una posición
temporal.
incf CONTADOR,1 ; Incrementamos CONTADOR en 1 y
ponemos
; el resultado de vuelta en CONTADOR.
movlw 0x0A ; Ponemos 10 en W.
subwf CONTADOR,0 ; Restamos W a CONTADOR y
ponemos
; el resultado en W.
btfss STATUS,0 ; Comprueba el flag CARRY. Se activará si
; CONTADOR es igual o mayor que w,
; y se activará como resultado de la instrucción
subwf
goto continua ; Si CONTADOR es <10, entonces continua
goto limpiar ; Si CONTADOR es >9, tenemos que ponerlo
a0
continua
bcf INTCON,0x01 ; Tenemos que poner a 0 este ''flag''
para
; permitir más interrupciones.
movfw TEMPORAL ; Restaura W al valor que tenía antes
de la interrupción.
retfie ; Salir de la rutina de interrupción.
limpiar
clrf CONTADOR ; Pon CONTADOR otra vez a 0.
bcf INTCON,1 ; Tenemos que poner a 0 este ''flag'' para
; permitir más interrupciones.
retfie ; Salir de la rutina de interrupción.
;
;***************PROGRAMA
PRINCIPAL***********************************
Principal ; Este es el comienzo de nuestro programa
principal.
;****************Configura los Registros de Interrupción*************
bsf INTCON,7 ; GIE – Global interrupt enable
(1=habilitado)
bsf INTCON,4 ; INTE - RB0 interrupt enable (1=habilitado)
bcf INTCON,1 ; INTF - A 0 por si acaso.
;
;****************Configura los puertos*******************************
bsf STATUS,5 ; Cambia al banco 1.
movw 0x01 ;
movwf TRISB ; Establece RB0 como entrada
movlw 0x10 ;
movwf TRISA ; Pone los 4 primeros pines del puerto A
como salida
bcf STATUS,5 ; Vuelve al banco 0.
;
;****************Ahora envía el valor de CONTADOR al
PORTA***********
Bucle
movf CONTADOR,0 ; Movemos los contenidos de
CONTADOR a W.
movwf PORTA ; Ahora lo movemos al puerto A.
goto Bucle ; Continuamos haciendo esto.
end ; Fin de nuestro programa.
El Watchdog Timer
La hoja de datos del PIC especifica que el WDT tiene un periodo desde
su inicio hasta el final de 18 ms. Esto depende de varios factores,
como el voltaje aplicado, la temperatura del PIC, etc... La razón de
esta aproximación es debida a que el reloj del WDT es suministrado
por una red RC interna. El tiempo de carga de la red RC depende del
voltaje de alimentación. También depende de los valores de los
componentes, los cuales cambian ligeramente dependiendo de su
temperatura. Así que por razones de simplicidad, tomaremos los 18
ms como el tiempo de reinicio del WDT.
Sin embargo, podemos hacer este tiempo mayor. Dentro del PIC hay
un elemento llamado Prescaler [Nota de la Traducción: "Prescaler" se
puede traducir como "etapa previa de ajuste de escala"]. Podemos
programar este prescaler para dividir el reloj interno de la red RC.
Cuanto mayor sea el factor de división, más tiempo tardará el WDT en
reiniciarse.
;
movlw 02
movwf CONTADOR
Bucle decfsz CONTADOR
goto Bucle
end
Software Programador
Programa de ejemplo
BUCLE1 ;
decfsz CONTADOR1 ; 9F x 1 ciclo + 1 ciclo = 160
ciclos
goto BUCLE1 ; 9E x 2 ciclos = 316 ciclos
BUCLE2 ;
decfsz CONTADOR2 ; 9F x 1 ciclo + 1 ciclo = 256
ciclos
goto BUCLE2 ; 9E x 2 ciclos = 316 ciclos
;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Esta parte reinicia el WDT ;;
;; Quita o comenta este comando para ver que hace el WDT.
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
clrwdt ; Esto simplemente reinicia el WDT.
; *************** Retorna desde nuestra rutina Retado***************
return ;
;
END ;