<meter id="pryje"><nav id="pryje"><delect id="pryje"></delect></nav></meter>
          <label id="pryje"></label>

          新聞中心

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

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

          作者: 時(shí)間:2013-05-28 來源:網(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)通過驅(qū)動(dòng)程序的調(diào)用來實(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)用程序通過調(diào)用進(jìn)入內(nèi)核函數(shù)后,內(nèi)核利用copy_from_user()獲得應(yīng)用層數(shù)據(jù),內(nèi)核驅(qū)動(dòng)程序也通過分層最終執(zhí)行物理訪問,之后把獲得的數(shù)據(jù)用copy_to_user()回傳給應(yīng)用程序的調(diào)用者。由于驅(qū)動(dòng)對(duì)外需要有個(gè)統(tǒng)一接口,所以定義了一些結(jié)構(gòu)體,鏈表等機(jī)制,以便讓應(yīng)用程序操作簡單化,數(shù)據(jù)在內(nèi)核一應(yīng)用之間的復(fù)制,填充結(jié)構(gòu)體等都需要時(shí)間開銷,有時(shí)按這種標(biāo)準(zhǔn)調(diào)用方式,因?yàn)椴僮鲿r(shí)間過長,無法完成設(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é)果也通過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)境下通過read/write進(jìn)行操作,為了評(píng)估效率,另外采用一個(gè)GPIO輸出脈沖指示操作過程,試驗(yàn)結(jié)果顯示效率非常低下,從應(yīng)用層執(zhí)行write代碼開始到SPI端口輸出時(shí)鐘,延時(shí)長達(dá)72μs,SPI操作之后,再回到應(yīng)用層的下一個(gè)語句也延時(shí)42μs,對(duì)于比較少的數(shù)據(jù)傳輸情況,附加的額外等待時(shí)間遠(yuǎn)遠(yuǎn)長于實(shí)際傳輸有效時(shí)間,從前面數(shù)據(jù)看出,通過標(biāo)準(zhǔn)庫調(diào)用嚴(yán)重影響系統(tǒng)性能,沒法滿足系統(tǒng)需求。通過查看驅(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è)的使用可以由程序控制,這樣可以簡化共享設(shè)備的互斥保護(hù),進(jìn)一步減少代碼量,提高了訪問效率。

          mmap函數(shù)調(diào)用實(shí)例
            mmap函數(shù)作用是將物理地址映射至用戶空間。下面是函數(shù)的參數(shù)簡單說明
          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ù)映射文件描述符

            通過這個(gè)函數(shù),我們可以在應(yīng)用層訪問對(duì)應(yīng)物理地址正確映射后的虛擬地址,這個(gè)函數(shù)使我們在應(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做了配置以便輸出脈沖,通過示波器來評(píng)估觀察。另外我們還使用若干時(shí)間標(biāo)志來記錄操作過程時(shí)間,對(duì)于在沒有示波器的情況下也能評(píng)估執(zhí)行時(shí)間。

            下面是測試程序代碼以及測試過程的示波器記錄抓圖。

          #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測試一次
          waittime=GETSYSCLK();
          test();
          }
          }
          }

            圖1示波器截圖添加了一些時(shí)間信息以便對(duì)應(yīng)代碼注釋說明,對(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é)語
            通過mmap方式應(yīng)用程序在下操作硬件寄存器,適合于關(guān)注高效率的訪問場合,在嵌入式應(yīng)用中,我們既能夠獲得使用操作系統(tǒng)管理任務(wù)和豐富開源驅(qū)動(dòng)庫的好處,同時(shí)又能在局部提升處理效率,提高處理數(shù)據(jù)的實(shí)時(shí)性。

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

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




          評(píng)論


          相關(guān)推薦

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

          關(guān)閉
          看屁屁www成人影院,亚洲人妻成人图片,亚洲精品成人午夜在线,日韩在线 欧美成人 (function(){ var bp = document.createElement('script'); var curProtocol = window.location.protocol.split(':')[0]; if (curProtocol === 'https') { bp.src = 'https://zz.bdstatic.com/linksubmit/push.js'; } else { bp.src = 'http://push.zhanzhang.baidu.com/push.js'; } var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(bp, s); })();