我们在编写驱动程序的时候,IO空间的起始地址是0x3f000000,加上GPIO的偏移量0x2000000,所以GPIO的物理地址应该是从0x3f200000开始的,然后在这个基础上进行Linux系统的MMU内存虚拟化管理,映射到虚拟地址上。
特别注意,BCM2708 和BCM2709 IO起始地址不同,BCM2708是0x20000000,BCM2709是0x3f000000,这是造成大部分人驱动出现“段错误”的原因。树莓派3B的CPU为BCM2709。
我们需要确定GPFSEL0的物理地址,就要找出偏移量


可以得到 GPFSEL0 的物理地址:0x3F00 0000+(0x7E20 0000- 0x7E00 0000)=0x3F20 0000
同理可得
GPSET0的地址为: 0x3F20 001C;
GPCLR0的地址为: 0x3F20 0028;
在驱动开发过程中,一般来说外设的IO内存资源的物理地址是已知的,由硬件的设计决定。但是CPU不会为这些已知的外设IO内存资源预先指定虚拟地址的值,所以驱动程序不可以直接就通过外设的物理地址访问到IO内存,而必须要将其映射到虚拟地址空间(通过页表),然后才能根据内核映射过后的虚拟地址来通过内存指令访问这些IO内存,并对其进行操作。
在Linux内核的io.h头文件中声明了 ioremap()函数,用来将IO内存资源映射到核心虚拟地址空间(3Gb~4GB)中,当然不用了可以将其取消映射 iounmap()。
void* ioremap(unsigned long phys_addr , unsigned long size , unsigned long flags)
void* iounmap(void * addr);
-------------------------------------------------------------------------------------------------------------------------
//用map映射一个设备意味着使用户空间的一段地址关联到设备内存上,这使得只要程序在分配的地址范围内进行读取或写入,实际上就是对设备的访问。-------------------------------------------------------------------------------------------------------------------------
第一个参数是映射的起始地址
第二个参数是映射的长度
第三个参数是要映射的IO空间和权限有关的标志
-----------------------------