ATMEGA UART-Übertragung mit Flusskontrolle

Hintergrund

Ich verwende einen ATmega328P, um Daten mit einer hohen Baudrate (230400) an ein anderes Gerät zu senden. Das Gerät, an das ich Daten sende, unterstützt die Flusskontrolle und erhöht das RTS-Signal, wenn es die Übertragung halten muss. Das Problem ist, dass das Gerät erwartet, dass ich die Übertragung sofort stoppe, nachdem RTS bestätigt wurde.

Der ATmega328P hat einen Übertragungspuffer (UDR) und ein Schieberegister, von dem Daten an die TX-Leitung gesendet werden. Da der ATmega328P keine Flusskontrolle in Hardware unterstützt, implementiere ich sie in Software. Vor dem Senden (bevor ich in UDR schreibe) überprüfe ich RTS und warte, bis es niedrig wird, aber anscheinend reicht dies nicht aus, da Daten im Sendepuffer und im Schieberegister noch gesendet werden und ein vollständiges Byte immer noch gesendet werden könnte, nachdem RTS gesendet wurde wird bestätigt, wenn der ATmega328P seine Puffer leert.

Anscheinend hilft das Deaktivieren des Senders, wenn RTS aktiviert ist (setzen des TXEN auf Null), nicht, da es nicht wirksam wird, bis laufende und anstehende Übertragungen abgeschlossen sind, gemäß Dokumentationsabschnitt 19.6.5 .

Eine Problemumgehung könnte darin bestehen, zu warten, bis Transmit Complete (TXC) abgeschlossen ist, bevor jedes Byte gesendet wird, aber dies würde sich auf die Datenrate auswirken, da ich den Byte-Stream nicht hintereinander sende, selbst wenn RTS nicht aktiviert ist (ich fange an ein neues Byte erst senden, nachdem das vorherige vollständig gesendet wurde).

Meine Frage ist:

Gibt es mit dem ATmega328P eine Möglichkeit, die RTS-Flusskontrolle im Sender zu unterstützen und gleichzeitig das Pipelining des Sendepuffers und des Schieberegisters zu nutzen?

Welche Art von Gerät erwartet, dass Sie die Übertragung mitten in einem Frame anhalten?
Ich sehe nicht, was an dem vollständigen Übertragungsansatz so falsch ist. Ja, es kann abhängig von der Taktrate Ihres Controllers zu einer einzelnen Bitverzögerung kommen, aber Sie müssen in jedem Fall den RTS-Zustand überprüfen. Selbst mit einem softwareimplementierten UART (was nicht so schwer ist) müssten Sie diese Überprüfung durchführen. Oder übersehe ich etwas? Ich denke schon, weil die Frage nicht so aussieht, als hätten Sie eine Weile nicht darüber nachgedacht. Erhöht das andere Gerät die RTS-Leitung "sofort", nachdem das letzte Bit empfangen wurde?
@IgnacioVazquez-Abrams nicht in der Mitte eines Frames (ein Byte), aber Atmega löscht den Übertragungspuffer vor dem Stoppen, sodass genau ein Byte noch gesendet werden kann, nachdem RTS bestätigt wurde.
Wählen Sie dann den RTS-Interrupt so aus, dass er Priorität gegenüber dem TXE-Interrupt hat.
@Rev1.0 Wenn der TXEN den Übertragungspuffer nicht geleert hat, bevor er wirksam wurde, würde das wahrscheinlich das Problem lösen. Oder ob das Gerät ein weiteres einzelnes Byte akzeptieren könnte, nachdem RTS aktiviert wurde. Glaubst du, es ist eine kleine Verzögerung? erst nachdem TXC bestätigt wurde, fange ich an, ein weiteres Byte zu senden. Je nach Szenario kann dies wahrscheinlich einige Zeit dauern. Ich denke, eine Lösung könnte SW-implementierter UART sein, aber das würde viel mehr Ressourcen von der MCU beanspruchen und macht keinen Sinn, wenn HW UART unterstützt.
@IgnacioVazquez-Abrams und was dann? Setzen Sie den Atmega in den Ruhezustand, um die UART-Uhr zu stoppen, bis RTS ausfällt? Das Problem ist, dass ich den Sender nicht deaktivieren kann, bis er seine Puffer geleert hat.
Wenn Sie "flush" sagen, sprechen Sie wohl davon, dass das nächste Byte aufgrund der doppelten Pufferung in den Sendepuffer geht? Warum nicht einfach nur ein einzelnes Byte in die Übertragungswarteschlange stellen (UDR=Daten), warten, bis TX abgeschlossen ist, ISR, prüfen, ob RTS gesetzt ist, wenn „Ja“, nächstes Byte in UDR schreiben, wenn „Nein“, nichts tun und RTS-Zustand prüfen in der Hauptschleife. Bezüglich der Datenrate: Wenn Sie mit 16MHz/8MHz laufen, haben Sie ungefähr 70/35 Anweisungen pro Bit (@230k Baud). Dies sollte ausreichen, um RTS auszuwerten und zu reagieren.
Und dann weisen Sie die Senderoutine an, das nächste Byte nicht zu senden.
@IgnacioVazquez-Abrams Ich sende das nächste Byte bereits nicht, wenn RTS geltend gemacht wird, aber es ist zu spät, da ein Byte bereits gepuffert ist und trotzdem gesendet wird (und dies geschieht, nachdem RTS geltend gemacht wurde).
@ Rev1.0 Was Sie vorschlagen, klingt nach einer gültigen Option und ist mehr oder weniger die Problemumgehung, die ich in meiner Frage vorstelle. Sie mögen Recht haben, dass die Strafe nicht hoch ist, aber zumindest schlägt ATMEL dies in ihren Dokumenten nicht so vor. Sie präsentieren ein Codefragment zum Senden von Daten auf UART (allerdings ohne Flusskontrolle). Im Codebeispiel in Abschnitt 19.6.1 in ihrer Dokumentation schlagen sie vor, abzufragen UDREnund nicht TXCn.

Antworten (1)

Klingt, als hätten Sie zwei grundlegende Probleme, ein schlecht konstruiertes externes Gerät und dass Sie versuchen, ungeeignete Hardware dazu zu bringen, eine Aufgabe zu erledigen, für die sie nicht vorgesehen ist.

Erstens: Benötigt dieses andere Gerät wirklich absolut keine Bytes mehr, nachdem RTS aktiviert wurde? Das wäre sehr ungewöhnlich. Es ist sehr üblich, dass Sender einen kleinen Puffer leeren, bevor sie RTS gehorchen. Wenn der andere Techniker also nicht weiß, was er tut, sollte das Gerät in der Lage sein, ein paar Bytes mehr zu tolerieren. Vielleicht kann es den "voll ausgestatteten" Fall von 16 weiteren Bytes nicht verarbeiten, aber es ist einfach schlechtes Design, 2-4 weitere Bytes nicht zuzulassen.

Zweitens, da Sie die Übertragung schneller beenden müssen, als die Größe des Ausgabepuffers, haben Sie die falsche Hardware gewählt. Ich kenne diese Reihe von Mikros nicht, aber das steht sicherlich im Datenblatt. Ich sehe drei Möglichkeiten:

  1. Holen Sie sich ein Mikro, das einen RTS-Eingang hat und das an der nächsten Byte-Grenze stoppt, wenn es bestätigt wird. Die meisten UARTs in Mikros haben diese zusätzliche Funktion nicht, aber es gibt sicherlich einige, die dies tun. Ich weiß, dass einige PIC 33F dies können, weil ich sie teilweise für diese Funktion ausgewählt habe. Sehen Sie sich in der Produktlinie von Atmel um. Sie können mit dieser Funktion auch ein paar Mikros haben.

  2. Verwenden Sie einen externen UART, der die Hardware-Flusssteuerung auf Byte-Ebene durchführt.

  3. Verwenden Sie den vorhandenen UART mit mehr Softwareeingriff. Sie können den internen Puffer nicht verwenden und müssen warten, bis ein Zeichen gesendet wird, RTS überprüfen und dann das nächste Zeichen in den UART schreiben. Dies wird erleichtert, wenn der UART unterbrechen kann, wenn er leer ist, und nicht nur, wenn Platz im Ausgangspuffer vorhanden ist.

Keiner dieser Kompromisse ist vielleicht das, was Sie wollen, aber das ist der Preis schlechter Architektur. Repariere es bei der nächsten Umdrehung oder antworte jetzt, wenn es wirklich wichtig ist. Die Welt hat nicht immer eine magische Antwort, nur weil dein Chef es jetzt will oder weil du dich in eine Ecke gemalt hast.

Die Sache ist, dass AVRs keinen UART-Ausgangspuffer haben ; Sie haben nur das einzelne Byte, das sie senden oder empfangen haben, daher bin ich mir nicht ganz sicher, wo das Problem überhaupt liegt.
@Ignacio: Wirklich? Der UART ist nicht einmal doppelt gepuffert? Das klingt seltsam, es sei denn, es handelt sich um einen kostenoptimierten Prozessor auf sehr niedrigem Niveau. Ich gehe davon aus, dass es mindestens ein Register gibt, in das Sie das nächste Byte schreiben können, während das vorherige Byte ausgetaktet wird. Das bedeutet, dass das Byte in diesem Halteregister gesendet wird, sobald das vorherige Byte fertig ist, ohne dass die Software RTS überprüfen und das Byte nicht senden kann. Selbst die dümmsten billigsten PICs haben dieses 1-tiefe FIFO, und ich finde es schwer zu glauben, dass keines der Atmel-Teile das hat.
Das Empfangen ist doppelt gepuffert, das Senden nicht.
@IgnacioVazquez-Abrams Das ist nicht korrekt - Sie können sich AVRs mit einem "Ein-Byte-Puffer" vorstellen: Sie haben ein Übertragungsdatenregister UDRn plus ein Übertragungsschieberegister. Die SW kann auf UDRnden nächsten zu sendenden Wert schreiben, während gleichzeitig der vorherige aus dem Schieberegister gesendet wird. Die Tatsache, dass die Übertragung nicht gestoppt werden kann, bis beide UDRnund das Sendeschieberegister geleert sind, ist hier das Problem.
@OlinLathrop Mir gefällt, wie du es ausdrückst "Die Welt hat nicht immer eine magische Antwort usw.", und ich stimme dir darin zu. Eine gültige Antwort auf meine Frage könnte also lauten: "Nein, es gibt keine Möglichkeit, dies zu tun", es sei denn, ich ersetze HW, implementiere UART in SW oder gebe das durch den Sendepuffer und das Schieberegister bereitgestellte Pipelining auf. Wenn dies die richtige Antwort ist, fühle ich mich tatsächlich besser, weil es bedeutet, dass ich vor dem Posten dieser Frage gut recherchiert habe. Ich habe die Frage für die kleine Chance gesendet, dass ich etwas übersehen habe und es einen anderen Weg gibt, sonst - ich habe bereits eine Problemumgehung.
@AmirGonnen: Ah, das erklärt, warum wir aneinander vorbeireden. Sie warten, bis UDREleer ist, aber was passieren sollte, ist, dass das nächste Byte nicht in die Warteschlange gestellt werden sollte, bis die Übertragung abgeschlossen ist. Dies ermöglicht eine Byte-für-Byte-Steuerung.