Schreiben und Lesen des UART-Empfangspuffers

Ich mache eine einfache Kommunikation zwischen einem ARM-MCU und einem GSM-Modul.

Das Problem, mit dem ich konfrontiert bin, ist, wie ich mit dem gleichzeitigen Schreiben und Lesen in und aus dem FIFO-Puffer umgehen soll.

Aufgrund der Größe des Programms habe ich ein großes Pufferarray von 1000 Bytes Länge.

Im Moment wird jedes Mal, wenn ein Byte im Datenregister des UART empfangen wird, ein Interrupt ausgelöst, und die Interrupt-Routine füllt meinen 1-KB-FIFO.

Meine Leseroutine berechnet die verfügbaren Daten im FIFO und kopiert einige der Daten in einen zweiten Puffer zur weiteren Verarbeitung.

Die Interrupt-Routine stoppt, um mehr Daten zu speichern, wenn kein Platz mehr im FIFO-Puffer vorhanden ist, um ein Überschreiben des Lesezeigers zu verhindern, falls mein Hauptprogramm nicht die Zeit hatte, die Daten zu verarbeiten.

Jetzt versuche ich, die beste Lösung zu finden, um das Überschreiben des Lesezeigers zu verhindern, während die Interrupt-Routine nie aufhört, Daten zu speichern. Gibt es dafür eine Lösung oder ist das Überschreiben unvermeidlich?

Ein schnellerer Prozessor oder schnellere Software.
Hat der GSM eine Handshake-Fähigkeit?
Eine Alternative könnte die Verwendung von Flusskontrollsignalen sein, um das Senden von Daten durch das GSM-Modul zu stoppen.
Was ist der Unterschied vom Verhindern der Interrupt-Routine zum Überschreiben beim Lesen von Zeigern?
Setzen Sie den Interrupt, wenn der FIFO halb voll ist.
Warum braucht man einen zweiten Buffer? Der UART füllt einen Ringpuffer, dann verarbeiten Sie den Puffer in der Anwendung. Sie müssten nur den Lesezeiger erhöhen und verarbeiten, wie viele Daten empfangen werden, nicht um sie zu kopieren. Macht keinen Sinn.
Ja, es ist wirklich unklar, was Sie hier fragen. Sie können es nicht schnell genug herausbekommen, es hält das GSM zu lange an, es verliert die Synchronisation, was?
"... Überschreiben des Lesezeigers verhindern, während die Interrupt-Routine nie aufhört, Daten zu speichern. Gibt es dafür eine Lösung" - Denken Sie darüber nach. Wenn der Puffer voll ist, wo kann die Interrupt-Routine die Daten speichern, ohne sie zu überschreiben? Sie müssen den Puffer mindestens so schnell leeren, wie er gefüllt wird, sonst läuft er über.
@MarkoBuršič Der zweite Puffer wird benötigt, um virtuelle serielle Ports zu erreichen. Ich habe das Protokoll GSM 0710 implementiert, in dem ich die eingehenden Daten jedes von mir verwendeten DLC separat aufbewahren muss.
Ihr erster Schritt besteht darin, die Arbeit aufzuschlüsseln, die Sie ausführen müssen, und zu entscheiden, wie viel Zeit die CPU für alle Aufgaben benötigt. Überlegen Sie dann, ob ein Teil des Prozesses aufgrund unzureichender Ressourcen ins Stocken geraten kann.
@MarkoBuršič. Vielleicht haben Sie die beste Antwort. Ein ISR MUSS seine Arbeit erledigen und schnell aussteigen. OP muss möglicherweise Kompromisse eingehen, indem es 1/2 FIFO-Inhalt auf einmal verschiebt. Vielleicht ist die MPU zu langsam. OP hat keine Taktraten von MPU oder UART angegeben
Wenn in der Praxis mit Pufferüberläufen zu rechnen ist und diese toleriert werden müssen, ist ohne Handshake davon auszugehen, dass ein voller Puffer genauso wahrscheinlich Datenverlust bedeutet. Ich würde den Puffer einfach leeren, wenn er jemals voll ist, damit Daten, von denen bekannt ist, dass sie abgeschnitten sind, nicht unnötig analysiert werden und man schneller mit gültigen Daten beginnen kann. Das Lesen bis zu einem gültigen „Block“-Ende könnte ein Kompromiss vor dem Spülen sein.
Es gibt eine grundlegende Regel für Warteschlangen/Puffer: Wenn Ihr System nicht schnell genug ist, um die eingehenden Daten zu verarbeiten, werden keine Mengen von Warteschlangen und Tricks es retten.

Antworten (2)

Auf UART-Empfangsereignis:

    FIFO[Wr_pointer]= UART_Char;
    WR_Pointer++;
    WR_Pointer & = 3FF;// FIFO[1024] roll over 1022..1023...0...1...2
    if WR_Pointer==RD_Pointer then
        Overrun= true;
    else
    {
        SizeFIFO = (WR_Pointer - RD_Pointer);
        SizeFIFO & = 3FF;
    }

Beispiel Ringspeicher. Es besteht keine Notwendigkeit, Daten vom FIFO an einen anderen Ort zu kopieren, da die Daten einfach überschrieben werden.

In der Bewerbung:

SizeFIFO_temp = SizeFIFO; //you don't want a bug when ISR will insert new data while processing the buffer
for i=0 to SizeFIFO_temp  do
{
   Data[i]=FIFO[RD_Pointer]; //it's all up to you how you process the FIFO's data
   RD_Pointer++;
   RD_Pointer &= 3FF; 
}
SizeFIFO = (WR_Pointer - RD_Pointer);
SizeFIFO & = 3FF;
Ich glaube, Sie haben das Bit verpasst, in dem er sagt: "Einige der Daten werden zur weiteren Verarbeitung in einen zweiten Puffer kopiert." Er gibt nicht an, wie lange es dort bleiben muss.
@Trevor_G Er kann es nur verarbeiten, solange der Puffer nicht mit neuen Daten überschrieben wird - überlaufen. Dies gilt für beide Fälle, wenn er einen Teil des Puffers kopiert oder die Daten innerhalb desselben Puffers verarbeitet.
Ja, die Frage hat einige große Löcher.
Die Daten müssen aus dem FIFO in einen anderen Puffer kopiert werden, da die ankommenden Daten vom GSM ankommende Rahmen des GSM 0710-Protokolls sind, das für virtuelle serielle Ports verwendet wird. Jeder DLC muss seinen eigenen Puffer haben, da einige AT-Antworten mit mehr als einem Rahmen mit vielen Millisekunden (oder sogar wenigen Sekunden) dazwischen empfangen werden können, während Rahmen immer für andere DLCs empfangen werden. ( etsi.org/deliver/etsi_ts/101300_101399/101369/07.01.00_60/… ). Also kopiere ich einen neuen Frame in einen zweiten Puffer, um anzuzeigen, dass ein neuer Frame empfangen wurde.
@MrBit Sie könnten Ihre Frage erweitern, da nicht klar ist, auf welche Probleme Sie stoßen. Wie das Zeigen eines Stücks ISR-Routine und Leseroutine.

Das Problem, mit dem ich konfrontiert bin, ist, wie ich mit dem gleichzeitigen Schreiben und Lesen in und aus dem FIFO-Puffer umgehen soll.

Sequentiell Nicht parallel.

Jetzt versuche ich, die beste Lösung zu finden, um das Überschreiben des Lesezeigers zu verhindern, während die Interrupt-Routine nie aufhört, Daten zu speichern

Code darf nie immer im ISR vorhanden sein (allgemein je nach Anwendung). Sie brauchen Zeit, um Daten zu kopieren, Daten zu verarbeiten, Flags und verschiedene Zustände zurückzusetzen usw., bevor eine neue Anfrage verarbeitet werden kann. Wenn Sie immer Daten empfangen, wann erledigen Sie den Rest?.

1.Wenn ein Interrupt auftritt: Deaktivieren Sie den Interrupt (viele vergessen dies)

2.Da Sie einen 1K-Puffer haben (Speicherverschwendung, implementieren Sie einen Ringpuffer mit geringerer Länge), kopieren Sie die Daten in einen Puffer und beobachten Sie einen Zähler, wenn die für eine Verarbeitung erforderliche Mindestanzahl von Bytes empfangen wird. Vergessen Sie nicht, ISR wieder zu aktivieren und den Zähler zurückzusetzen, bevor Sie ISR verlassen.

3. Wenn die für eine Verarbeitung erforderliche Mindestanzahl von Bytes empfangen wurde, verarbeiten Sie die Daten.

Alternativ können Sie den uC zum Master machen, der den GSM bei Bedarf nach Daten abfragt, sodass Sie die Kontrolle haben, anstatt dass GSM Ihnen immer Daten sendet.

Für Cortex-M besteht ein gültiger Codierungsstil darin, den gesamten Funktionscode in ISRs zu haben und die Funktion „Sleep on Exit“ zu verwenden, um in den Leerlauf zu wechseln, wenn keine Ausnahme zu handhaben ist. Ihr 4. Absatz scheint dem zu widersprechen.
Bei einem ereignisbasierten System ist es sinnvoll, das System in den Ruhezustand zu versetzen, sobald das Ereignis bedient wird.
Die Aussage „Code darf niemals im ISR vorhanden sein“ ist falsch. Manchmal befindet sich ALLER Code im System in ISRs.
hmm ... die Antwort geändert