Buenas,
Tengo un problema, o un error de concepto y lo que calculo luego no lo reproduce el PIC.
Veamos: quiero que se produzca una interrupción cada 1ms para realizar un cronometro de milésimas de segundo.
Usando la fórmula del T
MR0= 4·(1/F
osc)·(255-Precarga
TMR0)·Prescaler
Substituyo usando prescaler 4, cristal externo de 4Mhz y como decía 1ms para la interrupción, y obtengo una precarga para el TMR0 de 5.
Hasta aquí genial. Tengo mis dudas de si en la formula hay que restar a 255 o a 256, creo que es 255 ya que són 256 valores (de 0 a 255) no?
Pero vaya... el error no viene de aquí diría yo, seria una variación mínima. El problema es que cuando lo compilo y lo pongo en el PIC se demora...
Ahí va el código:
#include<16F84A.h>
#fuses XT,NOWDT,NOPROTECT //Fuses
#use delay(clock=4000000)
#use standard_io(B)
#use fixed_io(a_outputs=PIN_A0,PIN_A1,PIN_A2) //A0,A1,A2 como salidas en porta
char i=0,j=0,k=0; //variables globales
///LLAMADA FUNCION INTERRUPCION
#INT_TIMER0
void interrupcion()
{
if(i>9){ //¿se ha mostrado por 1º 7seg digito 9?
i=0; //SI -> i=0 (muestra digito 0) (*)
j++; //incremento indice j
if(j>9){ //¿se ha mostrado por 1º 7seg digito 9?
j=0; //SI -> j=0 (muestra digito 0) (*)
k++; //incremento indice k
if(k>9){ //¿se ha mostrado por 2º 7seg digito 9?
k=0;} //SI -> j=0 (muestra digito 0)
}
}
else{ //(*) NO -> incrementa i
i++;}
set_timer0(36); //reset TMR0
}
///PROGRAMA
void main(void)
{
int tab7seg[10]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x67}; //7seg hex 0-9
set_tris_b(0x00); //portb como salida
enable_interrupts(INT_TIMER0); //interrupcion TIMER0 activada
setup_counters(RTCC_INTERNAL,RTCC_DIV_4); //configuracion interrupcion TMR0
set_timer0(36); //5 carga TMR0 + 31 ciclos que demoran las instrucciones de la rutina
enable_interrupts(GLOBAL); //activadas interrupciones
do{ //bucle...
output_b(tab7seg[ i ]); //muestra por portb digito 7 segmentos
output_low(PIN_A2); //3º 7seg off
output_low(PIN_A1); //2º 7seg off
output_high(PIN_A0); //1º 7seg on
delay_ms(10);
output_b(tab7seg[ j ]); //muestra por portb digito 7 segmentos
output_low(PIN_A2); //3º 7seg off
output_high(PIN_A1); //2º 7seg on
output_low(PIN_A0); //1º 7seg off
delay_ms(10);
output_b(tab7seg[ k ]); //muestra por portb digito 7 segmentos
output_high(PIN_A2); //3º 7seg on
output_low(PIN_A1); //2º 7seg off
output_low(PIN_A0); //1º 7seg off
delay_ms(10);
}while(TRUE); //...infinito
}
Buscando y leyendo he entendido que las instrucciones dentro de la interrupción demoran la temporización, así que he mirado cuantos ciclos ocupaba y lo he compensado sumando 31 a la precarga. Queda 5+31=36
Esto es una chapuza, no acaba de ser exacto, pues hay instrucciones que no se si duran 1 o 2 ciclos. Así que a la vista y comparando con el crono de mi reloj parece que funciona bien, pero me gustaría más exactitud.
Para ello he pensado que si la interrupción tarda mucho menos en producirse, más rápido se ejecutaran las instrucciones no? pero sigo queriendo milisegundos. De esta manera creo una variable que controlará los milisegundos. Temporizo la interrupción para 10us y cada vez que entre a la interrupción la incremento hasta llegar al milisegundo (10usx100) y entonces ejecuto las instrucciones.
Usando la fórmula del T
MR0= 4·(1/F
osc)·(255-Precarga
TMR0)·Prescaler
Substituyo usando prescaler 2, cristal externo de 4Mhz y como decía 10us para la interrupción, y obtengo una precarga para el TMR0 de 250.
Ahí va de nuevo el código:
#include<16F84A.h>
#fuses XT,NOWDT,NOPROTECT //Fuses
#use delay(clock=4000000)
#use standard_io(B)
#use fixed_io(a_outputs=PIN_A0,PIN_A1,PIN_A2) //A0,A1,A2 como salidas en porta
char i=0,j=0,k=0,x=0; //variables globales
///LLAMADA FUNCION INTERRUPCION
#INT_TIMER0
void interrupt()
{
set_timer0(250); //reset TMR0
if(x==100){ //ajuste fino 1ms si interrupcion desborda cada 10us
x=0;
if(i>9){ //¿se ha mostrado por 1º 7seg digito 9?
i=0; //SI -> i=0 (muestra digito 0) (*)
j++; //incremento indice j
if(j>9){ //¿se ha mostrado por 2º 7seg digito 9?
j=0; //SI -> j=0 (muestra digito 0) (*)
k++; //incremento indice k
if(k>9){ //¿se ha mostrado por 3º 7seg digito 9?
k=0;} //SI -> j=0 (muestra digito 0)
}
}
else{ //(*) NO -> incrementa i
i++;}
}
else{
x++;
}
}
///PROGRAMA
void main()
{
int tab7seg[10]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x67}; //7seg hex 0-9
set_tris_b(0x00); //portb como salida
enable_interrupts(INT_TIMER0); //interrupcion TIMER0 activada
setup_counters(RTCC_INTERNAL,RTCC_DIV_2); //configuracion interrupcion TMR0
set_timer0(250); //5 carga TMR0
enable_interrupts(GLOBAL); //activadas interrupciones
while(true){ //bucle...
output_b(tab7seg[ i ]); //muestra por portb digito 7 segmentos
output_low(PIN_A2); //3º 7seg off
output_high(PIN_A1); //2º 7seg off
output_low(PIN_A0); //1º 7seg on
delay_ms(1);
output_b(tab7seg[ j ]); //muestra por portb digito 7 segmentos
output_low(PIN_A2); //3º 7seg off
output_low(PIN_A1); //2º 7seg on
output_high(PIN_A0); //1º 7seg off
delay_ms(1);
output_b(tab7seg[ k ]); //muestra por portb digito 7 segmentos
output_high(PIN_A2); //3º 7seg on
output_low(PIN_A1); //2º 7seg off
output_low(PIN_A0); //1º 7seg off
delay_ms(1);
} //...infinito
}
Y aquí es dónde llevo encallado des de hace ya demasiadas horas...
Si os fijáis he tenido que variar de 10 a 1 el delay de visualización del 7seg para ver los tres a la vez y aun así no se ve tan bien como antes.
Y ahora tarda casi siete segundos y medio en contar 999ms. Ahora que yo suponía que debía de ser más exacto es desastrosamente inexacto...
Está claro que debo de tener un error de concepto de como funciona o de como programarlo pero no se cual es por más vueltas que le doy...
Como puedo hacerlo?
Mil gracias por vuestra atención, espero que me podáis echar una mano