RustEmb_2.HelloWorld

說明

在傳統的C編寫STM32代碼時,有兩種使用方式:寄存器版本和HAL庫版本。
使用Rust編寫時也一樣,你需要考慮使用寄存器版本或者HAL庫版本。

toolchain與STM32對應關係

使用Rust編寫實質上是將Rust代碼編譯爲無STD庫的arm平臺代碼(二進制,彙編爲arm指令集),然後將其中的代碼段根據嵌入式硬件的要求,寫入到芯片中,從而實現在STM32中跑RUST的效果。

我們這裏使用的芯片STM32F407ZGT6 是一顆Cortex-M4架構的芯片,其對應RUST toolchain關係爲:

# target = "thumbv6m-none-eabi"    # Cortex-M0 and Cortex-M0+
# target = "thumbv7m-none-eabi"    # Cortex-M3
# target = "thumbv7em-none-eabi"   # Cortex-M4 and Cortex-M7 (no FPU)
# target = "thumbv7em-none-eabihf" # Cortex-M4F and Cortex-M7F (with FPU)

準備工作

本章節以LED閃爍爲目標,嘗試編寫代碼。

其中,IO使用GPIOF的Pin9和Pin10。

使用Cargo工具創建一個bin項目,名字爲stm32app, 然後在根目錄添加一個文件memory.x

MEMORY
{
  /* NOTE 1 K = 1 KiBi = 1024 bytes */
  FLASH : ORIGIN = 0x08000000, LENGTH = 512K
  RAM : ORIGIN = 0x20000000, LENGTH = 100K
}

特別注意FLASH和RAM值的大小,只能比真實的小,不能比真實的大。
這裏的memory.x是爲未來使用的一個庫cortex-m-rt準備的。

編譯時,使用如下命令行:
cargo build --release --target=thumbv7em-none-eabihf -C link-arg=Tlink.x
其中,target可以是v7m或者v7em中任何一個,但是不能是v6m.

就和使用C寫代碼一樣,我們完全可以全手工構造寄存器映射關係,然後進行讀寫,但是爲了方便,我們使用開源的庫加快我們的代碼速度。

其中,寄存器版本主要依賴 stm32f4 這個庫,而HAL版本依賴stm32f4xx-hal

他們沒有本質上的區別,只是寄存器版本庫對寄存器操作簡單明瞭,而HAL版本集成程度更高一點,你可以根據自己的情況進行選擇。

寄存器版本

Cargo.toml文件

[dependencies]
cortex-m = "*"
cortex-m-rt = "*"
cortex-m-semihosting = "*"
panic-halt = "*"

 [dependencies.stm32f4]
 features = ["stm32f407", "rt"]
 version = "*"

main.rs

#![no_std]
#![no_main]

extern crate stm32f4;
extern crate panic_halt;
extern crate cortex_m_rt;

use cortex_m_rt::entry;
use stm32f4::stm32f407;

// use `main` as the entry point of this application
#[entry]
fn main() -> ! {
    // get handles to the hardware
    let peripherals = stm32f407::Peripherals::take().unwrap();

    let pf = &peripherals.GPIOF;
    let rcc = &peripherals.RCC;

    // enable system clock RCC for gpiof
    rcc.ahb1enr.write(|w|{
        w.gpiofen().set_bit()
    });

    // config GPIOF pin9 and pin10 for led
    {
        // 1. mode 01 通用輸出
        pf.moder.write(|w|{
            w.moder9().output()
                .moder10().output()
        });
        // 2. otype 0 推輓輸出
        pf.otyper.write(|w|{
            w.ot9().push_pull()
                .ot10().push_pull()
        });
        // 3. ospeed 10, 50MHz high speed
        pf.ospeedr.write(|w|{
            w.ospeedr9().high_speed()
                .ospeedr10().high_speed()
        });
        // 4. pup 01, 上拉
        pf.pupdr.write(|w|{
            w.pupdr9().pull_up()
                .pupdr10().pull_up()
        });
        // 5. idr/odr/bsrr
        // by condition to read or set
        pf.odr.reset();
    }

    loop{
        pf.odr.write(|w| {
            w.odr9().set_bit()
                .odr10().clear_bit()
        });
        // 這個延時不準,只用來演示使用
        cortex_m::asm::delay(168*10000*3);

        pf.odr.write(|w| {
            w.odr9().clear_bit()
                .odr10().set_bit()
        });
        cortex_m::asm::delay(168*10000*3);
    }
}

使用準備工作中的命令行編譯後得到一個stm32app的arm程序,
通過arm-none-eabi-objdump -f stm32app就可以得到這個文件的詳細頭部信息了。

HAL庫版本

[dependencies]
embedded-hal = "*"
nb = "*"
cortex-m = "*"
cortex-m-rt = "*"
# Panic behaviour, see https://crates.io/keywords/panic-impl for alternatives
panic-halt = "*"

[dependencies.stm32f4xx-hal]
version = "*"
features = ["rt", "stm32f407"] # replace the model of your microcontroller here

main.rs

#![deny(unsafe_code)]
#![no_main]
#![no_std]

// Halt on panic
// #[allow(unused_extern_crates)] // NOTE(allow) bug rust-lang/rust#53964
extern crate panic_halt; // panic handler

use cortex_m;
use cortex_m_rt::entry;
use stm32f4xx_hal as hal;

use crate::hal::{prelude::*, stm32};

#[entry]
fn main() -> ! {
    if let (Some(dp), Some(cp)) = (
        stm32::Peripherals::take(),
        cortex_m::peripheral::Peripherals::take(),
    ) {
        // Set up the LED. On the Nucleo-446RE it's connected to pin PA5.
        let gf = dp.GPIOF.split();

        let mut led = gf.pf10.into_push_pull_output();

        // Set up the system clock. We want to run at 48MHz for this one.
        let rcc = dp.RCC.constrain();
        let clocks = rcc.cfgr.sysclk(168.mhz()).freeze();

        // Create a delay abstraction based on SysTick
        let mut delay = hal::delay::Delay::new(cp.SYST, clocks);

        loop {
            // On for 1s, off for 1s.
            led.set_high().unwrap();
            delay.delay_ms(500_u32);
            led.set_low().unwrap();
            delay.delay_ms(500_u32);
        }
    }

    loop {}
}

使用準備工作中的編譯指令就可以得到同樣的stm32app程序。

生成HEX/BIN文件

首先,上一步中生成的是一個arm程序,不是單片機可識別的HEX或者BIN程序,我們需要使用工具進行提取/轉換:

  • bin文件 arm-none-eabi-objcopy -O binary stm32app stm32app.bin
  • hex文件 arm-none-eabi-objcopy -O ihex stm32app stm32app.hex

其中,相比BIN文件,HEX文件多了機器碼的存儲地址等信息,建議轉換爲HEX文件進行下載。

下載

下載前,請確保開發板USB已經接好,並且CH340驅動成功,能在設備管理器裏面找到對應的COM口。

下載STM32 HEX文件有很多方式,我們是藉助串口進行ISP下載,這裏需要用到一些工具:

  1. FlyMcu
    這個軟件比較古老,但是基本能用。缺點是下載失敗率較高,建議波特率不要超過76800.

  2. PZ-ISP
    這個是買開發板提供的下載工具,比較推薦,成功率很高,不容易出問題。

  3. STM32 ISP下載工具
    這個據說是STM32專供的,沒怎麼使用。

以上工具使用方法建議自己查詢,方法都比較簡單。

注意:
如果使用J-Link進行下載需要配合OpenOCD使用,此方法比較複雜,建議有需求了再嘗試。

總結

RUST編寫STM32程序十分簡單,並且下載過程需要藉助arm工具集進行代碼轉換,其他的與普通方式一樣。

目前,ISP下載無法進行調試,如有需要,建議使用串口功能進行交互,OpenOCD+J-Link理論上可行,但是因爲涉及指令較多,這裏不做討論和嘗試。

當然,使用RUST開發處理需要RUST基礎知識之外,還需要對STM32本身有深入的理解,此方面的知識建議學習正點原子的教材:
手把手教你學STM32 系列視頻之 STM32F4-基於探索者F407

只有對C編寫STM32程序有了基礎的認知,纔有可能編寫出高質量的RUST代碼。

有人說,既然這樣,我爲什麼要用RUST寫嵌入式?

  1. RUST本身語言特性比C更安全,規範,可以寫出高質量的代碼。
  2. RUST工具集本身可實現更高層次的抽象,用高階語言寫嵌入式代碼可實現更復雜的功能和複用。
  3. 因爲我喜歡RUST呀(_)

其實,由於我們使用的是arm芯片,可以上一些RTOS或者Linux系統,未來我們也可以在這些系統的基礎上進行更深入的開發實踐,這時候,RUST就是一個比較好的選擇。

RUST官方對於嵌入式領域也很感興趣,有專門的團隊進行此方向的維護,再加上這個語言本身的特性,我相信未來它的工具鏈和生態會更加完善,最終與C一教高下。

說到底,這裏只是進行RUST嵌入式開發的一個學習和實踐,不要問太多問什麼,喜歡就好。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章