Un método para realizar temporizaciones de precisión en el PC

Aunque ausente en todos los manuales de referencia técnica y en todos los libros relacionados con la programación de PC, existe un método muy fácil y eficiente para temporizar, disponible en todos los ordenadores AT. Pese a no estar documentado, un programa muy usual como es el KEYB del MS-DOS (a partir de la versión 5.0 del sistema) lo utiliza sin importar el modelo de AT. Por ello, cabe suponer que seguramente los futuros equipos mantendrán la compatibilidad en este aspecto. Sucede que la salida del contador 1 del 8254, encargada del refresco de la memoria, controla de alguna manera desconocida (tal vez a través de un flip-flop) la generación de una onda cuadrada de unos 33 KHz que puede leerse a través del bit 4 del puerto 61h (no se trata de la salida OUT del contador 1: éste está programado en modo 2 y no genera precisamente una onda cuadrada). El contador 1 es programado por la BIOS en los PC con una cuenta 18, conmutando el nivel de la salida cada segundo 1193180/18 = 66287,78 veces. Para hacer un determinado retardo basta con contar las veces que el bit cambia de nivel. Para los más curiosos, decir que el bit 5 del puerto 61h es la salida OUT del contador 2 del 8254 (la línea OUT del contador 2 del 8253 de los PC/XT también puede consultarse a través del bit 5, pero del puerto 62h).

Este método es especialmente interesante en los programas residentes que precisen retardos de precisión, para sonido u otras tareas, tales como limitar la duración máxima de una comprobación en un bit de estado a unos milisegundos o microsegundos (control de timeouts); la principal ventaja es que no se modifica en absoluto la configuración de ningún chip que pueda estar empleando el programa principal, empezando por el 8254. Además, no requiere preparación previa alguna. En nuestro caso podemos utilizar este método como una forma de generar por software la modulación por anchura de pulso necesaria para el control de los servomotores.

La rutina pausa_asm ilustra el procedimiento antedicho. El único inconveniente del método es la alta frecuencia con que cambia el bit: esta misma rutina escrita en C podría no ser suficientemente ágil para detectar todas las transiciones en las máquinas AT más lentas a 6 MHz. A partir de 8 MHz sí puede ser factible, como evidencian las pruebas realizadas, aunque hay que extremar las precauciones para que el código compilado sea lo bastante rápido: utilizar las dos variables registro que realmente soportan los compiladores y huir de la aritmética de 32 bits, como puede observarse en la función pausa() del programa de ejemplo. Una mala codificación o el compilador podrían hacer inservible el método incluso en una máquina a 16 ó 20 MHz (ya prácticamente fuera de uso en estos momentos).

/****************************************************************/
/*								*/
/*	Rutinas de temporización de precisión en el PC		*/
/*	2002 Víctor R. González					*/
/*								*/
/****************************************************************/

/*
   El tiempo se mide en cuentas.
   Un segundo equivale a 66288 cuentas.
   Una cuenta equivale a 1/66287.78 s = 15.086 microsegundos.
   Ésta es la resolución temporal que se consigue con este método.
*/


void pausa(unsigned long cuenta)   /* método en C */
{
  register a = NULL, b;
  unsigned cuenta_h, cuenta_l;
  cuenta_h=cuenta >> 16;  cuenta_l=cuenta & 0xFFFF;
  do
	do {
	  while (a==(b=inportb(0x61) & 0x10));
	  a=b;
	} while (cuenta_l--);
  while (cuenta_h--);
}


void pausa_asm (unsigned long cuenta)   /* método en ensamblador (recomendado) */
{
	    asm   push  ax
            asm   push  cx
            asm   push  dx
	    asm   mov   cx,word ptr cuenta	/* DX:CX = cuenta */
	    asm   mov   dx,word ptr [cuenta+2]
	    asm   jcxz  fin_l           /* posible cuenta baja nula */
  esp_ref:  asm   in    al,61h
	    asm   and   al,10h          /* aisla bit 4 */
	    asm   cmp   al,ah
	    asm   je    esp_ref         /* espera cambio de nivel */
	    asm   mov   ah,al
            asm   loop  esp_ref         /* completa cuenta baja */
  fin_l:    asm   and   dx,dx
            asm   jz    fin_ret         /* posible cuenta alta nula */
	    asm   dec   dx
	    asm   jmp   esp_ref         /* completa cuenta alta */
  fin_ret:  asm   pop   dx
	    asm   pop   cx
	    asm   pop   ax
}

 

El temporizador 8253/8254

     El 8253/4 es un chip temporizador que puede ser empleado como reloj de tiempo real, contador de sucesos, generador de ritmo programable, generador de onda cuadrada, etc. Este integrado posee 3 contadores totalmente independientes, que pueden ser programados de 6 formas diferentes.
D7..D0: BUS de datos bidireccional de 3 estados.
CLK 0: CLOCK 0, entrada de reloj al contador 0.
OUT 0: Salida del contador 0.
GATE 0: Puerta de entrada al contador 0.
CLK 1: CLOCK 1, entrada de reloj al contador 1.
OUT 1: Salida del contador 1.
GATE 1: Puerta de entrada al contador 1.
CLK 2: CLOCK 2, entrada de reloj al contador 2.
OUT 2: Salida del contador 2.
GATE 2: Puerta de entrada al contador 2.
A0..A1: Líneas de dirección para seleccionar uno de los tres contadores o el registro de la palabra de control.
-CS: Habilita la comunicación con la CPU.
-WR: Permite al 8254 aceptar datos de la CPU.
-RD: Permite al 8254 enviar datos a la CPU.

Todos los AT y PS/2 llevan instalado un 8254 o algo equivalente; los PC/XT van equipados con un 8253, algo menos versátil; los PS/2 más avanzados tienen un temporizador con un cuarto contador ligado a la interrupción no enmascarable. Todos los contadores van conectados a un reloj que oscila a una frecuencia de 1.193.180 ciclos por segundo (casi 1,2 Mhz). La dirección base en el espacio de E/S del ordenador elegida por IBM cuando diseñó el PC es la 40h. Por tanto, los tres contadores son accedidos, respectivamente, a través de los puertos 40h, 41h y 42h; la palabra de control se envía al puerto 43h.

La señal GATE de los contadores 0 y 1 está siempre a 1; en el contador 2 es seleccionable el nivel de la línea GATE a través de bit 0 del puerto E/S 61h. La BIOS programa por defecto el contador 0 en el modo 3 (generador de onda cuadrada) y el contador 1 en el modo 2 (generador de ritmo); el usuario normalmente programa el contador 2 en el modo 2 ó 3.

La salida del contador 0 está conectada a IRQ 0 (ligado a la INT 8, que a su vez invoca a INT 1Ch); este contador está programado por defecto con el valor cero (equivalente a 65536), por lo que la cadencia de los pulsos es de 1.193.180/65.536 = 18,2 veces por segundo, valor que determina la precisión del reloj del sistema, ciertamente demasiado baja. Se puede modificar el valor de recarga de este contador en un programa, llamando a la vieja INT 8 cada 1/18,2 segundos para no alterar el funcionamiento normal del ordenador, si bien no es conveniente instalar programas residentes que cambien permanentemente esta especificación: los programas del usuario esperan encontrarse el temporizador a la habitual y poco útil frecuencia de 18,2 interrupciones/segundo.

 La salida del contador 1 controla el refresco de memoria en todas las máquinas, su valor normal para el divisor es 18; aumentándolo se puede acelerar el funcionamiento del ordenador, con el riesgo -eso sí- de un fallo en la memoria, detectado por los chips de paridad -si los hay-, que provoca generalmente el bloqueo del equipo. De todas maneras, en los PC/XT se puede aumentar entre 19 y 1000 sin demasiados riesgos, acelerándose en ocasiones hasta casi un 10% la velocidad de proceso del equipo. En los AT la ganancia de velocidad es mucho menor y además este es un punto demasiado sensible que conviene no tocar para no correr riesgos, aunque se podría bajar hasta un valor 2-17 para ralentizar el sistema. Sin embargo, no es conveniente alterar esta especificación porque, como se verá más adelante, hay un método para realizar retardos (empleado por la BIOS y algunas aplicaciones) que se vería afectado.

El contador 2 puede estar conectado al altavoz del ordenador para producir sonido; alternativamente puede emplearse para temporizar. Es el único contador que queda realmente libre para el usuario, lo que suele dar quebraderos de cabeza a la hora de producir sonido.


Temporización

Los contadores 0 y 1, especialmente este último, ya están ocupados por el sistema; en la práctica el único disponible es el 2. Este contador ha sido conectado con el doble propósito de temporizar y de generar sonido. Para emplearlo en las temporizaciones, es preciso habilitar la puerta GATE activando el bit 0 del puerto 61h; también hay que asegurarse de que la salida del contador no está conectada al altavoz (a menos que se desee música mientras se cronometra) poniendo a 0 el bit 1 del mismo puerto (61h):

        IN     AL,61h
        AND    AL,11111101b     ; borrar bit 1  (conexión contador 2 con el altavoz)
        OR     AL,00000001b     ; activar bit 0 (línea GATE del contador 2)
        JMP    SHORT $+2        ; estado de espera para E/S
        OUT    61h,AL

volver a Inicio