四、GPIO 的使用

首先在 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{} ,不知可不可以。我還未試過。


svd2rust - Rust (docs.rs)

此網(wǎng)站說明了為什么 Peripherals 被設(shè)計成單例模式

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容