NuttX 是一個類 Linux 的即時作業系統,並被 PX4 所採用,以下是一些簡單的學習筆記。
以下做一些目前接觸以來的直觀感受:
優點:
缺點:
FreeRTOS 作為 Software library 提供了最基本的多工、行程間通訊、同步機制等功能,但 Nuttx 相對起來更像是一個如同 Linux 的現代作業系統。
sudo apt install build-essential git zlib1g-dev libsdl1.2-dev automake* autoconf* \
libtool libpixman-1-dev lib32gcc1 lib32ncurses5 libc6:i386 libncurses5:i386 \
libstdc++6:i386 libusb-1.0.0-dev ninja-build
git clone git://git.code.sf.net/p/openocd/code openocd
cd openocd
./bootstrap
./configure --prefix=/usr/local --enable-jlink --enable-amtjtagaccel --enable-buspirate \
--enable-stlink --disable-libftdi
echo -e "all:\ninstall:" > doc/Makefile
make -j4
sudo make install
wget https://developer.arm.com/-/media/Files/downloads/gnu-rm/9-2019q4/gcc-arm-none-eabi-9-2019-q4-major-x86_64-linux.tar.bz2
tar jxf ./gcc-arm-none-eabi-9-2019-q4-major-x86_64-linux.tar.bz2
rm gcc-arm-none-eabi-9-2019-q4-major-x86_64-linux.tar.bz2
git clone git://git.qemu.org/qemu.git
cd qemu
git submodule init
git submodule update --recursive
mkdir build
cd build
../configure
make -j $(nproc)
~/.bashrc
並在最尾端增加以下指令:PATH=$PATH:${YOUR_PATH}/qemu/build
PATH=$PATH:${YOUR_PATH}/gcc-arm-none-eabi-9-2019-q4-major/bin
NuttX 系統結構分為 Kernel 本體以及 App,可以使用以下指令分別取得:
mkdir nuttx-git
cd nuttx-git
git clone https://github.com/apache/nuttx.git nuttx
git clone https://gitbox.apache.org/repos/asf/nuttx-apps.git apps
接著使用以下指令編譯 NuttX:
cd nuttx
tools/configure.sh stm32f4discovery:nsh #nsh console/UART2
make menuconfig
make -j $(nproc)
最終可以使用 QEMU 並將 UART2 重導向至 stdio 進行模擬:
qemu-system-arm -M netduinoplus2 -kernel nuttx.bin -serial /dev/null -serial stdio
進入 NSH 後可以測試 ps
和 hello
指令得到:
nsh> ps
PID GROUP PRI POLICY TYPE NPX STATE EVENT SIGMASK STACK COMMAND
0 0 0 FIFO Kthread N-- Ready 0000000000000000 001016 Idle_Task
2 2 100 RR Task --- Running 0000000000000000 002016 nsh_main
nsh> hello
Hello, World!!
這裡針對一些我有興趣的功能進行測試以及紀錄。
NuttX 提供了許多範例程式碼,因此若要了解各個功能的使用,最快的方式就是瀏覽這些範例。這一點個人認為與 NuttX 抽象化程度高有關,因此許多功能並不直接依賴硬體平台。
術語:
一些常見週邊的使用:
先在 menuconfig 中進行以下設定:
System Type -> [*] SysTick timer driver
-> STM32 Peripheral Support -> [*] TIM2
Device Drivers -> Timer Driver Support -> [*] Timer Support
-> [*] Timer Arch Implementation
Application Configuration -> Examples -> [*] Timer example
開機載入 NSH 後執行:
nsh> timer
預設情況下開機後會載入 NSH, 若要執行使用者定義的應用程式則須透過 NSH 輸入指令後載入。實際上我們可以在 NuttX 的 menuconfig 中修改預設執行的程式。
見 How to start directly my application instead starting NSH?
RTOS Features -> Tasks and Scheduling -> (hello_main) Application entry point
理論上可以自行設計一初始化程式,除了建立 NSH 的 Task 外,再載入其他應用程式的 Task。(待實驗後補上)
官方資料:
範例程式:
menuconfig
:
Application Configuration -> Examples -> [*] NuttX STM32 Message Queue Example
[*] NuttX STM32 Semaphore Example
nsh > mqueue
nsh > semaphore
優先權繼承:
NuttX 支援優先權繼承,考慮以下情境:
像是本例中原本優先權最高的 Task A 卻必須反過來等待其他低優先權 Task (即 B 和 C) 的狀況我們稱為 "優先權反轉 (Priority Inversion)"。
要解決這樣的問題可以使用所謂"優先權繼承"的機制,一般教科書上有兩種做法:
根據文件描述,NuttX 應該是實做了 Priority Inheritance Protocol (PIP) 的機制,但也允許開發者自行設計所想要的繼承機制。
官方資料:
範例程式:
menuconfig
:
Application Configuration -> Examples -> [*] NuttX Work Queue Example
nsh> work_queue
用於將某些任務卸載 (off-loading) 到多個 Threads 上,可分為:
參考但步驟有異:
使用 NUCLEO-746ZG 進行實驗:
cd nuttx-stm32/nuttx/
tools/configure.sh nucleo-144:f746-nsh
make menuconfig
menuconfig
須要進行以下設定 (有些選項有相依性,請依照順序設定):
RTOS Features -> Work queue support -> [*] High priority (kernel) worker thread
Networking Support -> Networking Support -> [*] Networking support
-> Link layer support -> [*] Local loopback
-> TCP/IP Networking -> [*] Enable TCP/IP write buffering
-> UDP Networking -> [*] UDP Networking
-> [*] UDP broadcast Rx support
-> ICMP Networking Support -> [*] Enable ICMP networking
-> ARP Configuration -> [*] ARP send
-> [*] Collect network statistics
Application Configuration -> Network Utilities -> [*] Telnet daemon
-> Network initialization -> [*] Network initialization thread
-> [*] Hardware has no MAC address
-> NSH Library -> [*] Have architecture-specific initialization
Device Drivers -> [*] Network Device/PHY Support
STM32 以及 路由器的 IP 位址以及 遮罩可由 menuconfig
進行修改:
Application Configuration -> Network Utilities -> Network initialization -> IP Address Configuration -> (0x0a000002) Target IPv4 address
-> (0x0a000001) Router IPv4 address
其中 0x0a000002
將 STM32 的 IP 位址設定為 10.0.0.2
,而 0x0a000001
將路由器的 IP 位址設定為 10.0.0.1
。
接著進行編譯以及燒錄韌體:
make -j16
cd ..
./flash.sh
將 STM32 透過 RJ45 線材接到 Linux 主機 (在此用作路由器) 後進行以下設定:
在 Linux 主機上輸入:
ping 10.0.0.2
即可得到如下回應:
64 bytes from 10.0.02: icmp_seq=0 ttl=64 time=0.060 ms
64 bytes from 10.0.02: icmp_seq=1 ttl=64 time=0.053 ms
64 bytes from 10.0.02: icmp_seq=2 ttl=64 time=0.072 ms
...