SISTEMAS DIGITALES II EJERCICIOS TEMA 2 RESUELTOS Departamento de Sistemas Electrónicos y de Control 2005-2006 1 2 SISTEMAS DIGITALES II EJERCICIOS RESUELTOS TEMA 2 EJERCICIO 1 Se dispone de un sistema basado en el 80C552 cuyas primeras posiciones de memoria contienen las siguientes instrucciones: Dirección Instrucción 0000H-0002H LJMP 4000H 0003H-0005H LJMP 6000H 000BH-000DH LJMP 6100H 0013H-0015H LJMP 6200H Además, se conoce que el contenido de algunos registros es el que se muestra en la tabla siguiente: Registro Contenido IEN0 10000111b IEN1 00000000b IP0 00000010b IP1 00000000b TCON 00000101b Por ultimo se sabe que como parte del código desarrollado para la aplicación existen las siguientes rutinas, ubicadas por el “enlazador” en las direcciones que se muestran: Función Dirección void rutina0 (void) interrupt 0 6000H void rutina1 (void) interrupt 1 6100H void rutina2 (void) interrupt 2 6200H Responda a las siguientes preguntas: a) ¿Qué interrupciones se utilizan en este sistema y cómo están programadas? b) ¿Qué función da servicio a cada una de las interrupciones utilizadas? c) Suponga que el microcontrolador, en un momento dado, se medio de la ejecución de la “rutina0” y se producen las dos restantes de forma simultánea. ¿Cuál sería la secuencia de código seguida por el microcontrolador? (indique tan sólo el 3 encuentra en interrupciones ejecución del nombre de la función, cuando considere que se debe ejecutar el programa principal indíquelo con “main”) d) Suponga que el microcontrolador, en un momento dado, se encuentra en medio de la ejecución de la “rutina0” y se producen las tres interrupciones de forma simultánea. ¿Cuál sería la secuencia de ejecución del código seguida por el microcontrolador? (indique tan sólo el nombre de la función, cuando considere que se debe ejecutar el programa principal indíquelo con “main”) e) ¿Qué se entiende por tiempo de latencia en una interrupción? SOLUCIÓN: a) Interrupción externa 0, activa por flanco de bajada y en el nivel bajo de prioridad. Interrupción externa 1, activa por flanco de bajada y en el nivel bajo de prioridad. Interrupción por desbordamiento del timer 0, en el nivel alto de prioridad. b) Externa 0: rutina0 (void) Externa 1: rutina2 (void) Timer 0: rutina1 (void) c) La secuencia en la ejecución sería la siguiente se producen las dos interrupciones restantes a la vez rutina0 → rutina1 → rutina0 (continuación) → main → → rutina2 → main d) La secuencia en la ejecución sería la siguiente se producen las tres interrupciones a la vez rutina0 → rutina1 → rutina0 (continuación) → main → → rutina0 (otra vez) → main → rutina2 → main e) El tiempo que tarda el microcontrolador desde que se produce la petición de interrupción hasta que se ejecuta la primera instrucción de la rutina de atención a la misma. No es un tiempo fijo, al contrario, se trata de un tiempo variable cuyo valor depende de tres parámetros principalmente: • Si se está ejecutando otra interrupción de mayor o igual prioridad, en cuyo caso habrá de esperar la nueva interrupción a que finalice la ejecución de la rutina en curso 4 • • Siempre tiene que ejecutarse por completo una instrucción antes de proceder con la secuencia de atención a la interrupción, por lo tanto el retraso dependerá del tiempo de ejecución de la instrucción en curso. Si la instrucción en curso es un RETI o una escritura en los registros IE o IP ha de ejecutarse una instrucción adicional. Por lo tanto, el tiempo de latencia es desconocido a priori y su valor dependerá de las circunstancias en las que se produzca la petición. EJERCICIO 2 Se quiere realizar un reloj de tiempo real con el 8051 programando uno de los timers para que genere interrupciones periódicamente, con una frecuencia que sea múltiplo de una centésima de segundo; también habrá que realizar la rutina de atención a la interrupción de manera que actualice los datos del reloj: las centésimas, los segundos, los minutos y las horas. Se utilizará el timer 0 del 8051 para realizar un temporizador con recarga automática, de manera que dicho timer interrumpa a la CPU con una frecuencia constante. La subrutina de atención a estas interrupciones se encargará de actualizar las variables que tendrán en todo momento los datos sobre las centésimas, los segundos, los minutos y las horas. Indique los valores (en binario) que deben tener los siguientes registros del 8051 para cumplir con las especificaciones anteriores, explicando brevemente el significado del contenido de cada uno de ellos: TMOD= TCON= IE= Sabiendo que el 8051 tiene conectado un reloj de 11.059 Mhz, calcule el número de cuenta que debería cargarse en TH0 para generar una interrupción cada centésima de segundo: TH0= Si el número calculado sobrepasa la capacidad de TH0 ¿cómo se puede resolver este problema? Realice el código en lenguaje C correspondiente al programa principal y a la rutina de atención a la interrupción del Timer 0. Teniendo en cuenta que: La rutina de atención a la interrupción del timer 0 debe actualizar convenientemente las centésimas, los segundos, los minutos y las horas de nuestro reloj. Cada uno de estos datos se manejará como un contador software que habrá que incrementar en uno o bien habrá que ponerlo a cero si ha llegado al final de su cuenta. 5 El programa principal, una vez realizada la inicialización del sistema, permanecerá en un bucle infinito en el que no deberá realizar ninguna tarea. SOLUCIÓN: unsigned short int cuenta_tics=0; /* cuenta las interrupciones del timer 0 */ unsigned short int centesimas=0; unsigned short int segundos=0; unsigned short int minutos=59; unsigned short int horas=23; /* Rutina de atención a la interrupción del timer 0 Actualiza los registros del reloj con el valor adecuado */ void tic_tac (void) interrupt 1 { cuenta_tics++; //almacena el numero de interrupciones que se producen if (cuenta_tics>63) //cuenta_tics = 64 indica que ha pasado una centésima //de segundo */ { cuenta_tics=0; // puesta a cero para iniciar la medida de una nueva //centésima centesimas++; if (centesimas>99) { centesimas=0; segundos++; if (segundos>59) { segundos=0; minutos++; if (minutos>59) { minutos=0; horas++; if (horas>23) horas=0; } } } } } main () { EA = 0; TMOD|=0x02; TMOD&=0xFD; // prohibimos interrupciones para programar los periféricos // timer 0 como temporizador sin GATE y en modo 2 TH0=256-144; // 1 centésima son 9216 tic_tacs del reloj de 11,059 MHz dividido por 12 9216/64 = 144, se cuentan 64 tic_tacs para medir una centésima /* ponemos en marcha el timer 0 */ TR0=1; 6 IE |= 0x02; EA = 1; /* habilitamos la interrupción del timer 0 */ /* habilitamos las interrupciones */ while (1); } EJERCICIO 3 NOTA: Se supone que la frecuencia del reloj del sistema es de 11MHz • Configurar el Timer 0 para que genere una interrupción de alta prioridad cada 8.9 mseg aproximadamente. Indique también las tareas que sería imprescindible realizar en la rutina de tratamiento de esta interrupción para que el funcionamiento fuera el correcto. • Calcular cual es el máximo periodo que se puede configurar entre interrupciones periódicas empleando este Timer • Realizar la programación del Timer para obtener una interrupción cada 200 useg • Calcular la precisión con la que se produce la interrupción anterior (suponiendo que es la única existente en el sistema) • Obtener el rango de periodos en los que se podría generar una interrupción empleando el modo 2 del Timer 0. • Indicar cómo podría generarse una interrupción cada segundo con la máxima resolución posible. SOLUCIÓN: a) Se emplea el Modo 0 ya que para conseguir este periodo se necesita un contador de 13bits TMOD & = 0xF0; IEN0 | = 0x02; IP0 | = 0x02; IEN0 | = 0x80; TCON | = 0x10; While (1); //Modo 0, temporizador //Habilita INTs Timer 0 //Int de alta prioridad //Habilita INTs //Habilita T0 En la rutina no hay que hacer nada puesto que el flag se borra solo. void timer_0 (void) interrupt 1 { Procesamiento de la interrupción } b) Se emplea el Modo 1 ya que este permite un mayor estado de cuenta al tener 16 bits 7 TMOD & = 0xF1; TMOD | = 0x01; IEN0 | = 0x02 ; IP0 | = 0x02; IEN0 | = 0x80; TCON | = 0x10; While (1); //Modo 1, temporizador //Habilita INTs Timer 0 //Int de alta prioridad //Habilita INTs //Habilita T0 En la rutina no hay que hacer nada puesto que el flag se borra solo. void timer_0 (void) interrupt 1 { Procesamiento de la interrupción } c) Se emplea el Modo 2 ya que este permite una recarga del contador que facilita la configuración de un tiempo concreto. Para 200useg son necesarios 183,3 ciclos de reloj del timer por lo que se aproxima por el entero más próximo 183 (0xB7). TMOD & = 0xF2; TMOD | = 0x02; TH0 = 0x49; IEN0 | = 0x02 ; IP0 | = 0x02; IEN0 | = 0x80; TCON | = 0x10; While (1); //Modo 2, temporizador //Habilita INTs Timer 0 //Int de alta prioridad //Habilita INTs //Habilita T0 El tiempo real entre interrupciones sería 199.69 useg En la rutina no hay que hacer nada puesto que el flag se borra solo. void timer_0 (void) interrupt 1 { Procesamiento de la interrupción } d) La precisión viene marcada por el reloj que le llega al contador: 1,09useg e) Para un valor de cuenta de 1 se obtiene un periodo de 1.09 useg Para un valor de cuenta de 255 se obtiene un periodo de 279,2 useg f) La solución pasa por contar interrupciones. La mayor precisión se obtendrá en el modo 2 que es recargable y permite afinar el periodo. Si por ejemplo si se cuentan hasta el valor 220 por parte del contador (TH0=0x23) se necesitarían 4166.66 interrupciones. En este caso el tiempo real entre interrupciones sería de 1.00008 segundos 8 EJERCICIO 4 NOTA: Se supone que la frecuencia del reloj del sistema es de 11MHz. a) Programar las comunicaciones asíncronas del 80c552 para enviar de forma continua el dato 0x55 por la línea de TX a la máxima velocidad posible empleando 8 bits de datos y dos de stop. El microcontrolador no debe realizar ninguna otra tarea. a. Empleando poolling b. Empleando interrupciones b) Programar el microcontrolador para enviar y recibir simultáneamente datos por el puerto serie a la velocidad de 9600 baudios con 8 bits de datos, 1 de parada y sin paridad. El dato a enviar será siempre el 0xAA; c) Programar el microcontrolador para enviar lo más rapidamente posible los 10 datos de un array de unsigned char llamado datos empleando velocidad de 200 baudios con 8 bits por dato, paridad par y con 1 bit de stop. Una vez finalizado el envio la UART debe quedar deshabilitada. d) Esperar a recibir 10 datos por el puerto serie y almacenarlos en el array unsigned char datos [10]. Una vez recibidos los 10 datos se inhabilitará toda Rx. Los datos llegan a 4800 baudios con 8 bits por dato, 1 bit de paridad y 1 bit de stop. (NOTA la paridad no es necesario analizarla) SOLUCIÓN: a) Empleando polling La máxima velocidad se obtiene con el modo 2 y activando el bit SMOD Unsigned char dato=0x55; PCON|=0x80; S0CON=0x88; //Se activa el bit SMOD //Modo 2 con dos bits de stop y 8 //de datos while (1) { S0BUF=dato; S0CON & =0xFD; While((S0CON&0x02)==0); } Empleando interrupciones La máxima velocidad se obtiene con el modo 2 y activando el bit SMOD Unsigned char dato=0x55; PCON| = 0x80; S0CON = 0x88; //Se activa el bit SMOD //Modo 2 con dos bits de stop y 8 //de datos //Interrupciones de la UART //Alta prioridad //Habilita ints IEN0 | = 0x10; IP0 | = 0x10; IEN0 | =0x80; 9 S0BUF=dato; While (1); //envía el primer dato Void Int_UART (void) interrupt 4 { if (S0CON&0x02) { S0BUF = dato; S0CON & = 0xFD; //Int de transmision? //enviar nuevo dato //Borrar el flag } } b) Para poder enviar 8 datos + 1 bit de parada es necesario trabajar en modo 1 puesto que los modos 2 y 3 solo permiten enviar tramas de 9 bits (+ 1 bit de stop) Para ajustar la velocidad de Tx/Rx es necesario emplear el Timer 1 como generador de reloj configurado en el modo 2 para posibilitar la recarga. El valor de recarga se obtendrá de la expresión BAUD=(2SMOD*fosc)/(32*12*(256-TH1)) Seleccionando SMOD = 1 y despejando queda TH1=250’032. Como se emplea 250 (0xFA) no se obtienen 9600 baudios sino 9548. Unsigned char dato_enviar=0x55, dato_recibir; TCON & = 0xB0; //Parar el T1 TMOD & = 0x2F; //Gate=0, CT=0 TMOD | = 0x20; //Modo 2 TH1=0xFA; //Ajuste de la velocidad PCON| = 0x80; //Se activa el bit SMOD S0CON = 0x50; //Modo 1 con 1 bit de stop y 8 //de datos. Se activa REN IEN0 | = 0x10; //Interrupciones de la UART IP0 | = 0x10; //Alta prioridad IEN0 | =0x80; //Habilita ints TCON | = 0x40; //Arranca el T1 S0BUF=dato; //envío del primer dato While (1); Void Int_UART (void) interrupt 4 { if (S0CON&0x02){ S0BUF = dato_enviar; S0CON & = 0xFD; } if (S0CON&0x01) { dato_recibir=S0BUF; Tratamiento (dato_recibir) S0CON & = 0xFE; } } 10 //Int de transmision? //enviar nuevo dato //Borrar el flag //Int de recepción? //se lee el dato //tratamiento de un dato recibido //Borrar el flag c) Para enviar 8+1+1 es necesario trabajar en modo 2 o modo 3. De estos dos el que permite ajustar la velocidad de salida es el modo 3. Para calcular el valor de recarga del Timer1 es necesario emplear la expresión BAUD=(2SMOD*fosc)/(32*12*(256-TH1)). Si se selecciona SMOD=0 el resultado es TH1=113 (para SMOD=1 no sería posible) Unsigned char datos[10]; Unsigned char paridad_par; TCON & = 0xB0; TMOD & = 0x2F; TMOD | = 0x20; TH1=0x71; PCON & = 0x7F; S0CON = 0xC0; IEN0 | = 0x10; IP0 | = 0x10; IEN0 | =0x80; TCON | = 0x40; Paridad_par=CalculaParidad(dato[0]); If (Paridad_par) S0CON & = 0xF7; Else S0CON | = 0x08; S0BUF=dato[0]; //Parar el T1 //Gate=0, CT=0 //Modo 2 //Ajuste de la velocidad //Desactiva el bit SMOD //Modo 3 con 1 bit de stop 1 de //paridad y 8 de datos. //Interrupciones de la UART //Alta prioridad //Habilita ints //Arranca el T1 //se pone a 0 la paridad //se pone a 1 la paridad //envio del primer dato While (1); Void Int_UART (void) interrupt 4 { static unsigned int cuenta=1; if (S0CON&0x02){ //Int de transmision? Paridad_par=CalculaParidad(dato[cuenta++]); If (Paridad_par) S0CON & = 0xF7; //se pone a 0 la paridad Else S0CON | = 0x08; //se pone a 1 la paridad If (cuenta<10) S0BUF = dato[cuenta]; //enviar nuevo dato S0CON & = 0xFD; //Borrar el flag } } unsigned char CalculaParidad(unsigned char dato) { unsigned char paridad=0; for (i=0;i<8;i++) { paridad=paridad^((dato>>i)&0x01); } 11 return paridad; } d) Para recibir 8+1+1 es necesario trabajar en modo 2 o modo 3. De estos dos el que permite ajustar la velocidad de salida es el modo 3. Para calcular el valor de recarga del Timer1 es necesario emplear la expresión BAUD=(2SMOD*fosc)/(32*12*(256-TH1)). Si se selecciona SMOD=0 el resultado es TH1=0xFA Unsigned char datos[10]; Unsigned char paridad_par; TCON & = 0xB0; TMOD & = 0x2F; TMOD | = 0x20; TH1=0xFA; PCON & = 0x7F; S0CON = 0xD0; //Parar el T1 //Gate=0, CT=0 //Modo 2 //Se ajusta la velocidad //Desactiva el bit SMOD //Modo 3 con 1 bit de stop 1 de //paridad y 8 de datos y Rx activa. //Interrupciones de la UART //Alta prioridad //Habilita INTs //Arranca el T1 IEN0 | = 0x10; IP0 | = 0x10; IEN0 | =0x80; TCON | = 0x40; While (1); Void Int_UART (void) interrupt 4 { static unsigned int cuenta=0; if (S0CON&0x01){ dato[cuenta++]= S0BUF; If (cuenta==10) S0CON&=0xEF; S0CON & = 0xFE; } } 12 //Int de recepcion? //guardar nuevo dato //Deshabilita Rx //Borrar el flag