2017-03-21 2 views
0

Klippe Notes Version Der TI F28377S hat zwei CPUs, eine Haupt- und eine Sekundär CPU (CLA, die nur eine Aufgabe zu einem Zeitpunkt durchführen kann, ohne Unterbrechung mit Aufgaben) -Sie Teilen Meldebereiche von RAM. Wenn eine Schlange etwa 15 Bytes (von einer maximalen Länge von 32 Warteschlangen), die die CLA aussendet, schnell zuführt, werden manchmal einige Bytes niemals übertragen. Ich denke, es gibt ein Problem mit den CPU-Unterbrechungen, das bewirkt, dass einzelne Bytes gelegentlich "verloren" gehen, während sie an den Puffer übergeben werden.Tricky Warteschlange Verwendung zwischen zwei CPUs

Vollversion (dies ist den TI F28377S verwendet, die einen Haupt-CPU hat bei 200 MHz getaktet ist, und einen zweiten unabhängigen CLA, die mit der gleichen Geschwindigkeit läuft, können aber nur zu einer Zeit, eine Aufgabe auszuführen. Sie können shareable Variablen teilen).

Ich bin ein wenig ratlos, wie man diese komplexere Aufgabe erledigt, die CLA und eine Warteschlange involviert.

Einige schnelle Hintergrund: Ich habe zwei CLA Hauptaufgaben, der erste (Task1) wird durch den ADC Ende der Konvertierung ausgelöst (die selbst von Timer0 bei 100 kHz ausgelöst wird), und der zweite (Task2) wird von Timer0 ausgelöst selbst (dies wurde nach vielem Experimentieren und Optimieren erreicht, da, wann immer Task2 öfter als die ADC-Task lief, die ADC-Task niemals starten würde - so stellte ich beide beide auf das gleiche Intervall ein, nur gestaffelt). Task1 funktioniert perfekt, speichert die ADC-Ergebnisse in einem vereinfachten Ringpuffer und führt eine einfache Berechnung im Task1-ISR nach Abschluss durch. Die zweite funktioniert meistens.

Task2 wird verwendet, um einige GPIO-Pins für die Kommunikation mit einem externen Gerät umzuschalten. Da die Gesamtlänge der Codes in der Größenordnung von 100 Mikrosekunden liegt, verwende ich für jeden Trigger eine einfache Gehäusestruktur, um zu bestimmen, ob es: nichts tun soll, die Codepins einschalten, den Strobe-Pin einschalten, Schalten Sie den Strobe-Pin aus und deaktivieren Sie die Codepins. Auf diese Weise wird jedes Mal, wenn die Task aufgerufen wird, fast sofort abgeschlossen, wobei die Ausgangscodes die richtige Länge für das externe Gerät haben. Die Aufgabe arbeitet mit einem Code pro Zeit, und wenn dies abgeschlossen ist, versucht sie, einen anderen Code aus einer Warteschlange zu übernehmen. Wenn nicht, geht es einfach weiter.

Jetzt, der schwierige Teil. Ich habe zwei Anforderungen: 1) dass ich Bytes am Ende der Warteschlange schneller als die Aufgabe hinzufügen kann (ziemlich einfach in Theorie und Praxis) und 2), dass ich ein Byte an der Spitze der Warteschlange hinzufügen kann (nicht Ersetzen des gerade übertragenden Bytes, nur die Vorderseite der Warteschlange). Die erste Möglichkeit besteht darin, mittel-kurze Nachrichten (2-20 Zeichen) zu senden. Diese zweite Fähigkeit ist notwendig, um ein einzelnes Byte über irgendwelche externen Interrupts zu senden, die hereinkommen - so schnell wie möglich und sogar während der Übertragung einer Nachricht. Ich habe es so eingerichtet, dass die Task genau 1 Byte pro 500 Mikrosekunden sendet (~ 300 "an" und ~ 200 "aus). Wenn eine Interrupt-Nachricht eingeht, wird garantiert, dass sie weniger als 1 empfängt

Was ist zur Zeit Arbeit ist dies: eine Funktion auf der CPU, die eingehenden Bytes (einzeln) nimmt und fügt sie zu einem CPU2CLA-Puffer und erhöht eine CPU2CLA Längenzähler.Jedes Mal, wenn Task2 ausgeführt wird, es überprüft diese Warteschlange und ergreift ein Byte von der Vorderseite eines CLAonly-Puffers, erhöht seine eigene Pufferlänge und gibt an, dass ein Byte verbraucht wurde.Wenn die Task2-Nach-Task-ISR ausgeführt wird, prüft es, ob ein Byte verbraucht wurde, und Entfernen Sie das erste Byte aus dem CPU2CLA-Puffer.Dieses doppelte Puffersystem hat derzeit kein Flag zum Hinzufügen nach vorne, so dass es sich nicht um den Interrupt-Fall kümmert

Was ich zuvor versucht habe, war eine Task3, die ein Byte, das CPU2CLA übergeben wurde, und führte es von der CPU mit einem Task3andWait. Obwohl diese Methode theoretisch beide Anforderungen erfüllen sollte, würden etwa die Hälfte der Zeit ein oder zwei Bytes einer Nachricht niemals übertragen werden (ein einzelnes Byte wurde immer gesendet).

Eine CLA-Task kann nie unterbrochen werden, aber eine CPU-Task kann. Aus diesem Grund habe ich versucht, alle Änderungen der Warteschlange nur in der CLA auftreten zu lassen, so dass es niemals einen unbestimmten Zustand gab, der eine Warteschlangenänderung unterbrechen könnte.

+0

TL: DR. Wenn Sie "am Ende der Warteschlange schneller Bytes hinzufügen können, als die Task sie verbraucht", müssen Sie die Eingabe blockieren. Wenn das das grundlegende Problem ist, das erfordert, dass Sie Bytes an den Anfang der Warteschlange schreiben, dann müssen Sie sicher eine ** Wertentscheidung ** für eingehende Bytes machen: ergo Ihre Funktionen machen mehr Verarbeitung als sie sollten, daher der Engpass . –

+0

Die Warteschlange ist momentan nur 32 Byte lang (fest). Und meine eingehende Funktion prüft einfach, ob die Länge == max_length, exit (d. H. Das Byte) ist. –

+0

Dann, auf welcher Prämisse, schreiben Sie Bytes an den Anfang der Warteschlange? Wie notwendig sind die Daten? Können Sie es sich leisten, es zu verlieren? Ist dies der Fall, setzen Sie einfach den Puffer zurück, wenn Sie die eingehenden Daten nicht schnell genug verarbeiten können. –

Antwort

0

Es klingt, als würde die Aufteilung der Objekte mit hoher Priorität und normaler Priorität in separate Puffer hier eine nahezu optimale Lösung darstellen.

Es würde auch sicherstellen, dass, wenn ein Gegenstand mit hoher Priorität, ein Gegenstand mit normaler Priorität und ein anderer Gegenstand mit hoher Priorität hergestellt werden, bevor etwas verbraucht wird, die Gegenstände mit hoher Priorität vor den Gegenständen mit normaler Priorität verbraucht werden.

(einen einzigen Puffer, dieser Fall führt zu dem Artikel mit normaler Priorität vor dem zweiten hohen Priorität Elemente verbraucht wird. Ich vermute, dass in höchstem Maße unerwünscht ist.)

Wenn ein Element im Hoch ist Prioritätspuffer, der als nächstes verbraucht wird. Andernfalls wird ein Element im Puffer mit normaler Priorität verbraucht.

Beide Puffer haben einen einzigen Hersteller und einen einzigen Verbraucher (also SPSC Typ) und werden in einer einfachen First-In-First-Out-Art behandelt; Daher sollte hier eine locklose Ringpuffer-Implementierung (für jeden Puffer) funktionieren.

(Wenn nur 32 Bytes für die beiden Puffer verfügbar sind, versuchen Sie es mit einem 8:24 Split.)

+0

Ich mag das. Ich habe nicht über zwei Warteschlangen nachgedacht, aber das löst vollständig das Problem, dass ein Element am Anfang eingefügt wird. Ich werde es ausprobieren. –

+0

Ich füge hinzu, dass ich herausfinden muss, wie man einen normalen Ringpuffer zwischen zwei Speicher- "Domains" bekommt. (Die CPU und CLA haben entweder RW oder R Zugriff auf jede einzelne Variable, aber das ist vor dem Kompilieren definiert. So ist es sowohl dem Hersteller als auch dem Verbraucher nicht möglich, beide die Länge zu ändern). –

+0

@AdamJones: Wenn 'next' der Index des nächsten zu konsumierenden Bytes ist, und' last' ist der Index des letzten zu konsumierenden Bytes (beide modulo buffer length), dann schreibt nur der Produzent nach 'next', und Verbraucher zu 'dauern'. –