這應該是裸機程式的最後一篇了,回顧一下第一篇 所說,到現代幾個進化的點,
- Rustup
- cargo config
- rlibc
- 用 library
前三點我們已經看過了
- 用 rustup 來安裝 target,雖然我後來想了一下,覺得這並不是 rust 勝過 C 而是 LLVM 勝過 gcc,rust 只是站在 LLVM 的肩膀上所以看得更遠而已。
- cargo config 就如前文所述,利用 .cargo/config 來設定編譯目標,以及連結時的 linker script ,只要套用不同的 config 就能對不同目標編譯。
- rlibc 可能比較隱晦一點,但 rlibc 要提供的 memset/memcpy/memmove ,在 core::ptr 裡面已經提供了核心的實作了,所以不需要再引入 rlibc。
最後就是用 library 啦,在前面的篇章都沒提到這個,為什麼?
答案是,library 已經把我們前幾篇做的事情都做完了,如果是我們這次的目標, rust 有個小團隊 cortex-m-team 在維護 cortex-m 函式庫 :
- reset_handler?library 已經寫好了,自己定義 main 吧。
- 要存取週邊?library 幫你封裝成 rust struct,呼叫對應的函式就好。
- cortex 的 semihosting?cortex-m 裡的 cortex-m-semihosting 可以處理這些。
所以說用了 library 之後前幾篇根本都不用寫了,最誇張的還有什麼,cortex-m team 還提供了 quickstart repository ,用 之前提過的 cargo generate 把專案複製下來,改一改就可以用了,超級方便。
我們這邊就來給來 quickstart 一下:
cargo generate --git https://github.com/rust-embedded/cortex-m-quickstart
專案裡面,只需要以下幾個步驟:
- 取得模板,如上面用 cargo generate 下載。
- 修改
.cargo/config
裡面的編譯目標,我們跟之前一樣用 cortex_m3 的 thumbv7m-none-eabi。 - 編輯 memory.x 裡面 flash 和 RAM 的起始位址和長度。
cargo build
基本設定完成。
修改
看一下它的 Cargo.toml,大致上就是需要下列相依:
cortex-m = "0.6.0"
cortex-m-rt = "0.6.10"
cortex-m-semihosting = "0.3.3"
panic-halt = "0.2.0"
cortex-m 是硬體虛擬化型別;cortex-m-rt 提供 runtime,也就是我們前幾篇做的:預設的 exception handler 等等;cortex-m-semihosting 則是 semihosting;panic-halt 提供 panic_handler。
把我們的 main 修改成這樣:
use cortex_m_semihosting::hprintln;
#[entry]
fn main() -> ! {
hprintln!("Hello World");
loop {}
}
跟前面一樣用 qemu 進去 debug,qemu 的參數記得加上 -semihosting
,用 gdb 進去就能看到 “Hello World” 真的被印出來了:
顯而易見,用上 cortex-m library 開發嵌入式程式的功夫簡化不少,目前來說最大的問題反而是 cortex-m 幾乎沒什麼文件跟使用案例 (cortex-m 裡面只有些許非常簡單的範例),想要使用也不知道怎麼用 Orz。
我想這系列文差不多就告一段落,這系統展現了 rust 的確有能力撰寫嵌入式系統的程式,雖然麻煩得多但不是不可能, 與四年前相比核心的支援也更加完善,我想就現實來看 rust 要能跟 C 競爭還需要一番功夫才行, 但 rust 也的確展現了如此的潛力,且讓我們期待未來的發展。