慶幸的是,GNU所倡導的自由軟件給開(kāi)發(fā)者帶來(lái)了福音。1984 年,旨在開(kāi)發(fā)一個(gè)類(lèi)似 Unix 的,并且是完全免費的完整操作系統和配套工具:GNU 系統(發(fā)音為“guh-NEW”)。GNU的操作系統和開(kāi)發(fā)工具都是免費的,遵循GNU 通用公共許可證 (GPL)協(xié)議,任何人都可以從網(wǎng)上獲取全部的源代碼。關(guān)于GNU和公共許可證協(xié)議的詳細資料,讀者可參看GNU網(wǎng)站的中文介紹:http://www.gnu.org/home.cn.html。
除了大家熟知的Linux操作系統外,GNU的軟件還包括編譯器(gcc,g++)、二進(jìn)制轉換工具(objdump,objcopy)、調試工具(gdb,gdbserver,kgdb)和基于不同硬件平臺的開(kāi)發(fā)庫。GNU開(kāi)發(fā)工具的主要缺點(diǎn)是采用命令行方式,用戶(hù)掌握和使用比較困難,不如基于 Windows系統的開(kāi)發(fā)工具好用。但是,GNU工具的復雜性是由于它更貼近編譯器和操作系統的底層,并提供了更大的靈活性。一旦學(xué)習和掌握了相關(guān)工具,也就了解了系統設計的基礎知識,為今后的開(kāi)發(fā)工作打下基礎。GNU的開(kāi)發(fā)工具都是免費的,遵循GPL協(xié)議,任何人都可以從網(wǎng)上獲取。筆者參與了一個(gè)基于 ARM平臺的嵌入式Linux系統開(kāi)發(fā),采用的是摩托羅拉龍珠系列的MC928MX1。從測試代碼、引導程序、嵌入式Linux移植、應用程序、圖形界面都可以用GNU工具進(jìn)行開(kāi)發(fā),不需要在開(kāi)發(fā)工具上做額外的投入。本文所介紹的開(kāi)發(fā)方法同樣適用于其它公司的基于A(yíng)RM的產(chǎn)品。
1 硬件平臺
MC928MX1(以下簡(jiǎn)稱(chēng)MX1)是摩托羅拉公司基于A(yíng)RM核心的第一款MCU,主要面向高端嵌入式應用。內部采用ARM920T內核,并集成了SDRAM/Flash、LCD、USB、藍牙(bluetooth)、多媒體閃存卡(MMC)、CMOS攝像頭等控制器。關(guān)于MX1的詳細資料,感興趣的讀者可以參考http://www.motorola.com.cn /semiconductors/。作為應用開(kāi)發(fā)的最小系統必須包括RAM(程序運行空間)、Flash(存放目標代碼)和串行接口(用于調試和下載程序)。MX1提供了6個(gè)片選端(CS0“CS5),內置了SDRAM控制器,數據寬度32位。在筆者的系統中采用了2片8M%26;#215;16位的 SDRAM和2片4M%26;#215;16位的同步Flash存儲器,分別接入數據線(xiàn)的低16位和高16位,如圖1所示。
圖1中SDRAM接片選端CS2,Flash接片選端CS3,其余為SDRAM/Flash的控制信號。最小系統還包括至少1個(gè)串行接口,可以采用 MX1內置的UART控制器。
2 自舉模式
目前,許多嵌入式處理器都提供了自舉模式(Bootstrap),供用戶(hù)寫(xiě)入引導代碼。自舉模式利用了固化在芯片內部的一段引導程序,當處理器復位時(shí),如果在特定引腳上加信號,則處理器將在復位后執行固化ROM中的程序。例如,MX1提供了4條復位引腳,復位時(shí)引腳不同的電平組合可以從不同的片選端啟動(dòng)系統。自舉ROM中的程序完成串口的初始化,然后等待用戶(hù)從串口寫(xiě)入用戶(hù)代碼。自舉模式所能接受的是一種專(zhuān)門(mén)格式的文本文件,包括數據和要寫(xiě)入/讀出的地址。關(guān)于自舉模式的代碼格式,可參考相關(guān)芯片的手冊。在摩托羅拉的網(wǎng)站還提供了許多小工具,幫助開(kāi)發(fā)者將其它格式的文件轉換成為自舉模式格式。通過(guò)自舉模式下載的通常是一段和上位機軟件(如超級終端)通信的程序,完成接收數據并寫(xiě)入Flash的操作。寫(xiě)入的數據可以是用戶(hù)自己的應用程序、數據或者操作系統的內核。通過(guò)自舉模式下載的引導程序同樣可以用GNU工具開(kāi)發(fā)。
3 GNU的編譯器和開(kāi)發(fā)工具
GNU提供的編譯工具包括匯編器as、C編譯器gcc、C++編譯器g++、連接器ld和二進(jìn)制轉換工具objcopy?;贏(yíng)RM平臺的工具分別為 arm-linux-as、arm-linux-gcc、arm-linux-g++、arm -linux-ld 和arm-linux-objcopy。GNU的所有開(kāi)發(fā)工具都可以從www.gnu.org上下載,基于A(yíng)RM的工具可以從 www.uclinux.org獲得。GNU的編譯器功能非常強大,共有上百個(gè)操作選項,這也是這類(lèi)工具讓初學(xué)者頭痛的原因。不過(guò),實(shí)際開(kāi)發(fā)中只需要用到有限的幾個(gè),大部分可以采用缺省選項。GNU工具的開(kāi)發(fā)流程如下:編寫(xiě)C、C++語(yǔ)言或匯編源程序,用gcc或g++生成目標文件,編寫(xiě)連接腳本文件,用連接器生成最終目標文件(elf格式),用二進(jìn)制轉換工具生成可下載的二進(jìn)制代碼。GNU工具都運行在Linux下,開(kāi)發(fā)者需要1臺運行Linux的PC 作為上位機。由于篇幅所限,不能完整地介紹整個(gè)嵌入式操作系統的開(kāi)發(fā)過(guò)程,將以第二節中提到的通過(guò)自舉模式下載的引導程序為例,說(shuō)明開(kāi)發(fā)的過(guò)程。對于像 Linux這樣的大系統,基本的開(kāi)發(fā)流程是一樣的。
引導程序將通過(guò)自舉模式下載到MX1的片內RAM,從地址0x00300000開(kāi)始并執行。完成串口和SDRAM的初始化后,引導程序將等待接收應用程序或操作系統內核,將接收到的數據放在SDRAM中。數據接收完畢后,引導程序將SDRAM中的數據寫(xiě)入Flash,下一次就可以從Flash中直接引導系統了。由于操作系統的內核比較大,如Linux有1 MB以上,下載過(guò)程必須考慮糾錯。因此,接收部分采用Xmode協(xié)議,可以用Windows下超級終端的Xmode發(fā)送方式發(fā)送文件。
(1)編寫(xiě)C、C++語(yǔ)言或匯編源程序
通常匯編源程序用于系統最基本的初始化,如初始化堆棧指針、設置頁(yè)表、操作ARM的協(xié)處理器等。初始化完成后就可以跳轉到C代碼執行。需要注意的是,GNU的匯編器遵循AT%26;amp;T的匯編語(yǔ)法,讀者可以從GNU的站點(diǎn)(www.gnu.org)上下載有關(guān)規范。匯編程序的缺省入口是 start標號,用戶(hù)也可以在連接腳本文件中用ENTRY標志指明其它入口點(diǎn)(見(jiàn)下文關(guān)于連接腳本的說(shuō)明)。
?。?)用gcc或g++生成目標文件
如果應用程序包括多個(gè)文件,就需要進(jìn)行分別編譯,最后用連接器連接起來(lái)。如筆者的引導程序包括3個(gè)文件:init.s(匯編代碼、初始化硬件) xmrecever.c(通信模塊,采用Xmode協(xié)議)和flash.c(Flash擦寫(xiě)模塊)。 分別用如下命令生成目標文件: arm-linux-gcc-c-O2-o init.o init.s arm-linux-gcc-c-O2-o xmrecever.o xmrecever.c arm-linux-gcc-c-O2-o flash.o flash.c 其中-c命令表示只生成目標代碼,不進(jìn)行連接;-o 命令指明目標文件的名稱(chēng);-O2表示采用二級優(yōu)化,采用優(yōu)化后可使生成的代碼更短,運行速度更快。如果項目包含很多文件,則需要編寫(xiě)makefile文件。關(guān)于makefile的內容,請感興趣的讀者參考相關(guān)資料。
?。?)編寫(xiě)連接腳本文件
gcc等編譯器內置有缺省的連接腳本。如果采用缺省腳本,則生成的目標代碼需要操作系統才能加載運行。為了能在嵌入式系統上直接運行,需要編寫(xiě)自己的連接腳本文件。編寫(xiě)連接腳本,首先要對目標文件的格式有一定了解。GNU編譯器生成的目標文件缺省為elf格式。elf文件由若干段(section)組成,如不特殊指明,由C源程序生成的目標代碼中包含如下段:.text(正文段)包含程序的指令代碼;.data(數據段)包含固定的數據,如常量、字符串;.bss(未初始化數據段)包含未初始化的變量、數組等。C++源程序生成的目標代碼中還包括.fini(析構函數代碼)和.init(構造函數代碼)等。有關(guān)elf文件格式,讀者可自行參考相關(guān)資料。連接器的任務(wù)就是將多個(gè)目標文件的.text、.data和.bss等段連接在一起,而連接腳本文件是告訴連接器從什么地址開(kāi)始放置這些段。例如筆者的引導程序連接文件link.lds為: ENTRY(begin) SECTION { 。=0x00300000; .text : { *(.text) } .data: { *(.data) } .bss: { *(.bss) } }
其中,ENTRY(begin)指明程序的入口點(diǎn)為begin標號;。=0x00300000指明目標代碼的起始地址為0x00300000,這一段地址為MX1的片內RAM;.text : { *(.text) }表示從0x00300000開(kāi)始放置所有目標文件的代碼段,隨后的.data: { *(.data) }表示數據段從代碼段的末尾開(kāi)始,再后是.bss段。
?。?)用連接器生成最終目標文件
有了連接腳本文件,如下命令可生成最終的目標文件: arm-linux-ld-nostadlib-o bootstrap.elf-T link.lds init.o xmrecever.o flash.o 其中,ostadlib表示不連接系統的運行庫,而是直接從begin入口;-o指明目標文件的名稱(chēng);-T指明采用的連接腳本文件;最后是需要連接的目標文件列表。
?。?)生成二進(jìn)制代碼
連接生成的elf文件還不能直接下載執行,通過(guò)objcopy工具可生成最終的二進(jìn)制文件: arm-linux-objcopy-O binary bootstrap.elf bootstrap.bin 其中-Obinary指定生成為二進(jìn)制格式文件。Objcopy還可以生成S格式的文件,只需將參數換成-O srec。如果想將生成的目標代碼反匯編,還可以用objdump工具: arm-linux-objdump-D bootstrap.elf 至此,所生成的目標文件就可以直接寫(xiě)入Flash中運行了。如果要通過(guò)自舉模式下載,還需要轉換為自舉模式的文件格式,相關(guān)轉換工具可以在摩托羅拉的網(wǎng)站上找到。
掌握了GNU工具后,開(kāi)發(fā)者就可以開(kāi)發(fā)或移植C或C++代碼的程序。用戶(hù)可以不需要操作系統,直接開(kāi)發(fā)簡(jiǎn)單應用程序。但對于更復雜的應用來(lái)說(shuō),操作系統必不可少。目前流行的源代碼公開(kāi)的操作系統如Linux、μC/OS都可以用GNU工具編譯。ARM的Linux已有很多成熟的版本,可以支持 ARM720、ARM920、 ARM1020等多種處理器,讀者可從www.uclinux.org或www.armdevzone.com上獲取最新信息。Linux移植過(guò)程中和處理器相關(guān)的代碼都放在arch/arm目錄下。對于內核,用戶(hù)需要做的是設定自己系統的內存映像,RAM起始地址,I/O地址空間和虛擬I/O地址空間,參看arch/arm/mach-integrator/arch.c文件。除了內核外,用戶(hù)還需要為自己的系統編制各種各樣的驅動(dòng)程序。
4 調試工具
Linux下的GNU調試工具主要是gdb、gdbserver和kgdb。其中g(shù)db和gdbserver可完成對目標板上Linux下應用程序的遠程調試。gdbserver是一個(gè)很小的應用程序,運行于目標板上,可監控被調試進(jìn)程的運行,并通過(guò)串口與上位機上的gdb通信。開(kāi)發(fā)者可以通過(guò)上位機的gdb輸入命令,控制目標板上進(jìn)程的運行,查看內存和寄存器的內容。gdb5.1.1以后的版本加入了對ARM處理器的支持,在初始化時(shí)加入-target==arm參數可直接生成基于A(yíng)RM平臺的gdbserver。gdb工具可以從ftp://ftp.gnu.org/pub/gnu /gdb/上下載。
對于Linux內核的調試,可以采用kgdb工具,同樣需要通過(guò)串口與上位機上的gdb通信,對目標板的Linux內核進(jìn)行調試。由于篇幅所限,感興趣的讀者可以從http://oss.sgi.com/projects/kgdb/上了解具體的使用方法。
結束語(yǔ)
本文以一個(gè)具體的實(shí)例為例,對GNU工具中的常用功能作了介紹。其實(shí)GNU工具的功能還遠不止這些,更進(jìn)一步的操作有:針對不同處理器,不同算法的軟件優(yōu)化、高效的內嵌匯編、大型項目管理功能等。相信GNU能成為越來(lái)越多開(kāi)發(fā)人員的選擇。
評論