Je code quelque chose en utilisant le contrôle direct de GPIO, il existe de bonnes ressources pour cela, comme http://elinux.org/RPi_Low-level_peripherals#GPIO_hardware_hacking ; le processus implique l'ouverture ("/ dev / mem"), puis une opération mmap mappe efficacement l'adresse physique souhaitée dans votre espace d'adressage virtuel. Ensuite, vous lisez la section 6 de ce http://www.raspberrypi.org/wp-content/uploads/2012/02/BCM2835-ARM-Peripherals.pdf pour savoir comment les E / S sont contrôlées.
Pour passer à la fonction d'une broche (entrée, sortie ou diverses fonctions spéciales), vous devez modifier ces champs de 3 bits dans les registres d'E / S GPFSELx (000 = entrée, 001 = instance de sortie ennemi). Ces opérations de modification sont compilées en opérations avec une charge et un stockage ordinaires (par exemple, pour changer GPIO0 en entrée: * (regptr) & = ~ 7; qui se compile en quelque chose comme
ldr r2, [r3, #0] ; r = *ptr (load r2 from I/O register)
bic r2, r2, #7 ; r2 &= ~7
str r2, [r3, #0] ; *ptr = r2 (store r2 to I/O register)
Le problème est le suivant: si une interruption se produit entre la charge et le stockage, et qu'un autre processus ou ISR modifie le même registre d'E / S, l'opération de stockage (basée sur une lecture périmée dans r2) inversera les effets de cette autre opération. La modification de ces registres d'E / S doit donc être effectuée avec une opération de lecture / modification / écriture atomique (verrouillée). Les exemples que j'ai vus n'utilisent pas une opération verrouillée.
Étant donné que ces registres d'E / S ne sont généralement modifiés que lors de la configuration de quelque chose, il est peu probable que des problèmes se produisent, mais «jamais» vaut toujours mieux que «peu probable». De plus, si vous avez une application où vous dénigrez des bits pour émuler une sortie à collecteur ouvert, cela (pour autant que je sache) implique de programmer la sortie sur 0, puis de la basculer entre la sortie (pour le bas) ou l'entrée ( pour off / high). Donc, dans ce cas, il y aurait des mods fréquents dans ces registres d'E / S, et des modifications dangereuses seraient beaucoup plus susceptibles de causer un problème.
Donc, il y a probablement un ARM `` comparer et définir '' ou une opération similaire qui peut être utilisée ici pour faire cela, quelqu'un peut-il m'indiquer cela et comment y arriver à partir du code C?
[Remarque, rien de spécial n'est nécessaire lorsque vous avez programmé une E / S en sortie et que vous la modifiez simplement de 0 à 1 ou vice versa; car il y a un registre d'E / S sur lequel vous écrivez, pour mettre les bits sélectionnés à 1 et un autre pour effacer les bits sélectionnés à 0. Aucune lecture / écriture n'est nécessaire pour cette opération, donc il n'y a aucun risque d'interruption].
/dev/mem
il semble que votre code soit du code de l'espace utilisateur. Je ne pense pas que dans un système d'exploitation moderne, il faut faire attention aux interruptions qui changent les valeurs des registres dans le code de l'espace utilisateur. Je crois que ce ne serait pas un problème, même dans le code de l'espace noyau, car Linux restaure tous les registres lorsque le gestionnaire d'interruption termine son travail.Réponses:
J'ai regardé cela, l'ARM a des instructions 'ldrex et' strex ', le strex retournera un résultat d'échec si l'exclusivité est perdue (ou peut avoir été perdue) depuis le ldrex, qui comprend un changement de contexte (ou un autre processeur modifiant le même s'inscrire dans un environnement multiprocesseur). Donc, cela peut être fait en utilisant cela; si le strex échoue, vous bouclez et refaites l'opération (avec un nouveau ldrex).
réf: http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dht0008a/ch01s02s01.html
Les routines ci-dessous semblent fonctionner sur le Raspberry Pi (en ce qu'elles génèrent l'assembleur que j'attendais; et que l'effet sur les bits lorsque je les utilise est comme prévu. Je n'ai pas vérifié qu'ils protègent contre le problème de changement de contexte) . Notez qu'il s'agit de fonctions en ligne plutôt que de fonctions, elles doivent donc être placées dans un fichier d'en-tête.
[ EDIT : Cela ne fonctionne pas pour le but discuté, il semble que ce soit interdit d'une manière ou d'une autre. Si j'utilise ces routines où * addr est une variable ordinaire, cela fonctionne très bien. Lorsque je l'utilise où * addr pointe vers un registre GPIO mappé, le processus obtient une erreur de bus. (Quand je change le ldrex / strex en ldr / str et désactive la boucle do, cela fonctionne alors). Il semble donc que le moniteur exclusif ARM ne soit pas en mesure ou ne soit pas configuré pour fonctionner sur les registres d'E / S mappés en mémoire, et la question reste ouverte.]
la source