INTRO
El punto central de un temporizador con interrupción por desbordamiento es que la interrupción se activará en un intervalo de tiempo preciso, siempre que el código ejecutado en la interrupción no tome más tiempo que el intervalo del temporizador.
EL PROBLEMA
En un PIC16LF1823, me he encontrado con el siguiente comportamiento y no lo entiendo. En resumen, se llamó a una de mis rutinas desde dentro de la interrupción y causó que la interrupción retrasara el encendido la próxima vez. Es como si la rutina de alguna manera hace que Timer0 se detenga mientras se está ejecutando. He reducido el código de problema tanto como puedo.
EMPEZANDO CON LO QUE FUNCIONA
Primero, el código básico sin adornos establece el procesador en 2MHz, inicia Timer0 sin preescalador, y en la rutina de interrupción, el pin de prueba (C3) se conmuta para verificar que todo esté funcionando. (Por cierto, estoy usando mikroC). Este código funciona como se esperaba:
void main() {
//2MHz
SPLLEN_bit = 0;
SCS1_bit = 1;
SCS0_bit = 0;
IRCF3_bit = 1;
IRCF2_bit = 1;
IRCF1_bit = 0;
IRCF0_bit = 1;
//Oscilloscope probe on C3
ANSC3_bit = 0;
TRISC3_bit = 0;
LATC3_bit = 1;
//Start Timer0
TMR0CS_bit = 0; //Internal clock
PSA_bit = 1; //No prescaler assigned to Timer0
//Enable timer interrups
TMR0IE_bit = 1;
//Enable general interupt
PEIE_bit = 0;
GIE_bit = 1;
LATC0_bit = ~LATC0_bit;
}
void interrupt(void){
if (TMR0IF_bit) {
//The interrupt fires every 132uS
TMR0 = 255 - 121;
TMR0IF_bit = 0;
LATC3_bit = ~LATC3_bit;
//Delay_us(100); //<<<<Uncommenting this widens the pulse, but the pulses are still 132uS apart
LATC3_bit = ~LATC3_bit;
}
}
He verificado que esto funciona. El pin C3 alterna una vez cada 132 µs con o sin el retraso.
CÓMO RODARLO
Sin embargo, en el siguiente código, la interrupción ahora se dispara cada 136 µs, en lugar de 132µs. La única diferencia es la adición de dos funciones (rutina1 y rutina2), y la declaración de un corto sin firmar. Mientras hago esta pregunta, solo eliminar el valor inicializado ('= 0') soluciona el problema.
void main() {
//2MHz
SPLLEN_bit = 0;
SCS1_bit = 1;
SCS0_bit = 0;
IRCF3_bit = 1;
IRCF2_bit = 1;
IRCF1_bit = 0;
IRCF0_bit = 1;
//Oscilloscope probe on C3
ANSC3_bit = 0;
TRISC3_bit = 0;
LATC3_bit = 1;
//Start Timer0
TMR0CS_bit = 0; //Internal clock
PSA_bit = 1; //No prescaler assigned to Timer0
//Enable timer interrups
TMR0IE_bit = 1;
//Enable general interupt
PEIE_bit = 0;
GIE_bit = 1;
LATC0_bit = ~LATC0_bit;
}
unsigned short mode=0; //<<<<<<<<<<Removing the initiator ('=0') fixes the problem!!!
void routine1(void){
unsigned short result;
mode = 0;
switch(mode) {
case 99:
//result = someFunction();
if (result == 0xFF) {
//Do something
}
break;
}
}
void routine2(void){
unsigned int result;
if (result == 0xFF) {
//Do something
}
}
void interrupt(void){
int tmp;
if (TMR0IF_bit) {
//The interrupt fires every 132uS
TMR0 = 255 - 121;
TMR0IF_bit = 0;
LATC3_bit = ~LATC3_bit;
//Delay_us(100);
LATC3_bit = ~LATC3_bit;
routine2();
}
}
¿Alguien puede explicar esto?
EDIT1:
CORRECCIÓN, el reloj está configurado a 4MHz, por lo que el reloj funciona a 1uS (1 / reloj de instrucciones). El comentario del código anterior es incorrecto.
El comentario de Bruce y m.Alin sobre el número mágico me hizo pensar. Debería poder codificar el código 132. Y luego se me ocurrió en el código original:
void interrupt(void){
if (TMR0IF_bit) {
TMR0 = 255 - 121; //Produces 132uS, but proves unpredictable
TMR0IF_bit = 0;
LATC3_bit = ~LATC3_bit;
LATC3_bit = ~LATC3_bit;
}
}
la línea:
TMR0 = 255 - 121;
es un intento de obtener la sincronización correcta, habiendo tenido en cuenta todo lo que sucede después se dispara la interrupción, pero antes tengo la oportunidad de restablecer el temporizador. factor de fudge mágico de 11. En otras palabras, estoy tratando de predecir cuál es el valor del temporizador en el momento en que lo preestablecí.
El hecho es que sé el valor: se mantiene en TMR0. Así que probé la siguiente línea a continuación:
TMR0 = TMR0 - 132; //Predictable but off by 3uS
Esto produjo 135uS pulsos, debido a la sentencia if, y la resta. Entonces, resté 3 de 132:
TMR0 = TMR0 - 132 - 3; //This produces 142uS pulses (132uS + 10uS for the extra operation)
Pero, por supuesto, eso no funcionó del todo porque al reducir el tiempo en 3, también agregué un par de operaciones que tomaron aún más tiempo. Así que intenté lo siguiente ...
TMR0 = TMR0 - 129; //This produces 132uS pulses (132 - 3)
Esto parece haber solucionado el problema. Esto debe significar que la interrupción no se dispara en exactamente de la misma manera cada vez. Algo debe estar deteniendo la interrupción a veces. Entonces, aunque esto es una solución, me deja sin saber exactamente cómo funciona el mecanismo de interrupción del temporizador.
¿Alguna idea?