Verursacht das Unterbrechen eines Mikrocontroller-Datenempfangs mit einem Interrupt mit höherer Priorität Datenverlust?

Ich würde gerne wissen, was passieren würde, wenn ein serielles Terminal eine große Datenmenge an einen PIC / AVR senden würde, beispielsweise über einen USART-Interrupt, der dann durch einen Interrupt mit höherer Priorität wie einen Timer unterbrochen würde, der die Daten verschieben würde überweisen. Würden die Daten verloren gehen? Oder hat das serielle Terminal vor dem Senden einen Puffer und hält die Daten fest, bis wir zum Empfangsinterrupt zurückkehren?

Zum Beispiel verwende ich einen atxmega128a1 und verwende diesen Interrupt, um Bytes zu empfangen:

//interrupt to receive a byte 
ISR(USARTC0_RXC_vect){                      //ISR for when receive flag is set
if(FIFO_Put(&RxFIFO, USARTC0_DATA)){        //put data into receive FIFO
    USARTC0_STATUS |= USART_RXCIF_bm;       //write 1 to clear bit
}
}

Wenn ich eine große Menge Bytes gleichzeitig über ein serielles Terminal sende, wird dieser Interrupt viele Male ausgelöst, um jedes Byte zu empfangen.

Ich habe auch einen Timer-Interrupt mit höherer Priorität, der lange dauern kann:

ISR(TCC0_OVF_vect){
    AXISid n = X;
    static BOOL cycle;
    TimerAxisInterrupt(n, &cycle);
} 

Um es noch einmal zu wiederholen, meine Frage lautet: Wenn der Interrupt mit höherer Priorität während einer großen Datenempfangsübertragung auftritt, gehen Daten verloren?

Falls relevant: Ich sende die USART-Daten über RS232 mit einem max3232-Logikkonverter. Ich habe die Handshaking-Leitungen und die RTS-CTS-Leitungen so geloopt, dass ich nur die Rx- und Tx-Leitungen verwende.

Antworten (1)

Unter der Annahme, dass kein Handshaking durchgeführt wird, verlieren Sie keine Daten, wenn der UART-Interrupt oft genug bedient wird, um zu verhindern, dass der Hardwarepuffer überläuft. Normalerweise ist das mindestens ein Zeichen, aber je nach Chip können es viele Zeichen sein (leistungsstärkere Chips haben tendenziell mehr Bytes Puffer).

Meiner Erfahrung nach können Sie sich nicht auf XON/XOFF-Handshaking verlassen, wenn die Puffergröße klein ist und der Absender ein PC ist – es kann einige Zeichen senden, bevor es dazu kommt, den Fluss zu unterbrechen.

Wenn also die Puffertiefe ein Zeichen beträgt und die Baudrate so ist, dass ein Zeichen 1 ms dauert, dürfen Sie in Ihrem Interrupt mit höherer Priorität nicht mehr als 1 ms wegnehmen, da Sie sonst Daten verlieren könnten (etwas weniger, weil Sie dies berücksichtigen müssen). Zeit in der UART ISR, um zu antworten und das eingehende Zeichen aus dem Puffer zu pflücken, und Sie sollten auch die Situation berücksichtigen, in der der UART-Interrupt ausgeführt wird, wenn er unterbrochen wird (sehen Sie sich an, wie er kurz vor und unmittelbar nach dem Lesen des eingehenden Zeichens auftritt).

Es gibt verschiedene Möglichkeiten, mit der Situation umzugehen, in der Sie zwei Interrupts mit schneller Antwort haben müssen. Ein Weg (normalerweise der beste Weg) wäre, nicht so viel in Ihrem Timer-Interrupt zu tun - Interrupt-Service-Routinen werden am besten verwendet, um schnell ein- und auszusteigen). Eine andere wäre, einen Puffer im Speicher zu implementieren und Ihren UART-Interrupt auf die höchste Priorität zu setzen (stecken Sie einfach das empfangene Zeichen in eine kreisförmige Warteschlange, erhöhen Sie die Modulo-Puffergröße des Schreibzeigers und kehren Sie zurück - Sie können die Gleichheit überprüfen, wenn Lese- und Schreibzeiger auf sehen, ob die Warteschlange leer ist). Abhängig von den Anforderungen und dem Prozessor gibt es noch andere Ansätze - vielleicht brauchen Sie die Timer-Routine nicht mit so hoher Priorität, oder vielleicht ändern Sie die Priorität dynamisch, sobald sie die kritischen Dinge erledigt hat, und ändern sie dann wieder auf hohe Priorität ,

Leistungsstärkere Prozessoren haben größere Puffer oder DMA, die es dem Prozessor ermöglichen, für sehr viele Zeichen herumzulaufen, bevor Datenverlust auftritt.

TL;DR Sie können dies vorhersagen, indem Sie Ihren Timer-Interrupt- und ISR-Interrupt-Code profilieren, um die maximale Ausführungszeit zu finden und mit der minimalen Zeit zum Füllen des Puffers (bei maximaler Baudrate) zu vergleichen.

Wenn der Timer-Interrupt für Timing-Funktionen wie Impulserzeugung, Timeouts usw. verwendet wird, sollte dem Timer-Interrupt nicht die höchste Priorität gegeben werden?
@AkshayImmanuelD Völlig anwendungsabhängig. Wenn Sie ein bisschen Jitter vom UART-Interrupt ertragen können, vielleicht nicht. Manchmal ist es nicht intuitiv. Ich habe zum Beispiel einen sehr komplexen Controller mit vielen kritischen Operationen, aber der Interrupt mit der höchsten Priorität ist das Umschalten eines Schallwandlers. Die anderen Operationen sind viel wichtiger , aber nicht so dringend , und ein paar Mikrosekunden Jitter haben keine praktischen Auswirkungen auf sie (aber Sie würden es im Klang hören).
Ich stimme zu. Wenn Sie eine Timeout-Toleranz zulassen können, sehe ich keinen Grund, warum der Timer-Interrupt die höchste Priorität haben muss. Aber andererseits völlig anwendungsspezifisch
@SpehroPefhany danke für deine Antwort. Um sicherzugehen, dass ich es verstehe, habe ich eine Baudrate von 19200 mit 10 Bits pro Zeichen (1 Start 1 Stopp 8 Datenbits). Das sind also etwa 521 us pro Charakter. Um Datenverlust zu vermeiden, sollten meine Interrupts mit höherer Priorität daher nicht länger als 521 us dauern oder bei einem 32-MHz-Takt nicht länger als 16667 Anweisungen sein. Richtig?
@SamBucca Eher wie die Summe der beiden ISR-Ausführungszeiten einschließlich Latenz und Kontextspeicherung, wenn Sie nur ein Pufferbyte haben. Außerdem können einige Anweisungen mehr als einen Zyklus dauern.
@SpehroPefhany: In einigen Fällen, in denen man in der Lage sein muss, einen vorberechneten Wert sofort als Reaktion auf ein nicht besonders häufiges Ereignis auszugeben, aber in denen die Berechnung des nächsten Werts eine Weile dauern würde, kann es hilfreich sein, ein Hoch zu haben -Interrupt mit Priorität auf das Ereignis reagieren, den Wert ausgeben, einen Interrupt mit niedriger Priorität senden und zurückkehren; der Interrupt mit niedriger Priorität könnte dann für das nächste Ereignis rechnen, ohne andere Interrupts mit mittlerer Priorität zu blockieren.