本文對 uClinux 在 S3C4510B 嵌入式開發板上的移植、網路服務設定、驅動以及使用者程式開發等問題進行了較為詳細的介紹。本文中所總結的經驗可供在上述嵌入式環境下工作的開發人員借鑑,也能為其他嵌入式平臺的開發提供一定的啟發。
嵌入式系統的特點在於:針對特定的應用,使用“量體裁衣”的方式將所需的功能嵌入到各種應用系統當中。其早期主要應用於軍事及航空、航天領域,之後逐步被工業控制、汽車電子、通訊和消費電子領域廣泛使用。與此同時,各種新興的應用也對嵌入式系統的處理能力、通訊能力等方面提出了更高的要求。
S3C4510B 正是 Samsung 公司針對新興網路應用而開發的一款價效比很高的 ARM 核 16/32 位RISC 微處理器。它內含一個由 ARM 公司設計的高效能、低功耗的 ARM7TDMI 核心,內建 10/100M Ethernet 控制器、HDLC 控制器等一系列網路通訊控制器,特別適合對價格和功耗均比較敏感的嵌入式網路應用。
uClinux 是針對通訊和控制領域的嵌入式作業系統,其主要的優勢在於開放原始碼、穩定、強大的網路通訊功能以及其精簡性。其核心功能與 Linux 基本相同,只是對記憶體管理和程式管理進行了改寫,主要應用於沒有 MMU 的微處理器平臺,如 S3C4510B 處理器。
本文根據筆者在實際工作中積累的經驗,對 uClinux 在S3C4510B上的移植和開發進行了較為詳細的介紹。本文首先介紹瞭如何將 uClinux 移植到 S3C4510B 嵌入式平臺,接下來將分別對 uClinux 在 S3C4510B 平臺上的各種網路服務設定、驅動及使用者程式開發進行詳細的論述。
移植 uClinux 至 S3C4510B 平臺
Uclinux 的核心現已發展至 2.6 版本。然而,考慮到嵌入式應用大多針對特定需求,開發者往往更關注諸如能耗、空間佔用、開發速度、向後相容等問題,因而在實際開發中 2.4 和 2.6 版本的 uClinux 都會根據專案情況被採用。根據筆者在實際工作中的經驗,本文下面將對 2.4 和 2.6 版本的 uClinux 移植到 S3C4510B 嵌入式平臺的方法分別進行介紹。
UClinux 2.4 的移植
Step 1:下載核心壓縮包和交叉編譯工具包
筆者使用的是分別是:uClinux-dist-20030522.tar.gz 和 arm-elf-tools-20030314.sh。將核心壓縮包解壓至開發主機,然後安裝 arm-linux 交叉編譯工具,為後續的編譯做好準備。
Step 2:修改 Makefile 檔案
- ../linux-2.4.x/Makefile
在# normal make targets下面的Section加入:
.PHONY: images
images:
$(MAKE) -C $(VENDDIR) images
all: subdirs romfs modules modules_install image |
上述語句將在make image的時候生成壓縮的映像檔案(image)。
- ../linux-2.4.x/vendors/Samsung/4510B/Makefile
首先,找到語句
genromfs -v -V "ROMdisk" -f $(ROMFSIMG) -d $(ROMFSDIR) |
在其下方加入:
arm-elf-ld -r -o $(ROOTDIR)/$(LINUXDIR)/romfs.o /
-b binary $(ROMFSIMG) |
此語句的作用是產生 romfs.o 供後續生成整體的映像檔案使用。
同時,在此 Makefile 的最後加入:
images:
arm-elf-objcopy -O binary -R .note -R .comment -S $(ROOTDIR)/$(LINUXDIR)/linux /
$(IMAGEDIR)/uclinux_ram.bin
cp $(ROOTDIR)/$(LINUXDIR)/arch/armnommu/boot/zImage /
$(IMAGEDIR)/uclinux_rom.bin |
上述語句將在交叉編譯的最後將所有生成的目標檔案 Dump 到可以下載到開發板中的映像檔案中(注意,這裡提供兩種形式:ROM 啟動的 uclinux_rom.bin 和 RAM 啟動的 uclinux_ram.bin)
- ../linux-2.4.x /arch/armnommu/boot/compressed/Makefile
將此檔案中的下列語句:
$(LD) $(ZLDFLAGS) $(HEAD) $(OBJS) piggy.o -o $(LINUX) |
修改為:
$(LD) $(ZLDFLAGS) $(HEAD) $(OBJS) piggy.o $(LIBGCC) -o $(LINUX) |
此語句將 arm-elf-tools 庫的 libgcc 連結入 uClinux 的 kernel 中。
Step 3:配置作業系統地址轉換
由於 S3C4510B 不含 MMU (Memory Management Unit), 因此其實體地址和虛擬地址是相同的。在../linux-2.4.x/include/asm/mmu.h 中需要加入下列程式碼以完成此轉換:
#define __virt_to_phys__is_a_macro
#define __virt_to_phys(vpage) ((unsigned long) (vpage))
#define __phys_to_virt__is_a_macro
#define __phys_to_virt(ppage) ((void *) (ppage))
#define __virt_to_bus__is_a_macro
#define __virt_to_bus(vpage) ((unsigned long) (vpage))
#define __bus_to_virt__is_a_macro
#define __bus_to_virt(ppage) ((void *) (ppage)) |
Step 4:配置romfs
由於uClinux預設使用romfs作為根檔案系統,因此需要對此進行配置:
- ../linux-2.4.x/arch/armnommu/vmlinux-armv.lds.in
在*(.got) /* Global offset table */語句後加入下列語句,從而將上文Step 2 (2) 中生成的romfs.o包含到.text段中:
romfs_data = .;
romfs.o
romfs_data_end = .; |
- ../linux-2.4.x /drivers/block/blkmem.c
此檔案是romfs的配置檔案,找到“arena[] = {”語句,在其花括號中加入下列3行:
#ifdef CONFIG_ARCH_SAMSUNG
{0, romfs_data, -1},
#endif |
Step 5:其他修改
- ../linux-2.4.x/arch/armnommu/mm/proc-arm6,7.S
此檔案中有一個錯誤語句:
mov r0, #0
#ifdef CONFIG_CPU_WITH_CACHE |
將其修改為:
#ifdef CONFIG_CPU_WITH_CACHE
mov r0, #0 |
至此,經過修改的uClinux核心已經可以在開發主機上使用make dep, make lib_only, make user_only, make romfs, make image和make 生成 uclinux_rom.bin 和uclinux_ram.bin 映像檔案 並在開發板上順利執行至uClinux歡迎介面和等待命令輸入提示符。需要注意的是, uclinux_ram.bin是壓縮過的核心映像,可以使用偵錯程式直接燒寫至SDRAM中執行,而 uclinux_rom.bin是沒有壓縮過的核心映像,需要燒寫入FLASH中執行。因此,在除錯過程中,一般將待除錯的核心映像uclinux_ram.bin通過偵錯程式或Bootlaoder(Redboot等)寫入SDRAM中進行除錯,待完全除錯好之後再將最終uclinux_rom.bin版本燒寫入FLASH中固化。
uClinux 2.6的移植
Step 1:下載核心壓縮包和交叉編譯工具包
對於2.6核心的uClinux,需要下載uClinux-dist-20051014.tar.gz核心壓縮包以及Linux 2.6版本核心及補丁:linux-2.6.9.tar.bz2、linux-2.6.9-hsc0.patch.gz;同時還需要下載交叉編譯工具並進行安裝:arm-uclinux-tools-base-gcc3.4.0-20040713.sh。
Step 2:為uClinux更新標準Linux 2.6版本核心
執行下列命令將uClinux-dist-20051014的核心替換為標準Linux的2.6核心:
tar -zxvf uClinux-dist-20051014.tar.gz
cp linux-2.6.9.tar.bz2 uCliux-dist
cd uClinux-dist
tar –xjvf linux-2.6.9.tar.bz2
gzip -dc ../linux-2.6.9-hsc0.patch.gz | patch -p0
rm -rf linux-2.6.x
mv linux-2.6.9 linux-2.6.x |
Step 3:配置核心中S3C4510B的引數設定
由於Step 2 中的補丁並沒有包括專門針對S3C4510B的核心配置,因此我們只能使用現有的 espd-4510的核心配置檔案:
$cp ../linux-2.6.x/arch/armnommu/configs/espd_4510b_defconfig /
../linux-2.6.x/vendor/Samsung/4510B/config.linux-2.6.x |
同時,由於沒有特別針對Linux 2.6核心的S3C4510B vendor配置檔案因此只能使用現有2.4版本的vendor配置檔案:
$cp ../linux-2.6.x/vendor/Samsung/4510B/config.vendor-2.4.x /
../linux-2.6.x/vendor/Samsung/4510B/config.vendor-2.6.x |
Step 4:修改Makefile檔案
- ../linux-2.6.x/vendor/Samsung/4510B/Makefile
由於現有的Makefile當中並沒有包含編譯可燒寫至嵌入式儲存系統中的映像檔案的相應命令,因此需要在此Makefile中新增:
image:
[ -d $(IMAGEDIR) ] || mkdir -p $(IMAGEDIR)
genromfs -v -V "ROMdisk" -f $(ROMFSIMG) -d $(ROMFSDIR)
arm-uclinux-ld -r -o $(ROOTDIR)/$(LINUXDIR)/romfs.o -b binary $(ROMFSIMG)
$(CROSS_COMPILE)objcopy -O binary --remove-section=.romvec /
--remove-section=.text --remove-section=.ramvec /
--remove-section=.init /
--remove-section=.bss --remove-section=.eram
$(ROOTDIR)/$(LINUXDIR)/linux $(IMAGEDIR)/linux.data |
Step 5:配置romfs
與移植uClinux 2.4核心相同,需要修改下列檔案以對romfs進行支援:
- ../linux-2.6.x/arch/armnommu/vmlinux-armv.lds.in
在*(.got) /* Global offset table */語句後加入下列語句,從而romfs.o包含到.text段中。
romfs_data = .;
romfs.o
romfs_data_end = .; |
- ../linux-2.6.x/arch/armnommu/kernel/setup.c
新增變數romfs_start和romfs_end:
extern int _stext, _text, _etext, _edata, _end;
extern int romfs_start,romfs_end; |
同時,對核心啟動時的default command line進行設定:
char *from = default_command_line;
sprintf(default_command_line, "root=/dev/ram0 initrd=0x%08lx", /
(unsigned long)&romfs_start); |
至此,uClinux 2.6版本的核心移植完成,可以通過make dep, make lib_only, make user_only, make romfs, make image和make編譯出可以燒寫入SDRAM或FLASH的核心映像檔案。
S3C4510B嵌入式平臺主要網路服務配置
在嵌入式開發和除錯中,最常用的網路服務是使用Telnet登入嵌入式開發板以及使用NFS檔案系統。本文下面將根據筆者的實際經驗對uClinux 2.4環境下這些網路服務的配置進行說明。
uClinux中Telnet伺服器的配置
由於Telnet使用串列埠建立終端進行通訊,因此為了在嵌入式開發板上架設Telnet伺服器,首先需要修改uClinux的核心配置。具體來說,需要在make menuconfig時進行下列配置:
- 在Character devices選項頁中,將Virtual Terminal和Support for console on virtual terminal兩個選項都選中,從而在核心中新增對Telnet所需的虛擬終端的支援。
- Network Application選項頁中,需要將telnet,à在Custom Vendor/Product telnetd以及inetd都選中
- Tinylogin選項頁中,需要將login, Passwd和getty都選中。à在Custom Vendor/Product
- Miscellaneousà在Custom Vendor/Product Application選項頁中,確認Login沒有被選中,因為我們希望使用Tinylogin提供的Login功能。
由於在上面的核心配置中選擇了對虛擬終端的支援,因此需要手動在../linux-2.4-x/.config中加入CONFIG_DUMMY_KEYB=y,否則編譯時會出現編譯錯誤。
同時,由於uClinux核心自身的問題,按照上述方式配置的Telnet在連線時總是出現login使用者名稱和密碼驗證錯誤的問題。此問題可以通過修改../linux-2.4-x/user/tinylogin/login.c,省略Telnet連線時的使用者名稱/密碼驗證來解決:
extern int login_main(int argc, char **argv)
{
char name[32];
char tty[BUFSIZ];
…
#ifdef TLG_FEATURE_SHADOWPASSWDS
struct spwd *spwd = NULL;
#endif /* TLG_FEATURE_SHADOWPASSWDS */
char **envp = environ;
initenv();
/* bypass username/password auth process. Directly start Sash Shell */
shell("/bin/sh", (char *) 0); /* exec the shell finally. */
return (0);
} |
重新編譯按照上述方法進行修改的核心並將編譯好的映像檔案燒寫入FLASH,系統上電啟動後完成後執行:
開發人員便可以通過Telnet訪問嵌入式開發板了。
uClinux中NFS的配置
為了在S3C4510B嵌入式開發板上使用NFS檔案系統,需要按照如下的方法修改uClinux核心:執行核心配置程式make menuconfig,選中Customized Kernel Settings和Customized Vendor/User Settings,然後在接下來的配置中按照如下方式進行設定:
- File systems選項頁中àCustomized Kernel Settings
選中NFS file system support和Provide NFSv3 client support。
- Network Application選項頁中àCustomized Vendor/User Settings
選中portmap,提供埠對映功能。
- BusyBox選項頁中àCustomized Vendor/User Settings
選中mount和mount: support NFS mounts。
完成上述配置之後,重新編譯核心,新核心便可提供NFS Server和Client的支援。在通常情況下, 一般會使用S3C4510B嵌入式開發板作為NFS Client,而開發主機作為NFS Server。例如,開發主機(192.168.0.8)通過配置/etc/exports檔案已將/nfs_mount目錄設定為NFS目錄,則在 S3C4510B嵌入式開發板即可通過下列命令將開發主機的NFS目錄掛載至本地/var目錄:
/bin > portmap &
/bin > mount 192.168.0.8:/nfs_mount /var |
S3C4510B嵌入式平臺驅動程式開發
uClinux目前並不支援使用insmod動態載入驅動程式,因此S3C4510B嵌入式平臺上的驅動程式都需要編譯進入核心在啟動時自動載入。下面分別對S3C4510B嵌入式平臺上的字元驅動程式和網路驅動程式的開發進行一些論述。
uClinux中字元驅動程式的開發
開發人員在編寫完所需的字元型別驅動程式之後,需要將驅動程式原始碼置於
../linux-2.4.x/driver/char目錄下,同時修改同級目錄下的Makefile。例如,筆者完成了一個S3C4510B的LCD驅動程式(lcd.c),則需要將lcd.c置於../linux-2.4.x/driver/char目錄,同時在../linux-2.4.x/driver/char/Makefile中加入:
同時,為了能夠在uClinux啟動時自動初始化此裝置,還需要修改../linux-2.4.x/driver/char/mem.c檔案,在其中加入:
- 新新增的字元驅動程式初始化函式宣告。例如:
extern void lcd_init(void); |
- 在字元裝置統一初始化函式int __init chr_dev_init(void)中呼叫新裝置的初始化函式。例如上面提到的LCD的例子,需要在函式int __init chr_dev_init(void)中加入語句:
函式int __init chr_dev_init(void)中,字元裝置的初始化函式將統一被呼叫,並完成各個字元驅動file_operations資料結構的註冊。初始化好之後就可以使用這些字元裝置了。
uClinux中網路驅動程式的開發
在uClinux中加入網路驅動裝置比較簡單,只需將編寫好的網路驅動程式置於../linux-2.4.x/driver/net,再修改../linux-2.4.x/driver/net/Makefile即可。例如,筆者編寫了HDLC的驅動程式
s3c4510b_hdlc.c,只需將其置於../linux-2.4.x/driver/net目錄下,然後修改../linux-2.4.x/driver/net/Makefile,在其中加入:
uClinux執行make時會根據此Makefile產生新的驅動模組,並將其連結入net.o,系統啟動時將會統一載入所有的網路驅動程式。
uClinux特殊網路裝置驅動程式開發
由於嵌入式應用中往往會對網路應用有特別的需求,因而需要在嵌入式開發板中支援一些不太常用的網路裝置(如HDLC裝置等)。對此感興趣的讀者可以參考筆者的另外一篇論文《基於 S3C4510B 和 uClinux 的 HDLC 介面的設計與實現》。
S3C4510B嵌入式平臺使用者空間程式開發
除了驅動程式,開發人員還需要在S3C4510B嵌入式平臺上進行使用者空間應用程式的開發,具體步驟如下:
Step 1:在../linux-2.4.x/user目錄中為應用程式建立相應目錄樹結構
例如,筆者在開發中需要開發一個在S3C4510B自帶的LCD上顯示訊息的使用者程式(write_lcd.c),則首先需要在../linux-2.4.x/user下新建一個目錄(如lcd),然後將應用程式置於此目錄下
(../linux-2.4.x/user/lcd/write_lcd.c)。
Step 2:修改2級Makefile檔案
- ../linux-2.4.x/user/Makefile
由於在../linux-2.4.x/user目錄下加入了新的目錄樹結構,因此需要修改../linux-2.4.x/user/Makefile檔案,使執行make時能夠進入新目錄進行編譯。在上面的例子中,需要在
../linux-2.4.x/user/Makefile中加入:
以指示系統在編譯時進入lcd目錄。
- ../linux-2.4.x/user/lcd/Makefile
新建目錄中必須提供一個Makefile供make時使用,下面是筆者在實際開發中使用的此處Makefile模板,不同的應用程式只需更改模板中的EXEC和OBJS巨集即可:
EXEC = write_lcd
OBJS = write_lcd.o
CFLAGS += -I.
all: $(EXEC)
$(EXEC): $(OBJS)
$(CC) $(LDFLAGS) -o $@ $(OBJS) $(LDLIBS)
romfs:
$(ROMFSINST) /bin/$(EXEC)
clean:
-rm -f $(EXEC) *.elf *.gdb *.o |
完成上述步驟之後,執行make user_only編譯命令時,將會對../linux-2.4.x/user/目錄下的使用者程式進行編譯,並將編譯出的可執行檔案置於romfs中的bin目錄下。
小結
本文根據筆者在實際工作中的經驗,對uClinux在S3C4510B嵌入式平臺上的移植和開發等主題進行了總結。本文給出了移植uClinux 2.4和2.6版本至S3C4510B平臺、配置常用網路服務、各種驅動程式以及使用者程式開發的詳細步驟,相信能夠幫助進行類似工作的開發人員加快開發速度;同時,也能夠對開發其他型別的嵌入式系統提供有益的借鑑。
參考資料