El temporizador PIC16 no funciona ... ¿es mi código?

3

Soy nuevo aquí como OP, pero he estado visitando este sitio durante años. Esta vez, solo mirar otros problemas no me ha ayudado. Por lo tanto, me inscribo y solo pregunto!

Estoy creando un temporizador de cuenta regresiva con 30 luces que se apagan una tras otra durante un tiempo ajustable (para la escuela primaria de mi novia). Estoy usando pic16f628a y MPLAB con el compilador XC8. El hardware no es un problema, he tenido bastante práctica. El software por otro lado ..

El temporizador funciona, ya que puedo usar los botones para aumentar el tiempo establecido, presionar inicio, todas las luces se encienden y ver las luces lentamente (o, a veces, rápidamente) haciendo una cuenta regresiva. Sin embargo, no puedo arreglar el tiempo. Por ejemplo, 2 minutos establecidos duran 3 minutos, 4 minutos duran 5. Lo que he hecho con todas las variables relevantes, no funciona. El tiempo cambia, pero nunca correctamente. Por ejemplo, duplicar el número de interrupciones por luz no duplica (¡ni siquiera casi!) El tiempo y nunca lo hago bien. He calculado y probado docenas de variables y medidas correctivas. La precisión absoluta no es necesaria. He estado en este problema por más de 2 semanas y me estoy desesperando. ¡Por favor ayuda! Aquí está mi código (creo relevante):

include <xc.h>
pragma config FOSC = INTOSCIO  // Oscillator Selection bits
pragma config WDTE = OFF       // Watchdog Timer Enable bit
pragma config PWRTE = OFF      // Power-up Timer Enable bit
pragma config MCLRE = ON       // RA5/MCLR/VPP pin function is MCLR
pragma config BOREN = ON       // Brown-out Detect Enable bit
pragma config LVP = OFF        // Low-Voltage Programming Enable bit
pragma config CPD = OFF        // Data EE Memory Code Protection bit 
pragma config CP = OFF         // Flash Program Memory Code Protection bit 
define _XTAL_FREQ 4000000                            

volatile int intr = 0; //number of interrupts

void interrupt TimerOverflow(void) 
{
  if(TMR0IE && TMR0IF)
  {
  TMR0 = 0;  //clear timer 0
  TMR0IF=0;  //clear flag
  intr++;    //count 1 interrupt
  }
}

void buzzer()  // this part just buzzes on start and on end, not included for cleanliness   
void display2(int c) //multiplexing, this function displays the last sections' digits. Working so not included. int c is the number of minutes, as received from display()
void display(int b) //multiplexing, this function displays the first sections. Working so not included. int b is the number of minutes, as received from init() and teller()

void teller(int a) //a is the number of minutes as received from init(), 'teller' is counter in dutch
{
int perlight = a*67 //number of minutes times a variable (now 67) to calculate number of interrupts per light-shut-off
PS0 = 1;        // Prescaler (1:256) is assigned to the timer TMR0
PS1 = 1;
PS2 = 1;
T0CS = 0; //internal clock selector
PSA = 0; //assign prescalar
TMR0IE = 1; //timer0 interrupt active
PEIE = 1; //peripheral interrupt active
GIE = 1; //global interrupt active
TMR0IF = 0; //clear timer0 flag
TMR0 = 0;   // clear timer0
int d = 30;     //turn all lights on starting the count
while(1) //endless loop
{
    if (intr>=perlight) //if number of interrupts is more than 'needed' for one-light-shutoff
    {
        intr = 0; //reset interrupts
        d--; //decrease count (turn one light off)
    }
    display(d); //send count to display
    if(d==0) //if count is done
    {
      buzzer(); //beep 3 times
      __delay_ms(500);
      buzzer();
      __delay_ms(500);
      buzzer();
      break;
    }
}
}

void init()
{
int min = 0; //number of minutes that the clock needs to run
while(1) //endless loop
{
    display(min); //send number of minutes to display function
    if(RA2==0) //if 'add minute' button has been pressed
    {
    __delay_ms(50); //debouncer
    if(RA2==0) 
        {
        min++; //add minute to count
        display(min); //send to display
        }
    } 
   if(RA3==0) //if start is pressed
    {
        __delay_ms(50); //debouncer
        if(RA3==0)
        {
            if(min!=0) //if start is pressed, buzz and start teller()
            {
                buzzer();
                teller(min);
            }
            if(min==0) // if start is pressed without timing set, buzz 3 times
            {
                buzzer();
                __delay_ms(500);
                buzzer();
                __delay_ms(500);
                buzzer();
            }
        }
   }
}

}
void main()
{
TRISB = 0b00000000; //RB as Output PIN
TRISA = 0b00101100; //RA as Input/Output PIN
CMCON = 0b00000111; //disable comparators
while(1)
{
  init(); //start init()
}
}

Realmente espero que alguien pueda ayudar a este C-amateur total .. ¡Gracias de antemano!
EDITAR: También he intentado usar timer2 con un contador de interrupciones cambiado. Esto fue incluso más extraño que antes, con 1 y 2 minutos de juego terminados en menos de un segundo, y 3 minutos y mucho más tiempo.
EDIT2 He intentado reemplazar la variable 'perlight' por un número entero (61 (2 minutos), segunda vez 122 (4 minutos)). ¡Tiempo perfecto! Pero ahora no es ajustable, ¿así que me imagino que algo está mal con 'perlight'?

    
pregunta Sjors Liebregts

2 respuestas

3

Probé su código en MPLab Sim y en hardware real, obteniendo los resultados esperados en ambos casos (4 minutos con perlight = a*61 y tiempo establecido en '2 minutos'). Es posible que su problema se deba a algún código que no nos haya mostrado o que tenga un problema de hardware. Sin embargo, puedo ver algunas cosas que pueden afectar el tiempo: -

  1. Estás incrementando la variable de 16 bits intr en el ISR, y luego la estás comparando con perlight en tu código principal. El 16F628 solo puede comparar 8 bits a la vez, y puede producirse una interrupción en cualquier momento , por lo que intr podría cambiar a la mitad de la comparación y dañar el resultado. Para evitar esta posibilidad, debe desactivar las interrupciones siempre que se acceda a intr desde el código principal.

  2. Una vez que finaliza la sesión de temporización, continúa la interrupción del temporizador y no reinicia intr antes de comenzar otra sesión de temporización. Esto hace que las sesiones de tiempo subsiguientes tengan una duración de hasta 4 segundos. Al comienzo de cada sesión de tiempo, debe deshabilitar las interrupciones, reiniciar el hardware del temporizador, restablecer intr a cero y luego volver a habilitar las interrupciones para comenzar a contar.

  3. Estás eliminando TMR0 en el ISR. Esto no es necesario porque el temporizador se ajusta automáticamente a cero cuando se desborda. Sin embargo, cualquier escritura en TMR0 también restablece el preescalador, perdiendo los ciclos que contó entre el desbordamiento del temporizador y la escritura en TMR0 . Dejar que el temporizador se ejecute libremente le permite mantener un tiempo constante incluso si la latencia de interrupción cambia.

  4. Con un reloj de 4 MHz y un prescaler de 1: 256 no puede obtener un retardo de tiempo preciso de 2 segundos porque 2 / 0.065536 no es un número entero. Sin embargo, puede acercarse mucho cambiando el prescaler a 128 y usando un multiplicador 'perlight' de 61, que equivale a 1.998848 segundos (128 * 256 * 61). Para obtener la mejor precisión de tiempo, debe usar un resonador de cristal o cerámica en lugar del oscilador interno (que puede estar fuera en un porcentaje).

respondido por el Bruce Abbott
1

No he analizado completamente su código, pero sospecho que lo que está sucediendo es que su temporizador se está desbordando más rápido de lo que se está ejecutando su bucle de código. Y, si es así, entonces estás desbordando tu registro intr, lo que está llevando a resultados extraños.

    
respondido por el bitsmack

Lea otras preguntas en las etiquetas