Sunteți pe pagina 1din 22

Tutorial PIC16F84A assembler - Comparadores

A medida que avanzamos en este tutorial, conocemos la función de cada registro


que se muestra.
Las siguientes líneas significativas en el programa son:
1 RES_VECT CODE 0x0000 ;processor reset vector
2 GOTO START ;go to beginning of program
Esto da el vector de reinicio para el código. Un vector de reinicio apunta a qué parte
del programa es el primero en ejecutarse. Aquí, esa parte es la que tiene la etiqueta
START. Otro vector utilizado es el INT_VECT o el vector de interrupción. Este vector
se discute en el tutorial de interrupción .
Abra el archivo P16F84A.INC y revise su contenido. Asegúrate de no cambiar nada
dentro de él, a menos que sepas lo que estás haciendo.
CBLOCK
Las líneas,
1 CBLOCK 0x0C
2 COUNT1
3 COUNT2
4 ENDC
usar el ensamblador CBLOCK operativo. Esto asigna los alias COUNT1 y COUNT2
a los registros de propósito general con dirección 0Ch y 0Dh respectivamente. Si
observa el mapa del archivo de registro de PIC16F84A anterior, estas direcciones
son parte de la SRAM y no tienen nombres específicos. Significa que podemos
usarlos de la manera que queramos. Los registros COUNT1 y COUNT2 se utilizarán
para nuestra subrutina de retardo.
Bancos cambiantes
La ejecución real del programa comienza en la línea,
1 BSF STATUS, RP0
El comando BSF significa "bit set register f", donde la siguiente declaración es el
registro f y el bit asociado con ese registro. Ver el resto del conjunto de
instrucciones . Para el de arriba, el registro f es el registro de ESTADO:

El bit RP0 se llama el bit de selección de banco de registro. El PIC16F84A agrupa


sus registros en dos bancos. Veamos nuevamente el mapa de registro del
dispositivo:
Antes de poder manipular un registro específico, primero debe ir al banco donde se
encuentra. Esto se hace a través de borrar (hacer que sea cero) o establecer (hacer
que sea uno) el bit RP0. Tenga en cuenta que algunos registros, incluido el registro
ESTADO, se encuentran en ambos bancos, lo que significa que no necesita cambiar
de banco para acceder a ellos.
Registro TRIS
Entonces, ¿por qué configuramos el bit RP0? Eso es porque las siguientes líneas,
1 MOVLW 0xFE
2 MOVWF TRISB
Utiliza el registro TRISB que se encuentra en el banco 1.
El MOVLW operativo significa "mover el valor literal al registro W". El registro W es
un registro de propósito general que actúa como un almacenamiento temporal al
mover los valores de un registro a otro.
El MOVWF operativo significa "mover los contenidos de W a TRISB". Esto significa
que después de estas dos líneas, el registro TRISB ahora tiene el valor 0xFE .
Entonces, ¿por qué este valor para TRISB? Primero discutiremos la función del
registro TRISB.
TRIS es la abreviatura de tri-state . Esto describe las características de los puertos
del microcontrolador, lo que significa que un puerto puede ser una (1) entrada, (2)
salida o (3) colgante izquierdo. El PIC16F84A tiene dos puertos: PORTA y
PORTB. Esto significa que también hay dos registros TRIS: TRISA y TRISB. Un
cero en cualquier bit TRIS convierte el bit PORT correspondiente en una
salida . A la inversa, uno en cualquier bit TRIS convierte el bit PORT
correspondiente en una entrada .
Para nuestro ejemplo, el valor de TRISB es 0xFE :

Verá que solo el bit 0 está bajo o borrado. Esto corresponde a que PORTB.0 o RB0
se conviertan en un pin de salida y el resto como pines de entrada. Si, por ejemplo,
desea que los pines de salida PORTB.0 y PORTB.1 y el resto sean pines de entrada,
su TRISB sería:

que es 0xFC en valor hexadecimal.


La siguiente línea:
1 BCF STATUS, RP0
simplemente nos regresa al banco 0 porque las siguientes líneas:
1 MAIN
2 BSF PORTB, 0
3 CALL delay
4 BCF PORTB, 0
5 CALL delay
6 GOTO main
implica el uso del registro PORTB que se encuentra en el banco 0.
El código de operación BSF significa "bit set f". La línea BSF
PORTB, comando 0 establece el bit cero del registro PORTB. Recuerde que el
registro PORTB es el que está vinculado a los pines RB físicos. Así que cuando se
establece PORTB.0, el pin RB0 es alto.
La línea BCF PORTB, 0 hace lo contrario del comando que acabamos de
comentar. Esto hará que el pin RB0 bajo.
Subrutina Delay
Entre los comandos bsf y bcf hay una llamada a la subrutina de retardo que se
encuentra en la parte final del código:
1 DELAY
2 LOOP1 DECFSZ COUNT1, 1
3 GOTO LOOP1
4 DECFSZ COUNT2,1
5 GOTO LOOP1
6 RETURN
¿Por qué es necesaria esta rutina? Como se mencionó anteriormente, cada línea se
procesa a una velocidad de 1 microsegundos para un oscilador de cristal de 4
MHz. Si no hay una subrutina de retardo, el tiempo entre RB0 es alto y luego bajo
solo sería 1 microsegundo. El parpadeo del LED será demasiado rápido para ver en
tiempo real.
El operativo DECFSZ , usado en la subrutina de retardo, es la abreviatura de
"decrementar f omitir si es cero". Disminuiría COUNT1 hasta que llegue a cero y
luego saltará la siguiente línea. Si COUNT1 aún no es cero, el programa procesará
el bucle como se indica en la instrucción "goto loop1". El número "1" junto a COUNT1
significa que el resultado de la disminución se coloca en COUNT1. El otro valor
posible aquí es "0", que colocará el resultado del decremento en el registro W.
Por cierto, COUNT1 tiene un valor inicial de 0xFF o 255. Este es el valor
predeterminado de los registros no utilizados. ¡Esto significa que el bucle continuará
durante 255 ciclos de instrucción!
Una vez que COUNT1 llegue a cero, saldrá del bucle y pasará a la línea DECFSZ
COUNT2, 1 . Esto disminuirá COUNT2, pero verá que la siguiente línea apunta el
programa a loop1. Esto significa que COUNT1 se decrementará una y otra vez hasta
que llegue a cero. Después de eso, COUNT2 (ahora con un valor de 254) disminuirá
y el bucle continuará.
Con todo, la rutina tomaría 255 x 255 ciclos de instrucción para procesar. Esto es
equivalente a 65 ms de tiempo de espera.
Resumen
Nuestro primer programa de lenguaje ensamblador PIC ahora se puede resumir de
arriba a abajo:
 Apunte la etiqueta START como la primera línea del programa (líneas 3 y 4)
 Registre COUNT1 y COUNT2 como variables para la subrutina de retardo
(8 a 11)
 Vaya al banco 1 y establezca RB0 como salida y luego vuelva al banco 0
(líneas 16 a 19)
 Establezca RB0, espere 65 ms (con la ayuda de la subrutina de retardo en
las líneas 22 a 23)
 y luego elimínelo y espere 65 ms nuevamente (líneas 24 a 25)
 Regrese a la parte del programa con la etiqueta "principal", haciendo un
bucle continuo del código (línea 26)
 Inicio de la subrutina de retardo (línea 28)
 Fin del programa (línea 35)
¡Eso es! Le sugiero que pruebe el código anterior y use el circuito provisto en este
artículo . Diviértete codificando en lenguaje ensamblador PIC!
Entrada / Salida con Microcontroladores PIC
Para la mayoría de los microcontroladores, los canales de entrada y salida se
manejan mediante registros de funciones especiales de dirección de datos. Los
microcontroladores AVR los llaman registros de dirección de datos (DDR), mientras
que el STM32 basado en ARM tiene un grupo de registros "GPIO". Los PIC los
llaman registros TRIS que se supone que son cortos para "TRIState". En este
artículo, veremos cómo manipular los pines del microcontrolador PIC mediante el
registro TRIS.

¿Por qué TRIState?


Los pines de un microcontrolador pueden tener tres estados: entrada, salida o
flotante. Si desea conectar un LED a un pin, debe establecer ese pin como salida. Si
necesita usar un botón, configure el pin del botón como entrada. Un pin flotante es
un pin que no se ha definido como entrada o salida. En general, no utilizamos pines
flotantes con PIC.
Para establecer un pin como entrada, configuramos su bit TRIS
correspondiente. Por ejemplo, si queremos configurar todos los PORTB como
entrada, el siguiente código ASM hace el trabajo:
1 MOVLW b’11111111’
2 MOVWF TRISB
Despejar el pin hace lo contrario. Entonces, si quieres establecer PORTA.0 como
salida, haz esto:
1 BCF TRISA, 0
Por supuesto, los ejemplos anteriores son algunas de las pocas formas de
establecer y borrar bits en un registro utilizando PIC ASM. Le sugiero que lea
el lenguaje ensamblador para los PIC si necesita un repaso.
El PIC16F84A tiene 5 pines PORTA y 8 pines PORTB. Todos los pines PORTB
pueden configurarse como entrada o salida. Puede hacer lo mismo para los 4 pines
en PORTA, con la excepción de PORTA.4, que solo puede sumir la corriente y no
la fuente. Esto significa que solo se puede establecer como entrada. En cuanto a
por qué es eso, no estoy del todo seguro. Pero sé que PORTA.4 tiene una función
secundaria de lectura de pulsos de reloj.
Programa de ensamblaje de entrada / salida
simple
Vamos a hacer un programa simple. Un LED está conectado a PORTA, 0 mientras
que un interruptor está conectado a PORTB, 0. El LED debe encenderse cuando el
interruptor está encendido y apagarse cuando el interruptor está apagado.
El circuito para este código debería verse así:
1 ; TODO INSERT CONFIG CODE HERE USING CONFIG BITS GENERATOR
2 #INCLUDE
3
4 RES_VECT CODE 0x0000 ; processor reset vector
5 GOTO START ; go to beginning of program
6
7 ; TODO ADD INTERRUPTS HERE IF USED
8
9 CBLOCK 0x0C
10 COUNT1
11 COUNT2
12 ENDC
13
14
15 MAIN_PROG CODE ; let linker place main program
16
17 START
18 BSF STATUS, RP0
19 CLRF TRISA
20 MOVLW 0xFF
21 MOVWF TRISB
22 BCF TRISB, 0
23 BCF STATUS, RP0
24
25 MAIN
26 BCF PORTA,0
27 BTFSS PORTB,0
28 GOTO on
29 GOTO MAIN
30 on BSF PORTA,0
31 CALL DELAY
32 GOTO main
33
34 DELAY
35 LOOP1 DECFSZ COUNT1, 1
36 GOTO LOOP1
37 DECFSZ COUNT2,1
38 GOTO LOOP1
39 RETURN
40
41 END
En la rutina de inicio, comenzamos con el cambio al banco 1 ya que los registros
TRIS se encuentran allí. Esto se hace estableciendo el bit RP0 en el registro de
ESTADO. Luego escribimos los literales correspondientes a los registros
TRIS. Después de eso, volvemos al banco 0 (línea 16).
La rutina principal verifica si PORTB.0 está configurado o borrado usando el código
de operación BTFSS. Si ese pin se establece (se apaga), la línea 20 se omite y el
programa hará un bucle sin fin. Por lo tanto, el LED permanece apagado. De lo
contrario, el programa saltará a la etiqueta on y establece PORTA.0. Se introduce
un retraso para la estabilidad y el programa verifica nuevamente el estado de
PORTB.0.
Entrada / Salida utilizando C
El código ASM anterior se puede implementar en XC8 de esta manera:
1 #define _XTAL_FREQ 4000000
2 #include <xc.h>
3
4
5 void main(void) {
6 TRISB = 0x01; // Initialize PORTB0 as all input
7 TRISA = 0x01; // Initialize All PORTA as output
8 while(1){
9 if(RB0 == 0){
10 PORTA = 0b00000001; // Set RA0
11 }else{
12 PORTA = 0b00000000; // Clear RA0
13 }
14 }
15 }

Un reto simple
Modifique el código anterior para que otro LED esté conectado a PORTA.1. Cuando
se enciende el interruptor, el LED PORTA.0 se ilumina, mientras que el LED
PORTA.1 se apaga. A la inversa, el LED PORTA.0 se apaga y el LED PORTA.1 se
enciende cuando se apaga el interruptor.
Conjunto de instrucciones de
montaje PIC | Dispositivos de
gama media
Cada instrucción de rango medio es una palabra de 14 bits dividida en un OPCODE
que especifica el tipo de instrucción y uno o más operandos que especifican la
operación de la instrucción.
El resumen del conjunto de instrucciones de rango medio en la tabla a continuación
enumera las instrucciones reconocidas por el ensamblador MPASM. El conjunto de
instrucciones es altamente ortogonal y se agrupa en tres categorías básicas:
 Operaciones orientadas a bytes.
 Operaciones orientadas a bits
 Operaciones literales y de control.
Para instrucciones orientadas a bytes , 'f' representa un designador de registro
de archivos y 'd' representa un designador de destino. El designador de registro de
archivo especifica qué registro de archivo debe utilizar la
instrucción. El designador de destino especifica dónde se colocará el resultado de
la operación. Si 'd' es cero, el resultado se coloca en el registro W. Si 'd' es uno, el
resultado se coloca en el registro de archivos especificado en la instrucción.
Para instrucciones orientadas a bits , 'b' representa un designador de campo de
bits que selecciona el número del bit afectado por la operación, mientras que 'f'
representa el número del archivo en el que se encuentra el bit.
Para operaciones de control y literales , 'k' representa una constante de ocho u
once bits o un valor literal.
Todas las instrucciones se ejecutan en un solo ciclo de instrucciones, a menos que
una prueba condicional sea verdadera o el contador del programa cambie como
resultado de una instrucción. En estos casos, la ejecución toma dos ciclos de
instrucción con el segundo ciclo ejecutado como un NOP. Un ciclo de instrucción
consta de cuatro períodos de oscilador. Por lo tanto, para una frecuencia de
oscilador de 4 MHz, el tiempo de ejecución de la instrucción normal es de 1 ms. Si
una prueba condicional es verdadera o el contador del programa se modifica como
resultado de una instrucción, el tiempo de ejecución de la instrucción es de 2 ms.
Las siguientes instrucciones son aplicables a dispositivos de rango medio (PIC12 y
PIC16)
Mnemónicos, Descripción Ciclos Palabra de instrucción Estado
Operandos de 14 bits afectado

OPERACIONES DE REGISTRO DE ARCHIVOS ORIENTADOS A BYTE

ADDWF f, d Agregue W y f 1 00 0111 dfff ffff C, DC,


ANDWF f, d Y W con f 1 00 0101 dfff ffff Z
CLRF f Borrar f Complemento 1 00 0001 1FFF ffff Z
CLRW - W 1 00 0001 0xxx xxxx Z
COMF f, d Complemento f 1 00 1001 dfff ffff Z
DECF f, d Disminución f 1 00 0011 dfff ffff Z
DECFSZ f, d Disminución f, Saltar si 0 1 (2) 00 1011 dfff ffff ZZ
INCF f, d Incremento f 1 00 1010 dfff ffff Z
INCFSZ f, d Incremento f, Omitir si 0 1 (2) 00 1111 dfff ffff Z
IORWF f, d Incluido O W con 1 00 0100 dfff ffff C
MOVF f, d f 1 00 1000 dfff ffff C
MOVWF f, Mover W hacia f 1 00 0000 1FFF ffff C, DC, Z
d No f Operación 1 00 0000 0xx0 0000 Z
NOP - Girar hacia la izquierda f hasta 1 00 1101 dfff ffff
RLF f, d Carry 1 00 1100 dfff ffff
RRF f, d Rotar hacia la derecha F a través 1 00 0010 dfff ffff
SUBWF f, d de llevar a 1 00 1110 dfff ffff
SWAPF f, d resta W de f 1 00 0110 dfff ffff
XORWF f, d Intercambiar mordiscos en f
Exclusivo O W con f

OPERACIONES DE REGISTRO DE ARCHIVOS ORIENTADOS A BIT

BCF f, b Bit Clear f 1 01 00bb bfff ffff


BSF f, b Bit Set f 1 01 01bb bfff ffff
BTFSC f, b Bit Test f, Skip si Clear 1 (2) 01 10bb bfff ffff
BTFSS f, b Bit Test f, Skip if Set 1 (2) 01 11bb bfff ffff

OPERACIONES LITERALES Y DE CONTROL.

ADDLW k Agregar literal y W 1 11 111x kkkk kkkk C, DC,


ANDLW k Y literal con la 1 11 1001 kkkk kkkk Z
CALL k subrutina de llamada W 2 10 0kkk kkkk kkkk ZTO, PD
CLRWDT - Borrar el temporizador de 1 00 0000 0110 0100 Z
GOTO k vigilancia 2 10 1kkk kkkk kkkk A, PD
IORLW k Ir a la dirección 1 11 1000 kkkk kkkk C, DC,
MOVLW k Inclusivo O literal con W 1 11 00xx kkkk kkkk Z
RETFIE - Mover el literal a W 2 00 0000 0000 1001 Z
RETLW k Retorno de la interrupción 2 11 01xx kkkk kkkk
RETURN - Regresar con el literal en W 2 00 0000 0000 1000
SLEEP - Regresar de la subrutina 1 00 0000 0110 0011
SUBLW k Ir al modo de espera 1 11 110x kkkk kkkk
XORLW k Restar W del literal 1 11 1010 kkkk kkkk
Exclusivo O literal con W
Formato de Opcode de Instrucciones
Usando el temporizador PIC con PIC16F84A
En este tutorial, discutiré cómo usar el módulo del temporizador del microcontrolador
PIC16F84A para una variedad de aplicaciones, incluyendo encender y apagar un
LED sin el uso de una subrutina de retardo de software.
El PIC16F84A tiene un temporizador de ejecución libre de 8 bits llamado TMR0 . El
término "ejecución libre" significa que se ejecuta continuamente en segundo plano y
no tiene control de inicio o detención. Puede leer o escribir valores desde / hasta
este temporizador. Dado que es un temporizador de 8 bits, el TMR0 puede contar
de 0 a 255 (2 8 = 256).
Otros microcontroladores tienen múltiples temporizadores con números de bits más
altos: el PIC16F877A tiene dos temporizadores de 8 bits y uno de 16 bits, mientras
que el PIC18F4550 tiene tres temporizadores de 16 bits y uno de 8 bits. Cada conteo
transcurre después de dos ciclos de instrucción por defecto. Recuerde que la
velocidad del ciclo de instrucción se calcula (para un oscilador de 4 MHz) usando
Puede extender el tiempo que toma para cada conteo
usando preescaladores . Cuando el temporizador llega a 255, el temporizador se
desborda y volverá a 0. Una interrupción está asociada con el desbordamiento del
temporizador .

Configurando el temporizador PIC


Todos los ajustes para el TMR0 se configuran utilizando el registro de OPCIÓN. La
hoja de datos PIC16F84A describe este registro de la siguiente manera:
El registro OPTION es un registro legible y grabable que contiene
varios bits de control para configurar el preescalador TMR0 / WDT,
la interrupción INT externa, TMR0 y los pull-ups débiles en PORTB.
Aquí está el contenido de este registro:
Mire el bit 5 del registro OPCIONAL denominado TOCS (TMR0 Clock Source
Select). Para usar el temporizador, este bit debe ser borrado. Si se configura, el
módulo TMR0 se usa para el conteo (se explica en un tutorial aparte).
Prescaler
El prescaler determinará cuántos bordes de origen incrementarán el
valor del registro TMR0 en 1. En resumen, el prescaler nos permite extender el
tiempo antes de que el temporizador se desborde. Hay ocho configuraciones de
preescala que se eligen a través de los bits PS <2: 0> del registro OPCIÓN:
El bit 3 es el bit de asignación de preescala. Al borrarlo se asignará la escala previa
a la TMR0. Al establecerlo, se asignaría la preescala al temporizador de vigilancia
(que es otro temporizador con un uso diferente). Tenga en cuenta que cualquier
instrucción de escritura en TMR0 ( clrf, bsf, movwf , etc.) borrará el valor de
preescala .

Ejemplo: si el prescaler, si se establece en TMR0 y PS <3: 0> es binario 2 (010 2 ),


se necesitarían 8 conteos antes de que el temporizador se incremente en uno y, por
lo tanto, se desbordará después de 2.04 milisegundos (suponiendo que el cristal de
4 MHz es utilizado) porque
Configuración del temporizador PIC a través de
ASM
Aquí hay un fragmento de código corto que configura el temporizador utilizando la
escala preliminar máxima.
1 bsf STATUS, RP0 ;switch to Bank 1; OPTION register is at Bank 1
2 movlw 27h ;binary 00000111 - maximum prescale, assigned to TMR0
3 movwf OPTION_REG ;this is the name of the register on the .INC of P16F84A
4 bcf STATUS, RP0 ;switch to Bank 0

Encender / apagar un LED usando el


temporizador PIC
1 list p=16f84a
2 include <P16F84A.INC>
3
4 org 0x00
5 goto start
6
7 start bsf STATUS, RP0 ;switch to Bank 1
8 bsf STATUS, RP0 ;switch to Bank 1; OPTION register is at Bank 1
9 movlw 0x00
10 movwf TRISB ;PORTB all output
11 movlw 27h ;binary 00000111 - maximum prescale, assigned to TMR0
12 movwf OPTION_REG ;this is the name of the register on the .INC of P16F84A
13 bcf STATUS, RP0 ;switch to Bank 0
14 clrf TMR0 ;initialize the timer to zero
15 ;---------------main routine
16 main
17 movf TMR0,0 ;move contents of TMR0 to W
18 andlw 0x01 ;mask so that only bit 0 of W is moved
19 movwf PORTB ;to PORTB
20 goto main
21 end
Lo que hace este código es simplemente cambiar el estado de PORTB.0 de forma
síncrona con el bit 0 del registro TMR0. El cambio es un poco rápido, ya que los
cambios de bit menos significativos son los más rápidos (alrededor de 254
microsegundos para un cristal de 4 MHz) para cada conteo.
Para hacerlo más lento, puede usar el siguiente bit (bit 1) cambiando
1 andlw 0x01
a
1 andlw 0x02
Por supuesto, PORTB.1 ahora estará parpadeando en lugar de PORTB.0. El
parpadeo más lento se puede lograr si se usa andlw 0x80 (enmascarando el bit más
significativo).
Un segundo retraso
Entonces, ¿cómo podemos lograr un retraso de un segundo? Si hizo los cálculos
matemáticos, el temporizador se desborda (preescala máxima, cristal de 4 MHz)
después de 65.536 milisegundos. Eso es alrededor de 1/15 de un segundo
completo. Sabiendo esto, podemos esperar a que el temporizador se desborde 15
veces antes de cambiar el estado de un pin. Esto se logra mediante el siguiente
código:
1 list p=16f84a
2 include <P16F84A.INC>
3
4 cblock 0x0c
5 COUNT
6 endc
7
8 org 0x00
9 goto start
10
11 start bsf STATUS, RP0 ;switch to Bank 1
12 movlw 0x10
13 movwf TRISA ;PORTA all output
14 movlw 0x00
15 movwf TRISB ;PORTB all output
16 movlw 07h ;00000111
17 movwf OPTION_REG ;Counter mode, falling edge, prescaler set to maximum
18 bcf STATUS, RP0 ;switch to Bank 0
19 movlw 0x0F
20 movwf COUNT
21
22 ;---------------main routine
23 main bcf PORTB,0
24 movf TMR0,0
25 sublw .255
26 btfss STATUS, Z
27 goto main ; keep looping until timer reaches 255
28 decfsz COUNT,1 ;if set, decrement COUNT
29 goto main ;if count is not yet zero, repeat the loop
30 main2 bsf PORTB,0 ;if count reaches zero, set PORTB.0
31 movf TMR0,0 ;repeat process to delay turn off
32 sublw .255
33 btfss STATUS, Z
34 goto main2
35 decfsz COUNT,1
36 goto main2
37 goto main
38 end
Lo que hice aquí es restar 255 continuamente de TMR0. Si TMR0 alcanza 255, el
resultado de la instrucción sublw .255 sería cero, estableciendo el bit Z del registro
STATUS. Cuando esto sucede, la variable COUNT, que se inicializa a 15, se
reducirá en 1. PORTB.0 se establecerá cuando la variable COUNT llegue a cero. El
proceso se repite para retrasar la eliminación de PORTB.0.
Aunque el código anterior funciona, todavía le sugiero que use la interrupción de
desbordamiento del temporizador para retrasar las cosas que utilizan menos
espacio en el programa.

¿Qué es una interrupción PIC?


Las interrupciones , desde la propia palabra, son eventos que el microcontrolador
tiene que reconocer, incluso si actualmente está ejecutando algo.
Una analogía de interrupciones puede ser útil. Imagina que estás leyendo un
libro. Cuando suene el teléfono, hará una pausa en la lectura, colocará un marcador
y luego contestará el teléfono. Si se desconoce el número de quien lo llama, usted
le pedirá el nombre de la persona que llama. Luego terminará la conversación
telefónica y volverá a donde dejó de leer.
Algo similar sucede con los microcontroladores e interrupciones: el dispositivo está
ocupado con un proceso principal (leer un libro) cuando se produce una interrupción
(timbre del teléfono). El dispositivo guardará el valor del contador del programa en
la pila (marcará la página), sabrá qué interrupción se activa (identifique a la persona
que llama) y ejecutará la rutina de interrupción (hable con la persona que
llama). Cuando finalice la rutina de interrupción, el dispositivo leerá el contador del
programa de la pila para que pueda volver al punto donde se detuvo en el proceso
principal (recoger el marcador y volver a la lectura).
Si las interrupciones son realmente importantes y no deben ignorarse, las
llamamos interrupciones no enmascarables . Ejemplos de estos son los errores
de la memoria interna, la corrupción de datos y otras cosas muy importantes que
podrían afectar directamente el funcionamiento del
microcontrolador. El PIC16F84A no tiene interrupciones no enmascarables, por lo
que todas sus interrupciones se pueden desactivar o habilitar a través del
código. Discutiré todos los tipos de interrupción para este microcontrolador con
códigos de ejemplo en ensamblaje. Los códigos para CCS C y PBP también se
darán en una sección separada. Aquí hay un resumen de las interrupciones para el
PIC16F84A (haga clic para saltar a la sección especificada):
Las interrupciones se habilitan / inhabilitan usando el registro INTCON (dirección
0x0B o 0x8B):
Para habilitar cualquier interrupción, se debe configurar el bit GIE (bit 7, Habilitación
de interrupción global). Los bits 3 a bit 0 son bits de marca que son bits que se
establecerían cada vez que se dispara una interrupción específica.
La ventaja de usar interrupciones sobre la programación tradicional "lineal" del
microcontrolador es que puede activar eventos casi instantáneamente. Por ejemplo,
si desea leer el estado de un botón en un pin, normalmente haría esto:
1 BTFSS PORTB,0 ;read the state of RB0
2 GOTO sub1 ;if RB0 is clear, go to sub1
3 GOTO sub2 ;if RB0 is set, go to sub2
4 ;some code here
Para que el dispositivo detecte el cambio de estado del pin, la línea 1 debe ser la
línea actual ejecutada. ¡Si el dispositivo ya está en la línea 4, presionar el botón en
RB0 no dispararía nada! Sin embargo, si utiliza la interrupción, no importará qué
línea se está ejecutando actualmente. La interrupción se activará en cualquier
momento siempre que esté habilitada.
Interrupción de pin externo
La interrupción del pin externo se activa cuando hay un "cambio de estado" en el
RB0 / INT (pin # 6 en el PIC16F84A). El cambio de estado significa que el pin pasó
de alto a bajo o de bajo a alto.
Para habilitar esta interrupción, debe configurar el bit INTE (bit 4, Habilitación de
interrupción externa) del registro INTCON. Un programa de ejemplo a continuación
ilustra esto:
1 ; TODO INSERT CONFIG CODE HERE USING CONFIG BITS GENERATOR
2 #INCLUDE
3
4 RES_VECT CODE 0x0000 ; processor reset vector
5 GOTO START ; go to beginning of program
6
7 INT_VECT CODE 0x0004 ; interrupt vector
8 GOTO ISR ; go to interrupt service routine
9
10
11 MAIN_PROG CODE ; let linker place main program
12
13 START
14 BSF STATUS, RP0
15 CLRF TRISA ;set all PORTA as OUTPUT
16 CLRF TRISB ;set all PORTB as OUTPUT
17 BSF TRISB, 0 ;set RB0 as input
18 BCF STATUS, RP0 ;go to bank 0
19 MOVLW b'10010000'
20 MOVWF INTCON ;Global interrupt enabled, External interrupt enabled
21 GOTO MAIN
22
23 ;Interrupt service routine--------------------------------------------------------
24 ISR
25 BCF INTCON, GIE ;Disable all interrupts inside interrupt service routine
26 BCF PORTA,0 ;clear RA.0
27 BCF INTCON,INTF ;Clear external interrupt flag bit
28 BSF INTCON, GIE ;Enable all interrupts on exit
29 GOTO MAIN
30
31 ;Main routine---------------------------------------------------------------------
32 MAIN
33 BSF PORTA,0 ;Set RA.0
34 GOTO MAIN ;Loop
35
36 END
Lo que hace este código es borrar RA.0 siempre que haya un cambio en el estado
de RB.0. Un LED conectado a RA.0, por ejemplo, permanecerá encendido a menos
que se presione el botón en RB.0.
Lo primero que notará es la adición del vector de interrupción:
1 INT_VECT CODE 0x0004
2 GOTO ISR
Esto apunta a la línea donde el código debería saltar cada vez que se desencadena
una interrupción. En este caso, el código salta a la sección con la etiqueta ISR (que
significa Rutina de servicio de interrupción, el nombre formal de la rutina que se
ejecutará cuando ocurra una interrupción, pero puede usar la etiqueta que
desee). Para los dispositivos PIC, el vector de interrupción no es programable (lo
que significa que no se puede cambiar).
Deberá deshabilitar todas las interrupciones dentro del ISR mediante la eliminación
del bit GIE. Esto garantiza que no se active ninguna otra interrupción cuando el
código esté procesando una interrupción. Si eliminas este, ¡el código quedará
atrapado en un bucle sin fin! También debe borrar el bit INTF que se estableció
cuando se activa la interrupción. Al salir de la ISR, no olvide volver a habilitar todas
las interrupciones.
Interruptor de desbordamiento del temporizador
Un desbordamiento del temporizador es otra fuente de interrupción para el
PIC16F84A. Todos los microcontroladores tienen temporizadores en ellos. Se
pueden utilizar para contar eventos e introducir retrasos en el código. Lea sobre el
uso de los temporizadores en el PIC16F84A aquí .
En este artículo , se usó una subrutina de retraso para introducir el tiempo de espera
antes de pasar a la siguiente línea. Sin embargo, esa rutina está limitada en cuanto
al tiempo que desea retrasar. La mejor opción es usar el temporizador incorporado.
El PIC16F84A tiene un temporizador de 8 bits (TMR0) y se encuentra en la
dirección 0x01. Como tiene 8 bits de ancho, solo puede contar de 0 a 255 (2 8 =
256, o incluido). ¿Qué pasa cuando llega a 255? El siguiente conteo sería 0. Esto
es cuando se activará la interrupción por desbordamiento del temporizador.
Cada cuenta del temporizador depende de la velocidad del ciclo de instrucciones. Si,
por ejemplo, se utiliza un cristal de 4 MHz, la velocidad del ciclo de instrucción será:

Esto significa que el temporizador se desbordará después de 255 uS. ¡Eso es


todavía demasiado rápido! Afortunadamente, existe la opción de prescaler. Esto
extenderá el temporizador más allá de su tiempo de conteo predeterminado. El valor
de preescala se establece a través del registro de OPCIÓN :
El PSA (bit 3, Asignación de Prescaler) debe borrarse para habilitar la preescala
para TMR0. Al establecer esto, se asignaría la preescala al temporizador de
vigilancia (que se describe en el artículo de los temporizadores). Es posible que note
el bit INTEDG (bit 6, Selección de borde de interrupción) que le permite elegir entre
cambio de borde ascendente o de borde descendente en el estado en el pin RB0
(descrito en la sección anterior ).
Los bits 2 a 0 son los bits de selección del prescaler:

Dar los bits 2 a 0 le da el valor máximo de preescala de 1: 256. Esto significa que
TMR0 ahora contará cada 256 uS en lugar de cada 1 uS. Eso es 65280 uS antes de
que TMR0 se desbordara.
Mire este código de parpadeo del LED que utiliza la interrupción PIC de
desbordamiento TMR0:
Assembly (x86)
1 ; TODO INSERT CONFIG CODE HERE USING CONFIG BITS GENERATOR
2 #INCLUDE
3
4 RES_VECT CODE 0x0000 ; processor reset vector
5 GOTO START ; go to beginning of program
6
7 INT_VECT CODE 0x0004 ; interrupt vector
8 GOTO ISR ; go to interrupt service routine
9
10
11 MAIN_PROG CODE ; let linker place main program
12
13 START
14 BSF STATUS, RP0
15 CLRF TRISB ;PORTB all output
16 MOVLW b'10100000'
17 MOVWF INTCON ;enable timer-overflow interrupt
18 MOVLW b'00000111'
19 MOVWF OPTION_REG ;instruction clock,falling edge,prescaler set to maximum
20 BCF STATUS, RP0 ;switch to Bank 0
21 CLRF TMR0 ;initialize timer
22 GOTO MAIN
23
24 ;Interrupt service routine--------------------------------------------------------
25 ISR
26 BCF INTCON, GIE ;Disable all interrupts inside interrupt service routine
27 BCF INTCON, T0IF ;disable timer-overflow interrupt flag bit
28 BCF PORTB,0 ;clear RB0
29 BSF INTCON, GIE ;re-enable all interrupts
30 GOTO MAIN
31
32 ;Main routine---------------------------------------------------------------------
33 MAIN
34 BSF PORTB,0 ;Set RB.0
35 GOTO MAIN ;Loop
36
37 END
Cuando el temporizador aún no se ha desbordado, el código se repite dentro de la
rutina principal que establece continuamente el pin RB0. Cuando el temporizador
se desborda y la interrupción se dispara, la rutina isr borra el pin RB0. Esto
efectivamente "parpadea" un LED si está conectado a RB0. Dado que se establece
el valor máximo de preescala, la velocidad de parpadeo sería de aproximadamente
65 mS.
RB cambio de interrupción
También puede adjuntar una interrupción PIC a los pines RB4, RB5, RB6 y RB7 al
igual que con RB0 / INT. La interrupción de cambio de RB se configura mediante el
ajuste RBIE (bit 3, Habilitar interrupción de cambio de RB) del registro
INTCON. Cuando se produce cualquier cambio de estado para cualquiera de los
pines mencionados, se establece el RBIF (bit 0, indicador de interrupción de
cambio de RB) del registro INTCON. Tenga en cuenta que el tiempo que llevó
cambiar el estado debe ser al menos igual al tiempo del ciclo de instrucción.
El ejemplo establece que RA0 lo borra cuando ocurre un cambio de estado en
cualquiera de los pines RB.4 a RB.7.

1 ; TODO INSERT CONFIG CODE HERE USING CONFIG BITS GENERATOR


2 #INCLUDE
3
4 RES_VECT CODE 0x0000 ; processor reset vector
5 GOTO START ; go to beginning of program
6
7 INT_VECT CODE 0x0004 ; interrupt vector
8 GOTO ISR ; go to interrupt service routine
9
10
11 MAIN_PROG CODE ; let linker place main program
12
13 START
14 BSF STATUS, RP0
15 CLRF TRISA ;set all PORTA as OUTPUT
16 MOVLW 0xF0
17 MOVWF TRISB ;buttons attached to RB4 to RB7
18 BCF STATUS, RP0 ;go to bank 0
19 MOVLW b'10001000'
20 MOVWF INTCON ;Global interrupt enabled, RB Change interrupt enabled
21 GOTO MAIN
22
23 ;Interrupt service routine--------------------------------------------------------
24 ISR
25 BCF INTCON, GIE ;Disable all interrupts inside interrupt service routine
26 BCF PORTA,0 ;clear RA.0
27 BCF INTCON,RBIE ;Clear external interrupt flag bit
28 BSF INTCON, GIE ;Enable all interrupts on exit
29 GOTO MAIN
30
31 ;Main routine---------------------------------------------------------------------
32 MAIN
33 BSF PORTA,0 ;Set RA.0
34 GOTO MAIN ;Loop
35
36 END

EEPROM Write Interrupt


Escribir en la ubicación EEPROM del PIC toma alrededor de 10 ms, una larga espera
para los microcontroladores. Esta es prácticamente la razón por la que existe esta
fuente de interrupción PIC: para que el PIC pueda hacer otras cosas útiles mientras
escribe datos en la EEPROM. La configuración de EEIE (bit-6) habilita esta
interrupción y EEIF (bit-4) es el bit de bandera correspondiente.
El código de ejemplo a continuación borra RA0, escribe datos en la EEPROM y
luego establece RA0 cuando se completa la escritura de EEPROM.
1 ; TODO INSERT CONFIG CODE HERE USING CONFIG BITS GENERATOR
2 #INCLUDE
3
4 RES_VECT CODE 0x0000 ; processor reset vector
5 GOTO START ; go to beginning of program
6
7 INT_VECT CODE 0x0004 ; interrupt vector
8 GOTO ISR ; go to interrupt service routine
9
10
11 MAIN_PROG CODE ; let linker place main program
12
13 START
14 BSF STATUS, RP0 ;go to bank 1
15 CLRF TRISA ;set all PORTA as OUTPUT
16 BSF EECON1, WREN ; Enable Write
17 ; this is a required sequence according to the datasheet
18 MOVLW 55h
19 MOVWF EECON2 ; Write 55h
20 MOVLW 0AAh ;
21 MOVWF EECON2 ; Write AAh
22 BSF EECON1,WR ; Set WR bit
23 ; end of required sequence
24 BCF STATUS, RP0 ;go to bank 0
25 BCF PORTA,0 ;clear RA0
26 MOVLW 0FFh ;write 0FFh to EEDATA
27 MOVWF EEDATA
28 MOVLW b'11000000'
29 MOVWF INTCON ;Global interrupt enabled, EEPROM write interrupt enabled
30 GOTO START
31
32 ;Interrupt service routine--------------------------------------------------------
33 ISR
34 BCF INTCON, GIE ;Disable all interrupts inside interrupt service routine
35 BCF INTCON,EEIE ;Disable EEPROM write interrupt bit
36 BSF PORTA,0 ;clear RA.0
37 BSF INTCON,EEIE ;Enable EEPROM write interrupt bit
38 BSF INTCON, GIE ;Enable all interrupts on exit
39 GOTO START
40
41
42 END

Interrupciones en XC8
En XC8, las interrupciones tienen su propio tipo de datos de interrupción. Esto
significa que puede crear ISR como funciones con interrupción como tipo de
retorno. Por ejemplo, esta es la forma en que implementaría el mismo código de
interrupción externo anterior en XC8:
1 #define _XTAL_FREQ 4000000
2 #include <xc.h>
3
4 void main(void) {
5 TRISB0 = 1; //Initialize RB0 as input
6 GIE = 1; //Enable Global Interrupt
7 INTE = 1; //Enable External Interrupt
8
9 while(1){
10 RA0=1; //Set RA0
11 }
12 }
13 //Interrupt Handler. Note you can use other function name as long as interrupt data type is there
14 void interrupt isr(void){
15 if(INTF){ //Check if External Interrupt Flag is set
16 RA0=0; //Clear RA0 on interrupt
17 __delay_ms(100);
18 INTF=0; //External Interrupt Flag must be cleared manually
19 }
20 }
Como puede ver, es necesario verificar el indicador de interrupción externo INTF
para verificar si esa es realmente la interrupción que se activó porque cualquier
interrupción puede activar la función isr ().
La interrupción del temporizador se codificaría así:
1 #define _XTAL_FREQ 4000000
2 #include <xc.h>
3
4 void main(void) {
5 TRISB = 0; // Initialize PORTB as all output
6 GIE = 1; // Enable Global Interrupt
7 T0IE = 1; // Enable Timer Interrupt
8 T0CS = 0; // Timer Clock Source is internal clock
9 PS = 7; // Maximum Prescale
10
11 while(1){
12 RB0=1; //Set RB0
13 }
14 }
15 //Interrupt Handler. Note you can use other function name as long as interrupt data type is there
16 void interrupt isr(void){
17 if(T0IF){ // Check if Timer Interrupt Flag is set
18 RB0 = 0; // Clear RB0 on interrupt
19 T0IF = 0; // Timer Interrupt Flag must be cleared manually
20 }
21 }
¿Cómo codificaría el cambio de RB y las interrupciones de escritura de EEPROM
en XC8?

S-ar putea să vă placă și