# ¿Cómo generar un segundo en un pic?



## Diederick (Abr 28, 2008)

HOla gente que tal sucede que estoy implementando un contador con pic16f877, no sé cómo calcular las interrupciones para el montaje del micro con un cristal de 20Mhz: solo sé que para un cristal de 4MHz el número de rtccs es de 15, *pero cuál es el procedimiento para calcular éstas RTCCS?*??

Agradezco su respuesta...

UN saludo...


----------



## aiken (Abr 28, 2008)

No se bien que es el RTCCS, pero un método con el cuál si se generaban un seg. era mediante el empleo del Timer0. 
El tiempo a ingresar se conoce mediante la siguiente fórmula:
Tiempo=4*T(oscialador)*(Cuenta*Escala+2)
TMR0=256-Cuenta
En tiempo estableces el tiempo que quieres que cuente el Timer, en Escala es la preescala que puedes seleccionar en el Option Reg.
Al ser un segundo tendrás que hacer un bucle ya que se desbordará antes el Timer.
Otra forma que puedes realizar,es mediante un bucle en la que primero miras cuantas instrucciones vas ha emplear y de hay sacas el tiempo que tarda en realizarlas el pic, antes del bucle creas unos registros en los que almacenas el tiempo necesario para realizar esas intrucciones, el llenado de estos registros entraran dentro del bucle, despues los vas descontando, primero uno, cuando llegue a cero quitas uno a otro registro y rellenas el anterior y vuelves a descontar hasta cero, vuelves a quitar otro al segundo, y asi sucesivamente, espero haberme explicado bien, si eso me dices.
Un saludo.


----------



## Diederick (Abr 29, 2008)

Mira sucede que estoy haciendo el programa en c la parte que debo modificar es la siguiente:

char const RTCCxS=15; // Número de RTCC's (interrupciones para obtener un segundo) para 1 segundo con 4 Mhz / 1:256.

El 15 hace referencia a que con un cristal de 4 MHz se deben hacer 15 interrupciones con un prescaler de  1:256, de ésta manera: 66.6 ms * 15= 999 ms (casi un seg).

Pero al hacer el montaje, cuando pongo un cristal de 20 MHz, porque obviamente está programado para 4 M; el reloj corre muy rapido, entonces necesito saber cuantas interrupciones con un prescaler de 1:256 se deben programar para que funcione perfecto con un cristal de 20 M y de ésa manera el contador incremente en tiempo real.

Hay una página dónde tratan de explicar eso pero no entiendo muy bien como ajustan las interrupciones: 

http://picmania.garcia-cuervo.com/Conceptos.php#CristalyTime

Si llegas a entender algo de lo que hay en ese link, agradecería tu respuesta...

Un saludo gracias.....


----------



## Manonline (Abr 29, 2008)

no se absolutamente nada sobre pics, ni sobre nada... pero mi sentido comun dice que RTCC debe ser 75 (regla de tres simple)

4MHz________15
20MHz_______x

20MHz x 15 / 4MHz = 75

no se... supongo, pero no cuesta nada probar...


----------



## f_point (Abr 29, 2008)

Excelente observacion la de Manonline... todos necesitamos mas sentido comun ;-)

Por otra parte, si no te molesta tomar una alternativa relativamente simple, y deseas una exactitud muy pero muy razonable, puedes usar el siempre confiable Timer2. El modulo Timer2 del PIC16F877A es extremadamente simple y permite generar bases de tiempo con una buena gama de valores de division de reloj posibles. Una solucion para generar 1s con el Timer2 seria:

Frecuencia de entrada al Timer2: Fosc/4 = 20MHz/4 = 5MHz

Seleccion de valores:
PR2 = 249 (hace sobreflujo cada 250 ciclos)
Valor de Prescaler: 16
Valor de Postscaler: 10

Luego, la frecuencia a la que Timer2 hace interrupciones seria:

F = 5MHz / (250 * 16 * 10) = 125Hz

Asi, para generar un segundo exacto, deberas esperar 125 interrupciones. No es nada dificil configurar el modulo Timer2 para ello, en serio. La unica parte dificil es manejar las interrupciones, que segun veo, ya tienes resuelto.

A lo mejor eso te sirva.
Saludos.


----------



## Diederick (Abr 29, 2008)

gracias por la respuesta...voy a probar el micro con las diferentes opciones que ustedes me dan....os comentaré una respuesta apenas obtenga los resultados sean buenos o malos....si alguien tiene otra opción bienvenida sea....

Un saludo..


----------



## ariel 37 (Sep 15, 2015)

Hola que tal queria hacer una consulta y salir de unas dudas con respecto a hacer una interrupcion, por timer1 cada 1 segundo.
Estoy usando este programa para medir una frecuencia de 100 a 400 hz, en el cual cuento la cantidad de pulsos que me ingresan por el timer0 ,del pic 16f876a 
(pin Ra4) ,

```
/

#int_timer1
void timer1_isr(void)
{
   contador=get_timer0();
   set_timer0(0);
   set_timer1(3036);
}
void main()
{
   set_tris_a(0b00010001); 
   output_a  (0b00000000);
  
   lcd_init();
   setup_timer_0(rtcc_ext_h_to_l | rtcc_div_1);
   setup_timer_1(t1_internal | t1_div_by_8);
   
   set_timer0(0);
   set_timer1(3036);
   enable_interrupts(int_timer1);
   enable_interrupts(global);
   
  //****************ADC**************************
   setup_adc_ports(AN0);
   setup_adc (ADC_CLOCK_INTERNAL);
 //********************************************************
   
  
   while(1)
    { 
    
       
    }
}
```
 con el timer1 hago una interrupcion cada 0,5 seg ,el resultado de los pulsos que obtengo los multiplico por 2 y asi obtengo la frecuencia.
Todo esto por que no me doy cuenta como hacer una interrupcion cada 1 segundo.
 El tiempo de 0,5seg lo obtuve con esta formula : T= 4/frec osc * prescaler*(65536-TMR1), donde frec osc =4Mhz y prescaler= 8 
Esta formula y esta explicación esta en un monton de paginas de internet ,ahora bien con esta formula el tiempo maximo que puedo calcular es de 0,524 seg
Entonces la pregunta es como hago para hacer una interrupcion cada 1 segundo ?
Lo que se me ocurre es seguir usando los 0,5 seg y contar los pulsos cada 2 interupciones pero no me doy cuenta como hacerlo, si es esta la manera de hacerlo.
Si alguien me puede dar una explicacion o un ejemplo de como hacerlo le estaré muy agradecido.Saludos !!!


----------



## Scooter (Sep 15, 2015)

También puedes medir cada cuanto tiempo llega el pulso y hacer la inversa, así tienes la frecuencia a cada pulso.


----------



## ariel 37 (Sep 15, 2015)

Te agradezco tu respuesta Scooter ,pero me gustaría aprender a hacer lo que mencioné antes , vos sabes como hacerlo?


----------



## D@rkbytes (Sep 15, 2015)

Si se puede hacer que desborde cada 500 ms, sumar, y obtener un segundo.
Pero te recomiendo que uses el oscilador externo del Timer 1 con un cristal de 32768 Hz.

Mira este ejemplo: *Obtener 1 segundo con el Timer 1*


----------



## ariel 37 (Sep 16, 2015)

Hola ,estuve probando de adaptar el progrma de D@rkabytes al pic 16f876a que es el que estoy usando ,pero no logré hacer  que cuando se produce la interrupcion me capture el valor del timer0,supongo que el problema esta en el oscilador externo, que tal vez no esta bien configurado para mi pic .Todo esto porsupuesto probado en simulacion.
Ahora encontré una rutina en esta pagina:http://www.puntoflotante.net/INTERRUPTC.htm Donde dice que hace una interrupcion cada 1 segundo y que es de alta presicion ,esta rutina es usada para hacer un reloj de tiempo real ,lo cual me venía a la perfeccion ya que mi idea es hacer un cuenta vueltas con cuenta horas inclusive.Pero tampoco logro hacer que cuando hace la interrupción me lea correctamente el valor del timer0, que es por donde cuento los pulsos que uso para hacer el cuentavueltas .Este es el programa que intento unificar

```
#include <16f876a.h>
#device adc=10
#use delay(clock=4000000)
#fuses HS,NOWDT,NOPROTECT,NOLVP,PUT,NODEBUG,BROWNOUT,NOCPD,NOWRT
#include <lcd.c> 
#define CRISTAL  4000000 
#define FRECUENCIA_TIMER1 CRISTAL/4
#use fast_io(B)
#use fast_io(c)
#use fast_io(a)

int16 contador=0,RPM;
float ajuste;
int32 Ticker;
int8 Segundos=0,Hora=0,Minuto=0;


void LCD_CEROS(void)
{
 char CEROS[16]="   00000:00:00";      //HORA INICIAL
 lcd_init();
 printf(lcd_putc,"  \f");
 lcd_putc("  CUENTA HORAS");
 lcd_gotoxy(4,2);
 printf(lcd_putc,"%s",CEROS);
} 

void DISPLAY_LCD(void)
{char wob[16];

  lcd_gotoxy(1,2);
 sprintf(wob,"   %05d:%02d:%02d",Hora,Minuto,Segundos);
 printf(lcd_putc,"%s", wob); 
}

void Inicializa_RTC(void) 
{ 
  Ticker = FRECUENCIA_TIMER1;                 // 1 Tick = 1 us. 
  setup_timer_1( T1_INTERNAL | T1_DIV_BY_1 ); // initializa TIMER1 para interrupción 
                                              // cada 65536 us.
  enable_interrupts( INT_TIMER1 );            // habilita interrupción TIMER1 
} 


#int_TIMER1                                
void TIMER1_isr()                          //subrutina de interrupción.
{  
  Ticker -= 65536;                        // Decrementa ticker 
  if ( Ticker < 65536 )                   // Si ya pasó 1 segundo 
  {  
     contador=get_timer0();              // esto es lo que yo le agregé
     set_timer0(0);                      //   
                                       
     Ticker += FRECUENCIA_TIMER1;          //   Incrementa ticker
     Segundos++ ;                          // Incrementa seg
     
  } 
  
  if(Segundos == 60)
  {
  Minuto++;
  Segundos=0;
    if(Minuto == 60)
    {
    Hora++;
    Minuto=0;
    }
  }
  
  
} 
 
                                          //
void main()
{
   int8 segundo_previo; 
    set_tris_a(0b00010001);
    output_a  (0b00000000);
    Inicializa_RTC(); 
    
    setup_timer_0(rtcc_ext_h_to_l | rtcc_div_1);
    enable_interrupts(GLOBAL);
    LCD_CEROS();
    lcd_init();
  //****************ADC**************************
   setup_adc_ports(AN0);
   setup_adc (ADC_CLOCK_INTERNAL);
 //********************************************************
   
  
   while(1)
    { 
       if (Segundos != segundo_previo) 
    {
    segundo_previo=Segundos;
      
    } 
    if (input(pin_c4)==1 )
    {
    DISPLAY_LCD();
    lcd_gotoxy(1,1);
    lcd_putc("  CUENTA HORAS");
    }   
       set_adc_channel(0);
       delay_ms(10);
       ajuste=(float)read_adc()*0.029;      
       RPM= contador*ajuste;
       if (input(pin_c4)==0 )
       {
       lcd_gotoxy(1,1);
       printf(lcd_putc,"     RPM %lu         " ,RPM);
       
       lcd_gotoxy(1,2);
       printf(lcd_putc,"Contador  %lu     " ,contador); 
       }
    }
}
```

El cuenta horas por si solo ,funciona bien y el cuentavueltas que coloque en el post 7 tambien ,este ultimo lo tengo armado y probado.
Si alguien me puede orientar un poco en como unificar los dos, si es que es posible , le estaré agradecido.
Desde ya muchas gracias por su atencion .
                                                              Saludos!!!


----------



## ariel 37 (Sep 17, 2015)

Hola ,bueno me respondo a mi mismo ja ja , para poder repreguntar.
Ya consegui hacer funcionar el cuenta horas y el cuenta vueltas juntos ,despues de varias horas y dolor de cabeza, me quedó funcionando y probado ya que tengo todo montado en un banco de pruebas.
Les adjunto el codigo por si le sirve a alguien.
 Lo que quiero hacer ahora es, poder almacenar las horas minutos y segundos en la eeprom para que cuando se apague el motor y vuelva a encender comience a contar ,el cuenta horas ,desde donde habia quedado .
La manera en que lo pensé es que cuando las vueltas del motor bajen de 600rpm el cuenta horas se detenga y me grabe los datos en la eeprom y cuando suba de este regimen, arranque a contar a partir de ese dato guardado.Esto lo hace mientras el pic este con alimentacion pero cuando le saco la alimentacion , el cuenta horas comienza de cero ,osea, o no me esta leyendo la eeprom o no me esta grabando.
La pregunta es ,como debería hacer esto ? por lo poco que entiendo yo, asi como esta el programa lo debería hacer, pero al parecer hay algo de lo que no me estoy dando cuenta 
Bueno espero se entienda la explicacion y puedan darme una manito .Saludos!!! 

```
#include <16f876a.h>
#device adc=10
#use delay(clock=4000000)
#fuses HS,NOWDT,NOPROTECT,NOLVP,PUT,NODEBUG,BROWNOUT,NOCPD,NOWRT
#include <lcd.c> 
#include <internal_eeprom.c>
#use fast_io(B)
#use fast_io(c)
#use fast_io(a)

int16 contador=0,RPM;
float ajuste;
int8 Segundos,Minuto, Hora;

void grabar(void)
{
  
   write_eeprom(0x10, Hora);
   delay_ms(150);
   write_eeprom(0x50, Minuto);
   delay_ms(150);
   write_eeprom(0x100, Segundos);
   delay_ms(150);
   

}
void leer_memoria(void)
{

      Hora=read_eeprom(0x10); 
      delay_ms(150);                  
        
      Minuto=read_eeprom(0x50);
      delay_ms(150);
      
      Segundos=read_eeprom(0x100);
      delay_ms(150);
      
  lcd_gotoxy(1,2);
  printf(lcd_putc,"   %05d:%02d:%02d ",Hora,Minuto,Segundos);      


} 

void DISPLAY_LCD(void)
{
//leer_memoria();
   char wob[16];   
   lcd_gotoxy(1,2);
   sprintf(wob,"   %05d:%02d:%02d ",Hora,Minuto,Segundos);
   printf(lcd_putc,"%s", wob); 
}

#int_timer1
void timer1_isr(void)
{
   contador=get_timer0();
   set_timer0(0);
   set_timer1(3036);
   
 if (rpm>600)
  { 
   
    Segundos++;
  if(Segundos == 120)
  {
  Minuto++;
  Segundos=0;
    if(Minuto == 60)
    {
    Hora++;
    Minuto=0;
    }
  }
} 
}
void main()
{
   set_tris_a(0b00010001); 
   output_a  (0b00000000);
    
   int8 segundo_previo;
   
   lcd_init();
   setup_timer_0(rtcc_ext_h_to_l | rtcc_div_2);
   setup_timer_1(t1_internal | t1_div_by_8);
   
   set_timer0(0);
   set_timer1(3036);
   enable_interrupts(int_timer1);
   enable_interrupts(global);
   grabar();
  leer_memoria();

     //****************ADC**************************
   setup_adc_ports(AN0);
   setup_adc (ADC_CLOCK_INTERNAL);
 //********************************************************
   
  
   while(1)
    { 
       if (Segundos != segundo_previo) 
         {
           segundo_previo=Segundos;
         } 
       if(rpm<600)
       {grabar();
       leer_memoria();
       }
      else
      {
      
      DISPLAY_LCD();
      }
   
       set_adc_channel(0);
       delay_ms(10);
       ajuste=(float)read_adc()*0.029;      
       RPM= contador*ajuste;
       lcd_gotoxy(1,1);
       printf(lcd_putc,"     RPM %lu         " ,RPM);
        
       }
    }
```

PD :las horas deberia guardarlas con un formato int16 ,esto tampoco lo tengo resuelto.


----------



## D@rkbytes (Sep 17, 2015)

Para la detección de corte eléctrico, puedes usar un canal análogo.
Y para guardar datos de 16 bits en la EEPROM interna, puedes usar la librería "internal_eeprom.c" (Viene incluida con el compilador)


----------



## ariel 37 (Sep 18, 2015)

Gracias D@rkbytes con lo de leer el adc para detectar el corte me di cuenta por que no me graba en la eeprom , a la noche les subo las conclusiones 
                                                      Saludos!!!


----------



## ariel 37 (Sep 19, 2015)

Hola, compañeros del foro.
Les cuento que ya pude resolver el tema de grabar en la eeprom el valor del cuenta horas, para luego poder apartir de ese valor, seguir incrementando el cuenta horas.

Lo que me pasa es que siempre estoy guardando el dato en la misma dirección de memoria y no sé si puedo dañar la memoria.

Se me había ocurrido que cada vez que grabe, aumente una dirección y cuando se complete que vuelva a cero, pero el tema es que no sé cómo hacer para que me lea la última dirección de memoria que se grabó.
Ésto para no sobrecargar siempre la misma direccion.
¿Alguien me puede orientar en cómo debería hacerlo?

También le implementé como dijo D@kbytes, que me lea un canal adc para detectar el corte de energia.

Estuve viendo en el mismo foro, que se trata el tema de colocar un capacitor para darle tiempo al PIC a que guarde los datos antes que se quede totalmente sin alimentación, así que creo que voy a implementar el circuito.

La idea de este circuito, es dejar el PIC siempre alimentado, sólo apagar el LCD y el resto de las cosas que tengan consumo.
Lo de guardar en la memoria, sería para el caso de cuando le cambian la batería al motor y que no se pierda el dato del cuenta horas.

Me gustaría saber que les parece la idea.

Éste es el código que sólo está probado en simulación y el lunes lo voy a probar en el PIC.

                                        Saludos!

```
#include <16f876a.h>
#device adc=10
#use delay(clock=4000000)
#fuses HS,NOWDT,NOPROTECT,NOLVP,PUT,NODEBUG,BROWNOUT,NOCPD,NOWRT
#include <lcd.c> 
#include <internal_eeprom.c>
#use fast_io(B)
#use fast_io(c)
#use fast_io(a)

int16 contador=0,RPM,corte, Hora;
float ajuste;
int8 Segundos,Minuto;



#int_timer1
void timer1_isr(void)
{  
   contador=get_timer0();
   set_timer0(0);
   set_timer1(3036);
   if (rpm>600)
    Segundos++; 
}
void main()
{
   set_tris_a(0b00010011); 
   output_a  (0b00000000);
    set_tris_c(0b00011000); 
   output_c  (0b00000000);
   
   
   lcd_init();
   setup_timer_0(rtcc_ext_h_to_l | rtcc_div_1);
   setup_timer_1(t1_internal | t1_div_by_8);
   
   set_timer0(0);
   set_timer1(3036);
   enable_interrupts(int_timer1);
   enable_interrupts(global);

 //****************ADC**************************
   setup_adc_ports(AN0_AN1_AN3);
   setup_adc (ADC_CLOCK_INTERNAL);
   
 //*************Lectura de eeprom **************
   Hora=read_int16_eeprom(0x10); 
   delay_ms(150); 
   Minuto=read_eeprom(0x50);
   delay_ms(150);
      
  
   while(1)
    { 
         set_adc_channel(0);
         delay_ms(10);
         ajuste=(float)read_adc()*0.029;      
         RPM= contador*ajuste;
       
         set_adc_channel(1);
         delay_ms(10);
         corte=read_adc();
       
         lcd_gotoxy(1,2);
         printf(lcd_putc,"   %05lu:%02u:%02u ",Hora,Minuto,Segundos);  
       
         lcd_gotoxy(1,1);
         printf(lcd_putc,"RPM %lu" ,RPM);
         
         lcd_gotoxy(9,1);
         printf(lcd_putc,"cont %lu " ,contador);

      if(Segundos == 5) //El 5 es solo para  acelerar la simulacion aca iría 120 ya que estoy                           
      {                        // cada 0,5 seg
        
          Minuto++;
          Segundos=0; 
     
         
      if(Minuto == 60)
       {
          Hora++;
          Minuto=0;
       }
      }
       
      if (corte<820)
       {
          write_int16_eeprom(0x10, Hora);
          delay_ms(150);
          write_eeprom(0x50, Minuto);
          delay_ms(150);
       }
             
      if (input(pin_c3)==1) // Reset de cuenta horas
        {
          Minuto=00;
          write_eeprom(0x50, Minuto);
          delay_ms(150);
          Hora=00000;
          write_int16_eeprom(0x10, Hora);
          delay_ms(150);
       }
    }
}
```


----------



## D@rkbytes (Sep 19, 2015)

ariel 37 dijo:


> Lo que me pasa es que siempre estoy guardando el dato en la misma dirección de memoria y no sé si puedo dañar la memoria.
> 
> Se me había ocurrido que cada vez que grabe, aumente una dirección y cuando se complete que vuelva a cero, pero el tema es que no sé cómo hacer para que me lea la última dirección de memoria que se grabó.
> Ésto para no sobrecargar siempre la misma dirección.
> ¿Alguien me puede orientar en cómo debería hacerlo?


La memoria EEPROM interna la puedes escribir más de 1,000,000 de veces y tiene una retención de más de 40 años.
Así que por ese lado no deberías preocuparte tanto.
Pero tampoco es recomendable que los datos sean escritos constantemente en la memoria.
Debes buscar en evento específico para realizar esa tarea.

Y por lógica, si quieres incrementar la dirección para ir guardando los datos en una locación diferente, también necesitarás usar una locación de la EEPROM para almacenar esa información.
Por lo tanto, también estarás usando una dirección de memoria constantemente.

Ahora que si no quieres dañar la memoria interna por tantas escrituras, también puedes usar una externa, que es reemplazable.


----------



## dogflu66 (Sep 20, 2015)

Hay varias formas de guardar las horas en una posición distinta cada vez:

Como las horas siempre se incrementan, siempre será mayor a la ultima guardada, así que haces una búsqueda comparando y grabas inmediatamente superior a la hora que más se acerca a la ultima a grabar.

Otro seria grabar un carácter de control después de la hora, por ejemplo un asterisco, grabas la hora y seguidamente el *, la siguiente vez buscas el asterisco y grabas la hora en él y seguidamente grabas otro asterisco y así en ese bucle.

Por supuesto esta operación se lleva un tiempo, por eso no se usan eeprom de mucha capacidad, por suerte la lectura de la eeprom es mucho más rápida que la escritura.


----------



## ariel 37 (Sep 20, 2015)

Muchas gracias por sus respuestas ,creo que me preocupé demasiado por guardar en la memoria pero pienso que si dejo el pic siempre alimentado, no será necesario escribir tantas veces la memoria solo cuando se efectue algún cambio de batería, pero esto puede ocurrir tal vez una vez al año ,asi que por lo que dicen uds no creo que tenga problema en grabar un par de veces en la misma posicion, de todos modos les agradezco por las ideas que tal vez me puedan servir para otra ocasion.
Ahora me surge la pregunta : se puede dejar al pic siempre alimentado ? Con la llave de contacto solo cortaría la alimentacion al lcd pero dejaría al pic alimentado directo de bateria (con su respectiva proteccion).Estaría bien así?
Gracias por colaborar .
                                                  Saludos !!!


----------



## dogflu66 (Sep 21, 2015)

Tengo proyectos funcionado 24 horas diarias desde hace años, otros 10 o 12 horas al días y otros por unas pocas horas nada más. Por supuesto que se pueden tener alimentados 24 horas al día 365 días al año.


----------



## ariel 37 (Oct 18, 2015)

Hola que tal!!! vuelvo con el tema de grabar en la eeprom, por que estuve modificando el programa para finalmente grabar 4 datos en la memoria en caso que se quede sin alimentacion el pic ,pero esta vez quería hacerlo por interrupcion externa ,basicamente detectar un flanco de bajada por Rb0 y mediante la interrupcion grabar en la memoria, pero cuando compilo el programa me tira errores de delay esta es la funcion que hice:

```
#int_ext
void corte()
       {
          write_int16_eeprom(100, Hora);
          delay_ms(15);
          write_int16_eeprom(110, HoraParcial);
          delay_ms(15);          
          write_eeprom(120, Minuto);
          delay_ms(15);
          write_eeprom(130, MinutoParcial);
          delay_ms(15);
```
y estos los errores del compilador :
  interrupts disabled call to prevent re-entrancy {@delay_ms1}
  interrupts disabled call to prevent re-entrancy { write_int16_eeprom}
Como debería hacer la funcion para que cuando entre la interrupcion  me grabe estos datos en la memoria lo mas rapido posible es decir antes que se quede sin alimentacion el pic 
Tambien probé hacerlo asi :

```
#int_ext
void corte()
 {
    a=1;         
 }
```


```
while(1)
    { 
 
         if (a==1)
       {
          write_int16_eeprom(100, Hora);
          delay_ms(15);
          write_int16_eeprom(110, HoraParcial);
          delay_ms(15);          
          write_eeprom(120, Minuto);
          delay_ms(15);
          write_eeprom(130, MinutoParcial);
          delay_ms(15);
          a=0;          
       }
```
pero logicamente aparte de esto tengo mucho mas codigo ,en el cual estaria perdiendo demasiado tiempo dependiendo de en que punto estaba el programa cuando entró la interrupcion y hasta que vuelva a llegar a aquí, tal vez no me alcance el tiempo para grabar antes que se termine la alimentacion .
Y un ultima pregunta, de que manera desactivo todos los perifericos cuando entre esta interrupcion ,me refiero si con una instruccion dentro de la interrupcion o como debería hacerlo ? ya que desactivando los perifericos estaría ahorrando energia y ganando tiempo para grabar.
Desde ya les agradezco cualquier ayudita .Saludos!!!!


----------



## Gudino Roberto duberlin (Oct 19, 2015)

Hola, para empezar, debes detectar una baja de tensión antes de la etapa estabilizadora, por ejem. en la entrada de un regulador 7805, de ésta manera habrá tiempo anticipado para registrar datos antes del shutdown.
Además puedes añadir filtros de capacidad cómo para asegurar una autonomía de varios cientos de milisegundos.
No utilices retardos entre segmentos de grabación de EEPROM, para ello existe un flag en un registro especifico que indica el finalizado de dicha tarea. Con eso optimizas los tiempos de ejecución de otras tareas antes del apagado.


----------



## ariel 37 (Oct 19, 2015)

Hola Roberto gracias por responder!!!,te cuento hoy logré dejar el circuito  funcionando ya detecto la falta de alimentacion y grabo en memoria ,mi duda es la manera de grabar en la memoria pero con la interrupcion externa, de la manera que explique arriba que con la INT EXT activo un flag y con este flag ya dentro del while grabo en memoria , ya me esta funcionando, pero me parece que no es la manera correcta .De todos modos no entendí bien tu explicacion .
Cual es el registro especifico que indica el finalizado de la grabacion?
Como y donde lo tengo que aclarar?
Si coloco la grabacion de eeprom ,dentro de la funcion de la interrupcion y quito los delays tambien me marca warning cuando compilo.
Tenia entendido que cuando se realizan varias grabaciones seguidas había que dar un delay de 15ms entre cada una
 Espero haberme explicado .
                                       Saludos !!!


----------



## D@rkbytes (Oct 20, 2015)

No te preocupes por el flag de escritura completa.
Eso es para lenguaje ensamblador y tu programa está en C.
En lenguajes de alto nivel, el proceso de escritura está controlado por las instrucciones nativas del compilador o realizado por la librería que uses.

Pero si quita esos retardos después de cada escritura, ya que no tienen sentido por lo que expliqué.


----------



## Ardogan (Oct 21, 2015)

ariel 37 dijo:


> ...
> 
> ```
> #int_ext
> ...



Re-entrancy en este contexto (sin sistema operativo de por medio, microcontroladores con código normalito o bare-metal como le dicen) se refiere a que una función es llamada a la vez desde código normal y desde una o más rutinas de interrupción.
Toda función que escriba variables globales (mejor dicho recursos globales, por ejemplo en este caso los registros eeprom) es no-reentrante.

Lo que está diciendo el error es que no se permiten llamadas a función desde la rutina de interrupción, pero eso veo que ya lo entendiste por lo que hiciste más abajo.



ariel 37 dijo:


> Como debería hacer la funcion para que cuando entre la interrupcion  me grabe estos datos en la memoria lo mas rapido posible es decir antes que se quede sin alimentacion el pic
> Tambien probé hacerlo asi :
> 
> 
> ...



La respuesta más sencilla es chequear varias si a==1, es decir, en vez de:


```
while(1)
{
    if(a==1)
    {
        ;//salvar todo
    }
    funcion1();
    funcion2();
    funcion3();
    funcion4();
    funcion5();
    funcion6();
    funcion7();
...
}
```

hacer algo como:


```
void salvarTodo(void)
{
    if(a==1)
    {
        ;//salvar todo
    }
}

while(1)
{

    funcion1();
    salvarTodo();
    funcion2();
    salvarTodo();
    funcion3();
    salvarTodo();
    funcion4();
    salvarTodo();
    funcion5();
    salvarTodo();
    funcion6();
    salvarTodo();
    funcion7();
...
}
```

No es para nada bonito, pero puede funcionar.
Un enfoque más sofisticado sería modificar la dirección de retorno (el contador de programa PC guardado en la pila) en la rutina de interrupción corte() (si mal no recuerdo es lo que hace por ejemplo el freeRTOS para controlar la ejecución de tareas).
Pero ahí ya vas a tener que averiguar como se guarda el PC en la pila al producirse una interrupción.



ariel 37 dijo:


> Y un ultima pregunta, de que manera desactivo todos los perifericos cuando entre esta interrupcion ,me refiero si con una instruccion dentro de la interrupcion o como debería hacerlo ? ya que desactivando los perifericos estaría ahorrando energia y ganando tiempo para grabar.
> Desde ya les agradezco cualquier ayudita .Saludos!!!!



Con más código... deshabilitando uno por uno.

Ups... recién me dí cuenta que ya habían contestado, pero ya escribí todo el choclo así que ahí queda.


----------



## ariel 37 (Oct 23, 2015)

Hola Ardogan muchas gracias por tu respuesta disculpa no conteste antes pero ando sin internet en casa,te agradezco mucho la explicacion, estoy teniendo poco tiempo para dedicarme al codigo pero ni bien tenga algo les comento, nuevamente muchas gracias
                                                                Saludos!!!


----------



## ariel 37 (Oct 27, 2015)

Hola quería contarles que al final decidí cambiar un poco el programa basandome en varios ciclos while con condiciones y asi lograr salir de cualquier parte del programa al momento de la interrupcion y entrar al while donde se activan las grabaciones ya lo tengo probado y funciona.Quedó algo asi :

```
#int_ext
void corte()
 {  
    x=0;
    grabar=1;         
 }
```

Resto del programa:


```
while(x==0)
    {   
      if(grabar==1)
       {
          write_int16_eeprom(100, Hora);
          write_int16_eeprom(110, HoraParcial);         
          write_eeprom(120, Minuto);
          write_eeprom(130, MinutoParcial);
          grabar=0;
          lcd_putc("\f");           
          lcd_putc("    APAGANDO     ");           
          delay_ms(500);
       } 
          lcd_gotoxy(1,1);           
          printf(lcd_putc," MENU PRINCIPAL   ");     
   }
while (x==1)
{
codigo
}
while (x==2)
{
codigo
}
```
Muchas gracias por sus respuestas ,Si alguien quiere el codigo completo con gusto lo subiré.
                                              Saludos!!!!


----------

