首先在 cargo.toml 的依賴項 [dependencies] 下增加一個依賴。
stm32f103xx-hal = { git = "https://gitee.com/EmergingTechnologies/stm32f103xx-hal.git" }
這是一個開源 hal 庫。
然后我在 examples/led.rs 里添加了一個學(xué)習(xí)用例。
#![no_std]
#![no_main]
extern crate panic_halt; // 如是不引入此庫,則要自己實現(xiàn)一個 panic 處理函數(shù)。好像
use cortex_m::asm;
use cortex_m_rt::entry;
use stm32f103xx_hal as hal;
use hal::device;
use hal::prelude::*;
#[entry]
fn main() -> ! {
asm::nop(); // To not have main optimize to do
let p = device::Peripherals::take().unwrap();
let mut rcc = p.RCC.constrain();
let mut gpiob = p.GPIOB.split(&mut rcc.apb2);
let mut led_red = gpiob.pb5.into_push_pull_output(&mut gpiob.crl);
let mut led_green= gpiob.pb0.into_push_pull_output(&mut gpiob.crl);
let mut led_blue= gpiob.pb1.into_push_pull_output(&mut gpiob.crl);
led_green.set_low();
led_blue.set_high();
led_red.set_high();
loop {
}
}
大部內(nèi)容網(wǎng)上或 B 站都有解釋,此我就不作過多表述了。
我主要想記錄 GPIO 的使用,以及一些分享經(jīng)驗。
device::Peripherals::take().unwrap() 是創(chuàng)建一個外設(shè)實例。請注意,其為單例模式,即其在整個程序運行過程,只產(chǎn)生一次,源碼是這樣寫的。因此 take() 返回的是一個 Option 類型。
p.RCC.constrain() 可以理解為獲取一個整理過的 Rcc 實例。注意,經(jīng)過 constrain() 后,RCC 變成了 Rcc。為何如此 "多此一舉" 。開始我也不明,直到源碼里有這樣一句話,此函數(shù)是把 RCC 適配為其他庫使用的形式。也就是說,此函數(shù)是適配器的作用。
p.GPIOB.split(&mut gpiob.crl) 與上例是一樣的功能。
接下來,就是操作 GPIO 了。
gpiob.pb5.into_push_pull_output(&mut gpiob.crl) 設(shè)置的 pin 引腳的工作模式。
請仔細(xì)看 led_red 變量前的修飾符為 mut 。我未看過源碼,不知其內(nèi)部是否含有狀態(tài)。但可以這樣理解,led.set_high() 是要改變 LED 的狀態(tài),所以此處聲明為 mut ,則與實際狀態(tài)可變相對應(yīng)。
另外在 vscode 里,提示 set_high() 為不推薦使用,應(yīng)使用 data::v2 里的。其意思是,此庫里 set_high() 是來自另一個庫里的 traits 里(忘記哪個了),但這個 traits 有一個問題,是不會返回失敗情況(好像是吧),所以該庫又推出了一個 set_high() 的 traits 放在 data::v2 里。
這個說明不一定正確,你最好自己看源碼。
注: 在學(xué)習(xí)此 hal 庫使用時,我發(fā)現(xiàn)了一個誤區(qū),就是 別以為引入一個類型,就能用他所有的方法。下面開始說明。
rust 和 python 在這點上類似,那我先以 python 為例。
當(dāng)你在一個文件里定義了一個類 class ,那么你在其他文件還可以為這個 class 添加新的方法。例如,
# a.py
class A:
def func1():
...
# b.py
A.func2 = func
# main1.py
import a
a.func1() # 正確
a.func2() # 錯誤,因為 `A.func2 = func` 未被解釋器跑過
# main2.py
import a
import b # 把 A.func2=func 跑了一次,那么 func2 就會添加到 A 的 __dict__ 里
上述代碼未經(jīng)測試,可能內(nèi)容有錯誤,但大概意思就該明白了。
同樣,Rust 也存在這樣說法。因為其 impl 可以在不同位置多次聲明的。不過 Rust 存在的孤兒原則,使得其不會如 python 那樣亂飛,起碼你找其出處時,要不是在類型定義處,要不在 traits 定義處。
下面舉一個不知在哪看到的源碼,請注意,未經(jīng)測試
let a = 1.cm();
let a = 50.hz();
從內(nèi)容可以看出,1.cm() 和 50.hz() 代表的是長度的 1 cm 和頻率 1 Hz。
其實現(xiàn)我猜是這樣的,
/* length.rs */
pub struct CentreMeter(f32);
pub trait Length {
pub fn cm(&self) -> CentreMeter;
}
impl Length for i32 {
pub fn cm(&self) -> CentreMeter {
CentreMeter(self as f32)
}
}
因此,你只要導(dǎo)入此文件的 Length,就可以為 i32 增加新的功能了。直接使用 impl i32{} ,不知可不可以。我還未試過。
此網(wǎng)站說明了為什么 Peripherals 被設(shè)計成單例模式