Autor Tema: Rutina de temporización utilizando un RTC.  (Leído 2556 veces)

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

Desconectado remi04

  • PIC24F
  • *****
  • Mensajes: 657
Rutina de temporización utilizando un RTC.
« en: 28 de Enero de 2020, 09:31:26 »
Hola a todos.

 aprovechando que tengo un rtc en la placa de un proyecto, he generado una función que básicamente es una especie de alarma. Cuando la llamas, le envías como argumento cuanto tiempo en segundos quieres temporizar. Si por ejemplo la llamo así  "tempo(60);" le estoy pidiendo que me diga a qué hora exacta se cumplirán 60 segundos.  Esa función lo que hace es leer la hora, minutos y segundos del rtc y calcula a qué hora exacta se da el vencimiento de ese tiempo que has pedido y te lo deja grabado en tres variables globales.

  Luego entonces, el proceso que haya programado esa alarma será el responsable de consultar periódicamente si la hora ya se ha dado, si aun no, o si se ha pasado de tiempo mediante otra función que se llama (tempo_out();) que devuelve "0" si no se ha vencido aun el tiempo, o  "1" si se ha vencido o se ha pasado.

   Todo esto está funcionando a la perfección. La idea de compartirlo es por si alguien encuentra algún fallo o circunstancia en la que el cálculo de la hora de alarma no se haga o se pueda hacer mal. Y también como no, si merece la pena hacerlo de otra forma.

  Notar que muchas variables son globales por que en el programa, estas funciones son llamadas permanentemente desde un bucle en el main.  Este programa tiene totalmente prohibido quedar detenido en una función mas de 500 ms.
No he querido usar los timer por varias razones, la fundamental es, que los tengo en uso, dos de ellos con señales externas (para contadores volumétricos)  y otros dos como base de tiempo para medir los periodos de los mismos. A parte , algunas funciones del programa si que llegan a demorar casi los 500 ms, y no se pueden interrumpir por lo que no daría tiempo a contar todos los desbordamiento del timer usado como base de tiempo. El micro es un 18f4550 a 8 Mhz.

 Por ello este follón de hacerlo mediante el RTC.

Este es el código de la función que calcula la hora del vencimiento:

Código: C
  1. void tempo(int16 seg) {          // recibe como argumento en segundos, el tiempo que se desea sumar a la hora actual.
  2.                                      // la función cargará en tres variables globales _hora, _minutos _segundos   a qué hora se dará el vencimiento
  3.    
  4.     if (busy == 1) return;         // no haremos nada si ya hay una temporizacion activa.
  5.                                             // busy será bajada a cero por tempo_out() una vez concluido el vencimiento.
  6.                                            // Por ello, es de vital importancia consultar tempo_out periodicamente cuando se haga uso de tempo()
  7.    
  8.     mas24 = 0;                      // bandera de control cruce de 23 a 00 horas inactivo inicialmente.
  9.    
  10. // primero descomponemos los segundos recibidos en el argumento y obtenemos  (segundos, minutos y horas). Lo llamamos tiempo de consigna a cronometrar
  11.     int8 segundos_consigna = seg % 60;        // pedimos modulo de 60 y obtenemos los segundos  en formato horario.
  12.     int8 minutos_consigna = (seg / 60) % 60;         // despejamos los minutos totales
  13.     int8 hora_consigna =  seg / 3600;                  // despejamos las horas totales.
  14.    
  15.    
  16.     // ya sabemos cuantas horas, minutos y segundos en formato correcto corresponden a la cifra en segundos recibida en el argumento
  17.     // ahora vamos a ver qué hora es ahora mismo y le vamos a sumar este tiempo consignado. Luego entonces, determinaremos a qué hora se dará el vencimiento.
  18.    
  19.     read_rtc();         // primero leemos la hora actual. Esta funcion leerá el rtc y actualizará las variables globales "hora,minutos,segundos" con la hora exacta en este momento.
  20.     segundos_ = segundos + segundos_consigna;         // sumamos los segundos actuales a los consignados arriba.
  21.     minutos_ = minutos + minutos_consigna;           // misma operación para los minutos.
  22.     hora_ = hora + hora_consigna;                 // misma operación para la hora.
  23.    
  24.     if (segundos_ > 60) {                            // si segundos_  es superior a 60, hallamos su modulo e incrementamos minutos_ en una unidad
  25.                         segundos_ = segundos_ % 60;
  26.                         minutos_++;
  27.                        }
  28.    
  29.     if (minutos_ > 60) {                               // Si _minutos es superior a 60, hallamos su modulo e incrementamos hora_ en una unidad.
  30.                         minutos_ = minutos_ % 60;
  31.                         hora_++;
  32.                        }
  33.    
  34.     if (hora_ > 23) {hora_ = hora_ - 24; mas24 = 1;}   // Si da mas de 23 horas, hayamos la diferencia desde 24 y determinamos la hora equivalente en el formato de 24h, además lo indicamos mediante la bandera mas24.
  35.                                                                                // para que la funcion tempo_out(); lo tenga en cuenta a la hora de evaluar si nos hemos pasado de hora o se a alcanzado.
  36.    
  37.     busy = 1;            // Indicamos que esta función ya tiene una temporización en curso. Si se intenta llamar con otro tiempo, se efectuará un return al principio sin hacer ni cambiar nada.
  38.                             // Esta bandera será bajada por (tempo_out(); cuando se indique vencimiento.  
  39.  
  40. }




Y esta es la funcion tempo_out(); que evalúa si se ha cumplido el tiempo o no.




Código: C
  1. int1 tempo_out() {  // devuelve 1 si se ha cumplido o superado la hora establecida por tempo();
  2.     read_rtc();    // leemos la hora actual y la cargamos en sus variables globales.
  3.  
  4.     // Primero preguntamos si la hora actual coincide o es superior a la establecida por tempo(); y si se ha cruzado o no el umbral de las 24h.
  5.     if ((mas24 == 0) && (hora >= hora_) && (minutos >= minutos_) && (segundos >= segundos_))  {busy = 0;return 1;}  // si es hora clave, y son los minutos claves, preguntamos si nos hemos pasado o estamos en segundos clave.
  6.                                                                                        //  Esto solo si mas24 es cero, es decir, no se ha cruzado el umbral de las 24 horas.
  7.                                                                                       // Alcanzada la hora de vencimiento, ponemos busy a cero para que se pueda volver a usar la funcion tempo();
  8.  
  9.     if ((mas24 == 1) && (hora == hora_) && (minutos >= minutos_) && (segundos >= segundos_)) {busy = 0;return 1;} // Si hemos atravesado el umbral de las 24 horas, la operación es la misma
  10.                                                                                                        // con la diferencia de que ahora solo compararemos la hora y esperaremos a que coincidan.
  11.                                                                                                        // Ya no podemos tener en cuenta si nos hemos pasado de hora por que está claro que va a ser superior.
  12.  
  13.     // si nada de lo anterior se ha cumplido devolvemos cero, aun no es la hora, ni nos hemos pasado.
  14.     return 0;
  15.    
  16.    
  17.    
  18. }


« Última modificación: 28 de Enero de 2020, 09:41:29 por remi04 »

Desconectado KILLERJC

  • Colaborador
  • DsPIC33
  • *****
  • Mensajes: 8242
Re:Rutina de temporización utilizando un RTC.
« Respuesta #1 en: 05 de Febrero de 2020, 12:18:51 »
Citar
La idea de compartirlo es por si alguien encuentra algún fallo o circunstancia en la que el cálculo de la hora de alarma no se haga o se pueda hacer mal. Y también como no, si merece la pena hacerlo de otra forma.

No hacer las variables globales para no ocupar los nombres (polucion del espacio global).
Es decir una función que te retorne el puntero a una estructura, únicamente la hora/min/seg actual del RTC por si queres leerlo, y otra "privado" (static) que solo se acceda desde la libreria del RTC que maneje el temporizador.
El leer el RTC solo actualizaría esta estructura.
El pedido de tiempo se podría hacer de hora, minuto y segundo, para no tener que hacer los modulos y divisiones (que son computacionalmente demandantes) de cada uno de los valores, y solo representen una suma.
Ademas si queres esperar HORAS, me parece mas sensato hacerlo como comenté, en ves de hacerlo como lo planteas. sino habría que pasar todas esas horas a segundos (trabajo extra para el programador).

Código: C
  1. if (segundos_ > 60)

No considera si segundos es 60, lo cual debería aumentar el minutero, lo cual el reemplazo seria por un mayor o igual o cambiarlo a 59. Y luego haces:

Código: C
  1. segundos_ = segundos_ % 60;

Lo cual no tiene sentido, debido a que el maximo de cada uno es 59, por lo tanto la suma llegaria 118, y solo deberias restarle 60. Asi lo mismo con los minutos.

La funcion temp_out, es muy dependiente del momento de lectura. Ejemplos:
Si las lecturas son en el mismo dia, es decir mas24 = 0, el peor caso que podemos llegar a tener es que ocurra que el temporizador quede seteado en 23h59m29s, eso haria que si no lo lees en ese segundo, el temporizador no funcionaria. Las soluciones que se pueden plantear y que se me ocurren son:
- Leer el dia tambien
- Mantener un bit interno que ademas de mantener la hora, cuando se este ejecutando el temporizador, revise cuando se pasaron las 24hs del reloj RTC, asi activar un bit y tener en cuenta que hubo un cambio de dia.


Si la lectura fuera del siguiente dia, es decir mas24=1. Se presenta el mismo problema que antes, y ademas la comparacion de horas no es >= sino ==, es decir tenes una ventana de 1 hora para leer los datos.


Por supuesto todo esto depende del tiempo entre de lecturas. La solución mas simple seria incluir el día también lo cual aumentaria la ventana y prestar especial atencion al caso de fin de mes con 23hs59m59s. Y limitar las horas, ejemplo :

Código: C
  1. /*
  2. * Parámetros:
  3. * In:
  4. *  1: hour  Cantidad de horas a esperar
  5. *  2: min   Cantidad de min a esperar
  6. *  3: seg   Cantidad de seg a esperar
  7. *
  8. *  Out:
  9. *  RTCT_BUSY_E       Error ante el cambio de datos cuando esta marchando
  10. *  RTCT_MAX_HOUR_E   Error se supera el máximo de horas, que son 24 o mas
  11. *  RTCT_MAX_MIN_E   Error se supera el máximo de minutos, que son 59 o mas
  12. *  RTCT_MAX_SEG_E   Error se supera el máximo de segundos, que son 59 o mas
  13. *  RTCT_OK           Temporizador seteado correctamente.
  14. *
  15. */
  16.  
  17. int8 tempo(int8 hour, int8 min, int8 seg){
  18.    
  19.     // Check
  20.     if (RTCTemp.busy == 1) return RTCT_BUSY_E;
  21.     if (hour > 23) return RTCT_MAX_HOUR_E;
  22.     if (min > 59) return RTCT_MAX_MIN_E;
  23.     if (seg > 59) return RTCT_MAX_SEG_E;
  24.  
  25.     // Set Initial Values
  26.     RTCTemp.mas24 = 0;
  27.  
  28.     // Set Temp
  29.     read_rtc();
  30.     RTCTemp.seg = RTC.segundos + seg;
  31.     RTCTemp.min = RTC.minutos + min;
  32.     RTCTemp.hour = RTC.hora + hour;
  33.    
  34.     // Correction
  35.     if (RTCTemp.seg > 59) { RTCTemp.seg -= 60; RTCTemp.min++;}
  36.     if (RTCTemp.min > 59) { RTCTemp.min -= 60; RTCTemp.hour++;}
  37.     if (RTCTemp.hour > 23) {RTCTemp.hour -= 24; RTCTemp.mas24 = 1;}
  38.    
  39.     // Leaving
  40.     RTCTemp.busy = 1;
  41.     return RTCT_OK;
  42. }

Al limitarlas en 24hs solo presentas el caso de cambio de dia.

Desconectado remi04

  • PIC24F
  • *****
  • Mensajes: 657
Re:Rutina de temporización utilizando un RTC.
« Respuesta #2 en: 13 de Febrero de 2020, 16:53:49 »
Muchísimas gracias Killer. Me lo revisaré en cuanto mejore del resfriado que no veas la que me está dando. 

  Un saludo.

Desconectado remi04

  • PIC24F
  • *****
  • Mensajes: 657
Re:Rutina de temporización utilizando un RTC.
« Respuesta #3 en: 20 de Febrero de 2020, 06:00:19 »
Una pregunta Killer. ¿ A que te refieres con polución del espacio global y en que puede afectar?

Desconectado KILLERJC

  • Colaborador
  • DsPIC33
  • *****
  • Mensajes: 8242
Re:Rutina de temporización utilizando un RTC.
« Respuesta #4 en: 20 de Febrero de 2020, 10:27:44 »
Generalmente en un proyecto pequeño no es problema, pero se trata de evitar en lo posible toda variable global por varias razones.

Si es global implica que no puedo usa el mismo nombre en otro lado, a pesar que se ajuste perfectamente a ambas "librerias".
Aparejado a lo que dije antes, implica implicitamente que cualquier funcion tiene acceso a las variables. Por lo tanto podes tener 20 archivos y una funcion cada una que modifica esa variable global, lo cual es un caos para recordar, ya que vos buscas generar una libreria que sea cerrada e independiente de todo lo que hagas, de esa forma al tener un error sabes que deberias revisar la libreria y no buscar entre los 20 archivos donde esta el problema.

Y el otro GRAN problema es que lo global ocupa RAM para siempre. No en esta libreria, pero muchas veces en las persona que recien comienzan ponen todas las variables en el espacio global incluso esas que tranquilamente podrias ser temporales.

PD: Obviamente muchas veces el remedio es peor que la enfermedad. Y cada caso deberia ser tratado aparte, si tenes una variable global que se usa en 2 archivos maximo, entonces por ahi no es sensato buscar alguna alternativa. Pero si tenes 10 variables globales que se usan en distintos archivos ya es un problema.

Vuelvo a repetir, la idea es ser ordenado para poder resolver los problemas y mejorar el codigo rapidamente. Que mejor tener las cosas bien separadas e independiente uno de otro.


 

anything