PIC ISR - jede Interruptquelle prüfen oder nur eine?

Ich bin gespannt, ob es bestimmte Vorteile gibt, einen Anruf anstelle eines goto zu verwenden, wenn jede Interrupt-Quelle in einer ISR überprüft wird. Mein aktueller Code würde in etwa so aussehen:

.intr   CODE        4

    pagesel     $

    btfsc       INTCON, TMR0IF          ; Timer0
      goto      Timer0_Entry

    banksel     PIR1                    ; Bank #0
    btfsc       PIR1, SSP1IF
      goto      I2C_Entry               ; I2C Peripheral
...

aber das ist auf einem verbesserten Midrange-Bild 16, wo die Kontextspeicherung für Sie erledigt wird. Wenn ich beispielsweise auf einem PIC16F88 wäre, gäbe es einen (kleinen) Gewinn, um den ganzen langwierigen Kontext in einer Situation zu vermeiden, in der zwei gleichzeitige Interrupt-Quellen anstehen.

Wenn Sie dies mit Gotos tun, wird implizit eine Prioritätsliste erstellt, die andere Quellen anhalten kann. Wenn ich mehrere Interrupts von einer Quelle hätte, würde diese Quelle die anderen IRQ-Quellen anhalten, bis sie gestoppt wird. Ich habe das einmal getroffen, indem ich vergessen habe, dass das IRQ-Flag der seriellen Schnittstelle standardmäßig aktiviert ist.

Aber abgesehen davon, ist es nur eine Frage des Geschmacks? Der Schlag, den Sie (in einer Situation mit mehreren IRQs) erleiden, indem Sie einen Retfie machen und dann sofort zurück in die ISR gehen, ist nicht besonders schwerwiegend.

Ich denke, das ist situationsbedingt. In den meisten Fällen (meiner Erfahrung nach) sollte es "selten" sein, dass zwei Interrupts anstehen. Wenn es also nicht dringend erforderlich ist, so schnell wie möglich auf beide Interrupts zu reagieren (vielleicht um Jitter oder so etwas zu reduzieren), müssen Sie sich keine Sorgen machen, einen Twofer zu bekommen.

Antworten (1)

TL; DR: Verwenden Sie so wenig Interrupts wie möglich und halten Sie ihre ISRs kurz. Das Auslagern unkritischer Arbeit in die Hauptschleife ist eine große Hilfe.


Auf einem PIC mit seinem einzelnen Interrupt-Vektor (doppelt für PIC18) mache ich oft Folgendes:

void ISR()
{
    if(FLAG1_EN && FLAG1)
    {
        //ISR1
    }
    if(FLAG2_EN && FLAG2)
    {
        //ISR2
    }
    //etc.
}

Dadurch werden Funktionsaufrufe von der ISR vermieden, was dazu führt, dass mein Compiler eine Menge zusätzlichen kontextsparenden Codes generiert, selbst auf einem Chip, der dies (teilweise) automatisch erledigt.

Bei Bedarf kann ich sie nach Priorität ordnen und von jedem zurückgeben, ohne die restlichen Flags zu überprüfen, sodass die höheren Prioritäten häufiger abgefragt werden. Oder um etwas Latenz zu sparen, können Sie zurückspringen und die ISR wiederholen und nur zurückkehren, wenn keine aktiv sind.

Oder wenn einige ISRs periodische Aufgaben sind, die nur auf die richtige Frequenz gemittelt werden müssen, könnten Sie ihre ISRs einfach ein Flag setzen und verlassen. Die Hauptschleife achtet dann auf die Flaggen und führt die Aktivitäten aus, wenn sie zu ihnen gelangt. Dies macht die eigentliche ISR superkurz und ermöglicht es einer Aufgabe mit höherer Priorität, ihre Aktivität zu unterbrechen. Tatsächlich könnten Sie sogar das Interrupt-Flag selbst in der Hauptschleife verwenden und den eigentlichen Interrupt dafür nicht aktivieren.

Auf dieser Plattform können Sie nie garantieren, dass ein Interrupt mit superhoher Priorität mit konstanter Latenz bedient wird, es sei denn, Sie können ihn zur einzigen echten Interrupt-Quelle machen. Es besteht immer die Möglichkeit, dass es verschoben wird, bis ein anderes fertig ist. Es gibt Möglichkeiten, diese Wahrscheinlichkeit und die Folgen ihres Auftretens zu verringern, aber niemals zu beseitigen.

Abgesehen davon hat Spehro einen guten Punkt, dass Interrupts normalerweise einzeln auftreten und normalerweise kein Problem darstellen. Es sei denn, Sie verbringen Ihre ganze Zeit im ISR; dann ist eine Kollision fast garantiert.