• <li id="00i08"><input id="00i08"></input></li>
  • <sup id="00i08"><tbody id="00i08"></tbody></sup>
    <abbr id="00i08"></abbr>
  • 新聞中心

    EEPW首頁(yè) > 嵌入式系統(tǒng) > 設(shè)計(jì)應(yīng)用 > 嵌入式Linux應(yīng)用程序訪問物理地址的實(shí)例

    嵌入式Linux應(yīng)用程序訪問物理地址的實(shí)例

    作者: 時(shí)間:2013-05-28 來(lái)源:網(wǎng)絡(luò) 收藏

    前言
      按照分層思想,外設(shè)與主機(jī)控制器的不相關(guān),主機(jī)控制器的驅(qū)動(dòng)不關(guān)心外設(shè),而外設(shè)驅(qū)動(dòng)也不關(guān)心主機(jī),外設(shè)訪問核心層的通用應(yīng)用程序進(jìn)行數(shù)據(jù)傳輸,主機(jī)和外設(shè)之間可以進(jìn)行任意的組合。這樣思想要求應(yīng)用程序不應(yīng)當(dāng)直接訪問物理地址,而是應(yīng)當(dāng)通過(guò)驅(qū)動(dòng)程序的調(diào)用來(lái)實(shí)現(xiàn),以便保持應(yīng)用程序的性,操作訪問的統(tǒng)一性,應(yīng)用程序利用系統(tǒng)的統(tǒng)一調(diào)用訪問外設(shè),如使用write(),read()等函數(shù)進(jìn)行實(shí)際的外設(shè)讀寫控制。應(yīng)用程序通過(guò)調(diào)用進(jìn)入內(nèi)核函數(shù)后,內(nèi)核利用copy_from_user()獲得應(yīng)用層數(shù)據(jù),內(nèi)核驅(qū)動(dòng)程序也通過(guò)分層最終執(zhí)行物理訪問,之后把獲得的數(shù)據(jù)用copy_to_user()回傳給應(yīng)用程序的調(diào)用者。由于驅(qū)動(dòng)對(duì)外需要有個(gè)統(tǒng)一接口,所以定義了一些結(jié)構(gòu)體,鏈表等機(jī)制,以便讓應(yīng)用程序操作簡(jiǎn)單化,數(shù)據(jù)在內(nèi)核一應(yīng)用之間的復(fù)制,填充結(jié)構(gòu)體等都需要時(shí)間開銷,有時(shí)按這種標(biāo)準(zhǔn)調(diào)用方式,因?yàn)椴僮鲿r(shí)間過(guò)長(zhǎng),無(wú)法完成設(shè)計(jì)目的。

    操作效率評(píng)估
      我們的一個(gè)項(xiàng)目中,系統(tǒng)由FPGA和ARM11結(jié)合為核心控制器,其中FPGA連接外部高速ADC、DAC和RF器件在ARM11的控制下,實(shí)現(xiàn)GB18000-6C標(biāo)準(zhǔn)的UHF RFID讀寫控制狀態(tài)機(jī)。FPGA與ARM11的接口采用SPI,其中ARM11選用三星S3C6410,作為SPI的主機(jī),F(xiàn)PGA作為SPI的從機(jī),受S3C6410的控制。在本系統(tǒng)中,SPI接口充當(dāng)ARM11和FPGA交互的橋梁,ARM11的命令和動(dòng)作參數(shù)傳給FPGA并啟動(dòng)FPGA處理狀態(tài)機(jī),F(xiàn)PGA動(dòng)作的結(jié)果也通過(guò)SPI回傳給ARM11,兩者之間的通訊效率在系統(tǒng)中需要重點(diǎn)關(guān)注。

      評(píng)估通訊接口時(shí),利用三星提供的SPI驅(qū)動(dòng)函數(shù),系統(tǒng)運(yùn)行在533MHz,SPI時(shí)鐘配置為16MHz,程序在linux3.0環(huán)境下通過(guò)read/write進(jìn)行操作,為了評(píng)估效率,另外采用一個(gè)GPIO輸出脈沖指示操作過(guò)程,試驗(yàn)結(jié)果顯示效率非常低下,從應(yīng)用層執(zhí)行write代碼開始到SPI端口輸出時(shí)鐘,延時(shí)長(zhǎng)達(dá)72μs,SPI操作之后,再回到應(yīng)用層的下一個(gè)語(yǔ)句也延時(shí)42μs,對(duì)于比較少的數(shù)據(jù)傳輸情況,附加的額外等待時(shí)間遠(yuǎn)遠(yuǎn)長(zhǎng)于實(shí)際傳輸有效時(shí)間,從前面數(shù)據(jù)看出,通過(guò)標(biāo)準(zhǔn)庫(kù)調(diào)用嚴(yán)重影響系統(tǒng)性能,沒法滿足系統(tǒng)需求。通過(guò)查看驅(qū)動(dòng)程序的源代碼,可以發(fā)現(xiàn)因?yàn)轵?qū)動(dòng)程序?qū)訉臃庋b,并且包含應(yīng)用層到內(nèi)核的copy_from_user()和內(nèi)核到應(yīng)用層的copy_to_user()兩次數(shù)據(jù)搬移,導(dǎo)致執(zhí)行效率很低。為了提高數(shù)據(jù)交互效率,就要設(shè)法繞開數(shù)據(jù)搬移等時(shí)間開銷,最好能直接操作寄存器,雖然這種想法與分層驅(qū)動(dòng)思想不相符合,但是在嵌入式系統(tǒng)中,有時(shí)需要高的執(zhí)行效率,如果利用系統(tǒng)一些特定函數(shù),實(shí)現(xiàn)高效率的數(shù)據(jù)交互從而完成設(shè)計(jì)目標(biāo)是有必要和可能的。

      linux存在名為mmap的函數(shù),能把物理地址映射為虛擬地址,并且這個(gè)函數(shù)能直接在應(yīng)用程序中直接調(diào)用而不是僅僅屬于內(nèi)核調(diào)用的函數(shù),這樣在應(yīng)用層直接操作S3C6410的物理外設(shè)成為可能。考慮到在特定的嵌入式系統(tǒng)中,特定外設(shè)的使用可以由程序控制,這樣可以簡(jiǎn)化共享設(shè)備的互斥保護(hù),進(jìn)一步減少代碼量,提高了訪問效率。

    mmap函數(shù)調(diào)用實(shí)例
      mmap函數(shù)作用是將物理地址映射至用戶空間。下面是函數(shù)的參數(shù)簡(jiǎn)單說(shuō)明
    void* mmap(void * addr, size_t len, int prot, int flags, int fd, off_t offset);
    addr: 指定文件應(yīng)被映射到進(jìn)程空間的起始地址
    len: 映射到用戶空間的字節(jié)數(shù)
    prot: 指定被映射空間的訪問權(quán)限,
    flags: 由以下幾個(gè)常值指定:
    fd: 映射到用戶空間的文件的描述符
    offset: 被映射內(nèi)存區(qū)在文件中的偏移值該函數(shù)映射文件描述符

      通過(guò)這個(gè)函數(shù),我們可以在應(yīng)用層訪問對(duì)應(yīng)物理地址正確映射后的虛擬地址,這個(gè)函數(shù)使我們?cè)趹?yīng)用層也具有對(duì)任意物理地址的操作權(quán)限,下面代碼配置S3C6410的SPI0,因?yàn)槭褂胢map映射,所以不論內(nèi)核是否帶有SPI驅(qū)動(dòng)都不影響我們使用SPI0,但是因?yàn)楸境绦蛐枰獙?duì)比研究標(biāo)準(zhǔn)驅(qū)動(dòng)方式與直接存儲(chǔ)器訪問方式的執(zhí)行差異,所以在內(nèi)核中編譯了標(biāo)準(zhǔn)SPI的驅(qū)動(dòng)程序。由于S3C6410多數(shù)腳都有復(fù)用功能,為了使SPI0正確工作,還需要配置相關(guān)對(duì)應(yīng)的GPIO為SPI功能(實(shí)際上因?yàn)槲覀兙幾g的內(nèi)核帶有SPI0的驅(qū)動(dòng),內(nèi)核程序已經(jīng)完成了SPI的初始化,有的內(nèi)核沒有編譯SPI,所以下面還是完整配置了SPI,供參考),同時(shí)為了觀察研究SPI的執(zhí)行效率,我們程序還對(duì)其他GPIO做了配置以便輸出脈沖,通過(guò)示波器來(lái)評(píng)估觀察。另外我們還使用若干時(shí)間標(biāo)志來(lái)記錄操作過(guò)程時(shí)間,對(duì)于在沒有示波器的情況下也能評(píng)估執(zhí)行時(shí)間。

      下面是測(cè)試程序代碼以及測(cè)試過(guò)程的示波器記錄抓圖。

    #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;//配置端口復(fù)用功能為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微秒時(shí)基定時(shí)器
    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;//產(chǎn)生GPIO負(fù)跳變
    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)5){};
    CSFPGAH();
    stoptime2=GETSYSCLK();
    GLEDstate|=0x00000001;
    *(volatile unsigned int *)(virt_addr2)=GLEDstate;
    GLEDstate=0xfffffffe;
    *(volatile unsigned int *)(virt_addr2)=GLEDstate;
    starttime1=GETSYSCLK();//產(chǎn)生GPIO一個(gè)正脈沖
    write(fspi,tx,5);
    stoptime1=GETSYSCLK();
    GLEDstate|=0x00000001;
    *(volatile unsigned int *)(virt_addr2)=GLEDstate; //產(chǎn)生GPIO正跳變
    printf("DRVtime=%d REGtime=%d ",starttime1-stoptime1,starttime2-stoptime2);
    }
    int main(void){
    SPI_init();Init_FPGA_SPI();Init_Timer();
    waittime=GETSYSCLK();
    while(1){
    if ((waittime-GETSYSCLK())>2000000){//2000ms測(cè)試一次
    waittime=GETSYSCLK();
    test();
    }
    }
    }

      圖1示波器截圖添加了一些時(shí)間信息以便對(duì)應(yīng)代碼注釋說(shuō)明,對(duì)應(yīng)于代碼mmap方式和標(biāo)準(zhǔn)驅(qū)動(dòng)調(diào)用方式產(chǎn)生了兩組SCK時(shí)鐘,GPIO觀察腳顯示第一次SPI訪問消耗5μs,第二次訪問消耗114μs,其中真正操作SPI的時(shí)間也就4μs不到,其它時(shí)間消耗在系統(tǒng)應(yīng)用層到內(nèi)核兩次雙向的數(shù)據(jù)拷貝以及為了統(tǒng)一對(duì)外接口所做的數(shù)據(jù)結(jié)構(gòu)配置等方面,由此對(duì)比可以看出兩種方式訪問效率上的巨大差異。

    圖 1


    結(jié)語(yǔ)
      通過(guò)mmap方式應(yīng)用程序在下操作硬件寄存器,適合于關(guān)注高效率的訪問場(chǎng)合,在嵌入式應(yīng)用中,我們既能夠獲得使用操作系統(tǒng)管理任務(wù)和豐富開源驅(qū)動(dòng)庫(kù)的好處,同時(shí)又能在局部提升處理效率,提高處理數(shù)據(jù)的實(shí)時(shí)性。

    linux操作系統(tǒng)文章專題:linux操作系統(tǒng)詳解(linux不再難懂)

    linux相關(guān)文章:linux教程




    評(píng)論


    相關(guān)推薦

    技術(shù)專區(qū)

    關(guān)閉
    主站蜘蛛池模板: 淮安市| 大同县| 额敏县| 静安区| 包头市| 扎兰屯市| 松江区| 颍上县| 大邑县| 西青区| 东台市| 宜春市| 景泰县| 黑龙江省| 鄢陵县| 镇安县| 渭源县| 自治县| 青川县| 卢湾区| 富川| 文成县| 长宁县| 珲春市| 汝城县| 隆化县| 肥城市| 叙永县| 库伦旗| 苗栗县| 赣州市| 太谷县| 巨野县| 孝昌县| 布尔津县| 错那县| 南岸区| 绥滨县| 崇义县| 定日县| 庄浪县|