Der Cortex M3 unterstützt ein nützliches Operationspaar (das auch bei vielen anderen Computern üblich ist), das als "Load-Exclusive" (LDREX) und "Store-Exclusive" (STREX) bezeichnet wird. Konzeptionell führt die LDREX-Operation ein Laden durch und stellt auch eine spezielle Hardware ein, um zu beobachten, ob der Speicherort, der geladen wurde, möglicherweise von etwas anderem geschrieben wurde. Wenn Sie einen STREX für die vom letzten LDREX verwendete Adresse ausführen, wird diese Adresse nur geschrieben , wenn sie zuerst von nichts anderem geschrieben wurde . Der STREX-Befehl lädt ein Register mit 0, wenn der Speicher stattgefunden hat, oder 1, wenn er abgebrochen wurde.
Beachten Sie, dass STREX oft pessimistisch ist. Es gibt eine Vielzahl von Situationen, in denen möglicherweise entschieden wird, das Geschäft nicht durchzuführen, selbst wenn der betreffende Ort tatsächlich nicht berührt wurde. Beispielsweise führt ein Interrupt zwischen einem LDREX und STREX dazu, dass der STREX davon ausgeht, dass der beobachtete Ort möglicherweise getroffen wurde. Aus diesem Grund ist es normalerweise eine gute Idee, die Codemenge zwischen LDREX und STREX zu minimieren. Betrachten Sie beispielsweise Folgendes:
inline void safe_increment (uint32_t * addr)
{
uint32_t new_value;
tun
{
new_value = __ldrex (addr) + 1;
} while (__ strex (new_value, addr));
}}
was zu etwas kompiliert wie:
;; Angenommen, R0 enthält die betreffende Adresse. r1 verwüstet
lp:
ldrex r1, [r0]
füge r1, r1, # 1 hinzu
strex r1, r1, [r0]
cmp r1, # 0; Testen Sie, ob nicht Null ist
bne lp
.. Code geht weiter
Die überwiegende Mehrheit der Zeit, in der der Code ausgeführt wird, passiert nichts zwischen LDREX und STREX, um sie zu "stören", so dass der STREX ohne weiteres erfolgreich sein wird. Wenn jedoch unmittelbar nach dem LDREX- oder ADD-Befehl ein Interrupt auftritt, führt der STREX den Speicher nicht aus, sondern der Code liest den (möglicherweise aktualisierten) Wert von [r0] zurück und berechnet einen neuen inkrementierten Wert basierend darauf.
Die Verwendung von LDREX / STREX zum Erstellen von Operationen wie safe_increment ermöglicht es, nicht nur kritische Abschnitte zu verwalten, sondern in vielen Fällen auch die Notwendigkeit dieser zu vermeiden.
while(STREXW(new_value, addr);
Wie können wir glauben, dass das, was Sie sagen, richtig ist, wenn Ihr Code nicht einmal kompiliert wird?Es hört sich so an, als ob Sie einige kreisförmige Puffer oder FIFOs in Ihrer MCU-Software benötigen. Indem Sie zwei Indizes oder Zeiger zum Lesen und Schreiben in das Array verfolgen, können sowohl Vordergrund als auch Hintergrund ohne Interferenz auf denselben Puffer zugreifen.
Der Vordergrundcode kann jederzeit in den Ringpuffer geschrieben werden. Es fügt Daten am Schreibzeiger ein und erhöht dann den Schreibzeiger.
Der Hintergrundcode (Interrupt-Behandlung) verbraucht Daten vom Lesezeiger und erhöht den Lesezeiger.
Wenn die Lese- und Schreibzeiger gleich sind, ist der Puffer leer und der Hintergrundprozess sendet keine Daten. Wenn der Puffer voll ist, weigert sich der Vordergrundprozess, mehr zu schreiben (oder kann je nach Bedarf alte Daten überschreiben).
Durch die Verwendung von kreisförmigen Puffern zum Entkoppeln von Lesern und Schreibern müssen Interrupts nicht mehr deaktiviert werden.
quelle
Ich kann mich nicht an den genauen Speicherort erinnern, aber in den Bibliotheken, die von ARM stammen (nicht TI, ARM, es sollte sich unter CMSIS oder so etwas befinden, ich verwende ST, aber ich erinnere mich, dass ich irgendwo gelesen habe, dass diese Datei von ARM stammt, also sollten Sie sie auch haben ) gibt es eine globale Interrupt-Deaktivierungsoption. Es ist ein Funktionsaufruf. (Ich bin nicht auf der Arbeit, aber ich werde morgen die genaue Funktion nachschlagen). Ich würde das mit einem schönen Namen in Ihrem System abschließen und die Interrupts deaktivieren, Ihr Ding machen und wieder aktivieren. Die bessere Option wäre jedoch die Implementierung eines Semaphors oder einer Warteschlangenstruktur anstelle der globalen Interrupt-Deaktivierung.
quelle