Le Cortex M3 prend en charge une paire utile d'opérations (communes à de nombreuses autres machines également) appelées "Load-Exclusive" (LDREX) et "Store-Exclusive" (STREX). Sur le plan conceptuel, l'opération LDREX effectue une charge, définit également un matériel spécial pour observer si l'emplacement qui a été chargé peut être écrit par autre chose. L'exécution d'un STREX à l'adresse utilisée par le dernier LDREX entraînera l'écriture de cette adresse uniquement si rien d'autre ne l'a écrite en premier . L'instruction STREX chargera un registre avec 0 si le stockage a eu lieu, ou 1 s'il a été abandonné.
Notez que STREX est souvent pessimiste. Il existe une variété de situations où il peut décider de ne pas exécuter le magasin même si l'emplacement en question n'a pas été effectivement touché. Par exemple, une interruption entre un LDREX et un STREX amènera le STREX à supposer que l'emplacement surveillé a peut-être été atteint. Pour cette raison, c'est généralement une bonne idée de minimiser la quantité de code entre LDREX et STREX. Par exemple, considérez quelque chose comme ceci:
inline void safe_increment (uint32_t * addr)
{
uint32_t new_value;
faire
{
new_value = __ldrex (addr) + 1;
} while (__ strex (nouvelle_valeur, addr));
}
qui se compile en quelque chose comme:
; Supposons que R0 détient l'adresse en question; r1 saccagé
lp:
ldrex r1, [r0]
ajouter r1, r1, # 1
strex r1, r1, [r0]
cmp r1, # 0; Test si différent de zéro
bne lp
.. le code continue
La grande majorité du temps où le code s'exécute, rien ne se passera entre le LDREX et le STREX pour les "déranger", donc le STREX réussira sans plus attendre. Si, cependant, une interruption se produit immédiatement après l'instruction LDREX ou ADD, le STREX n'effectuera pas le stockage, mais à la place, le code reviendra pour lire la valeur (éventuellement mise à jour) de [r0] et calculer une nouvelle valeur incrémentée sur la base de cela.
L'utilisation de LDREX / STREX pour former des opérations comme safe_increment permet non seulement de gérer les sections critiquesm, mais aussi dans de nombreux cas d'éviter leur besoin.
while(STREXW(new_value, addr);
comment pouvons-nous croire que ce que vous dites est correct si votre code ne compile même pas?Il semble que vous ayez besoin de tampons circulaires ou FIFO dans votre logiciel MCU. En suivant deux index ou pointeurs dans le tableau pour la lecture et l'écriture, vous pouvez avoir à la fois l'avant-plan et l'arrière-plan accédant au même tampon sans interférence.
Le code de premier plan est libre d'écrire dans le tampon circulaire à tout moment. Il insère des données au pointeur d'écriture, puis incrémente le pointeur d'écriture.
Le code d'arrière-plan (gestion des interruptions) consomme des données du pointeur de lecture et incrémente le pointeur de lecture.
Lorsque les pointeurs de lecture et d'écriture sont égaux, le tampon est vide et le processus d'arrière-plan n'envoie aucune donnée. Lorsque le tampon est plein, le processus de premier plan refuse d'écrire davantage (ou peut écraser les anciennes données, selon vos besoins).
L'utilisation de tampons circulaires pour découpler les lecteurs et les écrivains devrait supprimer la nécessité de désactiver les interruptions.
la source
Je ne me souviens pas de l'emplacement exact mais dans les bibliothèques qui proviennent d'ARM (pas TI, ARM, il devrait être sous CMSIS ou quelque chose comme ça, j'utilise ST mais je me souviens avoir lu quelque part que ce fichier venait d'ARM donc vous devriez l'avoir aussi ) il existe une option globale de désactivation des interruptions. Il s'agit d'un appel de fonction. (Je ne suis pas au travail mais je chercherai demain la fonction exacte). Je terminerais avec un joli nom dans votre système et désactiverais les interruptions, refaites votre travail et réactivez. Cela dit, la meilleure option serait d'implémenter un sémaphore ou une structure de file d'attente au lieu de désactiver l'interruption globale.
la source