Ich codiere etwas mithilfe der direkten Steuerung von GPIO. Dafür gibt es einige gute Ressourcen, z. B. http://elinux.org/RPi_Low-level_peripherals#GPIO_hardware_hacking . Der Prozess beinhaltet open ("/ dev / mem") und dann ordnet eine mmap-Operation die gewünschte physische Adresse effektiv Ihrem virtuellen Adressraum zu. Dann lesen Sie Abschnitt 6 dieser http://www.raspberrypi.org/wp-content/uploads/2012/02/BCM2835-ARM-Peripherals.pdf , um herauszufinden, wie die E / A gesteuert werden.
Um zur Funktion eines Pins (Eingabe oder Ausgabe oder verschiedene Sonderfunktionen) zu wechseln, ändern Sie diese 3-Bit-Felder in den GPFSELx-E / A-Registern (000 = Eingabe, 001 = Ausgabe der Feindinstanz). Diese Änderungsoperationen werden zu Operationen mit normalem Laden und Speichern kompiliert (z. B. um GPIO0 in Eingabe zu ändern: * (regptr) & = ~ 7;, was zu so etwas kompiliert wird
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)
Das Problem ist folgendes: Wenn ein Interrupt zwischen Laden und Speichern auftritt und ein anderer Prozess oder ISR dasselbe E / A-Register ändert, werden durch die Speicheroperation (basierend auf einem veralteten Einlesen in r2) die Auswirkungen dieser anderen Operation zurückgesetzt. Das Ändern dieser E / A-Register muss also wirklich mit einer atomaren (gesperrten) Lese- / Änderungs- / Schreiboperation erfolgen. Die Beispiele, die ich gesehen habe, verwenden keine gesperrte Operation.
Da diese E / A-Register im Allgemeinen nur beim Einrichten geändert werden, ist es unwahrscheinlich, dass Probleme auftreten, aber "nie" ist immer besser als "unwahrscheinlich". Wenn Sie eine Anwendung haben, in der Sie Bit-Bashing durchführen, um einen Open-Collector-Ausgang zu emulieren, müssen Sie (soweit ich das beurteilen kann) den Ausgang auf 0 programmieren und dann zwischen Ausgang (für niedrig) oder Eingang (für niedrig) umschalten. für aus / hoch). In diesem Fall würden diese E / A-Register häufig geändert, und unsichere Änderungen würden mit größerer Wahrscheinlichkeit ein Problem verursachen.
Es gibt also wahrscheinlich eine ARM-Operation zum Vergleichen und Setzen oder eine ähnliche Operation, die hier verwendet werden kann. Kann mich jemand darauf hinweisen und wie kann dies mit C-Code geschehen?
[Beachten Sie, dass nichts Besonderes erforderlich ist, wenn Sie eine E / A als Ausgabe programmiert haben und sie nur von 0 auf 1 oder umgekehrt ändern. Da es ein E / A-Register gibt, in das Sie schreiben, um ausgewählte Bits auf 1 und ein anderes, um ausgewählte Bits auf 0 zu setzen, ist für diesen Vorgang kein Lesen / Schreiben erforderlich, daher besteht keine Gefahr durch Interrupts.
/dev/mem
, scheint es, dass Ihr Code Userspace-Code ist. Ich denke nicht, dass man in einem modernen Betriebssystem vorsichtig sein muss, wenn Interrupts die Registerwerte im Userspace-Code ändern. Ich glaube, dass dies selbst im Kernel-Space-Code kein Problem wäre, da Linux alle Register wiederherstellt, wenn der Interrupt-Handler seinen Job beendet.Antworten:
Ich habe dies untersucht. Der ARM verfügt über die Anweisungen 'ldrex' und 'strex'. Der strex gibt ein Fehlerergebnis zurück, wenn die Exklusivität verloren geht (oder möglicherweise verloren gegangen ist), da der ldrex einen Kontextwechsel enthält (oder einen anderen Prozessor, der diesen modifiziert) in einer Multiprozessor-Umgebung registrieren). So kann es gemacht werden; Wenn der Strex ausfällt, führen Sie eine Schleife durch und wiederholen den Vorgang (mit einem neuen ldrex).
ref: http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dht0008a/ch01s02s01.html
Die folgenden Routinen scheinen auf dem Raspberry Pi zu funktionieren (da sie den von mir erwarteten Assembler generieren und die Auswirkungen auf die Bits, wenn ich sie verwende, wie erwartet sind. Ich habe nicht überprüft, ob sie vor dem Problem des Kontextwechsels schützen). . Beachten Sie, dass dies eher Inlines als Funktionen sind. Sie sollten daher in eine Header-Datei eingefügt werden.
[ EDIT : Dies funktioniert nicht für den besprochenen Zweck, es scheint, dass es irgendwie nicht erlaubt ist . Wenn ich diese Routinen verwende, bei denen * addr eine gewöhnliche Variable ist, funktioniert es einwandfrei. Wenn ich es verwende, wobei * addr auf ein zugeordnetes GPIO-Register verweist, wird beim Prozess ein Busfehler angezeigt. (Wenn ich ldrex / strex in ldr / str ändere und die do-Schleife deaktiviere, funktioniert es). Es scheint also, dass der exklusive ARM-Monitor nicht in der Lage oder nicht eingerichtet ist, auf speicherabgebildeten E / A-Registern zu funktionieren, und die Frage bleibt offen.]
quelle