Forum: Mikrocontroller und Digitale Elektronik interrupt-reihenfolge und timingproblem für pic16fxxx


von pic_ler (Gast)


Lesenswert?

Hallo

ich nutze den ccs c Compiler v4.128  und habe
für einen Zeitmesser ein gängiges Beispiel
hergenommen.
Das Beispiel funktioniert auch wunderbar, nur
wenn ein EXT mit einem Timer0 Überlauf
zusammentrifft, dann kommt es zu einem falschen
Ergebnis.


#int_TIMER0
void  TIMER0_isr(void)
{
      Counter0 = Counter0 + 1;
}
#int_EXT
void  EXT_isr(void)
{
      anzahl0 = (Counter0 * 65536) + get_timer0() ;
      if (anzahl0 < 100000)
      {
          output_high(PIN_C0);
      }
      else
      {
         output_low(PIN_C0);
      }
      set_timer0(0);
      Counter0 = 0;
}
void main()
{
   while(TRUE)
   {
   }
}

Ich weiß theoretisch, daß
Interrupts nacheinander abgearbeitet werden, warum
also kommt beim zeitnahen Zusammentreffen
des EXT und des Timer0 Überlaufes zu einem
Fehler und was kann man dagegen machen?

Der Fehler sieht so aus, daß wohl der Counter
nicht hochgezählt ist aber der get_timer0()
noch recht niedrig ist, also übergelaufen ist.


Auch ein disable_interrupts entweder im ext
oder timer0 Interrupt brachte kein Ergebnis.

Vielen Dank im Voraus für eure Ratschläge

von Max H. (hartl192)


Lesenswert?

pic_ler schrieb:
> Der Fehler sieht so aus, daß wohl der Counter
> nicht hochgezählt ist aber der get_timer0()
> noch recht niedrig ist, also übergelaufen ist.

Das wird wahrscheinlich daran liegen, dass der Timer überläuft, während 
der PIC die EXT_isr(void) abarbeitet. Der Timer geht automatisch wieder 
auf 0, der PIC kann Counter0 erst erhöhen, sobald er die EXT_isr(void) 
fertig hat.

Nam könnte es lösen, wenn man nicht mit dem EXT Interrupt, sondern mit 
dem Input Capture arbeitet.
Das funktioniert so: Bei einer Fallenden, Seigenden, oder bei jeder 2., 
4. Oder 16. steigenden Flanke (einstellbar)  am CCP Pin wird ein 
Interrupt ausgelöst und gleichzeitig (per Hardware) der Wert von Timer1 
ins CCPRx kopiert. Du hast in diesem Register also den Timerwert exakt 
zum Zeitpunkt der Flanke am CCP Pin stehen.
Du musst jetzt dafür sorgen, dass, wenn die Interrupts zur gleichen Zeit 
auftreten, die ISR des CCP-Interrupt vor der des Timer-Interrupt 
ausgeführt wird.

: Bearbeitet durch User
von pic_ler (Gast)


Lesenswert?

Hallo Max

danke für Deine Antwort,
ich bin Anfänger in der PIC-Programmierung
und mit CCP habe ich noch garnichts gemacht.

Deine Erklärung mit dem Überlauf klingt verständlich.

Wenn ich das also richtig verstehe ist das so,
daß der Timer unabhängig läuft und einen
Überlauf verursachen kann auch wenn ich z. B.
gerade in die EXT-Routine einsteige und das
programm erst nach der Abarbeitung in den
int_Timer0 springt.

Gibt es keine andere Lösung als mit CCP?
Wie könnte ein Pseudocode für den CCP aussehen?

Vielen Dank :)

von Max H. (hartl192)


Lesenswert?

pic_ler schrieb:
> Wenn ich das also richtig verstehe ist das so,
> [...]
> int_Timer0 springt.
Ja der Timer arbeitet komplett unabhängig vom Programm, wenn er einmal 
gestartet wurde.

pic_ler schrieb:
> Wie könnte ein Pseudocode für den CCP aussehen?
Welchen PIC genau verwendest du?

von pic_ler (Gast)


Lesenswert?

Hallo

ich habe den 16F690

von Max H. (hartl192)


Lesenswert?

pic_ler schrieb:
> Gibt es keine andere Lösung als mit CCP?

Neue Idee:
1
void  EXT_isr(void)
2
{
3
      timerwert=get_timer0()
4
      anzahl0 = (Counter0 * 65536) + timerwert;
5
      if(INTCONbits.T0IF && timerwert < 30)
6
            anzahl0=anzahl0+65536;
7
      
8
      if (anzahl0 < 100000)
9
      {
10
          output_high(PIN_C0);
11
      }
12
      else
13
      {
14
         output_low(PIN_C0);
15
      }
16
      set_timer0(0);
17
      Counter0 = 0;
18
}
Jetzt wird geprüft, ob ein Timerinterrupt stattgefunden hat, das noch 
nicht bearbeitet wurde. In diesem Fall wird 65536 zu anzahl0 addiert.

zu INTCONbits.T0IF: Wie man bei deinem Compiler einzelne bits abfragt 
musst du wissen, mein Code ist für den C18

: Bearbeitet durch User
von pic_ler (Gast)


Lesenswert?

Hallo

die Lösung ist prima, sie trifft genau den Punkt.

Wie ich das Bit abfrage muß ich natürlich noch
rausfinden, aber ich weiß jetzt, was ich machen
muß.

Tausend Danke für die schnelle und hervorragende Hilfe :)

von Max H. (hartl192)


Lesenswert?

pic_ler schrieb:
> Wie ich das Bit abfrage muß ich natürlich noch
> rausfinden, aber ich weiß jetzt, was ich machen
> muß.
1
if((INTCON&0b00000100)&&(timerwert < 30))
So sollte es ziemlich sicher funktionieren.

von Teo D. (teoderix)


Lesenswert?

pic_ler schrieb:
> set_timer0(0);
>       Counter0 = 0;

Warum setzt Du den Timer auf null?
Da mußt Du Dich nicht wundern, wenn ab und zu nach der EXT_ISR, 
Scheinbar einen Timer-ISR verloren geht, Du löscht es ja damit!

von Max H. (hartl192)


Lesenswert?

pic_ler schrieb:
> }
>       set_timer0(0);
>       Counter0 = 0;
> }
Das T0IF solltest du auch auf 0 setzten, sonst hast eventuell du ein 
Interrupt zu viel.

von pic_ler (Gast)


Lesenswert?

Hallo Max und Teo

danke für eure Hilfe und Beiträge.

Die Umsetzung wird natürlich etwas Zeit in Anspruch nehmen,
aber ihr habt auch sehr zum Verständnis über den
Interrupt-Ablauf und Handling beigetragen.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.