按照Linux分层驱动思想,外设驱动与主机控制器的驱动不相关,主机控制器的驱动不关心外设,而外设驱动也不关心主机,外设访问核心层的通用应用程序接口进行数据传输,主机和外设之间可以进行任意的组合。这样思想要求应用程序不应当直接访问物理地址,而是应当通过驱动程序的调用来实现,以便保持应用程序的可移植性,操作访问的统一性,应用程序利用系统的统一调用接口访问外设,如使用write(),read()等函数进行实际的外设读写控制。应用程序通过调用接口进入内核函数后,内核利用copy_from_user()获得应用层数据,内核驱动程序也通过分层最终执行物理访问,之后把获得的数据用copy_to_user()回传给应用程序的调用者。由于驱动对外需要有个统一接口,所以定义了一些结构体,链表等机制,以便让应用程序操作简单化,数据在内核一应用之间的复制,填充结构体等都需要时间开销,有时按这种标准调用方式,因为操作时间过长,无法完成设计目的。 操作效率评估 我们的一个项目中,系统由FPGA和ARM11结合为核心控制器,其中FPGA连接外部高速ADC、DAC和RF器件在ARM11的控制下,实现GB-6C标准的UHFRFID读写控制状态机。FPGA与ARM11的接口采用SPI,其中ARM11选用三星S3C,作为SPI的主机,FPGA作为SPI的从机,受S3C的控制。在本系统中,SPI接口充当ARM11和FPGA交互的桥梁,ARM11的命令和动作参数传给FPGA并启动FPGA处理状态机,FPGA动作的结果也通过SPI回传给arm11,两者之间的通讯效率在系统中需要重点 void*mmap(void*addr,size_tlen,intprot,intflags,intfd,off_toffset); addr:指定文件应被映射到进程空间的起始地址 len:映射到用户空间的字节数 prot:指定被映射空间的访问权限, flags:由以下几个常值指定: fd:映射到用户空间的文件的描述符 offset:被映射内存区在文件中的偏移值该函数映射文件描述符 通过这个函数,我们可以在应用层访问对应物理地址正确映射后的虚拟地址,这个函数使我们在应用层也具有对任意物理地址的操作权限,下面代码配置S3C的SPI0,因为使用mmap映射,所以不论内核是否带有SPI驱动都不影响我们使用SPI0,但是因为本程序需要对比研究标准驱动方式与直接存储器访问方式的执行差异,所以在内核中编译了标准SPI的驱动程序。由于S3C多数脚都有复用功能,为了使SPI0正确工作,还需要配置相关对应的GPIO为SPI功能(实际上因为我们编译的内核带有SPI0的驱动,内核程序已经完成了SPI的初始化,有的内核没有编译SPI,所以下面还是完整配置了SPI,供参考),同时为了观察研究SPI的执行效率,我们程序还对其他GPIO做了配置以便输出脉冲,通过示波器来评估观察。另外我们还使用若干时间标志来记录操作过程时间,对于在没有示波器的情况下也能评估执行时间。 下面是测试程序代码以及测试过程的示波器记录抓图。 #include"test.h" voidInit_FPGA_SPI(){//配置SPI端口 intfbb; fbb=open("/dev/mem",O_RDWR O_SYNC); map_base=(char*)mmap(0,,PROT_READ PROT_WRITE,MAP_SHARED,fbb,0x7f00b); *(volatileunsignedint*)(map_base+0x04)=0x00101;//CLK=16.MHz *(volatileunsignedint*)(map_base+0x08)=0x00; *(volatileunsignedint*)(map_base+0x0c)=0x02; *(volatileunsignedint*)(map_base)=0x03; FPGA_RUN=map_base+0x18; map_base=(char*)mmap(0,,PROT_READ PROT_WRITE,MAP_SHARED,fbb,0x7f008); GPC=map_base+0x40;//配置端口复用功能为SPI map_GPC=*(volatileunsignedint*)(GPC+4); *(volatileunsignedint*)(GPC)=0x; GPC+=4; virt_addr2=map_base+0x;//配置观察IO GLEDstate=*(volatileunsignedint*)(virt_addr2); } voidInit_Timer(){//添加加配置1微秒时基定时器 intfbb; unsignedinttemp; fbb=open("/dev/mem",O_RDWR O_SYNC); map_base=(char*)mmap(0,,PROT_READ PROT_WRITE,MAP_SHARED,fbb,0x7f006); ……………………篇幅原因略去部分次要代码 MYSYSTICK=map_base+0x14; } voidSPI_init(){ bits=8; speed=16; trr.len=20; trr.delay_usecs=0; trr.speed_hz=speed; trr.bits_per_word=bits; fspi=open("/dev/spidev0.0",O_RDWR); ioctl(fspi,SPI_IOC_RD_MODE,mode); ioctl(fspi,SPI_IOC_WR_MODE,mode); } __inlineunsignedintGETSYSCLK(){ return(*(volatileunsignedint*)(MYSYSTICK)); } __inlinevoidCSFPGAL(){ map_GPC=0xfffffff7; *(volatileunsignedint*)(GPC)=map_GPC; } __inlinevoidCSFPGAH(){ map_GPC =0x08; *(volatileunsignedint*)(GPC)=map_GPC; } voidtest(){ GLEDstate=0xfffffffe; *(volatileunsignedint*)(virt_addr2)=GLEDstate;//产生GPIO负跳变 starttime2=GETSYSCLK(); *(volatileunsignedint*)(FPGA_RUN-0x0c)=0x00; *(volatileunsignedint*)(FPGA_RUN-0x18)=0x23; *(volatileunsignedint*)(FPGA_RUN-0x18)=0x03; CSFPGAL(); *(volatileunsignedint*)(FPGA_RUN)=tx[0]; *(volatileunsignedint*)(FPGA_RUN)=tx[1]; *(volatileunsignedint*)(FPGA_RUN)=tx[2]; *(volatileunsignedint*)(FPGA_RUN)=tx[3]; *(volatileunsignedint*)(FPGA_RUN)=tx[4]; while(((*(volatileunsignedint*)(FPGA_RUN-4)0xfe)13)5){}; CSFPGAH(); stoptime2=GETSYSCLK(); GLEDstate =0x01; *(volatileunsignedint*)(virt_addr2)=GLEDstate; GLEDstate=0xfffffffe; *(volatileunsignedint*)(virt_addr2)=GLEDstate; starttime1=GETSYSCLK();//产生GPIO一个正脉冲 write(fspi,tx,5); stoptime1=GETSYSCLK(); GLEDstate =0x01;
*(volatileunsignedint*)(virt_addr2)=GLEDstate;//产生GPIO正跳变
printf("DRVtime=%dREGtime=%d",starttime1-stoptime1,starttime2-stoptime2);
}
intmain(void){
SPI_init();Init_FPGA_SPI();Init_Timer();
waittime=GETSYSCLK();
while(1){
if((waittime-GETSYSCLK())2){//2ms测试一次
waittime=GETSYSCLK();
test();
}
}
}通过mmap方式应用程序在Linux下操作硬件寄存器,适合于白癜风咨询网北京哪家医院治白癜风疗效好
|