Estou codificando algo usando o controle direto do GPIO, existem alguns bons recursos para isso, como http://elinux.org/RPi_Low-level_peripherals#GPIO_hardware_hacking ; o processo envolve aberto ("/ dev / mem") e, em seguida, uma operação mmap efetivamente mapeia o endereço físico desejado em seu espaço de endereço virtual. Em seguida, leia a seção 6 deste http://www.raspberrypi.org/wp-content/uploads/2012/02/BCM2835-ARM-Peripherals.pdf para descobrir como as E / S são controladas.
Para mudar para a função de um pino (entrada, saída ou várias funções especiais), você modifica esses campos de 3 bits nos registros de E / S GPFSELx (000 = entrada, 001 = instância de saída). Essas operações de modificação são compiladas para operações com carga e armazenamento comuns (por exemplo, para alterar GPIO0 para entrada: * (regptr) & = ~ 7; que compila para 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)
O problema é o seguinte: se ocorrer uma interrupção entre a carga e a loja, e outro processo ou ISR modificar o mesmo registro de E / S, a operação de armazenamento (com base em uma leitura obsoleta em r2) reverterá os efeitos dessa outra operação. Portanto, a alteração desses registros de E / S realmente precisa ser feita com uma operação de leitura / modificação / gravação atômica (bloqueada). Os exemplos que eu vi não usam uma operação bloqueada.
Como esses registros de E / S geralmente são alterados apenas ao configurar algo, é improvável que ocorram problemas, mas 'nunca' é sempre melhor do que 'improvável'. Além disso, se você tiver um aplicativo no qual você está fazendo um bit-bashing para emular uma saída de coletor aberto, (tanto quanto eu sei), isso envolve programar a saída para 0 e alterná-la entre saída (baixa) ou entrada ( para off / high). Portanto, nesse caso, haveria modificações frequentes nesses registros de E / S, e as modificações inseguras teriam muito mais probabilidade de causar um problema.
Portanto, provavelmente existe uma operação de comparação e configuração do ARM ou similar que pode ser usada aqui para fazer isso, alguém pode me indicar isso e como fazer isso acontecer com o código C?
[Observe que nada de especial é necessário quando você programa uma E / S como saída e apenas a altera de 0 para 1 ou vice-versa; como existe um registro de E / S no qual você escreve, para definir os bits selecionados como 1 e outro para limpar os bits selecionados como 0. Nenhuma leitura / gravação é necessária para esta operação, portanto, não há risco de interrupções].
/dev/mem
, parece que seu código é código do espaço do usuário. Eu não acho que em qualquer sistema operacional moderno seja necessário ter cuidado com as interrupções na alteração dos valores dos registros no código do espaço do usuário. Eu acredito que isso não seria um problema, mesmo no código de espaço do kernel, pois o Linux restaura todos os registros quando o manipulador de interrupções termina seu trabalho.Respostas:
Analisei isso, o ARM possui instruções 'ldrex e' strex ', o strex retornará um resultado de falha se a exclusividade for perdida (ou pode ter sido perdida) desde o ldrex, que inclui uma opção de contexto (ou outro processador que modifica o mesmo registrar em um ambiente com vários processadores). Então, isso pode ser feito usando isso; se o strex falhar, você executa um loop e refaça a operação (com um novo ldrex).
ref: http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dht0008a/ch01s02s01.html
As rotinas abaixo parecem funcionar no Raspberry Pi (na medida em que geram o assembler que eu esperava; e que o efeito nos bits quando os uso são os esperados. Não verifiquei se eles protegem contra o problema da troca de contexto) . Observe que essas linhas são inline, e não funções, portanto devem ser colocadas em um arquivo de cabeçalho.
[ EDIT : Isso não funciona para o propósito discutido, parece que é proibido de alguma forma. Se eu usar essas rotinas em que * addr é uma variável comum, funciona bem. Quando o uso onde * addr é apontado para um registro GPIO mapeado, o processo recebe um erro de barramento. (Quando altero o ldrex / strex para ldr / str e desativo o loop do, ele funciona). Portanto, parece que o monitor exclusivo do ARM não pode ou não está configurado para funcionar em registros de E / S mapeados na memória, e a questão permanece em aberto.]
fonte