Was ist falsch an dieser PIC-Pin-Änderungserkennung?

Ich habe zwei PIC18F4620 über SPI + Slave Select + zusätzliche IRQ-Leitung verbunden. Beide Controller werden von demselben Quarzoszillator mit denselben Takteinstellungen angesteuert. Der Master sendet ein Byte und wartet dann, bis der Slave diese zusätzliche IRQ-Leitung umschaltet. Die Dauer des Umschaltens beträgt 4 Befehlszyklen. Alle Kanten sehen auf dem Oszilloskop gut aus und die SPI-Kommunikation funktioniert ordnungsgemäß, mit Ausnahme der Erkennung des Umschaltens ( while(!PORTBbits.RB1);).

Dies ist mein SPI-Sendecode:

    while (spi_out_msg_buffer.write_cursor > spi_out_msg_buffer.read_cursor)
    {
        DisableInterrupts;
        SSPBUF = spi_out_msg_buffer.data[spi_out_msg_buffer.read_cursor];
        LATBbits.LATB0 = 1;
        while(!PORTBbits.RB1); // wait for toggle on IRQ line
        LATBbits.LATB0 = 0;
        EnableInterrupts;
        spi_out_msg_buffer.read_cursor++;
    }

Das while(!PORTBbits.RB1);wird in zwei Anweisungen übersetzt:

BTFSS PORTB, 1, ACCESS
BRA 0x188

Ich habe diese B0Zeile zu Debugging-Zwecken eingefügt, Sie können sie ganz unten in diesem Zeitdiagramm sehen:

zeitliche Koordinierung

Sie können das Umschalten der IRQ-Leitung (zweite von unten) sehen und wie es unentdeckt bleibt, weil die B0Debugging-Leitung hoch bleibt. Wenn ich die Ausführung über ICD stoppe, hängt es in der while. Es ist erwähnenswert, dass es normalerweise für ein paar Bytes funktioniert und dann stoppt, wie Sie hier sehen können:

ganzen Zeitablauf

Ich habe gemessen, dass der Impuls tatsächlich 4 Befehlszyklen (= 16 PLL-Zyklen = 4 Taktzyklen) lang ist:

Impulslänge

Ich denke, das sollte ausreichen, um den Puls zu erkennen. Selbst wenn das erste BTFSS es vermisst, weil der Port zu Beginn des Befehlszyklus abgetastet wird, sollte es das zweite bekommen:

noch mehr Timing

10 MHz -> PLL -> 40 MHz -> 10 M Befehle pro Sekunde -> 100 ns pro Befehl.

Sollte das nicht lang genug sein, um das zu verlassen while?

Überprüfen Sie Ihren Assembler-Code, es könnten leicht mehr als 4 Anweisungen sein, obwohl es in diesem Fall weniger sein könnten.
Warum nicht die Pegeländerungsfunktion einiger IO-Leitungen des PIC verwenden? Dies aktualisiert ein Flag (und/oder löst einen Interrupt aus), wenn sich der Wert am Pin ändert. Dies ist unabhängig vom Befehlstakt und sollte Ihr System zuverlässiger machen.

Antworten (3)

Sie verwenden einen Compiler, daher haben Sie keine Ahnung, wie viele Zyklen diese Abfrageschleife benötigt. In Assembler konnten Sie es auf 3 Zyklen reduzieren, aber Sie haben das Recht aufgegeben, Zyklen zu zählen, als Sie den Code in einer Hochsprache geschrieben haben.

Das eigentliche Problem ist jedoch der Gesamtansatz. Es ist keine gute Idee, den Code zu bitten, einen kurzen Fehler abzufangen. Selbst wenn Sie garantieren könnten, dass die Schleifenzeit kleiner als die Glitch-Zeit ist, können Sie jetzt während des Wartens keine Interrupts einschalten. Dies kann später zu architektonischen Problemen führen.

Eine viel bessere Idee ist es, die Hardware zu verwenden, die Sie bereits haben, um den Fehler zu erkennen, und dann die Firmware diese Hardware überprüfen zu lassen. Am einfachsten wäre es, den Glitch mit einer der INTx-Leitungen zu verbinden und dann nach dem INTxIF-Flag zu suchen. Änderungserkennungsstifte würden auch funktionieren, aber denken Sie daran, dass das Flag an beiden Rändern gesetzt wird und Sie die Nichtübereinstimmung beseitigen müssen, um die Bedingung zu beseitigen.

Sie können immer noch Zyklen zählen , wenn Sie eine Hochsprache verwenden, es erfordert nur etwas Graben. Ich stimme jedoch zu, dass die Verwendung von Hardware insgesamt ein besserer Weg wäre.
@ Kevin: Sie können Zyklen mit etwas Graben zählen, aber die Antwort ist nicht sehr aussagekräftig. Es wird Ihnen nur sagen, was diese Version des Compilers bei dieser Optimierungseinstellung und anderen möglicherweise nicht offensichtlichen Parametern tut. Wenn Sie die Anzahl der Zyklen in einer Schleife garantieren müssen, müssen Sie Interrupts deaktivieren und in Assembler schreiben.
Ich habe Interrupts deaktiviert, wie Sie im Code sehen können, und ich habe das Assembly-Snippet in der Frage direkt aus der Disassemblierung kopiert, also wird genau das auf dem PIC ausgeführt, nicht wahr?
Ich habe Pin B1 bereits bewusst für die IRQ-Leitung verwendet, damit ich tatsächlich seine Interrupt-Fähigkeit nutzen kann. Allerdings frage ich mich sehr, warum das nicht so funktioniert.

Die letzte Anweisung ist eine unbedingte Verzweigung, richtig? Normalerweise benötigen Verzweigungen und Speicherzugriffe (bei der ersten Anweisung durchgeführt, richtig?) etwa 2-3x mehr Zeit für die Ausführung. In Ihrem Fall könnten diese Anweisungen also etwa 16 Zyklen dauern, was 400 ns entspricht (im besten Fall). ).

Können Sie weitere Informationen darüber geben, wie das OP (Originalplakat) dies herausfinden könnte? Wie können Sie das umgehen? Lassen Sie uns ein Problem lösen! Ich wies in meinem Kommentar darauf hin, dass sie ihren Assemblercode überprüfen sollten.
Ich bin kein PIC18-Experte, aber laut dieser Site haben Sie ein Flag, das aktualisiert wird, wenn der SPI-Sende- / Empfangsvorgang endet. Versuchen Sie, diese Zeile durch zu ersetzen while(!SSPSTATbits.BF);.
Oh, das war mir zwar nicht bewusst, dass der BRA zwei Befehlszyklen braucht, aber 400 ns sollten doch reichen, oder nicht? Ich habe die Frage aktualisiert, wie ich mir den schlimmsten Fall vorstelle.
@AndreKR: PORTB, es ist eine Adresse, oder? Wenn dies der Fall ist, wird BRFSS wahrscheinlich genauso lange dauern wie die Verzweigung (weil die MCU den Wert auf PORTB in ein Register laden muss). Warum probierst du nicht meinen Vorschlag im obigen Kommentar aus, von dem ich glaube, dass er bei der ersten Antwort derselbe ist wie bei @MattJenkins?
Aus dem Datenblatt: Alle Einzelwortbefehle werden in einem einzigen Befehlszyklus ausgeführt, es sei denn, ein Bedingungstest ist wahr oder der Programmzähler wird als Ergebnis des Befehls geändert. In diesen Fällen dauert die Ausführung zwei Befehlszyklen, wobei der (die) zusätzliche(n) Befehlszyklus(e) als NOP ausgeführt werden. Dein Vorschlag hat nichts mit meinem Problem zu tun. MattJenkins' ist derselbe wie Olins Vorschlag, siehe meinen Kommentar dort.
@AndreKR: siehe Tabelle 24-2 des Handbuchs
Sie meinen die "1 (2 oder 3) Zyklen" des BTFSS? Das ist 1 Zyklus, wenn die Bedingung nicht erfüllt ist (Bit ist nicht gesetzt), 2 Zyklen, wenn und nur wenn das Bit gesetzt ist, sodass es überspringen muss, und 3 Zyklen, um eine Zwei-Wort-Anweisung zu überspringen (was BRA nicht ist).
@AndreKR: Entschuldigung, ich habe es falsch gelesen. Ich dachte, wenn die Bedingung nicht erfüllt ist, muss er die nächste Anweisung überspringen. Während ich einen Blick auf dieses Handbuch warf, sah ich einen Assembler-Code, um ihn in SPIbuf zu schreiben, Sie könnten einen Blick darauf werfen.
Kein Problem, eigentlich haben sie eine Anleitung dafür, aber die habe ich nicht benutzt. Wie auch immer, hier geht es überhaupt nicht um SPI, vielleicht hätte ich SPI nie erwähnen sollen.

Der eigentliche Grund war, dass ich ein Idiot war. Mein DisableInterruptsMakro hat es getan INTCONbits.GIE=1.

Ich habe das gefunden, indem ich während des geschäftigen Wartens eine Stecknadel umgeschaltet habe:

while(!PORTBbits.RB1)
{
    LATBbits.LATB0 = 0;
    LATBbits.LATB0 = 1;
}

Mir ist aufgefallen, dass das Toggeln in regelmäßigen Abständen aufhört und wenn der Benachrichtigungsimpuls in eines dieser Fenster fällt, bleibt es unbemerkt.

Geben Sie hier die Bildbeschreibung ein

Keiner von euch konnte das wissen, trotzdem +1 für beide Antworten, um mich auf den Cycle-Count und den Pinchange-Interrupt hinzuweisen.