Erkennung eines DMA-Überlaufs bei der Erzeugung beliebiger Wellenformen

Ich erzeuge eine komplexe Reihe von Impulsen auf einem STM32F103, im Wesentlichen wie im ST-App-Hinweis AN4776 Allzweck-Timer-Kochbuch , Abschnitt 5.3, beschrieben. Als kurze Zusammenfassung bedeutet das, dass ich den DMA-Burst-Modus des Timers verwende, um nach jedem Update-Ereignis des Timers neue Werte an ARR, RCR und CCR1 zu übertragen. Eine ausführlichere Beschreibung finden Sie am Ende der Frage, falls Sie mit der App-Notiz nicht vertraut sind.

Mein Problem hängt mit der DMA-Bandbreite zusammen: Die von mir erzeugten Impulse können im Prinzip beliebig eng beabstandet sein, einschließlich nur 1 Taktzyklus (der Timer hat einen Vorteiler von 1). Natürlich kann es der DMA in diesem Fall unmöglich rechtzeitig schaffen, die Daten für den nächsten Impuls zu übertragen, und es wird eine Störung in der Ausgabe geben. Da meine Anwendung kleine, seltene Zeitfehler tolerieren kann, verarbeite ich meine Impulse so vor, dass das kleinste Impulsintervall ein festes Minimum ist (ich verwende im Moment 96 Takte), um dem DMA eine Chance zu geben. Ich bekomme jedoch immer noch Störungen, die meiner Meinung nach auf den DMA zurückzuführen sind, und das Erhöhen der Mindestzeit selbst auf sehr große Zahlen scheint nicht zu helfen.

Der Schlüsselteil des vorherigen Satzes ist "... ich denke ...". Also würde ich gerne einen Weg finden, um sicher zu wissen, ob der DMA seine Übertragung verpasst hat oder nicht, vorzugsweise etwas, das ich im Code lassen kann, der immer läuft, so dass ich sogar sehr seltene Störungen finden werde.

Was ich mir bisher überlegt/ausprobiert habe ist:

  1. Blick auf die DMA-Fehlerregister. Allerdings scheint es kein "Transfer verpasst"-Flag oder ähnliches zu geben, was Sinn macht, da es meistens kein Fehler ist, wenn eine Übertragung für eine Weile ansteht. Auch in meinem Fall ist es nicht unbedingt ein Fehler, wenn die Übertragung eine Weile aussteht, also denke ich, dass mir das nicht helfen wird.
  2. Ich betreibe dies auf zwei verschiedenen Timern, TIM8 und TIM1, und gebe Impulse an zwei verschiedenen Pins aus. Die von mir vorbereiteten Impulsfolgen sind insgesamt immer 0,5 ms lang (damit ich innerhalb von 1,5 ms auf externe Ereignisse reagieren kann), dh es gibt ggf. einen Ruheimpuls, damit eines der Timer-Updates genau an der 0,5 ms-Grenze erfolgt . Der TIM8 ist der Master, und ich time meine Hauptschleife tatsächlich, indem ich darauf warte, dass die DMA-Warteschlange an der 0,5-ms-Grenze ist, d Stift in der Hauptschleife, dass dies funktioniert und genau ist). Wenn ich jetzt laut TIM8 zum richtigen Zeitpunkt gekommen bin, weiß ich genau, wo die TIM1-Warteschlange sein sollte, damit ich ihren CNDTR überprüfen kann.
  3. Woran ich gerade arbeite: Ich richte einen anderen Timer als Referenz ein und verwende seinen Erfassungskanal, um den Wert des Referenz-Timers zu erhalten, wenn das Aktualisierungssignal von TIM8 aktiviert wird. Der erfasste Wert sollte immer genau einer der Impulszeitpunkte sein, und insbesondere kann ich den letzten erfassten Wert in der Hauptschleife an den 0,5-ms-Grenzen überprüfen. Mit einem anderen Timer kann ich die gleiche Prüfung bei TIM1 durchführen. Der Nachteil hier ist, dass dies noch nicht ganz garantiert, dass die Übertragungen abgeschlossen sind, da die Register im Hauptzeitgeber in der Reihenfolge ARR, RCR, CCR1 aktualisiert werden. Wenn also ARR aktualisiert wurde, aber CCR1 verfehlt wurde, wären die Impulszeitpunkte selbst korrekt, obwohl die Ausgangsimpulslängen es nicht wären.

Natürlich könnten die Störungen, die ich sehe, auf einen Fehler im Code zurückzuführen sein, der die Puffer generiert, die der DMA an die Timer sendet. Aber genau deshalb würde ich gerne sicher wissen, ob dem DMA einige Übertragungen fehlen oder nicht, also würde ich wissen, ob ich auf der Suche nach einem Fehler in meinem Code bin oder nicht. Der Code selbst besteht eine einigermaßen umfassende Reihe von Komponententests, sodass der Fehler subtil wäre, wenn er vorhanden wäre.

Also, irgendwelche Ideen, um zu überprüfen, ob mein Problem auf DMA-Fehlschläge zurückzuführen ist oder nicht?

Details zu dem, was ich genau mache:

Ich erzeuge eine Reihe von Impulsen, die jeweils durch die Länge der "Ein"-Phase (Impulsbreite) und die Zeit (in Takten) bis zum nächsten Impuls bestimmt werden. In Bezug auf Datenstrukturen

struct pulse {


     /*
     * These are an image of the timer registers.
     * We don't use repeats but it must be there
     * since the DMA transfers it anyway
     *
     * TODO: support other capture/compare channels than 1
     */


    uint32_t length; //ARR
    uint32_t repeats; //RCR, we don't use this
    uint32_t pulsewidth; //CCR1
};

//Check that pulse is of the correct type for the DMA stream
static_assert(std::is_pod<pulse>::value, "pulse must be POD");
static_assert(sizeof(pulse) == 12, "pulse must not be packed");

pulse pulseArray[MAX_PULSES];

Dann erlaubt uns der Burst-Modus von TIM8 (und entsprechend TIM1), das folgende Schema einzurichten:

  1. Der DMA-Kanal des Timers ist so programmiert, dass er sich pulseArrayim zirkulären Modus zum TIM8_DMAR-Register "DMA-Adresse für vollständige Übertragung" bewegt (also muss ich ihn natürlich auf ähnliche zirkuläre Weise mit neuen Daten füllen).
  2. das TIM8_DCR "DMA-Steuerregister" ist mit der Burst-Länge 3 und der Basisadresse TIM8_ARR programmiert (siehe Datenblatt RM0008 Seite 360-361 für eine detaillierte Beschreibung dieser Register), und die DMA-Anforderung zur Aktualisierung wird über TIM8_DIER-Bit 8 aktiviert
  3. Nun aktiviert der Zeitgeber bei jeder Aktualisierung des Zeitgebers aufgrund des oben programmierten Burst-Modus die DMA-Anforderung dreimal und überträgt in dieser Reihenfolge die Felder length, repeats, und pulsewidth, und der Burst-Modus leitet diese zu den Registern TIM8_ARR, TIM8_RCR und TIM8_CCR1 . Da wir das Vorladen sowohl für den Timer selbst (TIM8_CR1 Bit 7) als auch für den Vergleichskanal (TIM8_CCMR1 Bit 3) aktiviert haben, werden die Daten dann in das Vorladeregister übertragen und bei der nächsten Aktualisierung (d. h. wenn der aktuelle Impuls abgeschlossen ist) aktiv )

Abbildung 30 und 33 in der App-Notiz sind sehr aufschlussreich für das Verständnis des oben Gesagten.

Und jetzt kann ich das Problem etwas detaillierter beschreiben: Angenommen length, es ist zum Beispiel 1. Dann die Anzahl der Taktzyklen, die dem DMA zur Verfügung stehen, um den nächsten Impuls zu übertragen (der aufgrund des Vorladens tatsächlich 2 Positionen im Puffer voraus ist registriert, aber das ist hier nicht wichtig) ist eins (vorausgesetzt, der Timer-Prescaler ist 1, was in diesem Fall der Fall ist). Offensichtlich ist es dem DMA unmöglich, 3 16-Bit-Wörter in einem Taktzyklus zu übertragen, und daher werden die Werte des vorherigen Zyklus wiederholt. Nennen wir das in Ermangelung eines besseren Begriffs einen "DMA-Fehler".

Andererseits muss es ein Minimum geben length, so dass der DMA während jedes längeren Impulses Zeit hat, alle Daten zu übertragen. lengthLeider hängt dieses Minimum von den genauen Bus-Timings, anderem DMA-Verkehr und deren Prioritäten ab, daher ist es wirklich schwierig, diese Länge mit Stift und Papier zu bestimmen.

Ich würde also gerne einen Weg finden, mit so viel Sicherheit wie möglich zu erkennen, dass kein "DMA-Fehlschlag" aufgetreten ist, damit ich mein Minimum feinabstimmen lengthund gleichzeitig sicher sein kann, dass einige andere Störungen auftreten sehe nicht auf einen "DMA-Miss" zurück.

Wie verwenden Sie DMA überhaupt im Burst-Modus auf einem STM32F1? Aus dem Timer-Kochbuch: In STM32-Mikrocontrollerfamilien gibt es zwei DMA-Peripherievarianten: • Die DMA-Burst-Übertragungsfunktion, die nur von der DMA-Peripherievariante der STM32F2-Produkte unterstützt wird, bei der das DMA-Peripheriegerät eine konfigurierbare Anzahl von Datenelementen neben einer einzigen Datenübertragung übertragen kann Abzug. • In einer anderen Variante, wie der der STM32F1-Produkte, unterstützt die DMA-Peripherievariante nur Einzelübertragungen; das bedeutet, dass neben einem Datenübertragungstrigger nur ein Datenelement übertragen wird.
@jms Der Timer hat einen Burst-Modus, siehe RM0008 Seite 360 ​​oder die entsprechende Seite für Allzweck-Timer. Das Kochbuch ist in der Tat etwas verwirrend in diesem Punkt, ich erinnere mich, dass ich irgendwann auch dachte, dass dies bei der F1-Serie nicht möglich ist.
Zeigen Sie die Wellenformen, was genau Sie archivieren möchten und was Glitches sind. Deine Beschreibung ist nicht nachvollziehbar. Verwirren Sie keine Methode mit Ihrem Ziel
@ PeterJ_01 Ich habe am Ende eine detailliertere Beschreibung hinzugefügt, hoffe das hilft?
@timo IMHO, die Antwort darauf wird die Details Ihrer tatsächlichen Implementierung einschalten. Wie Konfigurationsstrukturen aussehen, ob Sie die vorgefertigten Bibliotheken von ST verwenden oder nicht und welche anderen Dinge ausgeführt werden. Gibt es eine Möglichkeit, die gesamte Codebasis zu posten?
Die DMA-Parallelübertragungen müssen abgeschlossen sein, bevor sich der serielle Puffer wie ein PISO-FIFO leert, außer ich vermute, dass er durch zufällige verschachtelte Rasterscan-Speicherschreib- und Lesevorgänge durchläuft. Scheint ein komplizierter Zufallsdaten-Testsatz zu sein?

Antworten (2)

Sie verwenden DMA im Zirkularmodus; Wie bestimmen Sie die Aktualisierungszeit der Register? Da Sie nur 1 Puffer verwenden, gibt es kein so triviales Timing, das keine Störungen verursacht. Sie werden nur näher an die Störungsfreiheit herankommen, indem Sie die Genauigkeit des Timings erhöhen, aber niemals genau störungsfrei in Bezug auf die Wahrscheinlichkeit.

Auf stm32f4-Geräten gibt es eine einfach zu verwendende doppelte Pufferfunktion. Aber auf stm32f1-Geräten gibt es Elemente davon, um es manuell zu tun. Sie haben dort auf dem DMA-Peripheriegerät die Interrupt-Trigger für die halbe Übertragung und das Übertragungsende. Entwerfen Sie den Ringpuffer als zwei Sätze von Registern, tauschen Sie wie bei jedem Interrupt den Pufferzeiger aus, von dem angenommen wurde, dass er von DMA gelesen und von der CPU aktualisiert wird.

Wenn Sie eine komplexe Reihe sich überschneidender Timing-Probleme haben, ist es am besten, einen Hypervisor einzurichten, einen „Boss der Bosse“. Sie erstellen eine 4-Phasen-Master-Semaphore, die die Zählung von 0-3 endlos wiederholt.

Ich nenne sie Phase A, B, C, D. Sie fungieren als Enabler für die Ausführung bestimmter Threads. Linux und Android haben dies eingebaut. Ich habe es in LabView und MPLAB verwendet. Jedes komplexe Projekt hat eine 4-Phasen-Uhr zur Zeitmessung von Ereignissen. Eine Defacto-RTC.

Im schlimmsten Fall muss ein Teil des Codes warten, bis er an der Reihe ist, aber er hat keine Konflikte, wenn er ausgeführt wird.

Phasen:

A. Protokoll des vorherigen Laufs lesen, dann Bedingungsprüfungen einrichten und entscheidende Werte voreinstellen/zurücksetzen. Fenster öffnen, damit ISRs nur während Phase A ausgeführt werden.

B. Führen Sie als Nächstes den entscheidenden deterministischen Code aus. Lesen Sie alle Ergebnisse von ISRs. Dies bestimmt einiges von dem, was als Nächstes ausgeführt wird, abhängig von den verbrauchten Taktzyklen und der Priorität. Fehlerhandler werden zuerst ausgeführt.

C. Führen Sie den Hauptcode basierend auf den Ergebnissen der Phasen A und B aus. Dies umfasst das Wiederaufnehmen oder Starten von DMA-Burst-Modi, jetzt, wo Sie Zeitschlitze basierend auf der Laufzeit von Phase C haben. Schreiben Sie Code, damit er DMA-Flags prüft, bevor Phase C abläuft.

D. DMA-Übertragungen anhalten. Auf Fehler prüfen. Überprüfen Sie die „Schatten“-Timer. Prüfen Sie auf Überläufe oder ob es Zeit für einen kurzen DMA-Burst ist, um ein Paket abzuschließen. Führen Sie CRC und alle Fehler-Flags durch, jetzt, wo Sie ein Zeitfenster dafür haben. Hier finden Sie einen Fehler von nicht übereinstimmendem DMA im Vergleich zu einem Schattenzähler usw.

Hinterlassen Sie ein Protokoll oder einen numerischen Code für den Pass/Fail-Status und ist es in Ordnung, dass Phase B den normalen Pfad oder einen Fehlerhandler ausführt.

Danke für die Antwort! Das ist in der Tat genau das, was ich gerade mache (Punkt 3 in der ersten Liste in meiner Frage). Das Problem ist jedoch, dass es meines Wissens nach keine Flags oder solche gibt, die mir sagen würden, ob der DMA fehlgeschlagen ist oder nicht, was ich in Ihrem Punkt D tun würde Der Prozess ist in Echtzeit und Fehler sind sowieso selten, daher ist es etwas schwierig, anzuhalten, um nachzusehen.