Descripción (o un poco de filosofía barata)
Hay procesos que no sabemos cuánto duran (en tiempo) pero si sabemos cuánto es el máximo que pueden durar (o que queremos que duren).
A este tiempo máximo vamos a llamarlo periodo para
timeout (o salida de tiempo).
Un definición estándar en Informática podría ser : Parámetro que indica a un programa el tiempo máximo de espera antes de abortar una tarea o función. O
darla por concluida que es tipo de
timeout que vamos a implementar en nuestro pequeño ejemplo.
Uno de los usos mas frecuentes del control de procesos mediante
timeout es para abandonar procesos que deberían acabar normalmente pero que por cualquier circunstancia no prevista o incontrolada no se acaba.
Si durante un proceso
P la única condición de salida del mismo es
S y hay veces en que no se produce entonces el proceso
P se quedará "
colgado" al no abandonar nunca su funcionamiento dado que la condición
S no se produce.
El uso de
timeout consiste es arrancar un
timer al mismo tiempo que se inicia el proceso
P. Y en éste se implementan dos salidas posibles, nuestra anterior salida
S y en paralelo (
OR) una nueva: la de haber cumplido el tiempo
T que hemos fijado para el
timer.
A esta nueva condición de salida la conocemos como
Salida por Timeout.
Ejemplo (o la imaginación al poder)
Supongamos, imaginemos, planteemos un ejemplo donde lo que hemos visto nos sirva para algo (en caso contrario estaría un poco fuera de lugar ¿no?)
Supongamos, decíamos, que tenemos una
recepción vía USART serie de
N caracteres, número que desconocemos y que unas veces es un solo carácter y otras todo un
string de decenas de ellos.
Nunca sabemos cuando va a llegar el primero ni cuantos van a seguirle, pero sabemos que van a venir todos mas o menos juntos, uno tras otro (o en pequeños paquetes) pero que nunca, nuca, nunca va a haber entre dos caracteres consecutivos mas de unos pocos milisegundos, digamos 5 ms por poner un ejemplo concreto.
Podemos entonces decir que: Si recibimos un carácter inicial cualquier otro nos llegará antes de 5 ms, si pasan mas de esos 5 ms entonces el paquete a recibir está completo, ya ha acabado y podemos procesarlo.
En este caso es razonable decir también que si establecemos un
timeout de 10 ms tendremos la seguridad de que hemos recogido el paquete completo.
Vamos entonces a acotar nuestro ejemplo describiendo lo que debemos implementar:
Recibimos todos los caracteres que nos vayan llegando, cada vez que nos llega uno de ellos activamos un timer que cuente el paso de 10 ms y lo ponemos a cero, si el timer llega a completarse es que han pasado esos mismos 10 ms tras la recepción del último carácter. Ponemos entonces un flag en alto indicando esta circunstancia y paramos el timer.
Si recibimos un nuevo carácter todo comenzará de nuevo.
Notad que el truco está en "
reiniciar el timer del timeout cada vez que se recibe un carácter. Sólo cuando se dejen de recibir caracteres el timer completará su recorrido y hará saltar el indicador de timeout"
Implementación en C (o un razón para justificarme ante ustedes)
Bueno ahora solo nos queda escribir un poco de código que le enseñe a nuestro PIC que todo lo anterior no solo es posible hacerlo sino exactamente cómo queremos que lo haga.
Primero vamos a montar un timer que desborde cada 10 ms mediante su particular interrupción. En principio no la habilitamos ya que esto se hará cuando se reciba el primer carácter. (con ánimo de no complicar innecesariamente este ejemplo voy a dar por valido el que el Timer0 se desborda exactamente cada 10 ms, esto no es necesariamente cierto pero el hacer eso es cuestión de configurar correctamente el timer, y no aporta ni quita nada al ejemplo que estamos estudiando)
///////////////////////////////////////
// RAM
///////////////////////////////////////
int1 flagTimeOut = 0x00; // Flag quea indica timeout completo, inicializado a 0
///////////////////////////////////////
// INTERRUPCIONES : TIMER0 para timeout
///////////////////////////////////////
#int_timer0
void timer0_isr() { // Interrupción por desbordamiento de timer0
flagTimeOut = 1;
}
Despues una simple #int_rda que recibe caracteres sobre un buffer, pero tiene la particularidad de que habilita/inicializa el timer cada vez que recibe un nuevo caracter:
///////////////////////////////////////
// RAM
///////////////////////////////////////
int xbuff=0x00; // Índice: siguiente char en cbuff
char cbuff[lenbuff]; // Buffer de recepción
///////////////////////////////////////
// INTERRUPCIONES : RDA Recepción USART
///////////////////////////////////////
#int_rda
void serial_isr() { // Interrupción recepción serie USART
if(kbhit()){ // Si hay algo pendiente de recibir ...
cbuff[xbuff++]=getc();
set_timer0(0);
enable_interrups(int_timer0);
}
}
Y por fin en nuestro main() lo que hacemos es habilitar la recepción RDA pero no el Timer0 y esperar en el bucle infinito a que salte el
Timeout para poder procesar la recepción:
void main() {
enable_interrupts(int_rda); // Habilitamos la interrupción por recepción serie
enable_interrupts(global); // Habilitamos las interrupciones
do{ // inicio del Bucle infinito
if(flagTimeOut ==1){ // Si hemos completado una recepción ...
flagTimeOut =0; // lo desmarcamos para poder reiniciar el tema
disable_interrups(int_timer0); // detenemos el timer0
disable_interrupts(int_rda); // deshabilitamos la interrupción serie mientras procesamos
execute_lo_que_sea(); // Aqui podemos procesar lo recibido sobre cbuff.
xbuff=0x00; // reiniciamos el buffer de recepción
enable_interrupts(int_rda); // Habilitamos la interrupción por recepción serie
}
}while(TRUE); // final del Bucle infinito
}
Bueno, creo que eso es todo. Perdonad los errores u omisiones que haya podido cometer.
Espero que os sirva.
Mañana mas.