Mehrere gleichzeitige Interrupts PIC16F88

Ich bin neu bei PIC-Mikrocontrollern (ich habe etwas Erfahrung mit Arduino, wollte aber etwas "echte" Erfahrung = P). Ich habe mir einen PIC16F88 besorgt und beabsichtige, einen einfachen Roboter mit zwei 2 Gleichstrommotoren zu bauen. Jeder Motor wird unabhängig über PWM gesteuert.

Ich möchte die PWM-Signale in Software implementieren (ja, ich weiß, eine ziemlich gute Lernerfahrung!). Dieser Mikrocontroller hat nur ein PWM-Modul, aber ich würde es in Software machen wollen, auch wenn er mehr hätte.

Ohne weiteres:

Was passiert, wenn zwei Timer-Überlauf-Interrupts gleichzeitig auftreten? Ignoriert der Controller einen? Rufen beide nacheinander den Interrupt-Vektor auf?

PIC16F88 sind so billig, warum verwenden Sie nicht einen für jeden Motor und überspringen das Schreiben einer Firmware-PWM-Routine?
Nun, das wäre ein fauler Ansatz. Ich mag eine Herausforderung. Und 2 Bilder kosten 2 * und verbrauchen doppelt so viel Platz und Energie, also ja.
Kaufen Sie stattdessen einen PIC mit zwei PWM-Modulen ... Software-PWM ist einfach hässlich.
Nun gut. Ich werde mein Projekt beenden, ich mache das sowieso zum Spaß. Ich werde gerne meinen Code hier für Feedback teilen, sobald ich fertig bin. Dann könnt ihr mir sagen, ob es hässlich ist. Aber ich fange an, die Idee tatsächlich zu mögen. Ich kann mit einem einzigen supergünstigen Mikrocontroller buchstäblich individuelle PWM an ein Dutzend Geräte senden. Aber das ist eine ganz neue Diskussion und etwas unabhängig von der Frage dieses Threads.

Antworten (1)

Angenommen, Sie haben die Interrupts TMR0 und TMR1 aktiviert, indem Sie TMR0IE und TMR1IE gesetzt haben. Wenn eines der Interrupt-Ereignisse eintritt, werden ihre Interrupt-Flags (TMR0IF oder TMR1IF) gesetzt. Dies bewirkt nicht, dass der Code tatsächlich zur Interrupt-Routine vektorisiert, es sei denn, das GIE-Bit (Global Interrupt Enable) ist ebenfalls gesetzt.

Angenommen, GIE wurde festgelegt, und Sie erhalten einen TMR0-Überlauf. Dies setzt TMR0IF, Vektoren zur Unterbrechungsstelle und löscht das GIE-Bit . Jetzt, da GIE gelöscht ist, verursachen zukünftige Interrupt-Trigger kein Code-Vectoring. Die Interrupt-Flags werden jedoch weiterhin gesetzt.

Angenommen, Sie befinden sich in Ihrer Interrupt-Routine. Der TMR1-Interrupt wird ausgelöst. Dies setzt das TMR1IF-Bit, tut aber sonst nichts. Das Bit bleibt einfach gesetzt.

Wenn Ihre Interrupt-Routine beendet ist, verwenden Sie eine RETFIEAnweisung (in Assembler) oder eine return();(in c). Dies vektorisiert zurück zu Ihrem Mainline-Code und setzt das GIE-Bit .

Da nun sowohl das GIE- als auch das TMR1IF-Bit gesetzt sind, bewirkt dies einen sofortigen Vektor zurück in Ihren Interrupt-Code.

Sie verlieren also keine Interrupt-Daten; es verzögert sich nur.

Eine übliche Art, einen ISR zu strukturieren, sieht folgendermaßen aus:

if (TMR0IF && TMR0IE)
{
    (do something)
    TMR0IF = 0; // Clear the flag!
    return();
}
else if (TMR1IF && TMR1IE)
{
    (do something else)
    TMR1IF = 0;
    return();
}

Wenn Sie jedoch gleichzeitige Interrupts erwarten, möchten Sie möglicherweise zulassen, dass beide in nur einem Durchgang durch die ISR verarbeitet werden:

if (TMR0IF && TMR0IE)
{
    (do something)
    TMR0IF = 0;
}
if (TMR1IF && TMR1IE) // Notice this isn't an "else if"
{
    (do something else)
    TMR1IF = 0;
}
return();

Es gibt noch ein letztes Problem, dessen Sie sich bewusst sein sollten. Angenommen, beide Interrupt-Flags werden gleichzeitig (oder sehr nahe an der gleichen Zeit) gesetzt. Zum Zeitpunkt der Codevektoren an die Interrupt-Routine sind beide Flags bereits gesetzt. Die ISR weiß nicht, welche zuerst gesetzt wurde ; es läuft einfach durch Ihren Code. In meinen früheren Beispielen wird also zuerst der TMR0-Interrupt bedient, auch wenn das TMR1-Flag etwas früher war.

Ich hoffe das hilft :)

@Ethienne Gern geschehen! Ich habe mein Beispiel nur ein wenig bearbeitet, wenn Sie es sich ansehen möchten.
Sie haben RETURN und RETFIE verwechselt. RETURN öffnet einfach den Aufrufstapel, um von einer Unterroutine zurückzukehren. RETFIE macht das, setzt aber zusätzlich noch das GIE-Bit. Sie verwenden RETFIE, nicht RETURN, um von einer typischen Interrupt-Routine zurückzukehren.
@OlinLathrop Das war ich tatsächlich, danke! Ich werde es reparieren...
Ich würde mich nicht um das "&& TMRxIE" kümmern ... die Tatsache, dass Sie sich in der Interrupt-Service-Routine befinden, impliziert, dass Interrupts aktiviert sind.
@aja Der Grund dafür ist, dass Sie damit die Möglichkeit haben, einen bestimmten Interrupt im Code zu deaktivieren. Wenn Sie beispielsweise TMR1-Interrupts vorübergehend ignorieren möchten, würden Sie das TMR1IE-Bit löschen. Das TMR1IF-Bit kann trotzdem gesetzt werden, obwohl es keinen Sprung zur ISR bewirkt. Angenommen, Sie haben TMR1IE gelöscht und TMR1IF wird gesetzt. Dann lässt ein anderer Interrupt (wie TMR0) den Code in die ISR springen. Die ISR würde sehen, dass TMR1IF gesetzt wurde und sich entsprechend verhalten ...
@bitsmack Punkt genommen. Normalerweise schalte ich die Quelle des Interrupts aus, wenn ich kann (z. B. TMR1ON = 0). Auf diese Weise kann ich die ISR-Größe auf ein Minimum beschränken. Kommt auf die Situation an denke ich.