Autor Tema: El concepto de Timeout (y un ejemplo de uso en C)  (Leído 17663 veces)

0 Usuarios y 1 Visitante están viendo este tema.

Desconectado RedPic

  • Administrador
  • DsPIC33
  • *******
  • Mensajes: 5538
    • Picmania by Redraven
El concepto de Timeout (y un ejemplo de uso en C)
« en: 25 de Julio de 2009, 09:26:26 »
 :idea:   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.


 :arrow:   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)

Código: C#
  1. ///////////////////////////////////////
  2. // RAM
  3. ///////////////////////////////////////
  4.  
  5. int1  flagTimeOut = 0x00;     // Flag quea indica timeout completo, inicializado a 0
  6.  
  7.  
  8. ///////////////////////////////////////
  9. // INTERRUPCIONES : TIMER0 para timeout
  10. ///////////////////////////////////////
  11.  
  12. #int_timer0
  13. void timer0_isr() {          // Interrupción por desbordamiento de timer0
  14.  
  15.    flagTimeOut = 1;
  16.  
  17. }

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:

Código: C#
  1. ///////////////////////////////////////
  2. // RAM
  3. ///////////////////////////////////////
  4.  
  5. int  xbuff=0x00;     // Índice: siguiente char en cbuff
  6. char cbuff[lenbuff]; // Buffer de recepción
  7.  
  8. ///////////////////////////////////////
  9. // INTERRUPCIONES : RDA Recepción USART
  10. ///////////////////////////////////////
  11.  
  12. #int_rda
  13. void serial_isr() {          // Interrupción recepción serie USART
  14.  
  15.    if(kbhit()){              // Si hay algo pendiente de recibir ...
  16.       cbuff[xbuff++]=getc();
  17.       set_timer0(0);
  18.       enable_interrups(int_timer0);
  19.    }
  20. }

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:

Código: C#
  1. void main() {
  2.  
  3.    enable_interrupts(int_rda);    // Habilitamos la interrupción por recepción serie
  4.    enable_interrupts(global);     // Habilitamos las interrupciones
  5.  
  6.  
  7.    do{                            // inicio del Bucle infinito
  8.  
  9.       if(flagTimeOut ==1){          // Si hemos completado una recepción ...
  10.          flagTimeOut =0;      // lo desmarcamos para poder reiniciar el tema
  11.          disable_interrups(int_timer0); // detenemos el timer0
  12.          disable_interrupts(int_rda);    // deshabilitamos la interrupción serie mientras procesamos
  13.  
  14.  
  15.          execute_lo_que_sea(); // Aqui podemos procesar lo recibido sobre cbuff.
  16.  
  17.          xbuff=0x00;       // reiniciamos el buffer de recepción
  18.          enable_interrupts(int_rda);    // Habilitamos la interrupción por recepción serie
  19.          
  20.       }
  21.  
  22.    }while(TRUE);                  // final del Bucle infinito
  23. }


Bueno, creo que eso es todo. Perdonad los errores u omisiones que haya podido cometer.

Espero que os sirva.  :mrgreen:

Mañana mas.

« Última modificación: 25 de Julio de 2009, 09:28:55 por RedPic »
Contra la estupidez los propios dioses luchan en vano. Schiller
Mi Güeb : Picmania

Desconectado jhozate

  • Colaborador
  • PIC24H
  • *****
  • Mensajes: 1697
Re: El concepto de Timeout (y un ejemplo de uso en C)
« Respuesta #1 en: 25 de Julio de 2009, 15:24:59 »
Don diego ud es el maestro que se quisiera tener todas las clases...gracias por compartir ese conocimiento :-/
Ser Colombiano es un Premio, Saludos desde CALI-COLOMBIA

Desconectado MLO__

  • Colaborador
  • DsPIC33
  • *****
  • Mensajes: 4581
Re: El concepto de Timeout (y un ejemplo de uso en C)
« Respuesta #2 en: 25 de Julio de 2009, 17:37:00 »
Genial.

La habilitacion del Timer no deberia ser antes de la funcion getc();, ya que es justamente ahi donde existe el peligro de que el micro se quede colgado esperando eternamente la llegada del caracter?.
El papel lo aguanta todo

Desconectado RedPic

  • Administrador
  • DsPIC33
  • *******
  • Mensajes: 5538
    • Picmania by Redraven
Re: El concepto de Timeout (y un ejemplo de uso en C)
« Respuesta #3 en: 25 de Julio de 2009, 17:45:48 »
Podría pero ten en cuenta que getc() está protegido por kbhit() que hace que solo se ejecute getc() cuando realmente hay un carácter en la USART.

De hecho kbhit() no es necesario ya que esta implementación usa la interrupción #int_rda por lo que solo "saltará" cuando haya un carácter a recibir.

La interrupción podría ser perfectamente esta:

Código: C#
  1. ///////////////////////////////////////
  2. // INTERRUPCIONES : RDA Recepción USART
  3. ///////////////////////////////////////
  4.  
  5. #int_rda
  6. void serial_isr() {          // Interrupción recepción serie USART
  7.  
  8.       set_timer0(0);
  9.       enable_interrups(int_timer0);
  10.       cbuff[xbuff++]=getc();
  11. }


kbhit() está pensado para utilizarlo en el bucle infinito, no por interrupción, y que ejecute getc() sólo cuando llegue algo a la USART.

Así sería idéntico a la versión anterior pero sin interrupción:

Código: C#
  1. ///////////////////////////////////////
  2. // RAM
  3. ///////////////////////////////////////
  4.  
  5. int  xbuff=0x00;     // Índice: siguiente char en cbuff
  6. char cbuff[lenbuff]; // Buffer de recepción
  7. int1  flagTimeOut = 0x00;     // Flag que indica timeout completo, inicializado a 0
  8.  
  9. // INTERRUPCIONES : TIMER0 para timeout
  10. ///////////////////////////////////////
  11.  
  12. #int_timer0
  13. void timer0_isr() {          // Interrupción por desbordamiento de timer0
  14.  
  15.   flagTimeOut = 1;
  16.  
  17. }
  18.  
  19. void main() {
  20.  
  21.   enable_interrupts(global);     // Habilitamos las interrupciones
  22.  
  23.  
  24.   do{                            // inicio del Bucle infinito
  25.  
  26.      if(kbhit()){              // Si hay algo pendiente de recibir ...
  27.         cbuff[xbuff++]=getc();
  28.         set_timer0(0);
  29.         enable_interrups(int_timer0);
  30.      }
  31.  
  32.      if(flagTimeOut ==1){          // Si hemos completado una recepción ...
  33.         flagTimeOut =0;      // lo desmarcamos para poder reiniciar el tema
  34.         disable_interrups(int_timer0); // detenemos el timer0
  35.  
  36.         execute_lo_que_sea(); // Aqui podemos procesar lo recibido sobre cbuff.
  37.  
  38.         xbuff=0x00;       // reiniciamos el buffer de recepción
  39.      }
  40.  
  41.   }while(TRUE);                  // final del Bucle infinito
  42. }

« Última modificación: 25 de Julio de 2009, 17:56:28 por RedPic »
Contra la estupidez los propios dioses luchan en vano. Schiller
Mi Güeb : Picmania

Desconectado MLO__

  • Colaborador
  • DsPIC33
  • *****
  • Mensajes: 4581
Re: El concepto de Timeout (y un ejemplo de uso en C)
« Respuesta #4 en: 25 de Julio de 2009, 17:50:01 »
Si, tienes razon.

La ventaja de activar la interrupcion antes seria como si -suponiendo que todo es posible en esta vida- en medio de la transmision de un byte, digamos en el 4 bit, llegase a pasar algo ... o CCS ya contempla eso en la funcion kbhit()?

Esta de lujo. Gracias señor Diego.

Saludos
El papel lo aguanta todo

Desconectado RedPic

  • Administrador
  • DsPIC33
  • *******
  • Mensajes: 5538
    • Picmania by Redraven
Re: El concepto de Timeout (y un ejemplo de uso en C)
« Respuesta #5 en: 25 de Julio de 2009, 17:57:31 »
El problema (y es un gran problema) es que esta forma de implementar este sistema, sin interrupción, requiere que el resto de código dentro del bucle infinito que sigue al trozo de if kbhit(){...} no tarde mas del tiempo timeout que hayamos fijado ya que entonces saltará éste de forma errónea, no porque no nos lleguen mas caracteres en ese plazo sino porque nuestro programa no los recoge a la velocidad adecuada.

Este efecto perverso no se da con la interrupción ya que ésta si que hace que el programa esté donde esté recoja cada carácter recién llegado.  :mrgreen:
Contra la estupidez los propios dioses luchan en vano. Schiller
Mi Güeb : Picmania

Desconectado micronet3

  • PIC18
  • ****
  • Mensajes: 288
Re: El concepto de Timeout (y un ejemplo de uso en C)
« Respuesta #6 en: 24 de Agosto de 2009, 13:44:18 »
ese timeout no se puede incluir asi?

#use rs232(baud=19200, xmit=PIN_C6, rcv=PIN_C7, stream=pc,errors,timeout=5000);


Desconectado RedPic

  • Administrador
  • DsPIC33
  • *******
  • Mensajes: 5538
    • Picmania by Redraven
Re: El concepto de Timeout (y un ejemplo de uso en C)
« Respuesta #7 en: 24 de Agosto de 2009, 18:32:24 »
En la ayuda de mi CCS (3.242) no aparece el parámetro TIMEOUT.

De todas formas esto del RS232 solo lo he utilizado como ejemplo, hay cientos de situaciones en las que necesitamos "salir" por tiempo y no disponemos de un recurso como el que nos estás sugiriendo.
Contra la estupidez los propios dioses luchan en vano. Schiller
Mi Güeb : Picmania

Desconectado MLO__

  • Colaborador
  • DsPIC33
  • *****
  • Mensajes: 4581
Re: El concepto de Timeout (y un ejemplo de uso en C)
« Respuesta #8 en: 24 de Agosto de 2009, 22:19:54 »
Citar
TIMEOUT=X

To set the time getc() waits for a byte in milliseconds.  If no character comes in within this time the RS232_ERRORS is set to 0 as well as the return value form getc().  This works for both UART and non-UART ports.


Esto esta en las versiones nuevas del CCS.

El concepto que explicas Don Diego, lo aplico en el RS485, donde va de lujo, ya que ademas, se puede identificar que modulo fue el que no respondio correctamente.

Saludos

El papel lo aguanta todo

Desconectado micronet3

  • PIC18
  • ****
  • Mensajes: 288
Re: El concepto de Timeout (y un ejemplo de uso en C)
« Respuesta #9 en: 25 de Agosto de 2009, 13:57:51 »
pues si, esto creo que viene en las versiones modernas
redpic, una consulta, por favor, por lo que veo el timer0 se desborda dentro de la interrupcion int_rda, una vez que el timer0 se desborda  salta a la rutina de interrupcion del timer0(interrupciones anidadas), despues que termina de ejecutar las instrucciones en el timer0, a donde va a parar el contador del programa,

regresa a la interrupcion int_rda?  o regresa al programa principal?

he estado probando el timeout de esta manera
#use rs232(baud=19200, xmit=PIN_C6, rcv=PIN_C7, stream=pc,errors,timeout=5000); pero no me funciona, es como si inhabilitara la recepcion de datos.
estoy recibiendo datos con la funcion gets().



Desconectado micronet3

  • PIC18
  • ****
  • Mensajes: 288
Re: El concepto de Timeout (y un ejemplo de uso en C)
« Respuesta #10 en: 01 de Septiembre de 2009, 23:37:00 »
asup, yo si que me complico la vida, el ejemplo esta bien claro, gracias redpic


 

anything