• No se han encontrado resultados

RESPUESTAS Introducción a los sistemas de entrada/salida).

N/A
N/A
Protected

Academic year: 2021

Share "RESPUESTAS Introducción a los sistemas de entrada/salida)."

Copied!
15
0
0

Texto completo

(1)

RESPUESTAS Introducción a los sistemas de entrada/salida).

1. a) Puede ser posible pero implicaría la existencia de una incoherencia en el

software. Así, se tendrá que hacer código diferente en función de dónde se encuentre el periférico. Como posible ventaja se podría pensar, que además de utilizar de la E/S independiente, la E/S mapeada en memoria hace que el sistema sea más expandible.

b) En el microprocesador haría falta al menos una señal que indique a qué espacio de

espacio de memoria se accede. En cuanto al software (instrucciones) todo el software habría de ser ajustado a las nuevas instrucciones. Además, el S.O. tendría que cambiar su mapa de memoria y un nuevo mecanismo para poder gestionar tanto el viejo espacio de E/S como el nuevo.

2. a)

Mediante interrupciones se tiene que: Si en 200 ciclos de reloj Æ 32 bits En 50x106 Æ ????

Resulta que se transmiten 80x106 bits/s = 10x106 bytes/s Por otro lado con DMA se tiene que:

Si en 2000+1000 ciclos de reloj Æ 211x8 bits

En 50x106 Æ ????

Resulta que se transmiten aproximadamente 273x106 bits/s = 34,1x106 bytes/s Por lo tanto es más eficiente transmitir con el DMA ya que es más rápido. Esto es en general porque para transmitir por ejemplo 32 bits evidentemente es mejor utilizar las interrupciones. El DMA desde 1 byte hasta 2KB siempre tarda lo mismo, es decir, 3000 ciclos de reloj ¿A partir de qué número de bytes es mejor utilizar DMA?

P x 200 ciclos = 3000 ciclos Æ P = 15 paquetes de 32 bits

Æ A partir de 15 x 32 bits = 480 bits = 60 bytes, a partir de esa cantidad interesa más utilizar DMA

b) Ya visto en el apartado anterior. Por lo tanto en general:

10x106 bytes/s Æ 100 % velocidad 34,1x106 bytes/s Æ ????

Resulta que es unas 2,4 veces más rápido aproximadamente

3. Para ver la forma programar y conectar en modo 1 (y entrada) el 8255 hay que acudir

a las especificaciones técnicas de este chip. Antes de la conexión a los buses y al procesador hay que asignar al 8255 una serie de puertos de E/S y añadir la circuitería necesaria para que se produzca la codificación adecuada. Siguiendo las especificaciones del 8255 todo esto anterior se puede hacer fácilmente. Como esta circuitería no se pide en el enunciado no se tendrá en cuenta. Se decide tomar como puertos de E/S las direcciones que van de la 3FCh a la 3FFh (incluidas en el bus de expansión del mapa de E/S del i80x86).

Lo más complicado puede ser el hardware relativo a la gestión de las interrupciones. En el enunciado se dice que pueden recibirse interrupciones de los puertos A y B. Esto

(2)

implica que se tienen dos salidas de interrupciones INTRA e INTRB. Sin embargo el i80x86 sólo tiene una única entrada para todas las interrupciones. ¿Cómo conectar las dos salidas a la entrada de interrupciones del i80x86? Respuesta: usando una puerta OR (ver esquema en la siguiente figura). Se supone que no se dispone de un PIC tipo 8259. Si así fuese se podrían conectar directamente estas salidas al PIC y este luego al 8086.

Además se necesita un registro para guardar el identificador o vector de la interrupción (ID). Este valor siempre va a ser fijo (FFh) y por ello no se necesita un registro para guardar el ID de la interrupción. Se puede por tanto utilizar un buffer triestado (no programable) o también un registro de 8 salidas. No se necesita por tanto lógica o circuitería de selección del buffer triestado ya que no es necesario cargarlo con nuevos valores (ver esquema en la siguiente figura). Si se dispusiera de un PIC como el 8259 este buffer sobraría ya que se podría programar para que fuese él mismo el que se dejase el vector de interrupción en el bus de datos.

El circuito completo queda de la siguiente forma:

En cuanto al software necesario para implementar la E/S por interrupciones se necesitan dos rutinas. La primera de ella ha de ser de inicialización y la otra de atención a la interrupción.

La rutina de inicialización tiene que inicializar/configurar el funcionamiento de los grupos/puertos del 8255, habilitar la generación de interrupciones del 8255 y alterar el

(3)

vector de distribución de funciones para que cuando el buffer triestado/registro devuelva el valor/identificador FFh correspondiente a la rutina de atención a la interrupción. En este último caso la CPU tendrá que ir a leer el elemento 255 (FFh) del vector de distribución de funciones y ejecute la rutina a la que apunta ese elemento. Esta rutina como ya se ha dicho será la rutina de tratamiento de la interrupción.

#define PORTA 3FCh #define PORTB 3FDh #define PORTC 3FEh #define CONTROL 3FFh /* Rutina de inicialización */ void Inicializar () {

disable (); /* Inhibición de las interrupciones */

outportb (CONTROL, 0b 1011 1111); /* Establecimiento de modos del 8255 */ outportb (CONTROL, 0b 0000 1111); /* INTGA = 1 */

outportb (CONTROL, 0b 0000 0101); /* INTGB = 1 */

setvect (0xFF, Rutina_atencion); /* Alteración del vector de interrupciones */ enable (); /* Habilitación de interrupciones */

};

La rutina de servicio de la interrupción tiene que leer el dato que viene del puerto A ó B. Sin embargo antes debe averiguar cuál de los dos puertos ha producido la interrupción. Cuando un puerto recibe un dato activa una señal IBF para indicarle al periférico que ha llenado un buffer de entrada. Por lo tanto leyendo el valor de IBFA (C5) e IBFB (C1) se podrá averiguar de dónde

procede la interrupción. Para ello habrá que hacer una lectura del puerto C. /* Rutina de atención a la interrupción */

void interrupt Rutina_atencion () {

char a;

if (intportb(PORTC) & 0x20) { /* IBFA = 1 */

a = inportb (PORTA);

/* Ahora se operaría con el dato/byte ‘a’ */ } else if (intportb(PORTC) & 0x02) { /* IBFB = 1 */

a = inportb (PORTB);

/* Ahora se operaría con el dato/byte ‘a’ */ }

}

Pudiera darse el caso de que los dos puertos hubieran generado interrupción. En este caso se gestionaría primero el puerto/grupo A. Si se quiere cambiar la estrategia de prioridad habría que hacerlo por software en la rutina de atención a la interrupción.

A la hora de implementar la E/S de un computador hay dos opciones: una crear a medida el hardware necesario a partir de circuitos secuenciales y/o combinacionales básicos y la otra utilizar circuitos más o menos genéricos ya hechos como el 8255 o el 8259. La solución anterior presenta un camino intermedio a ambos extremos. Lo más normal no obstante hubiese sido utilizar también el PIC 8259 junto con el 8255. A continuación se va a modificar la anterior solución de forma que se sustituya el registro que guarda el vector de interrupción por un 8259.

(4)

Como se puede observar ahora las señales de interrupción del 8255 se direccionan a la IR7 del 8259. Esto es debido a que el vector de interrupción que se debe devolver es el FFh = 1111 1111b. Por lo tanto al inicializar la base de interrupciones del 8259 (número ó dirección a partir de la cual se van a devolver los 8 vectores de interrupciones) se ha de hacerlo con la dirección F8h = 1111 1000b. De esta forma al generarse la IR7 el 8259 devolverá al bus de direcciones el vector FFh, es decir, base más desplazamiento (F8h + 07h).

En cuanto al software la rutina de atención a la interrupción se queda igual que en la anterior solución y tan sólo habrá que completar la rutina de atención para inicializar el 8259. Antes hay que decidir en qué dirección de E/S se conecta el 8259. En las

arquitecturas PC se usan dos 8259 en cascada y el primero de ellos se sitúa a partir del puerto de E/S 20h. Se va a tomar en este caso también este puerto.

#define BASE _INT F8h #define PORT_8259_0 20h #define PORT_8259_1 21h ...

/* Rutina de inicialización */ void Inicializar () {

disable (); /* Inhibición de las interrupciones */

...

/* Como no se trabaja con 8259-s en cascada, con dos ICW (Inicialization Command

Words), en este caso la ICW1 y la ICW2, será suficiente. Consultar el manual para ver

más detalles al respecto. */

outportb (PORT_8259_0, 0b 0001 0010); /* ICW1: activación por flancos,

modo simple, no ICW4 */

outportb (PORT_8259_1, BASE _INT); /* ICW2 : base interrupciones */

/* La única OCW (Operation Command Words) que se usa es la primera ya que el resto sirven para cuando se gestionan más interrupciones o se tienen otros 8259 en cascada. */

outportb (PORT_8259_1, 0x7F); /* OCW1 : habilitar la IR7 y enmascarar

el resto (enmascarar el resto no es a priori necesario ya que no se usan). */ …

enable (); /* Habilitación de interrupciones */

(5)

4. Si se va a trabajar con un 8255 hay que decidir en primer lugar el modo en el que va

a trabajar. En este caso las señales no se ajustan al funcionamiento de las señales de protocolo de comunicación (handshake) del 8255. Por lo tanto los modos 1 y 2 se descartan. El 8255 tendrá uno de sus grupos en modo 0 y entrada. Por ejemplo el grupo 1 asociado al puerto A. La conexión será:

La conexión a un microprocesador del 8255 que en este caso será por ejemplo el i8088 es como sigue:

NOTAS: La señal ALE significa Address Latch Enabled y en los procesadores 8088 y primeros 8086 se utilizaban para multiplexar las líneas de datos y direcciones ya que compartían un mismo bus físico (señales AD0 a AD7). La señal servía para introducir en un latch la dirección de acceso a memoria mientras se recibían los datos a través de las mismas líneas o bus de direcciones. LA0 y LA1 son las señales de dirección A0 y A1 que salen del latch de almacenamiento de la dirección.

En el anterior circuito no se ha incluido el hardware necesario para la generación de interrupciones. Se adoptará una solución basada en la E/S programada.

(6)

Para recibir un dato el programa tiene primero que inicializar el 8255. Una inicializado el puerto A en el modo 0 de entrada se habrá de vigilar la señal de reloj para que cuando se produzca un flanco de bajada en la señal R, se recoja el dato que hay en la señal D. A continuación se habrá de almacenar el bit o valor leído de la señal D en alguna variable para ir componiendo el valor del número completo. En este caso se dice que el número va a ser un byte.

El programa podría agruparse en una sola función ya que al trabajar con E/S

programada no hace falta tener por separado una rutina de atención a la interrupción y otra de inicialización. El programa o función sería algo parecido a lo siguiente:

char RECIBIR () { char byte = 0;

short int contador = 0;

/* Inicializa el 8255. El primer bit siempre es uno. Los dos siguientes (00) indican el modo cero. El siguiente bit indica que el puerto es de entrada (1). El resto de bits no tienen importancia y en este caso se han dejado en cero. */ outportb (CONTROL, 0b1001 0000);

do {

byte << 1; /* Desplazamiento a la izquierda de un bit */

while ((inportb (PTOA) & 0x02) != 0); /* La señal R está en alta. Se espera a que baje */ byte = byte + inportb (PTOA) & 0x01; /* Lee la señal D */

contador ++;

while ((inportb (PTOA) & 0x02) == 0); /* La señal R está en baja. Se espera a que vuelva a estar en alta */

} while (contador < 8);

return byte;

};

NOTA: PTOA y CONTROL son macros en C que indican la dirección del puerto A y el registro de control del 8255. Así por ejemplo tomado una serie de direcciones

cualesquiera:

#define PTOA 0x158 /* A1=0 y A0=0 */ #define CONTROL 0x15B /* A1=1 y A0=1 */ Se va analizar ahora parte del código de la rutina:

¿Qué indica la condición ( inportb(PTOA) & 0x02) !=0 ) ? Al recogerse el dato del puerto A viene con el formato con el que se ha decidido conectar las señales R y D al 8255:

0 1 2 3 4 5 6 7 - - - R D Si se hace AND bit a bit con 0x02 el resultado será:

- - - R D AND

bit a bit 0 0 0 0 0 0 1 0 = 0 0 0 0 0 0 R 0

(7)

Es decir, si R es 1, entonces el resultado es distinto de cero.

La solución anterior es válida suponiendo que el periodo de la señal R es grande y da tiempo a que la CPU rastree la señal muchas veces. Ejemplo de rastreo de la señal por parte de la CPU:

Si esto no se cumple se deberán emplear algunos circuitos extras como por ejemplo un registro de carga serie/paralelo (74299 por ej.) que fuese cargando datos según los flancos de bajada de la señal R. También se necesitaría un contador que fuese restando desde 7 hasta 0 para saber cuándo se hubiesen recibido ya todos los bits del byte y se activase también según los flancos de bajada de la señal R. Al final de la cuenta atrás (llegado a cero) el contador generaría una señal de interrupción.

5. En este caso se dispone de un dispositivo que recibe señales de datos y un par de

señales asociadas al protocolo de comunicación (END# y START#). La solución podría ser relativamente sencilla empleando el PPI 8255 en modo 1. Sin embargo el 8255 sólo espera 350 ns antes de poner la señal de START# (OBF# en el 8255) a cero. De esta forma no se cumplirían los requisitos temporales del dispositivo de salida (en este caso un LCD).

(Notación: SEÑAL# significa SEÑAL negada o en baja)

La primera solución podría consistir en poner el 8255 en modo 0 y gestionar las señales de control mediante software. El esquema de conexión sería inmediato:

Con este esquema se puede plantear la escritura en el LCD mediante E/S programada. De esta forma se evita tener que añadir más hardware al circuito para gestionar las interrupciones. La rutina que gestiona la escritura en el LCD debe primero inicializar el 8255. A continuación debe poner el dato en la salida del puerto A, esperar 2 µs y finalmente activar la señal de START#. La señal START# hay que desactivarla cuando se reciba la señal END# (ACK). Se van a escribir dos rutinas: una de inicialización y otra de escritura:

(8)

/* Rutina de inicialización: */ void INIT () {

outportb (CONTROL, 0b10001 000); /* Poner el puerto A en modo 0 y

salida. Los 3 últimos bits no

importan */

outportb (PTOA, 0b1000 0000); /* Señal START# = 1, es decir, no se transmite ningún dato */

WRITE_LCD (‘0’); /* Limpia el LCD */ }

/* Rutina de escritura */ void WRITE_LCD (char C) {

outportb (PTOA, C & 0b0111 1111); /* Se coloca el código ASCII a la entrada del bus de datos del LCD */

delay (1); /* Se espera 1 milisegundo. Como mínimo se pedía 2 µs */

outportb (PTOA, C & 0b1000 0000); /* Señal START# = 0, es decir, el LCD ya puede recoger el dato */

while (inportb (PTOC) & 0x40 ¡=0); /* Se espera a la llegada de END# */ outportb (PTOA, C & 0b1111 1111); /* Señal START# = 1 */

/* También valdría para este último paso usar la siguiente instrucción: outportb (PTOA, 0b1000 0000); */

}

Otra solución al problema sería utilizar el modo 1 del 8255 y adaptar los tiempos por hardware para que pueda funcionar con el LCD. Para ello se ha de lograr que se retrase la señal START# al menos 2 µs. Esto se consigue con un circuito de cómo el siguiente:

De esta forma no se tendrían que activar las señales OBF.A y ACK.A por software. Falta saber cuando ha terminado el ciclo de escritura del LCD. Cuando el LCD termina, manda un pulso a través de la señal END#:

(9)

Por lo tanto el ciclo terminaría cuando START# = 1 y END# = 1. Eso se puede hacer leyendo el puerto C mediante un bucle de espera activa. El programa quedará ahora como sigue:

void INIT () {

outportb (CONTROL, 0b1010 0000); /* Los últimos 4 bits no importan. Pone el puerto A en modo 1 y salida */ }

void WRITE_LCD (char C) {

outportb (PTOA, C); /* Se escribe el carácter ASCII en el puerto A */ while (inportb (PTOC) & 0xC0) ¡= 0xC0); /* Se espera a recibir la señal

ACK# = END# */ }

La tercera opción para resolver el problema es utilizar las interrupciones del 8255 para que sea el propio 8255 el que avise cuando está en condiciones de mandar otro dato (byte) y si hay un dato que transmitir mandarlo directamente al 8255. Ahora hay que conectar la salida de interrupciones del 8255 al microprocesador y tener un registro que guarde el ID de la interrupción con una lógica de selección para poder modificar su contenido. El circuito quedaría como sigue:

(10)

Una opción al registro para guardar el ID la interrupción de es usar un PIC 8259 al cual se direccionan las señales de interrupción del PPI 8255 (INTR.A).

En cuanto al software a la hora de programar el sistema se habrá de tener una rutina de inicialización que configure el 8255 al principio. En esa rutina se deberán activar las interrupciones del 8255, poner la dirección de la rutina de atención (ID) en el registro y alterar el vector interrupciones.

En cuanto a la rutina de atención a la interrupción del 8255 habrá que hacer un análisis más detallado. La rutina de atención se ejecutará cuando el 8255 esté listo para

transmitir un dato nuevo (el periférico es de salida). En ese momento si existe algún otro dato para transmitir se envía y si no entonces no se hace nada. Esto sugiere la existencia de una cola de datos (caracteres ASCII) para enviar. De esta forma un programa cualquiera que quiera escribir en el LCD y mandará sucesivamente datos para que sean escritos a través de una rutina llamada PRINT_LCD por ejemplo que irá encolando los datos recibidos. Estos se irán almacenando en la cola y serán enviados al

(11)

8255 en cuanto esto sea posible a través de otra rutina que hará la salida de datos en el 8255 y que se llamará por ejemplo WRITE_LCD. De esta forma la sincronización con el periférico se hace transparente al programador a través de PRINT_LCD. Si se estuviese trabajando con un S.O. esta última rutina podría ser parte un driver ó controlador. El diagrama de flujo que muestra el funcionamiento del proceso es el siguiente:

Se tendrán por tanto que programar 3 rutinas, más la rutina de inicialización que no aparece en el esquema. Por simplicidad se va a suponer que la rutina PRINT_LCD sólo es llamada por un solo proceso del S.O. o programa de usuario (de esta forma se

eliminan posibles problemas de acceso concurrente a datos). La estructura que

implementa o representa la cola será de tipo global. El código de las diferentes rutinas sería el siguiente:

#define PTOA 0x3F0 /* Se reserva una parte del espacio de direcciones de la #define PTOB 0x3F2 E/S para conectar el 8255. En este caso desde la #define PTOC 0x3F4 dirección 0x3F0 hasta la 0x3F6. Se supone que se #define CONTROL 0x3F6 trabaja con 8086 de 16 bits */

#define REG_INT 0x3F8 /* Dirección en la cual se “mapea” el registro que guarda la dirección de la rutina de atención. Si se hubiera utilizado un 8259 esta sería una dirección del PIC */ #define ID_LCD 77 /* Número aleatorio */

/* Se definen e inicializan las variables globales o compartidas a todas las rutinas: */ #define LONG_COLA 255

int principio_cola = 0; /* Puntero al elemento anterior al elemento más antiguo introducido en la cola */

int fin_cola = 0; /* Puntero al último elemento introducido en la cola */ char cola [LONG_COLA]; /* La variable cola */

(12)

/* Antes de nada se van a implementar algunas funciones del TAD (tipo abstracto de datos) “cola circular” para luego poder trabajar más cómodamente con ella. */ void encolar (char C) {

fin _cola ++;

fin_cola = fin_cola % LONG_COLA; /* Por si se llega al final físico de la cola */

cola [fin_cola] = C; }

char desencolar () {

principio_cola ++;

principio_cola = principio_cola % LONG_COLA; return (cola[principio_cola]);

}

int esta_cola_llena () {

if (principio_cola - fin_cola == 1) return 1; /* La cola está llena */ return 0; /* La cola NO está llena */ }

int esta_cola_vacia () {

if (principio_cola == fin_cola)

return 1; /* La cola está vacía */ return 0; /* La cola NO está vacía */ }

/* Aunque en el esquema aparezca reflejado que se transmite una cadena desde el programa de usuario a PRINT_LCD realmente se va a tomar la decisión de transmitir un solo carácter. Sin embargo se podría modificar PRINT_LCD para procesar una cadena en vez de por ejemplo llamar dentro de un bucle a PRINT_LCD desde el programa de usuario. Si se toma esta decisión es por motivos de

modularidad/reusabilidad. */ void PRINT_ LCD (char C) {

while (esta_cola_llena()); /* Se espera a que haya espacio en la cola */ if (esta_cola_vacia()) {

outportb (CONTROL, 0b0000 1101); /* Habilitar las interrupciones del 8255 y comenzar a escribir */

encolar (C);

WRITE_LCD (); /* Ordena escritura por primera vez */ } else {

/* Seguir metiendo caracteres en la cola: */

encolar (C);

/* No se llama a la rutina de WRITE_LCD porque si hay más de un carácter la rutina de interrupción ya la llamará */

} }

(13)

void WRITE_ LCD () {

outportb (PTOA, desencolar()); /* Se saca el carácter a través del 8255 */ }

void interrupt INT_LCD () {

if (!esta_cola_vacia) /* Si todavía hay algo en la cola se escribe */ WRITE_ LCD ();

else /* Si no hay más que escribir se deshabilitan las interrupciones del 8255 */ outportb (CONTROL, 0b0000 1100);

}

/* Falta la rutina de inicialización que se define ahora: */ void INIT () {

disable (); /* Se deshabilitan las interrupciones */

outportb (CONTROL, 0b1010 0000); /* Se establece el modo 1 de funcionamiento del 8255 */ setvect (ID_LCD, INT_LCD); /* Se modifica el vector de distribución */ outportb (REG_INT, ID_LCD); /* Se almacena el ID de la rutina de atención */ enable (); /* Se habilitan las interrupciones */

(14)

6. El ADC0808 se puede conectar al 82C55 usando el puerto A como entrada de datos

y PC4 para testear la entrada EOC. Con el puerto B como salida, se controlan las líneas de selección del canal analógico, AEN y START.

El programa que controla el conversor se puede hacer con dos funciones. Ini_adc inicializa el modo de funcionamiento del 8255, con los puertos A y C como entrada y el B como salida. Todos los grupos funcionan en modo 0.

OE# WR

El programa que lee el conversor es: #define PORTA 300h

#define PORTB 302h #define PORTC 304h #define CONTROL 306h lee_adc(unsigned char nro_canal) {

unsigned char estado;

/* configura el modo de operación del 82C55 */

outportb(PORTB, 0); /* desactiva STAR y AEN */

outportb(CONTROL, 0b10011000) /* modo 0 para todos los grupos, PA0-7 y PC4-7 de entrada, el resto de salida */ /* selecciona el número de canal analógico de entrada. */

nro_canal=nro_canal&0b111; /* asegura que el número de canal está entre 0 y 7 */ outportb (PORTB,(inportb(PORTB)&0b11111000)|nro_canal); /* ajusta el numero de

canal en el puerto B*/ outportb (PORTB,(inportb(PORTB)|0b0001 0000); /* activa AEN */

outportb (PORTB,(inportb(PORTB)|0b0000 1000); /* activa START. El conversor

comienza operación de conversión */ outportb (PORTB,(inportb(PORTB)&0b1110 0111); /* desactiva activa AEN y START */ estado = inportb (PORTC);

while (estado&0b0001 0000 != 0)

estado = inportb (PORTC); /* queda esperando fin de conversión */ return (inportb(PORTA)); /*devuelve valor de conversión */

(15)

Referencias

Documento similar

Tras establecer un programa de trabajo (en el que se fijaban pre- visiones para las reuniones que se pretendían celebrar los posteriores 10 de julio —actual papel de los

En cuarto lugar, se establecen unos medios para la actuación de re- fuerzo de la Cohesión (conducción y coordinación de las políticas eco- nómicas nacionales, políticas y acciones

Products Management Services (PMS) - Implementation of International Organization for Standardization (ISO) standards for the identification of medicinal products (IDMP) in

This section provides guidance with examples on encoding medicinal product packaging information, together with the relationship between Pack Size, Package Item (container)

D) El equipamiento constitucional para la recepción de las Comisiones Reguladoras: a) La estructura de la administración nacional, b) La su- prema autoridad administrativa

b) El Tribunal Constitucional se encuadra dentro de una organiza- ción jurídico constitucional que asume la supremacía de los dere- chos fundamentales y que reconoce la separación

d) que haya «identidad de órgano» (con identidad de Sala y Sección); e) que haya alteridad, es decir, que las sentencias aportadas sean de persona distinta a la recurrente, e) que

Ciaurriz quien, durante su primer arlo de estancia en Loyola 40 , catalogó sus fondos siguiendo la división previa a la que nos hemos referido; y si esta labor fue de