Sei sulla pagina 1di 25

1 Manejo de Interrupciones y temporizadores

1.1 Introducción
Con este documento se pretende introducir al lector en el manejo de las interrupciones y
los temporizadores en la plataforma ENT2004CF. Este documento esta estructurado en 3
grandes partes:
En primer lugar se aborda el estudio de los temporizadores y su gestión para generar
interrupciones periódicas en tiempo real, que son las utilizadas para llevar un control del
tiempo en los sistemas.
En segundo lugar realizaremos un ejemplo en el que se captura una interrupción
externa producida por un flanco de subida en uno de los terminales de entrada de
interrupción.
En tercer lugar programaremos que se produzca una interrupción interna cuando se
cumpla un tiempo establecido (fin de temporización) posterior a la detección de un
flanco de subida en una entrada de captura de entrada.
En los tres casos abordaremos el desarrollo del tutorial centrándonos en un programa
desarrollado en ensamblador al que le faltan ciertas partes de algunas instrucciones, que
debemos ir completando a medida que avancemos por el tutorial.
1.2 Tutorial para el manejo de temporizadores e interrupciones en tiempo
real

Figura 1. Ejemplo de ejecución del programa InterrTimer.asm

En esta parte vamos a desarrollar un programa que, tras escribir una frase de comienzo
(“COMIENZA EL PROGRAMA”), va escribiendo letra a letra el mensaje “CADA SEGUNDO
ESCRIBO UNA LETRA”. Entre escritura de letras consecutivas, se realiza una pausa de 1
segundo aproximadamente. Este tiempo de espera se configura mediante un temporizador
y una interrupción asociada a dicho temporizador. En la Figura 1 vemos un ejemplo de la
ejecución del programa.
Comenzará el tutorial realizando una copia del código fuente del ejemplo (InterrTimer.asm),
en su directorio de trabajo. Esta copia será la que utilice a lo largo del tutorial, para ir
modificando las instrucciones incompletas. A continuación, abra dicho fichero con el editor
de textos.
1.2.1 Estructura del programa InterrTimer.asm

Observará que este fichero está estructurado en 4 zonas principales:


Una primera zona en la que se definen las constantes que vamos a utilizar a lo largo
del programa (DEFINICIÓN DE CONSTANTES). Entre las constantes definidas se
encuentran las direcciones de memoria donde están los registros que nos van a
permitir configurar tanto el temporizador como la interrupción interna generada
(MCFSIM_*). Además de estas direcciones de memoria, aparecen las constantes
SISTEMA y OUT que contienen respectivamente los valores del número de la TRAP
del sistema (15 en nuestro caso), y el código asociado con la salida de un carácter en
la pantalla del ordenador ($13). A continuación, tenemos las constantes asociadas a la
dirección de comienzo del programa y de las variables, y las asociadas a los vectores
de interrupción. Un detalle importante que hay que tener siempre en cuenta es que la
definición de constantes no ocupa ningún espacio en memoria por ser una directiva del
ensamblador.
La segunda zona (DEFINICIÓN DE VARIABLES) contiene las posiciones de memoria
donde vamos a guardar cierta información a lo largo del programa y que actúan como
variables. Esta zona está establecida a partir de la posición $30000 en memoria (ORG
DIR_VARIABLES).
La tercera parte lo constituye el propio programa en sí (COMIENZO DEL
PROGRAMA). Esta zona comienza a partir de la posición DIR_PROGRAMA ($20000)
en memoria. Como se puede observar, este programa siempre comienza en la
etiqueta PPAL y debe terminar en un bucle infinito que se queda esperando a que se
produzca alguna interrupción externa. En nuestro caso, el bucle está comprendido
entre la etiqueta BUCLE y la instrucción BRA BUCLE. Todos los programas que
desarrollaremos deben terminar en un bucle infinito o bien ser ellos mismo un bucle
infinito. De no ser así el contador de programa PC seguiría avanzando hasta salirse de
la zona de memoria donde está alojado el programa, produciendo un error de
ejecución.
La primera acción que realizamos al comienzo del programa es situar el puntero de la
pila en la posición DIR_PILA ($30000). Esta dirección es la posición de comienzo de la
zona de variables pero no habrá ningún problema puesto que el acceso a la pila se
hace siempre con PREDECREMENTO (se decrementa el puntero antes de guardar el
dato en la pila) con lo que se comenzará a almacenar a partir de la posición $2FFFF
hacia abajo.
A continuación del programa se encuentran todas las subrutinas que serán llamadas
desde el programa principal (DEFINICIÓN DE SUBRUTINAS). En los apartados
siguientes iremos viendo estas subrutinas.
1.2.2 Funcionamiento general del programa InterrTimer.asm

El programa principal (identificado mediante la etiqueta PPAL), tras inicializar el puntero de


la pila, salta a la subrutina INIT donde se realizan las tareas de inicialización y configuración
principales que veremos más adelante. Estas tareas permitirán configurar el temporizador 0
(TIMER0) para que genere una interrupción cada 1 ms. Posteriormente pasamos a
ejecutar un BUCLE infinito en el que se va imprimiendo en pantalla cada una de las letras
del mensaje (CADA SEGUNDO ESCRIBO UNA LETRA). Una vez completado el mensaje
comienza otra vez desde el principio.
El tiempo de espera de un segundo entre letra y letra se implementa mediante la subrutina
RETARDO que retiene la ejecución del programa principal un número de interrupciones
definido por la variable CONT_RETARDO. En nuestro caso, para esperar 1 segundo, al
tener interrupciones cada 1 ms, debemos esperar 1000 interrupciones.
Las subrutinas PRINTC y PRINTCAD permiten escribir en pantalla un carácter o una
cadena de caracteres respectivamente. La subrutina PRINTCAD (llamada desde la
subrutina INIT) escribe en pantalla el mensaje: (COMIENZA EL PROGRAMA).
La subrutina SUB_TIMER0 es la rutina de atención a la interrupción periódica. Tras la
programación que hemos realizado del temporizador 0 del ColdFire, sabemos que se va a
ejecutar cada 1 ms, momento en el que se produce una interrupción del temporizador. Esta
subrutina se encarga de ir decrementando la variable CONT_RETARDO utilizada por la
subrutina RETARDO (en el caso de que esta variable sea distinta de cero). Además,
realiza otras acciones relacionadas con la gestión del temporizador y de las interrupciones
asociadas que iremos viendo a lo largo del tutorial.
Esta estrategia de asociar una interrupción periódica a un contador es la más habitual en
los sistemas digitales. El objetivo fundamental es que con una sola interrupción podamos
medir la duración de distintos eventos en el programa principal. En este ejemplo, vamos a
medir un tiempo de un segundo utilizando interrupciones cada 1 ms, pero en programas
más complejos podemos seguir fácilmente la misma estrategia para medir varios eventos,
incluso en paralelo.
1.2.3 Configuración del temporizador y de las interrupciones.

En el manejo del temporizador 0 y de la interrupción interna asociada, están involucrados


los siguientes registros :
PIVR: registro con el valor del comienzo de los vectores de interrupción. Una vez fijado
este valor, el vector de interrupción correspondiente al temporizador 0 es el situado 5
vectores de interrupción más adelante.
7 6 5 0
PIVR IV - Vector de
interrupción base

Figura 2. Registro PIVR.

ICR1: registro de control de interrupciones. Para el caso del temporizador 0, los bits de
control son 12, 13 y 14 para fijar el nivel de prioridad, y 15 es el bit que indica si hay
interrupción pendiente. Para cambiar el nivel de prioridad, además de escribir en los
bits 12, 13 y 14, hay que escribir un 1 en el bit 15.
31 30 28 27 26 24 23 22 20 19 18 16 15 14 12 11 10 8 7 6 4 3 2 0
INT1 INT1 INT2 INT2 INT3 INT3 INT4 INT4 TMR0 TMR0 TMR1 TMR1 TMR2 TMR2 TMR3 TMR3 xPI: interrupción pendiente (1) o no (0)
ICR1 xIPL: nivel de prioridad de interrupción
PI IPL PI IPL PI IPL PI IPL PI IPL PI IPL PI IPL PI IPL

Figura 3. Registro ICR1.

TMR0: registro de control del temporizador 0, que permite configurar o determinar la


funcionalidad completa de dicho temporizador.
15 8 7 6 5 4 3 2 1 0
PS CE OM ORI FRR CLK RST Control
TMRn (Modo)
00: detiene contador
RST: reinicia (0) ó habilita (1) el temporizador 01: reloj del sistema
CLK: fuente de reloj 10: reloj del sistema / 16
FRR: modo continuo (0) o con reinicio (1) 11: reloj externo (TINn)
ORI: habilita (1) interrupciones en comparación
OM: pulso (0) o conmuta salida (1) 00: captura inhabilitada
CE: captura de flanco e interrupciones 01: flanco subida + interrupción
PS: escalado -1 10: flanco bajada + interrupción
11: ambos flancos + interrupción

Figura 4. Registro TMR0.

TRR0: registro que contiene el valor final del contador que indica el final de la cuenta, y
por tanto, el vencimiento del tiempo programado.
TCN0: registro que actúa sobre el contador permitiendo inicializarlo a 0.
15 0
TCNn Contador
TCAPn Captura
TRRn Referencia

Figura 5. Registro TCN0.

TER0: registro de estado. Informa de la captura de un flanco de entrada mediante el bit


0 (CAP) e indica si el contador ha llegado al valor final de cuenta, poniendo a 1 el bit 1
(REF). Para borrarlos hay que escribir un 1 en el REF o en CAP (según corresponda)
CAP: evento de captura
REF: evento de referencia
15 1 0
TERn - REF CAP Estado

Figura 6. Registro TER0.

SR: registro de estado del microcontrolador donde se habilitan las interrupciones del
sistema.
C: acarreo (C=1)
V: desbordamiento (V=1)
CCR Z: cero (Z=1)
N: negativo (N=1)
X: extensión (X=1)
15 13 12 10 9 8 4 3 2 1 0 I2-I0: máscara interrup.
M: maestro (M=1)
SR T - S M - I2I1I0 - - X N Z V C S: supervisor (S=1)
T: traza (T=1)

Figura 7. Registro SR.

Para consultar más detalles relativos a la utilización de estos registros, se recomienda la


consulta del libro de SEDG.
Al comienzo del programa se definen unas constantes que reflejan las posiciones de
memoria a las que están asociados dichos registros (MCFSIM_*).
Para hacer funcionar el programa debemos ir completando algunas instrucciones que
tienen algunos de sus operandos como incógnitas.
La configuración del temporizador 0 y de la interrupción asociada se realiza en la subrutina
INIT. Esta subrutina está formada por los siguientes pasos:
1. En primer lugar debemos situar el comienzo de los vectores de interrupción. Para ello
debemos guardar en la dirección del registro PIVR (MCFSIM_PIVR) un valor de inicio.
Típicamente seleccionaremos el primer valor válido, en nuestro caso un 64 ($40). En
realidad, no sería necesario por ser el valor predeterminado.
2. Considerando este hecho, el vector de interrupción asociado a la interrupción producida
por el temporizador 0 es el situado 5 vectores de interrupción más adelante. Introduzca
el valor que corresponde a AAAA? asociado al vector de interrupción del temporizador
0.
3. Para definir la posición de memoria donde debemos guardar la dirección de comienzo
de la subrutina que atenderá la interrupción, debemos considerar que cada vector de
interrupción ocupa 4 bytes. No podemos olvidar tampoco que el valor predeterminado
del registro VBR es $0, por lo que si no lo modificamos en el programa -que no lo
hacemos- el número de vector de interrupción lo debemos multiplicar por 4 para saber
la dirección donde está situado; es lo que hacemos en la definición de la constante
DIR_TIMER0. A continuación, procedemos a guardar en esa dirección la dirección de
comienzo de la subrutina de atención a la interrupción. Complete la dirección que
debemos guardar (BBB_BBBBBB?).
4. A continuación se imprime el mensaje “COMIENZA EL PROGRAMA” mediante la
llamada a la función PRINTCAD.
5. El siguiente paso es configurar el registro TMR0 del temporizador con el valor $4F3D.
Mediante esta configuración:
Consideramos una señal de reloj para el contador del propio reloj del sistema
dividido por 16.
Modo reinicio: para que cuando el contador llegue al valor máximo de la cuenta se
reinicie automáticamente.
Habilitamos la aparición de una interrupción interna cuando el contador llegue al
valor máximo especificado.
Se fija un valor de preescalado de 80 o $50 en hexadecimal (PS=$4F).
Conviene consultar la descripción de este registro que aparece al principio del
apartado para entender mejor la configuración realizada.
Habilitamos el funcionamiento del temporizador.
6. A continuación inicializamos a 0 el contador del temporizador (TCN0).
7. Seguidamente debemos fijar el valor máximo de la cuenta (CCCCC?) en la instrucción
con el comentario “FIJAMOS EL VALOR DE FIN DE CUENTA DEL CONTADOR” para
configurar un tiempo de 1 ms. Este valor debe tener en cuenta el reloj del contador
(reloj del sistema dividido por 16) y el valor de preescalado definido (80 o $50 debido a
PS=$4F).
8. Finalmente nos queda marcar como no pendiente y darle una prioridad de, por ejemplo,
nivel 4 a la interrupción del temporizador 0 en el registro ICR1. Conviene observar que
para marcar como no pendiente la interrupción asociada al temporizador 0 debemos
poner a 0 el bit 15 del registro ICR1. Este bit se pone a 0 escribiendo un 1. A
continuación habilitamos las interrupciones en el registro de estado del
microcontrolador (SR).
Antes de terminar la configuración del temporizador 0 debemos introducir un par de
instrucciones en la subrutina de atención a la interrupción que nos permitan reiniciar el bit
REF del registro TER0. Este bit se pone a uno cuando el contador ha llegado al final de la
cuenta y tenemos que ponerlo a 0 escribiendo un uno para que el temporizador pueda
seguir generando interrupciones.
En este punto debemos comentar que no es necesario inicializar el contador puesto que lo
configuramos en modo reinicio. De hecho, si lo hiciéramos, afectaríamos negativamente a
la temporización, dado que para cuando se ejecutara la puesta a cero del contador ya
habrían transcurrido unos ciclos de reloj, lo que modificaría las cuentas. Tampoco hace
falta volver a marcar la interrupción como no pendiente en el registro ICR1 (bit 15) puesto
que la interrupción producida por un temporizador es interna y esta labor la realiza el
ColdFire de manera automática.
Una vez completadas todas las instrucciones, ensamble, cargue y ejecute el programa.
Observará como después de imprimir un mensaje inicial en la ventana terminal, comienza
a imprimir un nuevo mensaje letra a letra de manera repetitiva.
Al final de este tutorial se muestra el código del programa “InterrTimer.asm” y en la Tabla 1,
apartado 2.1) se muestran las soluciones a las preguntas formuladas a lo largo de este
tutorial.
1.3 Tutorial para el manejo de una interrupción externa
En esta segunda parte vamos a desarrollar un programa que tras escribir una frase de
inicio (COMIENZA EL PROGRAMA), se queda esperando a que se produzca un flanco de
subida en la entrada definida como INT1 (véase la descripción de la plataforma de
desarrollo en el capítulo 3 de este manual). Cuando se produce un flanco de subida en este
terminal el programa genera el siguiente mensaje: HE DETECTADO UN FLANCO DE
SUBIDA. En la siguiente figura vemos un ejemplo de ejecución del programa.

Figura 8. Ejemplo de ejecución del programa InterrExterna.asm.

Al igual que en la primera parte debemos comenzar realizando una copia del código fuente
del ejemplo (InterrExterna.asm), en el directorio de trabajo. Esta copia será la que utilice a
lo largo del tutorial, para ir modificando las instrucciones incompletas. A continuación, abra
dicho fichero con el editor de texto.
1.3.1 Estructura del programa InterrExterna.asm

De igual forma al caso anterior, el programa contenido en este fichero está estructurado en
4 zonas principales:
Una primera zona en la que se definen las constantes que vamos a utilizar a lo largo
del programa (DEFINICIÓN DE CONSTANTES). Entre las constantes definidas se
encuentran las direcciones de memoria donde están los registros que nos van a
permitir configurar la interrupción externa INT1. Además de estas direcciones de
memoria, aparecen las constantes SISTEMA y OUT que contienen respectivamente
los valores del número de la TRAP del sistema (15 en nuestro caso), y el código
asociado con la salida de un carácter en la pantalla del ordenador. A continuación,
tenemos las constantes asociadas a la dirección de comienzo del programa y de las
variables, y las asociadas a los vectores de interrupción.
La segunda zona (DEFINICIÓN DE VARIABLES) contiene las posiciones de memoria
donde vamos a guardar cierta información a lo largo del programa y que actúan como
variables. Esta zona está establecida a partir de la posición DIR_VARIABLES ($30000)
en memoria.
La tercera parte lo constituye el propio programa en sí (COMIENZO DEL
PROGRAMA). Esta zona comienza a partir de la posición DIR_PROGRAMA ($20000)
en memoria. Como se puede observar este programa siempre comienza en la etiqueta
PPAL y debe terminar en un bucle infinito que se queda esperando a que se produzca
alguna interrupción externa. En nuestro caso, el bucle está comprendido entre la
etiqueta BUCLE y la instrucción BRA BUCLE. Todos los programas que
desarrollaremos deben terminar en un bucle infinito o bien ser ellos mismo un bucle
infinito. De no ser así el contador de programa PC seguiría avanzando hasta salirse de
la zona de memoria donde está alojado el programa produciendo un error de ejecución.
La primera acción que realizamos al comienzo del programa es situar el puntero de la
pila en la posición DIR_PILA ($30000). Esta dirección es la posición de comienzo de la
zona de variables pero no habrá ningún problema puesto que el acceso a la pila se
hace siempre con PREDECREMENTO (se decrementa el puntero antes de guardar el
dato en la pila) con lo que se comenzará a almacenar a partir de la posición $2FFFF
hacia abajo.
A continuación del programa se encuentran todas las subrutinas que serán llamadas
desde el programa principal (DEFINICIÓN DE SUBRUTINAS) y que veremos en los
apartados siguientes.
1.3.2 Funcionamiento general del programa InterrExterna.asm

El programa principal (identificado mediante la etiqueta PPAL), tras inicializar el puntero de


la pila, salta a la subrutina INIT donde se realizan las tareas de inicialización y configuración
principales que veremos más adelante. Estas tareas permitirán configurar la interrupción
externa que se producirá cuando aparezca un flanco de subida en la entrada INT1.
Adicionalmente a la subrutina INIT existen otras dos subrutinas:
PRINTCAD: que presenta una cadena de caracteres en pantalla. Esta subrutina toma
la dirección de comienzo del buffer del registro A0 y la dirección final la toma de A1.
SUB_INT1: subrutina de atención a la interrupción. Esta subrutina se ejecuta cuando se
produce una interrupción externa. Se encarga de presentar en pantalla el mensaje: “HE
DETECTADO UN FLANCO DE SUBIDA”. Además se encarga de marcar la
interrupción como no pendiente para permitir que se vuelva a producir otra interrupción.
1.3.3 Configuración de la interrupción externa INT1

En el manejo de la interrupción externa INT1 están involucrados los siguientes registros:


PIVR: registro con el valor del comienzo de los vectores de interrupción. Una vez fijado
este valor, el vector de interrupción correspondiente a INT1 es el situado una posición
más adelante.
7 6 5 0
PIVR IV - Vector de
interrupción base

Figura 9. Registro PIVR.

PITR: registro de flanco de interrupción programable. En este registro se programa el


tipo de flanco con el que se producirá la interrupción: 1 para flanco de subida y 0 para
flanco de bajada.
INTn: sensible a flanco de subida (1) o bajada (0)
31 30 29 28 6 5 0
Flanco de
PITR INT1 INT2 INT3 INT4 - INT5 INT6 - interrupción

Figura 10. Registro PITR.

ICR1: registro de control de interrupciones. Para el caso de INT1, los bits de control
son: 28, 29 y 30 para fijar el nivel de prioridad y el 31 es el bit que indica si hay
interrupción pendiente.
31 30 28 27 26 24 23 22 20 19 18 16 15 14 12 11 10 8 7 6 4 3 2 0
INT1 INT1 INT2 INT2 INT3 INT3 INT4 INT4 TMR0 TMR0 TMR1 TMR1 TMR2 TMR2 TMR3 TMR3 xPI: interrupción pendiente (1) o no (0)
ICR1 xIPL: nivel de prioridad de interrupción
PI IPL PI IPL PI IPL PI IPL PI IPL PI IPL PI IPL PI IPL

Figura 11. Registro ICR1.

SR: registro de estado del microcontrolador donde se habilitan las interrupciones del
sistema.
C: acarreo (C=1)
V: desbordamiento (V=1)
CCR Z: cero (Z=1)
N: negativo (N=1)
X: extensión (X=1)
15 13 12 10 9 8 4 3 2 1 0 I2-I0: máscara interrup.
M: maestro (M=1)
SR T - S M - I2I1I0 - - X N Z V C S: supervisor (S=1)
T: traza (T=1)

Figura 12. Registro SR.

Para consultar más detalles relativos a la utilización de estos registros, se recomienda la


consulta de libro de SEDG en el tema correspondiente.
Al comienzo del programa se definen unas constantes que reflejan las posiciones de
memoria a las que están asociados dichos registros.
Para hacer funcionar el programa debemos ir completando algunas instrucciones que
tienen algunos de sus operandos como incógnitas.
La configuración de la interrupción externa INT1 se realiza en la subrutina INIT. Esta
subrutina está formada por los siguientes pasos:
1. En primer lugar debemos situar el comienzo de los vectores de interrupción. Para ello
debemos guardar en la dirección del registro PIVR (MCFSIM_PIVR) un valor de inicio.
Típicamente seleccionaremos el primer valor válido. Complete el valor de comienzo de
los vectores de interrupción (DDD?). Para ello, consulte la tabla de vectores de
interrupción.
2. Considerando este hecho, el vector de interrupción asociado a la interrupción externa
INT1 es el vector situado una posición más adelante del comienzo de los vectores, de
ahí la definición de DIR_INT1.
3. Para definir la posición de memoria donde debemos guardar la dirección de comienzo
de la subrutina que atenderá la interrupción, debemos considerar que cada vector de
interrupción ocupa 4 bytes. No podemos olvidar tampoco que el valor predeterminado
del registro VBR es $0, por lo que si no lo modificamos en el programa -que no lo
hacemos- el número de vector de interrupción lo debemos multiplicar por 4 para saber
la dirección donde está situado; es lo que hacemos en la definición de la constante
DIR_INT1. A continuación, procedemos a guardar en esa dirección la dirección de
comienzo de la subrutina de atención a la interrupción, en nuestro caso SUB_INT1.
4. A continuación se imprime el mensaje “COMIENZA EL PROGRAMA” mediante la
llamada a la función PRINTCAD.
5. Para configurar el tipo de flanco que producirá la interrupción (1 para subida y 0 para
bajada), debemos configurar el bit correspondiente del registro PITR. Para el caso de
INT1 debemos poner a 1 (flanco de subida) el bit 31 de este registro.
6. En este momento nos falta marcar como no pendiente y asignar una prioridad de, por
ejemplo, nivel 3 a la interrupción externa INT1. Conviene observar que para marcar
como no pendiente esta interrupción debemos poner a 0 el bit 31 del registro ICR1.
Este bit se pone a 0 escribiendo un 1. En los bits 30, 29 y 28 debemos configurar el
nivel de prioridad de dicha interrupción. Complete el valor que debemos cargar en el
registro ICR1 (EEEEEEEEE?). Tenga en cuenta también el valor que debe asignar a
los otros registros de interrupción.
7. Finalmente habilitamos las interrupciones en el registro de estado del microcontrolador
(SR).
Antes de terminar la configuración de la interrupción INT1 debemos introducir un par de
instrucciones en la subrutina de atención a la interrupción (SUB_INT1) que nos permitan
volver a marcar la interrupción como no pendiente en el registro ICR1 (hay que escribir un 1
para que se borre), puesto que la interrupción INT1 es externa y no se hace
automáticamente. Una vez completadas todas las instrucciones, ensamble, cargue y
ejecute el programa. Observará cómo después de imprimir un mensaje inicial en la ventana
terminal, el programa se queda esperando un flanco de subida por la entrada marcada
como INT1 (ver descripción de la plataforma ENT2004CF en libro del entorno). Puede
producir un flanco de subida de manera artificial mediante la fuente de alimentación:
inicialmente conecte el terminal a masa y a continuación a 5V. Observará que este flanco
de subida produce una interrupción y que el programa escribe el mensaje: “HE
DETECTADO UN FLANCO DE SUBIDA”. Así mismo, sea consciente de que es probable
que se produzcan rebotes en dicha señal, lo que puede producir varias peticiones de
interrupción consecutivas. Una manera sencilla de hacer frente al problema es reiniciar el
flag de estado correspondiente al final de la rutina de atención a la interrupción para dar
tiempo a la señal a estabilizarse. Al final de este tutorial se muestra el código del programa
“InterrExterna.asm” y en el apartado 2.1 se muestran las soluciones a las preguntas
formuladas a lo largo de este tutorial.
1.4 Tutorial para el manejo de una interrupción por fin de temporización
En esta tercera parte vamos a combinar la utilización de la función de captura de entrada
con la de comparación de salida. Vamos a modificar el programa del tutorial anterior para
que el mensaje “HE DETECTADO UN FLANCO DE SUBIDA” aparezca al cabo de 2
segundos desde que se produce el flanco de subida. Además, vamos a conectar la señal
de entrada a la entrada TIN0 (correspondiente al temporizador 0) del ColdFire en lugar de
conectarla a la entrada INT1. Utilizaremos la funcionalidad de fin de temporización
mediante el temporizador 1 (aunque también se podría utilizar el mismo temporizador 0 si
se deseara).
Al igual que en los casos anteriores se debe comenzar realizando una copia del código
fuente del ejemplo (FinTemp.asm), en su directorio de trabajo, por ejemplo. Esta copia será
la que utilice a lo largo del tutorial, para ir modificando las instrucciones incompletas. A
continuación, abra dicho fichero con el editor de texto.
1.4.1 Estructura del programa FinTemp.asm

De igual forma al caso anterior, el programa contenido en este fichero está estructurado en
4 zonas principales:
Una primera zona en la que se definen las constantes que vamos a utilizar a lo largo
del programa (DEFINICIÓN DE CONSTANTES). Entre las constantes definidas se
encuentran las direcciones de memoria donde están los registros que nos van a
permitir configurar tanto los dos temporizadores como la interrupción interna generada
(MCFSIM_*). Además de estas direcciones de memoria, aparecen las constantes
SISTEMA y OUT que contienen respectivamente el valor del número de la TRAP del
sistema (15 en nuestro caso) y el código asociado con la salida de un carácter en la
pantalla del ordenador. A continuación, tenemos las constantes asociadas a la
dirección de comienzo del programa y de las variables, y las asociadas a los vectores
de interrupción.
La segunda zona (DEFINICIÓN DE VARIABLES) contiene las posiciones de memoria
donde vamos a guardar cierta información a lo largo del programa y que actúan como
variables. Esta zona está definida a partir de la posición DIR_VARIABLES ($30000) en
memoria.
La tercera parte lo constituye el propio programa en sí (COMIENZO DEL
PROGRAMA). Esta zona comienza a partir de la posición DIR_PROGRAMA ($20000)
en memoria. Como se puede observar este programa siempre comienza en la etiqueta
PPAL y debe terminar en un bucle infinito que se queda esperando a que se produzca
alguna interrupción externa. En nuestro caso, el bucle está comprendido entre la
etiqueta BUCLE y la instrucción BRA BUCLE. Todos los programas que
desarrollaremos deben terminar en un bucle infinito o bien ser ellos mismo un bucle
infinito. De no ser así, el contador de programa PC seguiría avanzando hasta salirse de
la zona de memoria donde está alojado el programa produciendo un error de ejecución.
La primera acción que realizamos al comienzo del programa es situar el puntero de la
pila en la posición DIR_PILA ($30000). Esta dirección es la posición de comienzo de la
zona de variables pero no habrá ningún problema puesto que el acceso a la pila se
hace siempre con PREDECREMENTO (se decrementa el puntero antes de guardar el
dato en la pila) con lo que se comenzará a almacenar a partir de la posición $2FFFF
hacia abajo.
A continuación del programa se encuentran todas las subrutinas que serán llamadas
desde el programa principal (DEFINICIÓN DE SUBRUTINAS) y que veremos en los
apartados siguientes.
1.4.2 Funcionamiento general del programa FinTemp.asm

El programa principal (identificado mediante la etiqueta PPAL), tras inicializar el puntero de


la pila, salta a la subrutina INIT donde se realizan las tareas de inicialización y configuración
principales que veremos más adelante. Estas tareas permitirán configurar las
interrupciones que se producirán cuando aparezca un flanco de subida en la entrada TIN0
y cuando termine la cuenta del temporizador 1.
Posteriormente pasamos a ejecutar un BUCLE infinito que se queda esperando a que se
produzca el suceso de captura de entrada (flanco de subida en TIN0). Al salir del bucle, se
salta a la subrutina REINIT donde se pone en marcha el contador del temporizador 1, que
generará una interrupción a los dos segundos. A continuación, se entra en un nuevo bucle,
en el que se espera a que CAP_RECIBIDA vuelva a valer 0, lo que se produce dentro de la
rutina de atención a la interrupción SUB_TIMER1 (a los dos segundos).
Además de las subrutinas INIT y REINIT existen otras tres subrutinas:
PRINTCAD: que presenta una cadena de caracteres en pantalla. Esta subrutina coge
la dirección de comienzo del buffer del registro A0 y la dirección de final de A1.
SUB_TIMER0: subrutina de atención a la interrupción del temporizador 0. Esta
subrutina se ejecuta cuando se produce un flanco de subida en la entrada de captura
de entrada TIN0. Lo único que hace es activar el flag CAP_RECIBIDA para que el
programa principal ponga en marcha el segundo temporizador. Además se encarga de
marcar la interrupción como no pendiente para permitir que se vuelva a producir otra
interrupción.
SUB_TIMER1: subrutina de atención a la interrupción del temporizador 1. Esta
subrutina se ejecuta cuando termina la cuenta del temporizador que equivale a 2
segundos. Se encarga de presentar en pantalla el mensaje: “HE DETECTADO UN
FLANCO DE SUBIDA”. Además se encarga de marcar la interrupción como no
pendiente para permitir que se vuelva a producir otra interrupción.
1.4.3 Configuración de las interrupciones de los temporizadores

En el manejo de las interrupciones de los temporizadores están involucrados los siguientes


registros:
PIVR: registro con el valor del comienzo de los vectores de interrupción. Una vez fijado
este valor, el vector de interrupción correspondiente a INT1 es el situado una posición
más adelante.
7 6 5 0
PIVR IV - Vector de
interrupción base

Figura 13. Registro PIVR.

ICR1: registro de control de interrupciones. Para el caso del temporizador 0, los bits de
control son 12, 13 y 14 para fijar el nivel de prioridad, y 15 es el bit que indica si hay
interrupción pendiente. Para el temporizador 1, los bits de control son 8, 9 y 10 para fijar
el nivel de prioridad, y 11 es el bit que indica si hay interrupción pendiente.
31 30 28 27 26 24 23 22 20 19 18 16 15 14 12 11 10 8 7 6 4 3 2 0
INT1 INT1 INT2 INT2 INT3 INT3 INT4 INT4 TMR0 TMR0 TMR1 TMR1 TMR2 TMR2 TMR3 TMR3 xPI: interrupción pendiente (1) o no (0)
ICR1 xIPL: nivel de prioridad de interrupción
PI IPL PI IPL PI IPL PI IPL PI IPL PI IPL PI IPL PI IPL

Figura 14. Registro ICR1.

TMR0/TMR1: registros de control de los temporizadores 0 y 1, que permiten configurar


o determinar la funcionalidad completa de dichos temporizadores. En el campo PS hay
que escribir el escalado deseado menos uno.
15 8 7 6 5 4 3 2 1 0
PS CE OM ORI FRR CLK RST Control
TMRn (Modo)
00: detiene contador
RST: reinicia (0) ó habilita (1) el temporizador 01: reloj del sistema
CLK: fuente de reloj 10: reloj del sistema / 16
FRR: modo continuo (0) o con reinicio (1) 11: reloj externo (TINn)
ORI: habilita (1) interrupciones en comparación
OM: pulso (0) o conmuta salida (1) 00: captura inhabilitada
CE: captura de flanco e interrupciones 01: flanco subida + interrupción
PS: escalado -1 10: flanco bajada + interrupción
11: ambos flancos + interrupción

Figura 15. Registros TMR0/TMR1.

TRR0/TRR1: registros que contienen el valor final del contador que indica el final de la
cuenta, y por tanto, el vencimiento del tiempo programado.
TCN0/TCN1: registros de los contadores, que permiten leerlos (sin detener su cuenta) y
permiten su inicialización a 0.
15 0
TCNn Contador
TCAPn Captura
TRRn Referencia

Figura 16. Registros TCN0/TCN1.

TER0/TER1: registros de estado. Informan de la captura de un flanco de entrada


mediante el bit 0 (CAP) e indica si el contador ha llegado al valor final de cuenta,
poniendo a 1 el bit 1 (REF).
CAP: evento de captura
REF: evento de referencia
15 1 0
TERn - REF CAP Estado

Figura 17. Registros TER0/TER1.

SR: registro de estado del microcontrolador donde se habilitan las interrupciones del
sistema.
C: acarreo (C=1)
V: desbordamiento (V=1)
CCR Z: cero (Z=1)
N: negativo (N=1)
X : extensión (X=1)
15 13 12 10 9 8 4 3 2 1 0 I2-I0: máscara interrup.
M: maestro (M=1)
SR T - S M - I2I1I0 - - X N Z V C S: supervisor (S=1)
T: traza (T=1)

Figura 18. Registro SR.

Para consultar más detalles relativos a la utilización de estos registros, se recomienda la


consulta del libro de SEDG.
Al comienzo del programa se definen unas constantes que reflejan las posiciones de
memoria a las que están asociados dichos registros.
Para hacer funcionar el programa debemos ir completando algunas instrucciones que
tienen algunos de sus operandos como incógnitas.
La configuración de las interrupciones de los temporizadores se realiza en la subrutina
INIT. Esta subrutina está formada por los siguientes pasos:
1. En primer lugar debemos situar el comienzo de los vectores de interrupción. Para ello
debemos guardar en la dirección del registro PIVR (MCFSIM_PIVR) un valor de inicio.
Típicamente seleccionaremos el primer valor válido, en nuestro caso un 64 ($40). En
realidad, no sería necesario por ser el valor predeterminado.
2. Considerando este hecho, el vector de interrupción asociado a la interrupción producida
por el temporizador 0 es el situado 5 vectores de interrupción más adelante y el del
temporizador 1 es el situado 6 vectores de interrupción más adelante. Por lo tanto, son
el $45 y el $46 respectivamente.
3. Para definir la posición de memoria donde debemos guardar la dirección de comienzo
de la subrutina que atenderá la interrupción, debemos considerar que cada vector de
interrupción ocupa 4 bytes. No podemos olvidar tampoco que el valor predeterminado
del registro VBR es $0, por lo que si no lo modificamos en el programa -que no lo
hacemos- conocido el número de vector de interrupción lo debemos multiplicar por 4
para saber la dirección donde está situado; es lo que hacemos en la definición de las
constantes DIR_TIMER0 y DIR_TIMER1. A continuación, procedemos a guardar en
esas direcciones la dirección de comienzo de la subrutina de atención a la interrupción.
Complete las direcciones que debemos guardar (FFF_FFFFFF? y GGG_GGGGGG?).
4. A continuación se imprime el mensaje “COMIENZA EL PROGRAMA” mediante la
llamada a la función PRINTCAD.
5. El siguiente paso es configurar el registro TMR0 del temporizador (para captura de
entrada) con el valor $4F45. El cual se obtiene mediante aplicando la siguiente
configuración:
Consideramos una señal de reloj para el contador del propio reloj del sistema
dividido por 16.
Modo de ejecución continua: no nos interesa que se reinicie el contador al
producirse la captura de entrada. De hecho, nos es indiferente el contenido del
contador.
Inhabilitamos la comparación de salida.
Habilitamos la captura de entrada con flancos de subida.
Se fija un valor de preescalado de 80, $50 en hexadecimal (porque PS=$4F).
Es un ejemplo, al usar el temporizador como captura de entrada no es
relevante.
Conviene consultar la descripción de este registro en para entender mejor la
configuración realizada.
Habilitamos el funcionamiento del temporizador.
6. El siguiente paso es configurar el registro TMR1 del temporizador (para comparación
de salida) con el valor HHHHH?. Este valor se debe implementar mediante la siguiente
configuración:
No habilitamos el funcionamiento del temporizador, porque se habilitará cuando
se haya producido la captura de entrada del temporizador 0.
Consideramos una señal de reloj para el contador del propio reloj del sistema
dividido por 16.
Modo de ejecución continua: de nuevo, no nos interesa que se reinicie el
contador al producirse la captura de entrada. En este caso, se pondrá a cero la
siguiente vez que se vaya a utilizar dentro del bucle del programa principal.
Habilitamos la comparación de salida y su interrupción.
Inhabilitamos la captura de entrada.
Se fija un valor de preescalado de 132. Al combinarlo con el valor del registro
TRR1, que se configura en el programa principal como en breve veremos, se
determina el tiempo para el fin de temporización de 2 segundos.
7. En este momento nos falta marcar como no pendiente y darle prioridades 2 y 4, por
ejemplo, a las interrupciones de los temporizadores 0 y 1. Conviene observar que para
marcar como no pendiente estas interrupciones debemos poner a 0 los bits 15 y 11 del
registro ICR1. Este bit se pone a 0 escribiendo un 1. En los bits 14-12 y 10-8 debemos
configurar el nivel de prioridad de dichas interrupciones. Complete el valor que
debemos cargar en el registro ICR1 (IIIIIIIII?).
8. Finalmente habilitamos las interrupciones en el registro de estado del microcontrolador
(SR).
Además, en el programa principal, dentro de la subrutina REINIT, debemos configurar el
registro TRR1 del temporizador 1 para que genere una interrupción al cabo de 2 segundos
desde el momento en que se ha detectado el flanco de subida en la captura de entrada.
Calcule el valor que debe tener dicho registro (JJJJJ?). Antes de terminar la configuración
de los temporizadores debemos introducir un par de instrucciones en las subrutinas de
atención a la interrupción que nos permitan reiniciar el bit CAP del registro TER0 y el bit
REF de TER1. Una vez completadas todas las instrucciones, ensamble, cargue y ejecute el
programa. Observará como después de imprimir un mensaje inicial en la ventana terminal,
el programa se queda esperando un flanco de subida por la entrada marcada como TIN0
(ver descripción de la plataforma ENT2004CF). Puede producir un flanco de subida de
manera artificial mediante la fuente de alimentación: inicialmente conecte el terminal a
masa y a continuación a 5V. Observará que este flanco de subida produce una interrupción
y que el programa escribe el mensaje: “HE DETECTADO UN FLANCO DE SUBIDA”, esta
vez al cabo de 2 segundos. Así mismo, sea consciente de que es posible que se
produzcan rebotes en dicha señal, lo que puede producir varias peticiones de interrupción
consecutivas. Una manera sencilla de hacer frente al problema es reiniciar el flag de estado
correspondiente al final de la rutina de atención a la interrupción para dar tiempo a la señal
a estabilizarse. Al final de este tutorial se muestra el código del programa “FinTemp.asm” y
en la apartado 2.1 se muestran las soluciones a las preguntas formuladas a lo largo de este
tutorial.
1.5 Código de los programas: en ensamblador (InterrTimer.asm,
InterrExterna.asm y FinTemp.asm) y C (InterrTimer.c, InterrExterna.c y
FinTemp.c)
A continuación, para su comodidad, se reproduce el código fuente de los tres programas
de ejemplo que se encuentran en el directorio .\Tutoriales_ASM, para la versión en
ensamblador, y en el directorio .\Tutoriales_C para la versión en C.
1.5.1 InterrTimer.asm e InterrTimer.c

1.5.1.1 InterrTimer.asm

****************************************************************************
* INTERRTIMER.ASM: EJEMPLO PARA APRENDER EL MANEJO DE INTERRUPCIONES
* TEMPORIZADAS MEDIANTE UN TEMPORIZADOR CON EL SISTEMA COLDFIRE 5272
*
* AUTORES: RUBÉN SAN SEGUNDO, JUAN MANUEL MONTERO Y RICARDO DE CÓRDOBA
***************************************************************************

*********** DEFINICIÓN DE CONSTANTES ****************


MCF_MBAR EQU $10000000 * DIRECCIÓN BASE
MCFSIM_PIVR
MCFSIM_ICR1 EQU
EQU $1000003F
$10000020 *
* VECTOR DEDE
REGISTRO INTERRUPCIONES PROGRAMABLE
CONTROL DE INTERRUPCIONES
MCFSIM_TMR0 EQU $10000200 * REGISTRO DE CONTROL TIMER0
MCFSIM_TRR0 EQU $10000204 * REGISTRO DE REFERENCIA TIMER0
MCFSIM_TCN0 EQU $1000020C * REGISTRO DE CUENTA TIMER0
MCFSIM_TER0 EQU $10000210 * REGISTRO DE ESTADO TIMER0

OUT EQU $13


SISTEMA EQU 15
DIR_PROGRAMA EQU $20000
DIR_VARIABLES EQU $30000
DIR_PILA EQU $30000
V_BASE EQU $40
DIR_TIMER0 EQU AAAA? * RESULTADO DE OPERAR 4*(V_BASE+??)

*********** DEFINICIÓN DE VARIABLES **********************


ORG DIR_VARIABLES
CONT_RETARDO DC.L $0
CAD1 DC.B 'COMIENZA EL PROGRAMA',13,10
CAD2 DC.B 'CADA SEGUNDO ESCRIBO UNA LETRA',13,10
FIN_CAD2 DC.B '-'

*********** COMIENZO DEL PROGRAMA **********************


ORG DIR_PROGRAMA
PPAL MOVEA.L #DIR_PILA,SP * SITUAMOS EL PUNTERO A LA PILA
BSR INIT
LEA CAD2,A0
LEA FIN_CAD2,A1
BUCLE MOVE.L #1000,D1 * CONTAMOS 1000 VECES INTERRUPCIONES CADA 1MS
MOVE.L D1,CONT_RETARDO
BSR RETARDO
MOVE.B (A0)+,D1
MOVE.L #OUT,D0
TRAP #SISTEMA
CMPA.L A0,A1
BNE BUCLE
LEA CAD2,A0
BRA BUCLE
* TODOS LOS PROGRAMAS DEBEN TERMINAR CON UN BUCLE INFINITO AL FINAL
*********** DEFINICIÓN DE SUBRUTINAS **********************
* RUTINA DE INICIALIZACIÓN
INIT MOVE.L #MCFSIM_PIVR,A0 * FIJAMOS EL COMIENZO DE LOS VECTORES DE
INTER.
MOVE.B #V_BASE,(A0) * TÍPICAMENTE EN LA POSICIÓN 64 ($40).

LEA DIR_TIMER0,A0
MOVE.L #BBB_BBBBBB?,(A0) * ESCRIBIMOS LA DIRECCIÓN DE LA
SUBRUTINA.

LEA CAD1,A0 * ESCRIBIMOS EL MENSAJE DEL COMIENZO.


LEA CAD2,A1
BSR PRINTCAD
MOVE.L #MCFSIM_TMR0,A0 * CONFIGURAMOS EL TIMER0: PS,
* TMR0: Escalado=$50-1=80-1 CE=00 OM=1 ORI=1 FRR=1 CLK=10 RST=1
MOVE.W #$4F3D,(A0)
MOVE.L #MCFSIM_TCN0,A0 * PONEMOS A 0 EL CONTADOR DEL TIMER0.
MOVE.W #$0000,(A0)

MOVE.L #MCFSIM_TRR0,A0 * FIJAMOS LA CUENTA FINAL DEL CONTADOR.


MOVE.W #CCCCC?,(A0)
* SE GENERA UNA INTERRUPCIÓN CADA 1 MS.
MOVE.L #MCFSIM_ICR1,A0
MOVE.L #$8888C888,(A0) * MARCAMOS LA INTERRUPCIÓN DEL TIMER0
* COMO NO PENDIENTE Y DE NIVEL 4.
* HABILITAMOS INTERRUPCIONES
MOVE.W SR, D0
ANDI.L
MOVE.W #$F8FF,D0
D0,SR
RTS
***********************************************************
* RUTINA PARA SACAR UN CARACTER DE D1 POR PANTALLA
PRINTC MOVE.L #OUT,D0
TRAP #SISTEMA
RTS
***********************************************************
* RUTINA PARA SACAR UNA CADENA POR PANTALLA
PRINTCAD ADDA.L #-60, A7
MOVEM.L D0-D7/A0-A6,(A7)
CLR.L D1
SIG_CAR MOVE.B (A0)+,D1
MOVE.L #OUT,D0
TRAP #SISTEMA
CMPA.L A0,A1
BNE SIG_CAR
MOVEM.L (A7),D0-D7/A0-A6
ADDA.L #60,A7
RTS
***********************************************************
* RUTINA QUE INTRODUCE UN DETERMINADO RETARDO
RETARDO TST.L CONT_RETARDO
BNE.S RETARDO
RTS
***********************************************************
* RUTINA DE INTERRUPCIÓN PARA TIMER0
SUB_TIMER0 ADDA.L #-60, A7
MOVEM.L D0-D7/A0-A6,(A7)
MOVE.L #MCFSIM_TER0,A0 * RESETEAMOS EL BIT FIN DE CUENTA
MOVE.W #$0002,(A0)
* PROCESO
TST.L CONT_RETARDO
BEQ FIN_TMR0
SUB.L #1,CONT_RETARDO
FIN_TMR0 MOVEM.L (A7), D0-D7/A0-A6
ADDA.L #60,A7
RTE

1.5.1.2 InterrTimer.c

//------------------------------------------------------------------------------
// InterrTimer.c
// Ejemplo para aprender el manejo de interrupciones temporizadas
// y fines de temporización mediante un temporizador con el sistema
// ENT2004CF.
//
// Autores: Juan Manuel Montero, Rubén San Segundo y Ricardo de Córdoba
// Adaptado a C por: Javier Guillén Álvarez
//------------------------------------------------------------------------------
#include "m5272.h"
#include "m5272lib.c"

#define V_BASE 0x40 // Dirección de inicio de los vectores


de interrupción
#define DIR_VTMR0 4*(V_BASE+5) // Dirección del vector de TMR0
#define FREC_INT 1000 // Frec. de interr. TMR1 = 1000
Hz (cada 1ms)
#define CNT_INT1 MCF_CLK/(FREC_INT*0x4F*16) // Valor de precarga del
temporizador de interrupciones TRR1
#define BORRA_REF 0x0002 // Valor de borrado de interr.
pendientes de tout1 para TER1
volatile ULONG cont_retardo;
//------------------------------------------------------
// void rutina_tout0(void)
// Descripción:
// Función de atención a la interrupción para TIMER0
//------------------------------------------------------
void rutina_tout0(void)
{
mbar_writeShort(MCFSIM_TER0,BORRA_REF); // Reset del bit de fin de cuenta
if(cont_retardo > 0){
cont_retardo--; // Decrementa el contador cada 1ms
}
}
//------------------------------------------------------
// void __init(void)
// Descripción:
// Función por defecto de inicialización del sistema
//------------------------------------------------------
void __init(void)
{
mbar_writeByte(MCFSIM_PIVR,V_BASE);// Comienzo de vectores de interrupción.
ACCESO_A_MEMORIA_LONG(DIR_VTMR0)= (ULONG)_prep_TOUT0;
// Escribimos la dirección de la función para TMR0
output("COMIENZA EL PROGRAMA\r\n");
mbar_writeShort(MCFSIM_TMR0, 0x4F3D);
// TMR0: PS=0x50-1 CE=00 OM=1 ORI=1 FRR=1 CLK=10 RST=1
mbar_writeShort(MCFSIM_TCN0, 0x0000);
// Ponemos a 0 el contador del TIMER0
mbar_writeShort(MCFSIM_TRR0, CNT_INT1); // Fijamos la cuenta final del contador
mbar_writeLong(MCFSIM_ICR1, 0x8888C888);
// Marca la interrupción del TIMER0 como no pendiente y de nivel 4.
cont_retardo = 0;
sti(); // Habilitamos interrupciones
}
//------------------------------------------------------
// void sleep(ULONG milisegundos)
// Descripción:
// Función que introduce el retardo indicado en
// milisegundos.
//------------------------------------------------------
void sleep(ULONG milisegundos)
{
cont_retardo = milisegundos; // Inicializa el contador
while(cont_retardo > 0); // Espera a que llegua a cero
return;
}
//------------------------------------------------------
// void bucleMain(void)
// Descripción:
// Función del programa principal
//------------------------------------------------------
void bucleMain(void)
{
char* cad = "CADA SEGUNDO ESCRIBO UNA LETRA\r\n";
while(*cad){
sleep(1000); // Introduce un retardo de 1 segundo
outch(*cad++); // Escribe una la siguiente letra de cadena
}
}
//---------------------------------------------------------
// Definición de rutinas de atención a la interrupción
// Es necesario definirlas aunque estén vacías
void rutina_int1(void){}
void rutina_int2(void){}
void rutina_int3(void){}
void
void rutina_int4(void){}
rutina_tout1(void){}
void rutina_tout2(void){}
void rutina_tout3(void){}

1.5.2 InterrExterna.asm e InterrExterna.c

1.5.2.1 InterrExterna.asm

****************************************************************************
* INTERREXTERNA.ASM: EJEMPLO PARA APRENDER EL MANEJO DE INTERRUPCIONES
* EXTERNAS EL SISTEMA COLDFIRE 5272
*
* AUTORES: JUAN MANUEL MONTERO, RUBÉN SAN SEGUNDO Y RICARDO DE CÓRDOBA
***************************************************************************
*********** DEFINICIÓN DE CONSTANTES **********************
MCF_MBAR EQU $10000000 * DIRECCIÓN BASE

MCFSIM_PIVR EQU $1000003F * REGISTRO DE VECTOR DE INTERRUPCIONES


PROGRAMABLE
MCFSIM_PITR EQU $10000034 * REGISTRO DE FLANCO DE INTERRUPCIÓN
MCFSIM_ICR1 EQU $10000020 * REGISTRO DE CONTROL DE INTERRUPCIONES

OUT EQU $13


SISTEMA EQU 15
DIR_PROGRAMA EQU $20000
DIR_VARIABLES EQU $30000
DIR_PILA EQU $30000
V_BASE EQU DDD?
DIR_INT1 EQU $104 * RESULTADO DE OPERAR: 4*(V_BASE+1)

*********** DEFINICIÓN DE VARIABLES **********************


ORG DIR_VARIABLES
CAD1 DC.B 'COMIENZA EL PROGRAMA',13,10
CAD2 DC.B 'HE DETECTADO UN FLANCO DE SUBIDA',13,10
FIN_CAD2 DC.B '-'
*********** COMIENZO DEL PROGRAMA *************************
ORG DIR_PROGRAMA
PPAL MOVEA.L #DIR_PILA,SP * SITUAMOS EL PUNTERO A LA PILA
BSR INIT
BUCLE BRA BUCLE
* TODOS LOS PROGRAMAS DEBEN TERMINAR CON UN BUCLE INFINITO AL FINAL
*********** DEFINICIÓN DE SUBRUTINAS **********************
* RUTINA DE INICIALIZACIÓN
INIT MOVE.L #MCFSIM_PIVR,A0 * FIJA EL COMIENZO DE LOS VECTORES DE
INTER.
MOVE.B #V_BASE,(A0) * TÍPICAMENTE EN LA POSICIÓN 64 ($40).
LEA DIR_INT1,A0
MOVE.L #SUB_INT1,(A0) * DIRECCIÓN DE LA SUBRUTINA.

LEA CAD1,A0 * ESCRIBIMOS EL MENSAJE DEL COMIENZO.


LEA CAD2,A1
BSR PRINTCAD
MOVE.L #MCFSIM_PITR,A0
MOVE.L (A0),D1
ORI.L #$80000000,D1 * CON EL FLANCO DE SUBIDA
MOVE.L D1,(A0)

MOVE.L #MCFSIM_ICR1,A0
MOVE.L #EEEEEEEEE?,(A0) * MARCAMOS LA INTERRUPCIÓN INT1
* COMO NO PENDIENTE Y DE NIVEL 3.

* HABILITAMOS INTERRUPCIONES
MOVE.W SR, D0
ANDI.L
MOVE.W #$F8FF,D0
D0,SR
RTS
***********************************************************
* RUTINA PARA SACAR UNA CADENA POR PANTALLA
PRINTCAD ADDA.L #-60, A7
MOVEM.L D0-D7/A0-A6,(A7)
CLR.L D1
SIG_CAR MOVE.B (A0)+,D1
MOVE.L #OUT,D0
TRAP #SISTEMA
CMPA.L A0,A1
BNE SIG_CAR
MOVEM.L (A7),D0-D7/A0-A6
ADDA.L #60,A7
RTS
***********************************************************
* RUTINA DE INTERRUPCIÓN PARA INT1
SUB_INT1 ADDA.L #-60, A7
MOVEM.L D0-D7/A0-A6,(A7)

* ESCRIBIMOS MENSAJE DE LA INTERRUPCIÓN


LEA CAD2,A0
LEA FIN_CAD2,A1
BSR PRINTCAD

MOVE.L #MCFSIM_ICR1,A0 * AL SER INTERRUPCIÓN EXTERNA


MOVE.L #EEEEEEEEE?,(A0) * HAY QUE VOLVER A ACTIVAR

MOVEM.L (A7), D0-D7/A0-A6


ADDA.L #60,A7
RTE

1.5.2.2 InterrExterna.c

//------------------------------------------------------------------------------
// InterrExterna.c
// Ejemplo para aprender el manejo de interrupciones externas con la
// plataforma ENT2004CF.
//
// Autores: Ricardo de Córdoba, Juan Manuel Montero y Rubén San Segundo
// Adaptado a C por: Javier Guillén Álvarez
//------------------------------------------------------------------------------
#include "m5272.h"
#include "m5272lib.c"
#define V_BASE 0x40 // Dirección de inicio de los vectores de
interrupción
#define DIR_VINT1 4 * (V_BASE + 1) // Dirección del vector de INT1
//------------------------------------------------------
// void rutina_int1(void)
// Descripción:
// Función de atención a la interrupción para la
// interrupción externa.
//------------------------------------------------------
void rutina_int1(void)
{
// Imprime el mensaje de la interrupción
output("HE DETECTADO UN FLANCO DE SUBIDA\r\n");
// Al ser interrupción externa debemos volverla a activar
mbar_writeLong(MCFSIM_ICR1, 0xB8888888);
}

//------------------------------------------------------
// void __init(void)
//
// Descripción:
Función por defecto de inicialización del sistema
//------------------------------------------------------
void __init(void)
{
// Fijamos el comienzo de vectores de interrupción en V_BASE
mbar_writeByte(MCFSIM_PIVR,V_BASE);
// Escribimos la dirección de la función de atención a la interrupción
ACCESO_A_MEMORIA_LONG(DIR_VINT1) = (ULONG) _prep_INT1;
// Imprimimos el mensaje de comienzo
output("COMIENZA EL PROGRAMA\r\n");
// Configuramos INT1 para que se active con flanco de subida
mbar_writeLong(MCFSIM_PITR, mbar_readLong(MCFSIM_PITR) | 0x80000000);
// Marcamos la interrupción INT1 como no pendiente y de nivel 3
mbar_writeLong(MCFSIM_ICR1, 0xB8888888);
// Habilitamos interrupciones
sti();
}
//------------------------------------------------------
// void bucleMain(void)
// Descripción:
// Función del programa principal
//------------------------------------------------------
void bucleMain(void){}
//---------------------------------------------------------
// Definición de rutinas de atención a la interrupción
// Es necesario definirlas aunque estén vacías
void rutina_int2(void){}
void rutina_int3(void){}
void rutina_int4(void){}
void rutina_tout0(void){}
void rutina_tout1(void){}
void rutina_tout2(void){}
void rutina_tout3(void){}

1.5.3 FinTemp.asm y FinTemp.c

1.5.3.1 FinTemp.asm

****************************************************************************
* FINTEMP.ASM: EJEMPLO PARA APRENDER EL MANEJO DE INTERRUPCIONES
* TEMPORIZADAS Y FINES DE TEMPORIZACIÓN MEDIANTE UN TEMPORIZADOR CON EL
* SISTEMA COLDFIRE 5272
*
* AUTORES: RICARDO DE CÓRDOBA, JUAN MANUEL MONTERO Y RUBÉN SAN SEGUNDO
***************************************************************************
*********** DEFINICIÓN DE CONSTANTES ****************
MCF_MBAR EQU $10000000 * DIRECCIÓN BASE

MCFSIM_PIVR EQU $1000003F * REGISTRO DE VECTOR DE INTERRUPCIONES


PROGRAMABLE
MCFSIM_ICR1 EQU $10000020 * REGISTRO DE CONTROL DE INTERRUPCIONES

MCFSIM_TMR0 EQU $10000200 * REGISTRO DE CONTROL TIMER0


MCFSIM_TRR0 EQU $10000204 * REGISTRO DE REFERENCIA TIMER0
MCFSIM_TCN0 EQU $1000020C * REGISTRO DE CUENTA TIMER0
MCFSIM_TER0 EQU $10000210 * REGISTRO DE ESTADO TIMER0

MCFSIM_TMR1 EQU $10000220 * REGISTRO DE CONTROL TIMER1


MCFSIM_TRR1 EQU $10000224 * REGISTRO DE REFERENCIA TIMER1
MCFSIM_TCN1 EQU $1000022C * REGISTRO DE CUENTA TIMER1

MCFSIM_TER1 EQU $10000230 * REGISTRO DE ESTADO TIMER1


OUT
SISTEMA EQU
EQU $13
15
DIR_PROGRAMA EQU $20000
DIR_VARIABLES EQU $30000
DIR_PILA EQU $30000
V_BASE EQU $40
DIR_TIMER0 EQU $114 * RESULTADO DE OPERAR: 4*(V_BASE+5)
DIR_TIMER1 EQU $118 * RESULTADO DE OPERAR: 4*(V_BASE+6)

*********** DEFINICIÓN DE VARIABLES **********************


ORG DIR_VARIABLES
CAD1 DC.B 'COMIENZA EL PROGRAMA',13,10
CAD2 DC.B 'HE DETECTADO UN FLANCO DE SUBIDA',13,10
FIN_CAD2 DC.B '-'
CAP_RECIBIDA DC.B 0
*********** COMIENZO DEL PROGRAMA **********************
ORG DIR_PROGRAMA
PPAL MOVEA.L #DIR_PILA,SP * SITUAMOS EL PUNTERO A LA PILA
BSR INIT
BUCLE TST.B CAP_RECIBIDA
BEQ BUCLE

BSR REINIT
BUCLE2 TST.B CAP_RECIBIDA
BNE BUCLE2
MOVE.L MCFSIM_TMR1,D0
BCLR.L #16,D0
MOVE.L D0,MCFSIM_TMR1 * Y SE PARA
BRA BUCLE
* TODOS LOS PROGRAMAS DEBEN TERMINAR CON UN BUCLE INFINITO AL FINAL

*********** DEFINICIÓN DE SUBRUTINAS **********************


***********************************************************
* RUTINA DE INICIALIZACIÓN
INIT CLR.B CAP_RECIBIDA
MOVE.L #MCFSIM_PIVR,A0 * COMIENZO DE LOS VECTORES DE INTER.
MOVE.B #V_BASE,(A0) * TÍPICAMENTE EN LA POSICIÓN 64 ($40).

LEA DIR_TIMER0,A0
MOVE.L #FFF_FFFFFF?,(A0) * DIRECCIÓN DE LA SUBRUTINA.

LEA DIR_TIMER1,A0
MOVE.L #GGG_GGGGGG?,(A0) * DIRECCIÓN DE LA SUBRUTINA.

LEA CAD1,A0 * MENSAJE DEL COMIENZO.


LEA CAD2,A1
BSR PRINTCAD
MOVE.L #MCFSIM_TMR0,A0 * CONFIGURA EL TIMER0 (CAPTURA
ENTRADA)
* TMR0: PS=$50-1 CE=01 OM=0 ORI=0 FRR=0 CLK=10 RST=1

MOVE.W #$4F45,(A0)
MOVE.L #MCFSIM_TMR1,A0 * TIMER1: COMPARACIÓN SALIDA
MOVE.W #HHHHH?,(A0)

MOVE.L #MCFSIM_ICR1,A0
MOVE.L #IIIIIIIII?,(A0) * MARCA LA INTERRUPCIÓN DE TIMER0
* Y TIMER1
* COMO NO PENDIENTE Y DE
* PRIORIDADES 2 y 4
* HABILITAMOS INTERRUPCIONES
MOVE.W SR, D0
ANDI.L #$F8FF,D0
MOVE.W D0,SR
RTS
***********************************************************
* RUTINA DE REINICIALIZACIÓN
REINIT MOVE.L #MCFSIM_TCN1,A0 * PONEMOS A 0 EL CONTADOR DEL TIMER0.
MOVE.W #$0000,(A0)

MOVE.L #MCFSIM_TRR1,A0 * FIJAMOS LA CUENTA FINAL DEL CONTADOR.


MOVE.W #JJJJJ?,(A0)
* SE GENERA UNA INTERRUPCIÓN AL CABO DE 2 SEG.
MOVE.L #MCFSIM_TMR1,A0 * CONFIGURA EL TIMER1 (COMPARACIÓN
* SALIDA)
MOVE.W #HHHHH?,(A0)

MOVE.L MCFSIM_TMR1,D0
BSET.L #16,D0
MOVE.L D0,MCFSIM_TMR1 * COMIENZA TIMER1
RTS
***********************************************************
* RUTINA PARA SACAR UNA CADENA POR PANTALLA
PRINTCAD ADDA.L #-60, A7
MOVEM.L D0-D7/A0-A6,(A7)
CLR.L D1
SIG_CAR MOVE.B (A0)+,D1
MOVE.L #OUT,D0
TRAP #SISTEMA
CMPA.L A0,A1
BNE SIG_CAR
MOVEM.L (A7),D0-D7/A0-A6
ADDA.L #60,A7
RTS
***********************************************************
* RUTINA DE INTERRUPCIÓN PARA TIMER0
* Configurado como captura de entrada
SUB_TIMER0 ADDA.L #-60, A7
MOVEM.L D0-D7/A0-A6,(A7)

MOVE.L #MCFSIM_TER0,A0 * RESETEAMOS EL BIT QUE INDICA FIN DE CUENTA


MOVE.W #$0001,(A0)
* PROCESO
MOVE.B #$FF,D0
MOVE.B D0,CAP_RECIBIDA
MOVEM.L (A7), D0-D7/A0-A6
ADDA.L #60,A7
RTE
***********************************************************
* RUTINA DE INTERRUPCIÓN PARA TIMER1
* Controla el fin de temporización
SUB_TIMER1 ADDA.L #-60, A7
MOVEM.L D0-D7/A0-A6,(A7)

* ESCRIBIMOS MENSAJE DE LA INTERRUPCIÓN


LEA CAD2,A0
LEA FIN_CAD2,A1
BSR PRINTCAD
CLR.B CAP_RECIBIDA

MOVE.L #MCFSIM_TER1,A0 * RESETEAMOS EL BIT QUE INDICA FIN DE CUENTA


MOVE.W #$0002,(A0)
MOVEM.L (A7), D0-D7/A0-A6
ADDA.L #60,A7
RTE

1.5.3.2 FinTemp.c

//------------------------------------------------------------------------------
// FinTemp.c
// Ejemplo para aprender el manejo de interrupciones temporizadas
// y fines de temporización mediante un temporizador con el sistema
//
// ENT2004CF.
// Autores: Ricardo de Córdoba, Juan Manuel Montero y Rubén San Segundo
// Adaptado a C por: Javier Guillén Álvarez
//------------------------------------------------------------------------------
#include "m5272.h"
#include "m5272lib.c"
#define V_BASE 0x40 // Dirección de inicio de los vectores
de interrupción
#define DIR_VTMR0 4*(V_BASE+5) // Dirección del vector de TMR0
#define DIR_VTMR1 4*(V_BASE+6) // Dirección del vector de TMR1
#define PERIODO_INTERR 2 // Periodo de interrupción de TMR1
#define CNT_INT1 MCF_CLK*PERIODO_INTERR/(0x84*16) // Valor de precarga del
temporizador de interrupciones TRR1
#define BORRA_CAP 0x0001 // Valor de borrado de interr.
pendientes de tout1 para TER1
#define BORRA_REF 0x0002 // Valor de borrado de interr.
pendientes de tout1 para TER1

volatile BOOL cap_recibida;


// Varible declarada volátil porque se utiliza en el programa principal
// y es modificada por las funciones de atención a la interrupción.
//------------------------------------------------------
// void rutina_tout0(void)
// Descripción:
// Función de atención a la interrupción para TIMER0
//------------------------------------------------------
void rutina_tout0(void)
{
mbar_writeShort(MCFSIM_TER0,BORRA_CAP); // Reset del bit de captura
cap_recibida = TRUE;
}
//------------------------------------------------------
// void rutina_tout1(void)
// Descripción:
// Función de atención a la interrupción para TIMER1
//------------------------------------------------------
void rutina_tout1(void)
{
output("HE DETECTADO UN FLANCO DE SUBIDA\r\n");
cap_recibida = FALSE;
mbar_writeShort(MCFSIM_TER1,BORRA_REF); // Reset del bit de fin de cuenta
}
//------------------------------------------------------
// void __init(void)
//
// Descripción:
// Función por defecto de inicialización del sistema
//------------------------------------------------------
void __init(void)
{
mbar_writeByte(MCFSIM_PIVR,V_BASE);
// Fija comienzo de vectores de interrupción en V_BASE.
ACCESO_A_MEMORIA_LONG(DIR_VTMR0)= (ULONG)_prep_TOUT0;
// Vector de interrupción para tout0
ACCESO_A_MEMORIA_LONG(DIR_VTMR1)= (ULONG)_prep_TOUT1;
// Vector de interrupción para tout1
output("COMIENZA EL PROGRAMA\r\n");
mbar_writeShort(MCFSIM_TMR0, 0x4F45);
// TMR0: PS=0x50-1 CE=01(Flanco Subida) OM=0 ORI=0 FRR=0 CLK=10 RST=1
mbar_writeShort(MCFSIM_TMR1, 0x8334);
// TMR1: PS=0x84-1 CE=00(No captura) OM=1 ORI=1 FRR=0 CLK=10 RST=0
//(Todavía no cuenta)
mbar_writeLong(MCFSIM_ICR1, 0x8888AC88);
// Marca la interrupción del TIMER0 y del TIMER1 como no pendiente y de
// prioridades 2 y 4 respectivamente.
cap_recibida = FALSE;
sti();
}
//------------------------------------------------------
// void reInit(void)
// Descripción:
// Reinicializa la interrupción TMR1
//------------------------------------------------------
void reInit(void)
{
mbar_writeShort(MCFSIM_TCN1, 0x0000);
// TCN1 Pone a cero el contador de TIMER1
mbar_writeShort(MCFSIM_TRR1, CNT_INT1);
// TRR1 Valor de precarga del temporizador
mbar_writeShort(MCFSIM_TMR1, 0x8334);
// TMR1 PS=0x84-1 CE=00(No captura) OM=1 ORI=1 FRR=0 CLK=10 RST=0
// (Todavía no cuenta)
bsetl(16, MCF_MBAR + MCFSIM_TMR1); // Arranca el temporizador TIMER1
}
//------------------------------------------------------
// void bucleMain(void)
//
// Descripción:
// Función del programa principal
//------------------------------------------------------
void bucleMain(void)
{
// Espera a que TMR0 detecte un flanco de subida
while(!cap_recibida);
// Inicia el temporizador de 2 segundos
reInit();
// Espera hasta que transcurran los 2 segundos e imprime
// el mensaje de flanco detectado
while(cap_recibida);
// Detiene TMR1
cli(); // Inhabilita las interrupciones
bclrl(16,MCF_MBAR + MCFSIM_TMR1); // TMR1 Para el TIMER1
sti(); // Vuelve a habilitar las interrupciones
}

//---------------------------------------------------------
// Definición de rutinas de atención a la interrupción
// Es necesario definirlas aunque estén vacías
void rutina_int1(void){}
void rutina_int2(void){}
void rutina_int3(void){}
void rutina_int4(void){}
void rutina_tout2(void){}
void rutina_tout3(void){}
2 Anexo I: Soluciones a los tutoriales

2.1 Manejo de interrupciones y temporizadores


Tabla 1. Soluciones del tutorial para la gestión de temporizadores e interrupciones.

INCÓGNITA DESCRIPCIÓN SOLUCIÓN


AAAA Dirección de memoria del vector asociado al temporizador 0 $114
BBB_BBBBBB Dirección de la rutina de atención a la interrupción SUB_TIMER0
CCCCC Configuración del registro TRR del temporizador 0 $0034

Tabla 2. Soluciones del tutorial para la gestión de una interrupción externa.

INCÓGNITA DESCRIPCIÓN SOLUCIÓN


DDD Valor de PIAR $40
EEEEEEEEE Configuración del registro ICR1 $B8888888

Tabla 3. Soluciones del tutorial para la gestión de una interrupción producida por finalización de un temporizador

INCÓGNITA DESCRIPCIÓN SOLUCIÓN


FFF_FFFFFF Dirección de la rutina de atención a la interrupción del TIMER 0 SUB_TIMER0
GGG_GGGGGG Dirección de la rutina de atención a la interrupción del TIMER 1 SUB_TIMER1
HHHHH Configuración del registro TMR del temporizador 1 $8334
IIIIIIIII Configuración del registro ICR1 $8888AC88
JJJJJ Configuración del registro TRR del temporizador 1 $F424

25

Potrebbero piacerti anche