Timer PIC16 funktioniert nicht. Ist es mein Code?

Ich bin neu hier als OP, aber ich besuche diese Seite seit Jahren. Diesmal hat es mir nicht geholfen, nur auf andere Probleme zu schauen. Daher melde ich mich an und frage einfach!

Ich erstelle einen Countdown-Timer mit 30 Lichtern, die sich für eine einstellbare Zeit nacheinander ausschalten (für die Grundschule meiner Freundin). Ich verwende pic16f628a und MPLAB mit XC8-Compiler. Die Hardware ist kein Problem, ich habe ziemlich viel Übung. Die Software dagegen..

Der Timer funktioniert, da ich Tasten verwenden kann, um die eingestellte Zeit zu erhöhen, Start zu drücken, alle Lichter aufleuchten und beobachten können, wie die Lichter langsam (oder manchmal schnell) herunterzählen. Allerdings kann ich den Zeitpunkt nicht festlegen. Zum Beispiel dauert ein 2-Minuten-Set 3, ein 4-Minuten-Set 5. Was auch immer ich mit allen relevanten Variablen gemacht habe, funktioniert nicht. Das Timing ändert sich, aber nie richtig. Zum Beispiel verdoppelt die Verdoppelung der Anzahl der Interrupts pro Licht nicht (nicht einmal annähernd!) die Zeit und ich bekomme es nie richtig hin. Ich habe Dutzende von Variablen und Korrekturmaßnahmen berechnet und ausprobiert. Absolute Genauigkeit ist nicht erforderlich. Ich beschäftige mich seit mehr als 2 Wochen mit diesem Problem und bin verzweifelt. Bitte helfen Sie! Hier ist mein (ich denke relevanter) Code:

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()
}
}

Ich hoffe wirklich, dass jemand diesem totalen C-Amateur helfen kann.. Danke im Voraus!!
EDIT: Ich habe versucht, timer2 auch mit einem geänderten Interrupt-Zähler zu verwenden.
Dies war noch seltsamer als zuvor, da 1- und 2-Minuten-Sets in weniger als einer Sekunde endeten und 3 Minuten und mehr sehr lange dauerten Zeit 122 (4 Minuten)). Perfektes Timing! Aber jetzt ist es nicht einstellbar, also denke ich, dass etwas mit 'perlight' nicht stimmt?

@line 49 von der aktuellen Codeanzeige (d); //Senden der Zählung zur Anzeige kann Probleme verursachen (Verzögerungen verursachen). Entfernen Sie es und posten Sie die Ergebnisse
Hallo Triak, danke für deine Antwort. Ich habe Ihren Vorschlag implementiert und getestet und festgestellt, dass er das allgemeine Timing dieses Countdown-Timers nicht beeinflusst. Der Zeitpunkt ist noch sehr unsicher. EDIT2 in meinem Beitrag verwirrt mich; irgendwie berechnet es 'Perlight' nicht richtig..

Antworten (2)

Ich habe Ihren Code in MPLab Sim und auf echter Hardware getestet und in beiden Fällen die erwarteten Ergebnisse erhalten (4 Minuten mit perlight = a*61einer Zeiteinstellung von '2 Minuten'). Vielleicht wird Ihr Problem durch einen Code verursacht, den Sie uns nicht gezeigt haben, oder Sie haben ein Hardwareproblem. Ich kann jedoch einige Dinge sehen, die das Timing beeinflussen könnten: -

  1. Sie erhöhen die 16-Bit-Variable intrin der ISR und vergleichen sie dann mit perlightIhrem Hauptcode. Der 16F628 kann nur 8 Bits gleichzeitig vergleichen, und es kann jederzeit ein Interrupt auftreten , der intrsich mitten im Vergleich ändern und das Ergebnis verfälschen könnte. Um diese Möglichkeit zu verhindern, sollten Sie Interrupts deaktivieren, wenn intrvom Hauptcode darauf zugegriffen wird.

  2. Nachdem die Timing-Sitzung beendet ist, läuft Ihr Timer-Interrupt weiter, und Sie setzen ihn nicht zurück, intrbevor Sie eine weitere Timing-Sitzung starten. Dies führt dazu, dass nachfolgende Zeitmesssitzungen bis zu 4 Sekunden kurz sind. Zu Beginn jeder Timing-Sitzung sollten Sie Interrupts deaktivieren, die Timer-Hardware zurücksetzen, auf Null zurücksetzen und intrdann Interrupts wieder aktivieren, um mit dem Zählen zu beginnen.

  3. Sie löschen TMR0im ISR. Dies ist nicht erforderlich, da der Timer automatisch auf Null springt, wenn er überläuft. Allerdings setzt jeder Schreibvorgang TMR0auch den Prescaler zurück, wodurch die Zyklen verloren gehen, die er zwischen dem Timer-Überlauf und dem Schreibvorgang gezählt hat TMR0. Wenn Sie den Timer frei laufen lassen, kann er eine konsistente Zeit beibehalten, selbst wenn sich die Interrupt-Latenz ändert.

  4. Mit einem 4-MHz-Takt und einem Vorteiler von 1:256 können Sie keine genaue Zeitverzögerung von 2 Sekunden erhalten, da 2/0,065536 keine ganze Zahl ist. Sie können dem jedoch sehr nahe kommen, indem Sie den Prescaler auf 128 ändern und einen Perlight-Multiplikator von 61 verwenden, was 1,998848 Sekunden (128*256*61) entspricht. Für die beste Timing-Genauigkeit sollten Sie anstelle des internen Oszillators (der um mehrere Prozent abweichen kann) einen Kristall- oder Keramikresonator verwenden.

Ich habe Ihren Code nicht vollständig analysiert, aber ich vermute, dass Ihr Timer möglicherweise schneller überläuft, als Ihre Codeschleife ausgeführt wird. Und wenn ja, dann überlaufen Sie Ihr Intr-Register, was zu seltsamen Ergebnissen führt.

Bitsmack, danke für deine schnelle Antwort! Aber soll der Timer nicht alle 256 Zählungen von timer0 überlaufen? Eine Zählung ist also 1 us, Überlauf bei 256 us, mit Prescaler 0,065 s, mal 67 = 4 s?
Nur um sicherzugehen, habe ich 67 durch 15745 ersetzt, um die Zeit pro Licht auf 4 s zu bringen. Dieser Schritt hat die Uhr sehr langsam gemacht. Ich habe auch das int 'intr' durch ein langes 'intr' ersetzt, nur für den Fall. Trotzdem kein vorhersehbarer Zeitpunkt.
@SjorsLiebregts Du hast natürlich Recht :) Ich habe meinen Beitrag korrigiert. Ich komme morgen wieder und schaue nochmal. Hoffentlich hat bis dahin jemand eine Antwort für dich!