前语
依照Linux分层驱动思维,外设驱动与主机操控器的驱动不相关,主机操控器的驱动不关心外设,而外设驱动也不关心主机,外设拜访中心层的通用运用程序接口进行数据传输,主机和外设之间能够进行恣意的组合。这样思维要求运用程序不应当直接拜访物理地址,而是应当经过驱动程序的调用来完结,以便坚持运用程序的可移植性,操作拜访的一致性,运用程序运用体系的一致调用接口拜访外设,如运用write(),read()等函数进行实践的外设读写操控。运用程序经过调用接口进入内核函数后,内核运用copy_from_user()取得运用层数据,内核驱动程序也经过分层终究履行物理拜访,之后把取得的数据用copy_to_user()回传给运用程序的调用者。由于驱动对外需求有个一致接口,所以界说了一些结构体,链表等机制,以便让运用程序操作简略化,数据在内核一运用之间的仿制,填充结构体等都需求时刻开支,有时按这种规范调用方法,由于操作时刻过长,无法完结规划意图。
操作功率评价
咱们的一个项目中,体系由FPGA和ARM11结合为中心操控器,其间FPGA衔接外部高速ADC、DAC和RF器材在ARM11的操控下,完结GB18000-6C规范的UHF RFID读写操控状态机。FPGA与ARM11的接口选用SPI,其间ARM11选用三星S3C6410,作为SPI的主机,FPGA作为SPI的从机,受S3C6410的操控。在本体系中,SPI接口充任ARM11和FPGA交互的桥梁,ARM11的指令和动作参数传给FPGA并发动FPGA处理状态机,FPGA动作的成果也经过SPI回传给ARM11,两者之间的通讯功率在体系中需求要点重视。
评价通讯接口时,运用三星供给的SPI驱动函数,体系运行在533MHz,SPI时钟装备为16MHz,程序在linux3.0环境下经过read/write进行操作,为了评价功率,别的选用一个GPIO输出脉冲指示操作进程,实验成果显现功率十分低下,从运用层履行write代码开端到SPI端口输出时钟,延时长达72μs,SPI操作之后,再回到运用层的下一个句子也延时42μs,关于比较少的数据传输状况,附加的额定等待时刻远远善于实践传输有用时刻,从前面数据看出,经过规范库调用严重影响体系功用,无法满意体系需求。经过检查驱动程序的源代码,能够发现由于驱动程序层层封装,而且包括运用层到内核的copy_from_user()和内核到运用层的copy_to_user()两次数据搬移,导致履行功率很低。为了进步数据交互功率,就要设法绕开数据搬移等时刻开支,最好能直接操作寄存器,尽管这种主意与Linux分层驱动思维不相契合,可是在嵌入式体系中,有时需求高的履行功率,假如运用体系一些特定函数,完结高功率的数据交互然后完结规划方针是有必要和或许的。
linux存在名为mmap的函数,能把物理地址映射为虚拟地址,而且这个函数能直接在运用程序中直接调用而不是只是归于内核调用的函数,这样在运用层直接操作S3C6410的物理外设成为或许。考虑到在特定的嵌入式体系中,特定外设的运用能够由程序操控,这样能够简化同享设备的互斥维护,进一步削减代码量,进步了拜访功率。
mmap函数调用实例
mmap函数作用是将物理地址映射至用户空间。下面是函数的参数简略阐明
void* mmap(void * addr, size_t len, int prot, int flags, int fd, off_t offset);
addr: 指定文件应被映射到进程空间的开始地址
len: 映射到用户空间的字节数
prot: 指定被映射空间的拜访权限,
flags: 由以下几个常值指定:
fd: 映射到用户空间的文件的描述符
offset: 被映射内存区在文件中的偏移值该函数映射文件描述符
经过这个函数,咱们能够在运用层拜访对应物理地址正确映射后的虚拟地址,这个函数使咱们在运用层也具有对恣意物理地址的操作权限,下面代码装备S3C6410的SPI0,由于运用mmap映射,所以不管内核是否带有SPI驱动都不影响咱们运用SPI0,可是由于本程序需求比照研讨规范驱动方法与直接存储器拜访方法的履行差异,所以在内核中编译了规范SPI的驱动程序。由于S3C6410大都脚都有复用功用,为了使SPI0正确作业,还需求装备相关对应的GPIO为SPI功用(实践上由于咱们编译的内核带有SPI0的驱动,内核程序现已完结了SPI的初始化,有的内核没有编译SPI,所以下面仍是完好装备了SPI,供参阅),一起为了调查研讨SPI的履行功率,咱们程序还对其他GPIO做了装备以便输出脉冲,经过示波器来评价调查。别的咱们还运用若干时刻标志来记载操作进程时刻,关于在没有示波器的状况下也能评价履行时刻。
下面是测验程序代码以及测验进程的示波器记载抓图。
#include “test.h”
void Init_FPGA_SPI(){ //装备SPI端口
int fbb;
fbb=open(“/dev/mem”,O_RDWR | O_SYNC);
map_base=(char *)mmap(0,4096,PROT_READ|PROT_WRITE,MAP_SHARED,fbb,0x7f00b000);
*(volatile unsigned int *)(map_base+0x04)=0x00000101; //CLK=16.625MHz
*(volaTIle unsigned int *)(map_base+0x08)=0x00000000;
*(volaTIle unsigned int *)(map_base+0x0c)=0x00000002;
*(volaTIle unsigned int *)(map_base)=0x00000003;
FPGA_RUN=map_base+0x18;
map_base=(char *)mmap(0,4096,PROT_READ|PROT_WRITE,MAP_SHARED,fbb,0x7f008000);
GPC=map_base+0x40; //装备端口复用功用为SPI
map_GPC=*(volaTIle unsigned int *)(GPC+4);
*(volatile unsigned int *)(GPC)=0x12201222;
GPC+=4;
virt_addr2=map_base+0x824;//装备调查IO
GLEDstate=*(volatile unsigned int *)(virt_addr2);
}
void Init_Timer(){ //添加加装备1微秒时基定时器
int fbb;
unsigned int temp;
fbb=open(“/dev/mem”,O_RDWR | O_SYNC);
map_base=(char *)mmap(0,4096,PROT_READ|PROT_WRITE,MAP_SHARED,fbb,0x7f006000);
…………………… 篇幅原因省略部分非必须代码
MYSYSTICK=map_base+0x14;
}
void SPI_init(){
bits=8;
speed = 16625000;
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);
}
__inline unsigned int GETSYSCLK(){
return(*(volatile unsigned int *)(MYSYSTICK));
}
__inline void CSFPGAL(){
map_GPC&=0xfffffff7;
*(volatile unsigned int *)(GPC)=map_GPC;
}
__inline void CSFPGAH(){
map_GPC|=0x00000008;
*(volatile unsigned int *)(GPC)=map_GPC;
}
void test(){
GLEDstate&=0xfffffffe;
*(volatile unsigned int *)(virt_addr2)=GLEDstate;//发生GPIO负跳变
starttime2=GETSYSCLK();
*(volatile unsigned int *)(FPGA_RUN-0x0c)=0x00;
*(volatile unsigned int *)(FPGA_RUN-0x18)=0x23;
*(volatile unsigned int *)(FPGA_RUN-0x18)=0x03;
CSFPGAL();
*(volatile unsigned int *)(FPGA_RUN)=tx[0];
*(volatile unsigned int *)(FPGA_RUN)=tx[1];
*(volatile unsigned int *)(FPGA_RUN)=tx[2];
*(volatile unsigned int *)(FPGA_RUN)=tx[3];
*(volatile unsigned int *)(FPGA_RUN)=tx[4];
while (((*(volatile unsigned int *)(FPGA_RUN-4)&0xfe000)》》13)2000000){ //2000ms测验一次
waittime=GETSYSCLK();
test();
}
}
}
图1示波器截图添加了一些时刻信息以便对应代码注释阐明,对应于代码mmap方法和规范驱动调用方法发生了两组SCK时钟,GPIO调查脚显现第一次SPI拜访耗费5μs,第2次拜访耗费114μs,其间真实操作SPI的时刻也就4μs不到,其它时刻耗费在体系运用层到内核两次双向的数据复制以及为了一致对外接口所做的数据结构装备等方面,由此比照能够看出两种方法拜访功率上的巨大差异。
图 1
结语
经过mmap方法运用程序在Linux下操作硬件寄存器,适合于重视高功率的拜访场合,在嵌入式运用中,咱们既能够取得运用操作体系管理使命和丰厚开源驱动库的优点,一起又能在部分进步处理功率,进步处理数据的实时性。
责任编辑:gt