ubuntu安装 rust nightly_Rust 嵌入式开发环境搭建指南 (一):让世界闪烁吧
引
因為這是本專欄的第一篇文章,所以我打算先在這里介紹下專欄的寫作目標(biāo)。
Rust 是一種系統(tǒng)編程語言。 它有著驚人的運行速度,能夠防止段錯誤,并保證線程安全。Rust 官方一直標(biāo)榜著自己是系統(tǒng)編程語言,然而最根本的系統(tǒng)編程就是嵌入式系統(tǒng)開發(fā)。如果不能在嵌入式系統(tǒng)里大施拳腳,那么 Rust 就沒有底氣能與 C 語言叫板。經(jīng)過了 3 年迭代,Rust 在嵌入式開發(fā)領(lǐng)域已經(jīng)日漸成型,并且官方也成立了嵌入式工作組特別關(guān)注 Rust 嵌入式庫與工具鏈的開發(fā),同時也在不斷完善The embedded rust book。這里推薦大家關(guān)注工作組的 newsletter,里面有很多工作組最新工作進展。
而本專欄將會更面向于嵌入式開發(fā)的入門教程和實踐,也就是說,本專欄的文章并不假定讀者擁有任何嵌入式開發(fā)的知識或經(jīng)驗,但是要求讀者有一定 Rust 語言基礎(chǔ),比如說熟悉借用所有權(quán)系統(tǒng),懂得使用 unsafe 手動操作內(nèi)存結(jié)構(gòu)等等。
專欄文章會分為幾大類:
- 單片機架構(gòu)的基礎(chǔ)知識
- Rust 嵌入式開發(fā)的技巧
- 各種可以跟著動手的實踐項目
希望通過本專欄可以吸引 Rust 小伙伴加入嵌入式領(lǐng)域,<del>同時拐騙一波正在使用 C 語言開發(fā)嵌入式的水深火熱的程序員。</del>
準(zhǔn)備
為了能夠自己動手實踐嵌入式開發(fā),我們需要先準(zhǔn)備好一些材料:
- STM32F103 最小系統(tǒng)開發(fā)板 (約 10 元)
- STLINK V2 仿真器 (約 20 元)
- 母對母杜邦線
- USB 轉(zhuǎn) TTL 串口模塊 (約 5 元)
STM32F103 是現(xiàn)在應(yīng)用非常廣泛,性能強大而且成本低廉的一款單片機,擁有著高達 72Mhz 的主頻率,完全吊打 Arduino 等開發(fā)平臺。
STM32F103 最小系統(tǒng)核心版
仿真器是連接 pc 與單片機的重要模塊,主要用于程序燒寫與調(diào)試。
STLINK V2
串口模塊用于 pc 接收單片機的串口信息用以調(diào)試,由于現(xiàn)代計算機普遍已經(jīng)取消了串口接口,所以使用 USB 串口就是最經(jīng)濟可靠的選擇。
USB2TTL
Rust 工具鏈
2. 除了默認的標(biāo)準(zhǔn)庫外,我們還需要提前編譯好的 core 核心庫。在我們這里添加幾個常用的編譯目標(biāo)指令集,rustup 就會自動把核心庫下載下來。
> rustup target add thumbv6m-none-eabi thumbv7m-none-eabi thumbv7em-none-eabi thumbv7em-none-eabihf info: downloading component 'rust-std' for 'thumbv6m-none-eabi' info: downloading component 'rust-std' for 'thumbv7m-none-eabi' info: downloading component 'rust-std' for 'thumbv7em-none-eabi' info: downloading component 'rust-std' for 'thumbv7em-none-eabihf'3. 另外我們還需要一些傳統(tǒng)而好用的二進制工具 (binary tool) 和調(diào)試器。在 ARM官網(wǎng)頁面 下載適合平臺的最新版安裝即可。這一步安裝的工具包括 arm-none-eabi-nm, arm-none-eabi-gdb, arm-none-eabi-objcopy 還有 arm-none-eabi-size 等等。
4. 最后我們還差 openocd,它負責(zé)保持與與仿真器的通訊連接,我們需要使用它來進行燒寫和調(diào)試指令操作。openocd 的安裝途徑有很多,建議向購買仿真器的商家索要,或者可以從這里下載(可能需要科學(xué)上網(wǎng))。
注: 上述 3,4 步的工具需要加入 Path 環(huán)境變量。
Blinky
Blinky 是嵌入式世界的 hello world —— 讓一盞 LED 閃爍。這篇文章的最終目標(biāo)就是把最小系統(tǒng)版上唯一一顆 LED 燈閃爍起來。
我們先創(chuàng)建一個新的項目。
> cargo new blinkyCreated binary (application) `blinky` package打開 Cargo.toml 添加幾個依賴項。
[dependencies] cortex-m = "0.5.8" # cortex-m 核心指令集 cortex-m-rt = "0.6.5" # 最小運行時,負責(zé)啟動內(nèi)存初始化 panic-halt = "0.2.0" # 定義發(fā)生 panic 時采取立即停機的行為同一架構(gòu)的單片機的內(nèi)存容量往往有很大差異,不同廠家的內(nèi)存排布也不一定相同,所以這里我們要用 memory.x 文件里定義開發(fā)板的內(nèi)存結(jié)構(gòu)。在項目目錄中新建文件 memory.x 并寫入:
MEMORY {FLASH : ORIGIN = 0x08000000, LENGTH = 128KRAM : ORIGIN = 0x20000000, LENGTH = 20K }這里定義了我們這個 MCU 擁有 128k ROM 和 20k RAM,內(nèi)存起點分別在 0x08000000 和 0x20000000。
memory.x 事實上是一段鏈接器腳本 (Linker Script),鏈接器腳本用來在內(nèi)存中規(guī)劃如何排布代碼和靜態(tài)變量。很明顯僅靠這小段腳本還不足以聲明好運行所需的所有段 (SECTION)。幸運的是,cortex-m-rt 運行庫已經(jīng)為我們寫好了通用的鏈接腳本,我們僅僅需要在編譯時將名為 memory.x 的內(nèi)存定義腳本放在編譯目錄,memory.x 就會被自動 include 到模板中。 所以這里需要一段編譯時自動拷貝 memory.x 的 build script。在項目目錄中新建文件 build.rs 并寫入:
use std::env; use std::fs::File; use std::io::Write; use std::path::PathBuf;fn main() {// Put the linker script somewhere the linker can find itlet out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());File::create(out.join("memory.x")).unwrap().write_all(include_bytes!("memory.x")).unwrap();println!("cargo:rustc-link-search={}", out.display());// Only re-run the build script when memory.x is changed,// instead of when any part of the source code changes.println!("cargo:rerun-if-changed=memory.x"); }接著打開 src/main,寫入:
#![no_std] #![no_main]extern crate panic_halt;use cortex_m::asm; use cortex_m_rt::entry;#[entry] fn main() -> ! {asm::nop();loop { } }雖然這段代碼看起來毫無作用,但是對于編譯來說已經(jīng)足夠了。
可以注意一下這里的 main 函數(shù)并不是 Rust 語言內(nèi)嵌的主函數(shù),事實上,這個主函數(shù)僅僅是用戶代碼的入口,真正的主函數(shù)定義在 cortex_m_rt 庫中,在啟動后負責(zé)靜態(tài)變量和中斷向量表的內(nèi)存初始化,接著才將執(zhí)行權(quán)交回給這里的 main 函數(shù)。
執(zhí)行編譯。
> cargo build --target thumbv7m-none-eabiCompiling blinky v0.1.0Finished dev [unoptimized + debuginfo] target(s) in 0.62s至此 Blinky 已經(jīng)成功編譯好了,執(zhí)行文件應(yīng)該會出現(xiàn)在 /target/thumbv7m-none-eabi/debug/blinky 。接下來我們要把這個程序燒寫到芯片的 ROM 上。首先使用杜邦線連接上仿真器與開發(fā)板,對應(yīng)著接口上的名字,應(yīng)該很容易將四條連接線接好,四個接口分別是 SWDIO, SWCLK, 3.3V 和 GND。
接著啟動 openocd。
> openocd -f interface/stlink-v2.cfg -f target/stm32f1x.cfg 64-bits Open On-Chip Debugger 0.10.0-dev-00289-g5eb5e34 (2016-09-03-09:40) Licensed under GNU GPL v2 For bug reports, readhttp://openocd.org/doc/doxygen/bugs.html...Polling target stm32f1x.cpu failed, trying to reexamine Info : stm32f1x.cpu: hardware has 6 breakpoints, 4 watchpoints這一步以出現(xiàn) xxxxx.cpu: hardware has x breakpoints, x watchpoints 提示為連接成功。 如果出現(xiàn)了其他錯誤,先檢查是否已經(jīng)安裝仿真器的驅(qū)動,4條連接線有沒有松動,或者更換一個 USB 口試試。
保留 openocd 終端,再打開一個新的終端啟動 GDB (GNU Debugger) ,使用 GDB 進行執(zhí)行程序燒寫:
> arm-none-eabi-gdb GNU gdb (GNU Tools for ARM Embedded Processors 6-2017-q1-update) 7.12.1.20170215-git...For help, type "help". (gdb)加載目標(biāo)文件
(gdb) file ./target/thumbv7m-none-eabi/debug/blinky Reading symbols from ./target/thumbv7m-none-eabi/debug/blinky...done.連接上 openocd。(openocd 的默認端口為 3333)
(gdb) target remote :3333 Remote debugging using :3333 0x00000000 in ?? ()重置 MCU,因為在運行狀態(tài)無法進行燒寫。
(gdb) monitor reset halt stm32f1x.cpu: target state: halted target halted due to debug-request, current mode: Thread xPSR: 0x01000000 pc: 0x0800016c msp: 0x20000260開始寫入
(gdb) load Start address 0x0, load size 0 Transfer rate: 0 bits in <1 sec.寫入后 MCU 默認會暫停在初始狀態(tài),這里要手動運行。
(gdb) continue Continuing.好了到目前為止,如果我們的開發(fā)板毫無反應(yīng),那就對了,我們現(xiàn)在要給它加上最重要的 Blinky 邏輯。
打開 Cargo.toml 再加上兩個依賴
stm32f103xx-hal = { git = "https://github.com/japaric/stm32f103xx-hal.git" } # MCU 外圍部件操作的統(tǒng)一接口 nb = "0.1" # stm32f103xx-hal 的異步阻塞模塊,用來實現(xiàn)時鐘等待同步修改 src/main.rs
#![no_std] #![no_main]extern crate panic_halt; extern crate stm32f103xx_hal as hal; #[macro_use] extern crate nb;use cortex_m_rt::entry;use hal::prelude::*; use hal::stm32f103xx; use hal::timer::Timer;#[entry] fn main() -> ! {let cp = cortex_m::Peripherals::take().unwrap();let dp = stm32f103xx::Peripherals::take().unwrap();let mut flash = dp.FLASH.constrain();let mut rcc = dp.RCC.constrain();// 設(shè)置時鐘總線let clocks = rcc.cfgr.freeze(&mut flash.acr);// 設(shè)置通用引腳 (GPIO)let mut gpioc = dp.GPIOC.split(&mut rcc.apb2);// LED 對應(yīng)的 PC13 引腳let mut led = gpioc.pc13.into_push_pull_output(&mut gpioc.crh);// 淘寶上有些版本的核心板的 LED 會接在 PB12 引腳上,這樣的話用下面兩行替換// let mut gpiob = dp.GPIOB.split(&mut rcc.apb2);// let mut led = gpiob.pb12.into_push_pull_output(&mut gpiob.crh);let mut timer = Timer::syst(cp.SYST, 1.hz(), clocks);loop {block!(timer.wait()).unwrap();// 點亮 LEDled.set_high();block!(timer.wait()).unwrap();// 關(guān)閉 LEDled.set_low();} }重新編譯 Rust。
> cargo build --target thumbv7m-none-eabiCompiling blinky v0.1.0Finished dev [unoptimized + debuginfo] target(s) in 0.1s回到 GDB 終端,此時如果還在運行上一段代碼,那按下 Ctrl + C 就可以中斷執(zhí)行。
Continuing. Program received signal SIGINT, Interrupt. 0x08000240 in ?? () (gdb)直接執(zhí)行 load 指令,GDB 會自動識別到可執(zhí)行文件的變更并進行覆寫。
(gdb) load Start address 0x0, load size 0 Transfer rate: 0 bits in <1 sec. (gdb) continue Continuing.至此,我們的藍色 LED 就應(yīng)該會開始以一秒間隔開始閃爍了!
如果你有幸看到這,那就幫忙點個贊,讓更多人看到吧!
總結(jié)
以上是生活随笔為你收集整理的ubuntu安装 rust nightly_Rust 嵌入式开发环境搭建指南 (一):让世界闪烁吧的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 如何让图片开口说话 3DMeNow教程
- 下一篇: 开发安卓app游戏_电竞直播APP软件定