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

          新聞中心

          EEPW首頁 > 嵌入式系統(tǒng) > 設(shè)計(jì)應(yīng)用 > 基于ARM+uClinux的嵌入式系統(tǒng)的開發(fā)

          基于ARM+uClinux的嵌入式系統(tǒng)的開發(fā)

          作者: 時(shí)間:2016-12-02 來源:網(wǎng)絡(luò) 收藏

            以上也是為什么我認(rèn)為開發(fā)嵌入式linux程序主機(jī)應(yīng)該選用linux環(huán)境。對于以前沒用過linux的人來說(比如我),開發(fā)程序前應(yīng)該花3,4天時(shí)間熟悉linux環(huán)境,尤其是它的編輯器,用慣集成編譯環(huán)境的人有時(shí)連編譯器和編輯器的概念都模糊了,所以一般是直接進(jìn)入集成編譯環(huán)境,連寫帶編一氣呵成,殊不知有些集成編譯器提供的編輯器弱智的一塌胡涂,如果用熟了linux下的emacs,你就會發(fā)現(xiàn)他們之間的差距大概……要像我和蓋茨那么大吧。所以編程序時(shí)應(yīng)該選一款優(yōu)秀的編輯器,linux下,我當(dāng)然選emacs,雖然剛看見它的感覺是外表丑陋,使用復(fù)雜。但只要多用多練,對提高效率很有幫助。(將你的程序用兩個(gè)編輯器完成,一半是用emacs的,一半是不用emacs的,看看效果:-)
            對具體的linux編程我就不板門弄斧了,需要提個(gè)醒的是咱硬件出身的人作軟件應(yīng)該養(yǎng)成良好的編程習(xí)慣,別讓作軟件的笑話咱。因?yàn)樽髁诵┚W(wǎng)絡(luò)應(yīng)用,所以介紹一些網(wǎng)絡(luò)編程時(shí)要用到的網(wǎng)站和書籍;
            <>w.Richard.Stevens. 這可是linux網(wǎng)絡(luò)編程的圣經(jīng)級的書籍
            http://www.fanqiang.com/a4/b7/適合于網(wǎng)絡(luò)編程的入門。
            還有IBM中國上關(guān)于linux的教程和文章,都是翻譯過來的,有很多寫非常不錯(cuò)。
            其實(shí)類似的資源不計(jì)其數(shù),遇到問題時(shí)應(yīng)該先到google上狂搜一圈。
          重點(diǎn)想說些關(guān)于編譯器的東西,不了解它,在交叉編譯環(huán)境下編譯程序就寸步難行了,這無非是因?yàn)榻徊婢幾g環(huán)境下目標(biāo)板編譯器所處的寄人籬下的悲慘環(huán)境。想想在linux下將myprogram.c編譯鏈接成應(yīng)用程序myprogram,最簡單的一句gcc –o myprogram myprogram.c 就可以了。(其實(shí)在諸如VC下你也可以找到類似的命令,集成開發(fā)環(huán)境只不過替你來調(diào)用它了)。一切看起來天經(jīng)地義。
            但試著把/usr/include路徑改一個(gè)名字(比如改成stupid_include),再這樣編譯一下,會發(fā)現(xiàn)程序中被< >引用的頭文件(比如#include)都找不到了。因?yàn)榫幾g器看見這樣的頭文件會到系統(tǒng)指定的路徑下尋找,而這個(gè)路徑是由環(huán)境變量保存的(linux和windows下都是這樣的)。針對以上情況,不將路徑名字改回去,但是給編譯器加一個(gè)參數(shù)如下:
            gcc –I/usr/stupid_include –o myprogram myprogram.c
            會發(fā)現(xiàn)錯(cuò)誤信息沒了,一切又恢復(fù)了往日的寧靜,頓時(shí)明白,不用環(huán)境變量,通過參數(shù),同樣可以將這些信息告訴編譯器。 返回來說說你的目標(biāo)編譯器,雖然占用了人家的地盤,編譯器,頭文件,庫文件,一個(gè)都不少,但你要編一個(gè)程序編譯器照樣發(fā)暈,因?yàn)闆]有環(huán)境變量告訴它自己需要的頭文件和庫文件在哪里??磥碇挥袃煞N辦法,一個(gè)是搶占了主機(jī)的環(huán)境變量改成自己的(整個(gè)兒一個(gè)土匪),或者在編譯時(shí)加上必要參數(shù)(還是這樣紳士一些),告訴編譯器需要的文件的位置。(除此之外,還有其他一些參數(shù)也是如此)。
            從源程序到可執(zhí)行文件根據(jù)情況不同可能分好幾步,一般每一步可能都會有一個(gè)應(yīng)用程序?qū)崿F(xiàn),像gnu提供的arm開發(fā)工具鏈其實(shí)就是這么一組程序。提供從編譯到鏈接到格式轉(zhuǎn)化的全套服務(wù)。你可以用arm-elf-gcc命令一步到底直接產(chǎn)生可執(zhí)行文件(其實(shí)也是在自己的任務(wù)完成后調(diào)用下一個(gè)程序),也可以每一步加上自己的參數(shù),只作自己的事。
            編譯器的主要參數(shù)的使用下次將程序的移植時(shí)再講。這里想說一下編譯器產(chǎn)生應(yīng)用程序的幾個(gè)主要步鄹,講這個(gè)問題的原因還是很多人無法區(qū)分諸如編譯和鏈接,不用問,這一切還是IDE集成開發(fā)環(huán)境惹的禍。有人會說,IDE招你惹你了,你老貶它。其實(shí)不然,首先以上說的東西一般在IDE的project菜單下的option或build option中找到,只是一般不用管罷了。另一個(gè)方面,IDE就像是傻瓜照相機(jī),很多工作他都幫你完成了,使用簡單。但如果要做攝影師的話,你就少不了要對每一個(gè)細(xì)節(jié)都了解。其實(shí)編譯程序也是一樣。(你可以對優(yōu)化,警告級,宏定義等諸多選項(xiàng)進(jìn)行自己的選擇)。以下是幾個(gè)主要步鄹:(以下有些我也不確認(rèn),如發(fā)現(xiàn)問題,請及時(shí)糾正。

          (1) 預(yù)編譯。 主要工作就是處理所有#開頭的,包括頭文件。以前搞不清頭文件和可執(zhí)行文件有沒有什么聯(lián)系(因?yàn)榭偪匆妰蓚€(gè)文件名字取一樣的),現(xiàn)在知道,他們之間沒有任何聯(lián)系。在預(yù)編譯結(jié)束后,頭文件的使命就結(jié)束了。在下一次介紹不同平臺程序移植時(shí)可以看到,預(yù)編譯有時(shí)非常有用。
          (2) 編譯。編譯應(yīng)該是最主要的一步,就是將源文件生成CPU能識別的語言,一般是 后綴為.o的目標(biāo)文件,應(yīng)該說,此時(shí)的文件就已經(jīng)可以執(zhí)行了。當(dāng)然這個(gè)時(shí)候外部函數(shù)等外部符號都沒有引入,對于被編譯程序來說,這些外部符號還只是留一個(gè)倩影,壓根兒不知它在不在。你可以在你的程序里調(diào)用一個(gè)不存在的函數(shù),甚至都不用聲明,在編譯階段,很多編譯器只是給個(gè)警告。只有在鏈接時(shí)才會報(bào)錯(cuò)。(呵呵,夠弱智?。?br /> (3) 鏈接:鏈接才是清帳的時(shí)候,以前在程序里用到的外部符號都要把真正的東西交出來。你可以指定需要連接在一起的目標(biāo)文件,也可以告訴編譯器庫文件的名字和路徑(指定方法下次講)。編譯器會去找,需要注意的是,庫的指定需要注意順序。首先,如果不同的庫里有同名函數(shù),并且該函數(shù)被調(diào)用,那么在前面的就被鏈接進(jìn)去了,這一點(diǎn)對于頭文件路徑的指定也適用,如果你自己的頭文件和系統(tǒng)頭文件相同,并且你的頭文件路徑在系統(tǒng)頭文件路徑前面,你的頭文件就會代替頭文件。庫文件是由相應(yīng)的程序(linux下是ar命令)將需要被添加到庫里的目標(biāo)文件(該文件是編譯階段生成的)組織起來生成檔案文件,同時(shí)可以建立一個(gè)檢索,檢索內(nèi)容為所包含的目標(biāo)文件中定義的符號。也就是說,庫文件并不是必須的,但它為經(jīng)常使用的目標(biāo)文件中的函數(shù)提供了快速的檢索機(jī)制。

          以上就是主要的步鄹,當(dāng)然除此之外,還有一些用于格式轉(zhuǎn)換的工具等。不一一介紹。知道編譯器的細(xì)節(jié)對于程序的開發(fā)和移植都是很有好處的。

            程序開發(fā)過程中調(diào)試也是至關(guān)重要,因?yàn)榭梢韵仍谥鳈C(jī)上調(diào)試,所以可以使用linux下的gdb,(有點(diǎn)像dos 下的debug)。但是只是用到了皮毛,還有一個(gè)專用于宿主機(jī)模式的調(diào)試工具gdbserver,一直沒時(shí)間研究,希望用過的大俠多發(fā)些文章鋪路。
            另外還會遇到如何作ramdisk,如何讓系統(tǒng)啟動自己的程序,這些都太linux了,沒接觸過linux的人會暈,為了大家的健康,就不講了,遇到問題可以給我email,大家一起討論。

          4.不同平臺間程序的移植--簡單程序的移植

            研究程序移植的那兩周是最痛苦的兩周,沒有太多可以借鑒的東西,只能摸黑向前走,于是更加堅(jiān)定決心要整理些東西給后來的弟兄。不過話說回來,各位弟兄別被我前面說的嚇倒,只要搞清你要作什么,程序移植其實(shí)是比較簡單的事情。
          首先列出一些問題:
            (1) X86上運(yùn)行的程序能不能在51單片機(jī)上運(yùn)行,為什么,有沒有可能,如果可以,應(yīng)該做哪些工作才可以實(shí)現(xiàn)。
            (2) 相同CPU平臺,DOS的程序?yàn)槭裁纯梢栽趙indows下運(yùn)行,能不能在linux下運(yùn)行,為什么,作什么工作可能實(shí)現(xiàn)。


          為什么可以移植程序,為什么要移植程序?
            程序可以移植首先要感謝開發(fā)出高級語言的大牛們,記住,無論多么漂亮的代碼經(jīng)過編譯以后都要變成CPU可以識別的機(jī)器語言,而幾乎一千種CPU說著一千種語言。為保證大家有共同語言,規(guī)定一種高級語言――高級語言。每一個(gè)CPU派出自己的翻譯――編譯器。這個(gè)翻譯精通兩國語言,高級語言和自己的語言。(由此已經(jīng)可以看出編譯工具在程序移植中的重要性)。只要程序沒有硬件上的約束,可以說這種溝通是無極限的,甚至于不同操作系統(tǒng)平臺。(操作系統(tǒng)也是程序,也可以移植嘍)舉例:在x86的windows下用VC(或TC,BC)編一個(gè)c程序?qū)崿F(xiàn)i=i+1,絲毫不改就可以用51的C編譯器重新編譯并在51單片機(jī)上運(yùn)行。一次小型的移植就結(jié)束了。
            可以移植已有的程序還要感謝開放源代碼的弟兄,沒有這些C文件和H文件讓你重新編譯一下,怎么在你的CPU上跑?其實(shí)不止這些,后面還會看見開源組織的牛人專為程序可移植性所作的專門的工作。
            那么為什么要移植程序?
            問這問題就像問地上有個(gè)錢包為什么要撿一樣,答案不言而喻?,F(xiàn)成的東西為什么不要。當(dāng)然,移植程序可沒有撿錢包那么簡單,尤其是第一次,后面會說一些移植之前應(yīng)該考慮的問題。(就像現(xiàn)在地上有個(gè)錢包也千萬別上去就揣自己兜里,說不定就是套)。另一方面,你給我你寫好的程序讓我拿去用,我還要考慮一下,或許里頭問題多的還不如自己寫一個(gè)。我這里所說的可移植的程序應(yīng)該是維護(hù)比較好,比較成熟的源代碼(像我后面的所說的UCD-SNMP),目前的開放源代碼運(yùn)動決不僅僅是把自己的程序公開就行了,而是有了一套成熟完整的版本控制,BUG報(bào)告和PATCH提交流程。這樣的代碼才有更大的使用價(jià)值。

          什么時(shí)候可以考慮移植程序?
            在基于嵌入式操作系統(tǒng)進(jìn)行開發(fā)時(shí),具有一定規(guī)模的程序都可以到網(wǎng)上查一查都沒有成熟的源代碼可用。前面已經(jīng)說到,程序的移植最終只針對CPU,其實(shí)和操作系統(tǒng)沒什么關(guān)系,但另一方面,因?yàn)榇a中可能會使用一些庫函數(shù),這些庫會包括C語言標(biāo)準(zhǔn)庫和操作系統(tǒng)提供的API(應(yīng)用程序接口)庫。假設(shè)源代碼中只包括C標(biāo)準(zhǔn)庫,那么該程序就可以跨操作系統(tǒng)去移植。例如hello world程序中使用了printf,因?yàn)樵摵瘮?shù)是C標(biāo)準(zhǔn)函數(shù),所以在X86上使用TC(BC或VC)可以直接編譯運(yùn)行,在ARM+uClinux平臺下也一樣,但如果程序中調(diào)用了vfork函數(shù),那么只有l(wèi)inux一脈相承的操作系統(tǒng)支持這種特殊服務(wù)了,在window或dos操作系統(tǒng)下無法直接編譯該程序了。只能找該操作系統(tǒng)支持的類似的功能來實(shí)現(xiàn)。再進(jìn)一步,硬件上的生理缺陷也會為移植帶來麻煩,S3C4510B不支持MMU,在其上運(yùn)行的 uClinux也不提供和MMU有關(guān)的服務(wù)(其實(shí)uClinux本身可以支持MMU),于是在移植前相關(guān)的函數(shù)(比如FORK)都要被替代掉(使用 VFORK)。好在uClinux和linux提供的應(yīng)用接口大部分還是相同的。所以這樣的工作還可以承受。
            由上可知,如果是在各種嵌入式linux(除了uClinux以外,還有好幾種)平臺上開發(fā),那么針對該平臺以及l(fā)inux平臺上的源代碼都可以使用,但是要牢記他們之間的差異。在我的系統(tǒng)中需要實(shí)現(xiàn)網(wǎng)絡(luò)監(jiān)控,所以想使用snmp協(xié)議,該協(xié)議和http,ftp一樣屬于應(yīng)用層的成熟協(xié)議,專用于網(wǎng)絡(luò)管理。目前已經(jīng)有一些針對該協(xié)議成熟的代碼,最有名的是ucd-snmp,不光軟件本身功能強(qiáng)大,可移植性也比較好,在linux,unix等平臺上都可以移植,于是決定將它移植到ARM+uClinux平臺上(別看現(xiàn)在說的這么輕松,當(dāng)時(shí)接這活時(shí)都有點(diǎn)哆嗦)。
            簡單總結(jié)一下,移植應(yīng)用程序的前提是有源代碼,移植的關(guān)鍵工具是編譯器,源代碼中和硬件平臺相關(guān)的東西越少越好(這里主要指使用了匯編,或做了針對自己平臺的事,比如將指針指向特定地址然后操作),另一方面,如果該程序是基于某個(gè)操作系統(tǒng)(利用了操作系統(tǒng)提供的特殊服務(wù),即API),要看自己的操作系統(tǒng)是否提供了相關(guān)服務(wù)。
            下面簡單列出一些我認(rèn)為移植時(shí)需要考慮的問題:
              (1) 自己的操作系統(tǒng)的特點(diǎn)以及在當(dāng)前版本下支持的特性。
              例如:uClinux不支持MMU,同樣就無法支持相應(yīng)的特性。
              (2) 硬件資源。
            因?yàn)榍度胧较到y(tǒng)資源比較緊張,硬件資源考慮必須要周全:
              (1) 軟件存儲空間的大小
              這一般要等用目標(biāo)編譯器重新編譯完以后可能才會知道,所以只能大概估算,但千萬不要看這個(gè)程序在linux下只有幾十k,就認(rèn)為程序很小,這是因?yàn)閘inux下程序多時(shí)使用動態(tài)庫,而在嵌入式系統(tǒng)中,很有可能是把用到的庫都鏈接在一起,所以程序的尺寸會大大增加。
              (2) 程序運(yùn)行空間。.
              (3) 硬件以及相應(yīng)的驅(qū)動是否完備

          以上工作應(yīng)該盡量做,但有時(shí)事先無法把握,只能聽天由命了(有沒有搞錯(cuò)?。。?br />
            可能有人已經(jīng)要暈菜了,振奮一下大家,如果找到了好的源代碼(可移植性好),那么剩下的如要工作就是玩轉(zhuǎn)你的編譯器,只要你能順利的把源代碼用你的編譯器重新編譯一下。90%的工作就完成了(不是嗎)
            上回已經(jīng)介紹了一些編譯器方面的東西,下面針對我的ARM編譯器的具體參數(shù)來講解一些編譯器主要參數(shù)的設(shè)置。
            加入我已經(jīng)有了hello.c,在x86的linux平臺下編譯鏈接一下。
              gcc –c hello.c 產(chǎn)生.o
              gcc –o hello hello.o 產(chǎn)生可執(zhí)行文件
            上回說過,主機(jī)編譯器參數(shù)都有環(huán)境變量保存,所以看起來很簡單。這里我故意分兩個(gè)步鄹。
            下面看一下用我的編譯器編這個(gè)程序(心臟不好的先吃藥)。
              arm-elf-gcc -Iroot/uClibc/include -msoft-float -mcpu=arm7tdmi -fomit-frame-pointer -fsigned-char -mcpu=arm7tdmi -Os –Wall -DEMBED -D_uclinux_ -c hello.c

          這只是編譯,將參數(shù)逐一講解。

          Arm-elf-gcc 是gnu的arm編譯工具
          1)Include地址:參數(shù):-I 值:root/uClibc/include(這是在主機(jī)上我的uClinux的頭文件路徑) 用法:-I root/uClibc/include
          -I參數(shù)保證后面的頭文件路徑在搜索系統(tǒng)頭文件路徑前被搜索從而有可能替代系統(tǒng)的頭文件,如果有多個(gè)這樣的參數(shù),則搜索的順序是從左到右,然后是系統(tǒng)的頭文件。
          2)-m 是針對CPU的選項(xiàng)。
          -mcpu=arm7tdmi 說明CPU類型
          -msoft-float 產(chǎn)生包含浮點(diǎn)庫的輸出
          -fsigned-char 讓char類型有符號
          -fomit-frame-pointer 對所有不需要幀指針的函數(shù)都不將其保存在寄存器中。
          3) -Os –Wall
          -Wall:所有警告都顯示
          Os:優(yōu)化尺寸,該選項(xiàng)使能所有所有不增加尺寸的O2優(yōu)化,并且進(jìn)一步根據(jù)尺寸優(yōu)化
          4) = -DEMBED -D_uclinux_
          -D: 將-Dmacro 后的macro定義為字符串1。

          以下是鏈接:
          arm-elf-ld -L/root/uClibc/lib -L/usr/local/gnu/arm-elf/lib -L/usr/local/gnu/lib/gcc-lib/arm-elf/3.0.1 -elf2flt –o hello /root/uClibc/lib/crt0.o /usr/local/gnu/lib/gcc-lib/arm-elf/3.0.1/crtbegin.o hello.o
          /usr/local/gnu/lib/gcc-lib/arm-elf/3.0.1/crtend.o -lc -lgcc –lc

          其中

          1) 鏈接工具: arm-elf-ld
          2) -L指明需要鏈接的庫的路徑,用法和-I一樣,自己的庫的路徑也可以在這里加入。 -L/root/uClibc/lib -L/usr/local/gnu/arm-elf/lib
          -L/usr/local/gnu/lib/gcc-lib/arm-elf/3.0.1
          3) –o 后面緊跟生成的最終的文件名
          4)/root/uClibc/lib/crt0.o /usr/local/gnu/lib/gcc-lib/arm-elf/3.0.1/crtbegin.o OBJECTS.o
          /usr/local/gnu/lib/gcc-lib/arm-elf/3.0.1/crtend.o
          這是需要鏈接在一起的.o文件
          5) -lc -lgcc –lc -l 后面緊跟的是需要鏈接的庫的名字,一般庫的名字是libxxx.a,使用時(shí)為-lxxx即可,不加lib和.a。還要注意位置,自己的庫文件應(yīng)該加在他的庫前面。

            編譯通過后,移植就算完成了,對于比較小的源代碼都可以這樣,即先分析他的編譯選項(xiàng)(用到了那些頭文件,庫文件等),然后用自己的編譯器對照相應(yīng)參數(shù)重新編譯一下就行了。
            當(dāng)然這只是簡單程序的移植,復(fù)雜案例在下一次講吧。

          上一頁 1 2 下一頁

          關(guān)鍵詞: ARMuClinux嵌入式系

          評論


          技術(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); })();