Autor Tema: Serie Técnicas en C : Múltiples lapsus largos de tiempo con una interrupción.  (Leído 6501 veces)

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

Desconectado RedPic

  • Administrador
  • DsPIC33
  • *******
  • Mensajes: 5391
    • Picmania by Redraven
Múltiples lapsus largos de tiempo con una sola interrupción.

A.- Conceptos involucrados:

Hay ocasiones en que nuestro programa tiene la necesidad de disparar un multitud de eventos cada uno con un lapsus relativamente largos de tiempos distintos, y que además no sabemos cuando ha de dispararse cada uno de ellos.

Puede ser que necesitemos que cierto pin se ponga en alto durante 1 segundo, otro durante 3 segundos y aún otro más que lo haga durante solo alguna fracción se segundo, algunas centenas de milisegundos, y en cualquier relación entre ellos, solapándose entre sí  a veces y sin que por ello nuestro programa pueda ni deba esperar a que uno de ellos termine o comience para disparar cualquiera de los otros.

En estas circunstancias nos está totalmente prohibido el usar instrucciones del tipo delay_ms() ya que su uso implica detener toda actividad de nuestro programa hasta que dicho lapsus de tiempo termine de pasar.

La idea es implementar un juego de contadores, de segundos o milisegundos según nos interese la unidad de medida a utilizar, que se decrementen dentro de una interrupción de alguno de los timers de nuestro PIC. Cuando uno de estos contadores alcanza el valor de 0 activa un flag indicándonos que el tiempo ha transcurrido completo.

Para activar entonces uno de estos contadores no tenemos mas que ponerles el valor de las unidades de tiempo que deseamos medir y entonces esperar a que su valor llegue a cero, que nos será indicado mediante el flag correspondiente. Detectamos dicho flag, lo bajamos y podemos proceder a hacer que lo íbamos ha hacer transcurrido el tiempo deseado.

B.- Técnica a Aplicar:

Para implementar nuestros disparadores de eventos vamos a utilizar un recurso del que disponen la inmensa mayoría de los PIC's, tanto los de la serie 18F como los 16F: El TIMER1.

* El TIMER1 es un contador de 16 bits que se incrementa cada 4 ciclos de Reloj (FOSC /4). A este tiempo le vamos a llamar Tick de TIMER1.

Cuando el TIMER1 alcanza el valor 0xFFFF continúa de nuevo por 0x0000, y así hasta el infinito y mas allá. Cuando pasa de 0xFFFF a 0x0000 genera una Interrupción por Desbordamiento de Timer1 que es la que vamos a utilizar para definir nuestra unidad mínima de cómputo de tiempo.

Si el cristal de cuarzo con el calzamos nuestro PIC es de 20 Mhz, por ejemplo, entonces cada 4 * 1/20.000.000 = 0,0000002 segundos (0.2 microsegundos) se produce un Tick de TIMER1.

Para una vuelta completa del TIMER1, desde 0x0000 hasta 0xFFFF (65536 pasos), ocupa un tiempo total de 0,0000002 * 65.536 = 0,0131072 segundos (13,1072 milisegundos) o sea 65536 Ticks del Timer1.

Para estos tiempos calculados con este cristal en concreto podemos hacer un sencillo cálculo de cuantas interrupciones deben ocurrir para que contemos un segundo transcurrido: 1000 / 13,1072 = 76,2939453125 lo que significa que con 76 interrupciones contaremos 76 * 13,1072 = 996,1472 milisegundos que va a constituir nuestro "segundo" (Para un artículo posterior veremos la técnica necesaria para conseguir un "segundo perfecto").

Así establecemos una constante a la que llamaremos nTimer1_for_1_second que nos dice cuantas interrupciones de Timer1 necesitamos para computar un segundo y en este caso va a valer 76.

Con distintos Cristales vamos a tener distintos tiempos de disparo y entonces debemos rehacer los cálculos tal como hemos visto anteriormente para obtener el valor correcto de nTimer1_for_1_second.

El núcleo central de nuestra técnica va a estar construida alrededor de la interrupción #INT_TIMER1 en la que vamos a disponer de dos recursosen uno distintos: El contador de interrupciones propiamente dicho: una variable en RAM a la que llamaremos nTimer1Overflow y que incrementaremos cada vez que entremos en la interrupción. Y en paralelo un "contador de segundos" que vamos a conseguir comparando nuestro contador de interrupciones nTimer1Overflow con la constante de nTimer1_for_1_second, cuando ambos sean iguales sabremos que ha transcurrido un segundo, haremos lo que tengamos que hacer y pondremos a cero nTimer1Overflow  para empezar a contar otro segundo.

Para cada uno de los distintos Disparadores de Evento que implementemos vamos a necesitar  un par de variables en RAM: Un contador retrógrado de segundos que tenemos que esperar para disparar el evento al que llamaremos genéricamente nSECS_for_Event_1 y un flag llamado flag_for_Event_1 que nos indique que ese lapsus en concreto ha transcurrido.

Disparar un evento consiste en poner a cero su flag, cargar su contador de segundos con el valor que deseemos y esperar a que el flag se ponga a 1.

C.- Implementación en C:

Para implementar nuestro Código en C vamos a hacerlo primero con la estructura de la rutina de servicio de la interrupción y después atacaremos un ejemplo de su uso desde el main().
 
Las constantes, la  RAM y la interrupción queda de la siguiente forma:
Código: C#
  1.  
  2. // Constantes
  3. const int8 nTimer1_for_1_second=76;
  4.  
  5. // Variables en RAM
  6. int8 nTimer1Overflow; // Contador de Interrupciones
  7. int8 nSECS_for_Event_1; // Contador Segundos para Evento 1
  8. int1 flag_for_Event_1; // Flag para detectar evento 1
  9.  
  10.  
  11. // Interrupción por desbordamiento de Timer1
  12. #int_timer1
  13. void interrupt_service_rutine_timer1(void) {
  14.  
  15.    ////////////////////////////////////////////////////////////////////////////////////////////////
  16.    // Cómputo de segundos completos (13,1072 milisegundos * _nTimer1_for_1_second = 996,1472 milisegundos)
  17.    // Todo lo que se controla dentro de este bloque sólo se computa por segundos completos.
  18.    ////////////////////////////////////////////////////////////////////////////////////////////////
  19.    if(++nTimer1Overflow==nTimer1_for_1_second){
  20.  
  21.       // Control de Evento Número 1
  22.       if(nSECS_for_Event_1!=0){ // si hay algo que contar ...
  23.          if(--nSECS_for_Event_1==0){ // Descuento un segundo y si he terminado ...
  24.            flag_for_Event_1=1; // Activo el flag de notificación del Evento 1.
  25.          }
  26.       }
  27.       // Restauro contador de interrupciones para cómputo de segundos completos
  28.       nTimer1Overflow=0;
  29.    }
  30. }
  31.  
  32.  

Para mostraros un ejemplo de su uso vamos a suponer que tengo que activar .... hum ... un Relé, por ejemplo, conectado por PIN_C1 durante 3 segundos cuando recibo un pulso de un Botón pulsador por PIN_C0.

Código: C#
  1.  
  2. void main(void){
  3.  
  4.    // Pongo a cero todas las variables
  5.    nTimer1Overflow=0;
  6.    nSECS_for_Event_1=0;
  7.    flag_for_Event_1=0;
  8.    // Habilito las interrupciones
  9.    enable_interrupts(int_timer1);
  10.    enable_interrupts(global);
  11.  
  12.    // Bucle infinito ...
  13.    do{
  14.  
  15.       /////////////////////////////////////////////////////////////////////////////////////////////
  16.       // Detecto la pulsación del botón (con antirrebote incluido) y pongo en marcha
  17.       // el relé y el Disparador de eventos
  18.       /////////////////////////////////////////////////////////////////////////////////////////////
  19.       if(!input(PIN_C0)){ // Si pulso el botón ...
  20.          delay_ms(125);
  21.          if(!input(PIN_C0)){ // Si 125 ms después sigue pulsado el botón ...
  22.             delay_ms(125);
  23.             if(!input(PIN_C0)){ // Si 250 ms después sigue pulsado el botón ...
  24.                output_high(PIN_C1); // Activo lo que deseo activar, o sea mi relé
  25.                flag_for_Event_1=0; // Pongo a cero el flag (por si acaso)
  26.                nSECS_for_Event_1=3; // Cargo los segundos que deseo esperar
  27.             }
  28.          }
  29.       }
  30.  
  31.       /////////////////////////////////////////////////////////////////////////////////////////////
  32.       // Detecta fin del lapsus del Evento 1 y
  33.       // desactivo el relé
  34.       /////////////////////////////////////////////////////////////////////////////////////////////
  35.       if(flag_for_Event_1==1){ // Si ha transcurrido el lapsus ...
  36.          flag_for_Event_1 =0; // Desactivo la notificación que he recibido
  37.          output_low(PIN_C1); // y actúo en consecuencia, desactivo el relé.
  38.       }
  39.  
  40.    }while(1);
  41. }
  42.  
  43.  

Como fácilmente puede verse ampliar el número de eventos a controlar es solo repetir una y otra vez todas las rutinas donde aparece nSECS_for_Event_X y flag_for_Event_X; todas ellas van a controlar de forma independiente cada evento perfectamente solapados entre si y sin que el programa principal esté esperando "sordo" al resto de tareas que le hemos encomendado.

Hay un interesante ampliación que consiste en contar también lapsus de solo un número determinado de interrupciones, no solo de segundos completos. Si en lugar de utilizar variables del estilo de nSECS_for_Event_X las usamos computando en Numero de Interrupciones nTOF_for_Event_X y en la interrupción hacemos su decremento fuera del if() de segundos podremos contar en unidades de tiempo de una sola interrupción, 13,1072 ms en nuestro ejemplo.

Así un disparador de evento que nos avise por ejemplo cuando transcurra un lapsus aproximado de medio segundo debería esperar solo 500 / 13,1072 = 38,14697265625 interrupciones, con lo que con 38 interrupciones nos avisaría transcurridos 498,0736 milisegundos.

Ampliamos nuestro código fuente para contemplar también estos eventos "cortos" y nos queda de la siguiente forma:
Código: C#
  1.  
  2. // Constantes
  3. const int8 nTimer1_for_1_second=76;
  4.  
  5. // Variables en RAM
  6. int8 nTimer1Overflow; // Contador de Interrupciones
  7. int8 nSECS_for_Event_1; // Contador Segundos para Evento 1
  8. int1 flag_for_Event_1; // Flag para detectar evento 1
  9.  
  10. int8 nTOFS_for_Event_2; // Contador Interrupciones para Evento 2
  11. int1 flag_for_Event_2; // Flag para detectar evento 2
  12.  
  13.  
  14. // Interrupción por desbordamiento de Timer1
  15. #int_timer1
  16. void interrupt_service_rutine_timer1(void) {
  17.  
  18.    ////////////////////////////////////////////////////////////////////////////////////////////////
  19.    // Cómputo de segundos completos (13,1072 milisegundos * _nTimer1_for_1_second = 996,1472 milisegundos)
  20.    // Todo lo que se controla dentro de este bloque sólo se computa por segundos completos.
  21.    ////////////////////////////////////////////////////////////////////////////////////////////////
  22.    if(++nTimer1Overflow==nTimer1_for_1_second){
  23.  
  24.       // Control de Evento Número 1
  25.       if(nSECS_for_Event_1!=0){ // si hay algo que contar ...
  26.          if(--nSECS_for_Event_1==0){ // Descuento un segundo y si he terminado ...
  27.            flag_for_Event_1=1; // Activo el flag de notificación del Evento 1.
  28.          }
  29.       }
  30.       // Restauro contador de interrupciones para cómputo de segundos completos
  31.       nTimer1Overflow=0;
  32.    }
  33.    ////////////////////////////////////////////////////////////////////////////////////////////////
  34.    // Cómputo de periodos de tiempo individualizados en NTOFS.
  35.    // Dentro de este bloque se computan periodos de n x 13,1072 ms.
  36.    ////////////////////////////////////////////////////////////////////////////////////////////////
  37.  
  38.    // Control de Evento Número 2
  39.    if(nTOFS_for_Event_2!=0){
  40.       if(--nTOFS_for_Event_2==0){
  41.          flag_Event_2=1;
  42.       }
  43.    }
  44.  
  45. }
  46.  
  47.  

Su tratamiento pues en el main() es idéntico al anterior, salvo que en lugar de cargar segundos cargamos número de interrupciones a esperar.

Bueno, y ya está bien por hoy. Mañana más.   :mrgreen:



« Última modificación: 24 de Octubre de 2014, 10:02:36 por RedPic »
Contra la estupidez los propios dioses luchan en vano. Schiller
Mi Güeb : Picmania

Desconectado manutek

  • Colaborador
  • PIC24F
  • *****
  • Mensajes: 555
Re: Serie Técnicas en C : Múltiples lapsus de tiempo con una solo interrupción.
« Respuesta #1 en: 13 de Febrero de 2009, 21:41:38 »
gracias redpic por la explicacion del metodo, yo lo aplicaba pero jamas lo conoci por su nombre lo seguire de serca al post.
salute  :-) :-) :-)

manutek
No es la conciencia del hombre la que determina su ser, sino, por el contrario, es su ser social el que determina su conciencia

Desconectado RedPic

  • Administrador
  • DsPIC33
  • *******
  • Mensajes: 5391
    • Picmania by Redraven
Re: Serie Técnicas en C : Múltiples lapsus largos de tiempo con una interrupción.
« Respuesta #2 en: 15 de Febrero de 2009, 09:20:48 »
Si, menutek, este artículo lo he escrito pensando en las consultas que creo que hiciste tu mismo sobre el tema. El problema es que ando escasísimo de tiempo y me ha costado sangre, sudor y lágrimas poder empezarlo y acabarlo. Pero bueno, ya está y bien está lo que bien acaba.  :mrgreen:
Contra la estupidez los propios dioses luchan en vano. Schiller
Mi Güeb : Picmania

Desconectado Suky

  • Moderador Local
  • DsPIC33
  • *****
  • Mensajes: 6743
    • Micros-Designs
Re: Serie Técnicas en C : Múltiples lapsus largos de tiempo con una interrupción.
« Respuesta #3 en: 15 de Febrero de 2009, 19:34:26 »
Muchas gracias RedPic por el esfuerzo que realizas en explicar estos conceptos muy utiles! :-)
No contesto mensajes privados, las consultas en el foro

Desconectado micro_cadaver

  • Colaborador
  • PIC24H
  • *****
  • Mensajes: 2102
    • blog microembebidos
Re: Serie Técnicas en C : Múltiples lapsus largos de tiempo con una interrupción.
« Respuesta #4 en: 16 de Febrero de 2009, 17:37:41 »
si señor, muchas gracias por su eterno aporte a la comunidad.
a cosechar!!!... :P
pic32... ahi voy....
aguante el micro 16f84  !!!!

visita mi pagina: http://www.microembebidos.wordpress.com

Desconectado RedPic

  • Administrador
  • DsPIC33
  • *******
  • Mensajes: 5391
    • Picmania by Redraven
Re: Serie Técnicas en C : Múltiples lapsus largos de tiempo con una interrupción.
« Respuesta #5 en: 16 de Febrero de 2009, 17:53:37 »
Muchas gracias amigos por vuestras palabras. Siempre es gratificante saber que uno no trabaja en vano.  :mrgreen:
Contra la estupidez los propios dioses luchan en vano. Schiller
Mi Güeb : Picmania

Desconectado Azicuetano

  • Moderadores
  • PIC24H
  • *****
  • Mensajes: 1020
    • Aplicaciones Electrónicas en Alicante.
Re: Serie Técnicas en C : Múltiples lapsus largos de tiempo con una interrupción.
« Respuesta #6 en: 18 de Febrero de 2009, 05:36:55 »
Excelente!

Yo creo que si cada uno ponemos 1€ te publicamos un libro  :mrgreen:


Un saludo desde Alicante.

Desconectado Nocturno

  • Administrador
  • DsPIC33
  • *******
  • Mensajes: 17488
    • MicroPIC
Re: Serie Técnicas en C : Múltiples lapsus largos de tiempo con una interrupción
« Respuesta #7 en: 18 de Febrero de 2009, 05:37:42 »
Yo pongo 2€ e invito a café
Un saludo desde Sevilla, España.
Visita MicroPIC                                                                                        ɔ!doɹɔ!ɯ ɐʇ!s!ʌ

Desconectado Leon Pic

  • Colaborador
  • DsPIC30
  • *****
  • Mensajes: 3471
    • Mensajes de la Virgen María
Re: Serie Técnicas en C : Múltiples lapsus largos de tiempo con una interrupción
« Respuesta #8 en: 18 de Febrero de 2009, 06:21:38 »
Se agradece enormemente REDPIC  :-/ :-/ :-/ :-/
Él dijo: "destruyan a la Iglesia y yo la levantaré en tres días". Con esto definió que la Iglesia, somos nosotros; el cuerpo y alma, y no el edificio o templo.
-"Ámense los unos a los otros como yo los he amado" Nuestro Señor Dios hecho hombre: Jesús.
-Él, fue a la cruz, pagó nuestro pecado con un dolor increible siendo inocente de lo que lo acusaban, para salvarnos.

-Mi propio Foro de Meteorología
www.meteorologiafacil.com.ar/foros/index.php

-Web
www.meteorologiafacil.com.ar

Desconectado ncoliv

  • PIC16
  • ***
  • Mensajes: 130
Re: Serie Técnicas en C : Múltiples lapsus largos de tiempo con una interrupción.
« Respuesta #9 en: 18 de Febrero de 2009, 21:07:19 »
Gracias!! por los aportes redpic, sin duda que enseña y muchas serán sus aplicaciones.

Desconectado pajaro

  • PIC24H
  • ******
  • Mensajes: 1121
Re: Serie Técnicas en C : Múltiples lapsus largos de tiempo con una interrupción.
« Respuesta #10 en: 01 de Julio de 2012, 14:58:22 »
HoLa RedPIC

despues de más de 120 dias revivo el tema,
Maestro RedPIC encontre un error en su codigo
y me causaria mucha satisfación la clasificación, revisión y reduccion del error
para el completo y correcto funcionamineto
del codigo del programa expuesto.

error :

fue encontrado en el codigo numero 3 en la linea 41 del post :
enlace:

http://www.todopic.com.ar/foros/index.php?topic=24801.msg202369#msg202369

en la linea 41, aca linea 20:

Código: C++
  1. ...
  2. ..
  3. // Control de Evento Número 1
  4.      if(nSECS_for_Event_1!=0){ // si hay algo que contar ...
  5.         if(--nSECS_for_Event_1==0){ // Descuento un segundo y si he terminado ...
  6.           flag_for_Event_1=1; // Activo el flag de notificación del Evento 1.
  7.         }
  8.      }
  9.      // Restauro contador de interrupciones para cómputo de segundos completos
  10.      nTimer1Overflow=0;
  11.   }
  12.   ////////////////////////////////////////////////////////////////////////////////////////////////
  13.   // Cómputo de periodos de tiempo individualizados en NTOFS.
  14.   // Dentro de este bloque se computan periodos de n x 13,1072 ms.
  15.   ////////////////////////////////////////////////////////////////////////////////////////////////
  16.  
  17.   // Control de Evento Número 2
  18.   if(nTOFS_for_Event_2!=0){
  19.      if(--nTOFS_for_Event_2==0){
  20.         flag_Event_2=1;   //--- linea de error
  21.      }
  22. ..
  23. ...
  24.  
  25.  
  26.  

flag_Event_2=1;

esta variable no esta definida en el codigo

o fue definida con otro nombre


Puedes confirmarme: si estoy en lo cierto.

**aprovecho para preguntarte como configuras :

setup_timer_1(T1_INTERNAL|T1_DIV_BY_1);

o asi
setup_timer_1(T1_EXTERNAL|T1_DIV_BY_1);

** me quede con 1 duda conseguiste el segundo perfecto
¿Como lo apañaste?


Un Cordial Saludo.




« Última modificación: 01 de Julio de 2012, 19:59:51 por pajaro »