我正在使用GPIO的直接控制进行编码,为此有一些不错的资源,例如http://elinux.org/RPi_Low-level_peripherals#GPIO_hardware_hacking;该过程涉及open(“ / dev / mem”),然后mmap操作有效地将所需的物理地址映射到您的虚拟地址空间中。然后,阅读本http://www.raspberrypi.org/wp-content/uploads/2012/02/BCM2835-ARM-Peripherals.pdf的第6节,以了解有关如何控制I / O的信息。
要更改引脚的功能(输入或输出,或各种特殊功能),请修改GPFSELx I / O寄存器中的这3位字段(000 =输入,001 =输出对象实例)。这些修改操作被编译为具有普通加载和存储的操作(例如,将GPIO0更改为输入:*(regptr)&=〜7;编译为类似
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)
问题是这样的:如果在加载和存储之间发生中断,并且另一个进程或ISR修改了相同的I / O寄存器,则存储操作(基于读入r2的陈旧)将还原该其他操作的效果。因此,更改这些I / O寄存器确实需要使用原子(锁定)读取/修改/写入操作来完成。我看到的示例未使用锁定操作。
由于这些I / O寄存器通常仅在设置某些内容时才更改,因此不太可能出现问题,但是“从不”总是比“不太可能”更好。另外,如果您有一个应用在其中要模拟集电极开路输出,那么(据我所知)这涉及将输出编程为0,然后在输出(低)或输入( (高/低)。因此,在这种情况下,这些I / O寄存器会经常出现Mod,而不安全的修改将更可能引起问题。
因此,可能有一个ARM“比较并设置”或类似的操作可以在这里使用,有人可以指出这一点,以及如何通过C代码实现这一点?
[注意,当您将I / O编程为输出并将其从0更改为1或反之亦然时,不需要任何特殊操作;因为有一个您要写入的I / O寄存器,用于将选定的位设置为1,另一个用于将选定的位清除为0。此操作不需要读/写,因此没有中断的危险]。
/dev/mem
,您的代码似乎是用户空间代码。我认为,在任何现代操作系统中,都不必担心中断更改用户空间代码中的寄存器值的情况。我相信即使在内核空间代码中这也不是问题,因为当中断处理程序完成其工作时,Linux会还原所有寄存器。