天天看點

Nuttx romfs與啟動腳本rcS1. romfs簡介2. 制作romfs3. Nuttx系統romfs實驗4. 制作啟動腳本rcS5. APM中生成romfs6. 啟動腳本rcS文法介紹

ARM系統上電後,系統将flash位址映射到零位址處,處理器從零位址處開始運作第一條指令。而在零位址處,一般是系統複位中斷向量,此處存放的是一條跳轉指指令,通過該條換指令,處理器運作複位服務函數。Cortext-M系列的實作稍有不同,它的零位址處放置的棧指針,接着才是複服務函數位址。

複位服務函數開始處是由彙編語言編寫,借助彙編函數調用C函數,将系統帶到C函數中。Nuttx的C入口函數便是os_start(),在os_start()執行作業系統地初始化。

Nuttx作業系統初始化完成後,首先建立第一個程序,idle線程便是這第一個程序的線程。有了第一個程序,系統接着建立使用者程序,即在os_bringup()函數中,調用os_do_appstart()建立init程序。init程序的第一個預設線程是nsh_main。nsh_main調用nsh_consolemain()函數,先運作romfs檔案系統上的啟動腳本,然後nsh_main線程進入nsh互動界面。至此,作業系統完全啟動。其他程序,或者線程可以通過啟動腳本啟動運作,或者通過nsh啟動。。

在預設情形下,nsh_main線程将運作romfs中“/etc/init.d/rcS”這個啟動腳本上的指令。romfs啟動腳本有它自己的文法,Nuttx中的sh可以解析這些文法,并執行相關的指令。這些指令可以設定程序環境變量,讀取環境變量,或者啟動其他線程。

這一節将說明Nuttx制作romfs的方法,接着說明如何定制romfs,即讓Nuttx在啟動的過程中能夠運作我們制作的啟動腳本。最後我們對啟動腳本的文法進行簡要說明。

1. romfs簡介

romfs,顧名思義是在rom中實作的一種檔案系統,linux 2.1.21版本中最早支援該檔案系統。這種檔案系統是基于塊存儲媒體的隻讀檔案系統,具有體積小、讀取速度快、可靠性高的優點。它的缺點是,這種檔案系統是隻讀的,而且對檔案的描述資訊很少,沒有通路權限限制。

romfs通常用在嵌入式系統中,用來存儲系統子產品,隻讀的檔案等,将其部署在EEPROM或者其他塊儲存設備中。

更多關于romfs檔案系統的子產品結構和協定,以及驅動程式,可以在網上搜尋。

2. 制作romfs

在linux系統下使用“genromfs”工具來生成romfs映像檔案,該映像檔案可以被挂載到系統中作為隻讀檔案系統。

2.1 生成romfs

這裡在linux系統下示範一下生成并挂載romfs檔案系統。首先主機上建立我們所要生成romfs的目錄和檔案,比如目錄romfs_dir。通過”tree”指令檢視該目錄結構

$ tree romfs_dir
romfs
|——dir_a
|    |——a.txt   
|——dir_b
     |——b.txt

 directories,  file
           

寫入字元串”this is a.txt”到a.txt檔案,寫入字元串”this is b.txt”到b.txt檔案。

使用”genromfs”指令将romfs_dir檔案夾制作成一個romfs映像,并挂載到系統”/mnt”。”genromfs”指令使用方法可以通過”genromfs -h”檢視。在romfs_dir所在目錄下執行指令:

$ genromfs –f romfs.img –d romfs_dir –v –V “romfstest”
           

上面genromfs指令工具以檔案夾romfs_dir為輸入,生成romfs.img二進制映像檔案,映像卷名為”romfstest”。”-v”參數使得”genromfs”輸出詳細資訊。

我們通過”mount”指令将生成的romfs挂載到系統”/mnt”下,并檢視其内容是否符合預期。

$ sudo mount romfs.img  /mnt –o loop
mount: warning: /mnt seems to be mounted read-only
$ tree /mnt
/mnt
|——dir_a
|    |——a.txt   
|——dir_b
     |——b.txt

 directories,  file
           

2.2 制作romfs頭檔案

Nuttx系統中注冊romfs檔案系統,需要romfs檔案系統頭指針,以便将檔案系統編譯連結進可執行檔案。是以,需要将romfs映像檔案生成一個頭檔案。

Linux系統下的”xxd”工具指令能夠将标準輸入或者給定的二進制檔案輸出到檔案或者标準輸出。關于”xxd”工具指令的詳細使用方法可以通過”xxd -h”或者”man xxd”指令檢視。如果使用”-i”參數,”xxd”可以輸出C語言頭檔案形式的檔案。

我們以上節中生成的romfs映像檔案制作一個C語言頭檔案,頭檔案名為nsh_romfsimg.h, 而頭檔案nsh_romfsimg.h中的數組名為romfs_img。指令如下:

生成的頭檔案中數組名稱由輸入的romfs映像檔案名決定,如果檔案名中含有”.”分隔符,那麼”.”将會被替換為”_”。

3. Nuttx系統romfs實驗

Nuttx系統在nsh_main線程中,通過調用nsh_initialize()函數,進而調用nsh_romfsetc()向系統注冊romfs,并挂載到預設的位置”/etc”目錄。注意,nsh_romfsetc()需要配置CONFIG_NSH_ROMFSETC,該選項可以在”make menuconfig”界面中配置,或者直接在.config檔案中手動配置。另外,也需要系統支援ROMFS,即CONFIG_FS_ROMFS檔案系統選項需要啟用。

我們需要做的是生成romfs頭檔案,将其放置到系統的某個位置(預設位置為apps/nshlib目錄,如果配置了CONFIG_NSH_ARCHROMFS,則放置到config/< board >/inlclude目錄),重新編譯系統。将生成的romfs頭檔案nsh_romfsimg.h放置到apps/nshlib目錄,確定頭檔案nsh_romfsimg.h中數組名為”romfs_img”,重新編譯下載下傳系統,并運作。通過nsh終端驗證如下:

NuttxShell (NSH)
nsh> ls
/:
 dev/
 etc/
nsh> cd /etc
nsh> ls
/etc:
 .
 dir_a/
 dir_b/
nsh> cd dir_a
/etc/dir_a:
 .
 a.txt
nsh> cat a.txt
this is a.txt
nsh> 
           

4. 制作啟動腳本rcS

通過前面兩節,我們已經可以定制romfs檔案系統的内容了,那麼制作Nuttx系統下的啟動腳本init.d/rcS就易如反掌。

我們重起爐竈,另外建立一個檔案夾top_dir,在該檔案夾下建立init.d檔案夾,再在init.d檔案夾下建立檔案rcS,目錄結構如下:

$ tree top_dir
top_dir
|——init.d
     |——rcS

 directory,  file
           

作為練習,我們現在rcS檔案中寫入簡單地指令,隻是挂載proc檔案系統而已。注意:rcS檔案中以”#”字元開頭的行将被sh解析為注釋行。我們的rcS檔案的内容如下:

$ cat top_dir/init.d/rcS
# this is rcS file

mount –t procfs /proc
           

要挂載proc檔案系統,Nuttx系統需要啟用CONFIG_FS_PROCFS檔案系統選項。

按照上述兩節中的步驟,将top_dir目錄制作為一個romfs.img映像檔案,然後再生成nsh_romfsimg.h的頭檔案,将這個頭檔案放置到apps/nshlib路徑下,編譯下載下傳并運作,nsh終端輸出如下:

NuttxShell (NSH)
nsh> ls
/:
 Dev/
 etc/
 proc/
nsh> cat /etc/init.d/rcS
# this is rcS file

mount –t procfs /proc

nsh> 
           

從nsh終端的輸出可以看出,Nuttx運作了/etc/init.d/rcS啟動腳本,将proc檔案系統成功地挂載到系統。最後也列印出了/etc/init.d/rcS的内容,全都符合我們的期望。

5. APM中生成romfs

啟動APM飛控,從nsh終端檢視它的”/etc”目錄,可以看到下面有四個目錄,分别為bootloader, px4io, tones 和init.d。bootloader用于更新飛控的bootloader。px4io是飛控上面io控制器的固件,該固件在飛控啟動後從主要(stm32f427)下載下傳到io控制器(stm32f103)。tones是控制蜂鳴器發出報警聲的源檔案。最後一個init.d檔案夾下面便是APM的啟動腳本rcS。

沒有經過編譯的APM的固件代碼,用于生成romfs的檔案夾隻包含tones和init.d兩個子檔案夾,并沒有bootloader和px4io,這兩個檔案夾是在編譯時候生成的。這點可以從makefile檔案中找到,以px4-io-v2為例。

px4-io-v2: $(PX4_ROOT)/Archives/px4io-v2.export
    $(v)+ $(MAKE) -C $(PX4_ROOT) -f $(PX4_ROOT)/Makefile.make px4io-v2_default EXTRADEFINES="-DARDUPILOT_BUILD"
    $(v) cp $(PX4_ROOT)/Images/px4io-v2_default.bin px4io-v2.bin
    $(v) cp $(PX4_ROOT)/Build/px4io-v2_default.build/firmware.elf px4io-v2.elf
    $(v) mkdir -p $(MK_DIR)/PX4/ROMFS/px4io/
    $(v) cp px4io-v2.bin $(MK_DIR)/PX4/ROMFS/px4io/px4io.bin
    $(v) mkdir -p $(MK_DIR)/PX4/ROMFS/bootloader/
       $(v) cp $(SKETCHBOOK)/mk/PX4/bootloader/px4fmuv2_bl.bin $(MK_DIR)/PX4/ROMFS/bootloader/fmu_bl.bin
    $(v) echo "PX4IOv2 Firmware is in px4io-v2.bin"
           

這段Makefile将生成的px4io-v2.bin檔案複制到PX4/ROMFS/px4io/px4io.bin,将PX4/bootloader/px4fmuv2_bl.bin複制到PX4/ROMFS/bootloader/fmu_bl.bin。

制作APM romfs的檔案準備好之後,将PX4/ROMFS檔案夾制作成romfs.img二進制映像檔案。

# Generate the ROMFS image from the root
$(ROMFS_IMG): $(ROMFS_SCRATCH) $(ROMFS_DEPS) $(GLOBAL_DEPS)
    @$(ECHO) "ROMFS:   $@"
    $(Q) $(GENROMFS) -f $@ -d $(ROMFS_SCRATCH) -V "NSHInitVol"
           

然後将romfs.img轉換成romfs.o檔案

# Turn the ROMFS image into an object file
$(ROMFS_OBJ): $(ROMFS_IMG) $(GLOBAL_DEPS)
    $(call BIN_TO_OBJ,$<,$@,romfs_img)
           

最後romfs.o會被連結進可執行檔案,生成固件firmware.px4。

6. 啟動腳本rcS文法介紹

rcS啟動腳本實質上是Nuttx下實作的類似于linux中shell腳本的一種腳本檔案,Nuttx中sh可以解釋這種腳本的文法和執行指令。本小節簡要說明Nuttx中支援的指令和基本文法。

6.1 基本指令

在Nuttx的腳本中可以運作指令,指令還可以帶參數。腳本支援的指令是sh所支援的指令,可以在nsh終端輸入help指令檢視sh支援的指令。

nsh> help
help usage: help [-v] [<cmd>]
[               dirname     help            mh      set     unset
?               dd          hexdump     mount   sh      usleep
basename    df          kill            mv      sleep   xd
break       echo            ls          mw      test
cat         exec            mb          ps      time
cd          exit            mkdir       pwd     true
cp          false           mkfatfs     rm      uname
cmp         free            mkrd        rmdir   umount
           

指令”help -v”可以檢視所有指令詳細使用格式, 檢視單個指令的詳細使用格式,使用指令”help –v [< cmd >]”,比如檢視指令”ls”的詳細使用格式為”help –v ls”。

6.2 文法

6.2.1 if…else…

if… else… 條件語句的格式如下:

if  <cmd>
then
        [sequence of <cmd>]
else
        [sequence of <cmd>]
fi
           

如果< cmd >結果為true,則執行then分支,否則,執行else分支。

6.2.2 while循環

while循環格式如下:

while  <cmd>
do
        [sequence of <cmd>]
done
           

如果條件< cmd >傳回為true,将循環執行do…done之間語句。

6.2.3 until循環

until循環格式如下:

until  <cmd>
do
        [sequence of <cmd>]
done
           

如果條件< cmd >不為真,将循環執行do…done之間的語句。這點與while循環正好相反。