Estoy codificando algo usando el control directo de GPIO, hay algunos buenos recursos para esto, como http://elinux.org/RPi_Low-level_peripherals#GPIO_hardware_hacking ; el proceso implica abrir ("/ dev / mem") y luego una operación de mmap mapea efectivamente la dirección física deseada en su espacio de dirección virtual. Luego, lea la sección 6 de este http://www.raspberrypi.org/wp-content/uploads/2012/02/BCM2835-ARM-Peripherals.pdf para averiguar cómo se controlan las E / S.
Para cambiar a la función de un pin (entrada, salida o varias funciones especiales), modifique estos campos de 3 bits en los registros de E / S GPFSELx (000 = entrada, 001 = instancia de salida de enemigo). Estas operaciones de modificación se compilan en operaciones con carga y almacenamiento ordinarios (por ejemplo, para cambiar GPIO0 a input: * (regptr) & = ~ 7; que se compila en algo como
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)
El problema es este: si se produce una interrupción entre la carga y el almacenamiento, y otro proceso o ISR modifica el mismo registro de E / S, la operación de almacenamiento (basada en una lectura obsoleta en r2) revertirá los efectos de esa otra operación. Por lo tanto, cambiar estos registros de E / S realmente debe hacerse con una operación atómica (bloqueada) de lectura / modificación / escritura. Los ejemplos que he visto no usan una operación bloqueada.
Dado que estos registros de E / S generalmente se cambian solo cuando se configura algo, es poco probable que ocurran problemas, pero 'nunca' siempre es mejor que 'improbable'. Además, si tiene una aplicación en la que está aplicando bit-bashing para emular una salida de colector abierto, entonces (por lo que puedo decir) esto implica programar la salida a 0 y luego cambiarla entre salida (para baja) o entrada ( para apagado / alto). Entonces, en ese caso, habría modificaciones frecuentes en estos registros de E / S, y las modificaciones inseguras tendrían muchas más probabilidades de causar un problema.
Entonces, probablemente haya una operación ARM 'compare and set' u operación similar que se puede usar aquí para hacer esto, ¿alguien puede señalarme eso y cómo hacer que eso suceda desde el código C?
[Tenga en cuenta que no se necesita nada especial cuando ha programado una E / S como salida y solo la cambia de 0 a 1 o viceversa; dado que hay un registro de E / S en el que escribe, para establecer los bits seleccionados en 1 y otro para borrar los bits seleccionados en 0. No se necesita lectura / escritura para esta operación, por lo que no hay peligro de interrupciones].
/dev/mem
parece que tu código es un código de espacio de usuario. No creo que en ningún sistema operativo moderno haya que tener cuidado con las interrupciones que cambian los valores de los registros en el código del espacio de usuario. Creo que esto no sería un problema incluso en el código de espacio del núcleo ya que Linux restaura todos los registros cuando el controlador de interrupciones finaliza su trabajo.Respuestas:
Investigué esto, el ARM tiene instrucciones 'ldrex y' strex ', el strex devolverá un resultado de falla si se pierde la exclusividad (o puede haberse perdido) desde el ldrex, que incluye un cambio de contexto (u otro procesador que modifica el mismo registrarse en un entorno multiprocesador). Entonces se puede hacer usando eso; si el strex falla, realiza un bucle y vuelve a realizar la operación (con un ldrex nuevo).
ref: http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dht0008a/ch01s02s01.html
Las rutinas a continuación parecen funcionar en la Raspberry Pi (en el sentido de que generan el ensamblador que esperaba; y que el efecto en los bits cuando los uso es el esperado. No he verificado que protejan contra el problema del cambio de contexto) . Tenga en cuenta que se trata de líneas en lugar de funciones, por lo que deben colocarse en un archivo de encabezado.
[ EDITAR : Esto no funciona para el propósito discutido, parece que no está permitido de alguna manera. Si uso estas rutinas donde * addr es una variable ordinaria, funciona bien. Cuando lo uso donde * addr apunta a un registro GPIO mapeado, el proceso obtiene un error de bus. (Cuando cambio el ldrex / strex a ldr / str y deshabilito el bucle do, entonces funciona). Por lo tanto, parece que el monitor exclusivo ARM no puede o no está configurado para funcionar en registros de E / S mapeados en memoria, y la pregunta permanece abierta.]
fuente