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嵌入式开发的一个学习和实践,不要问太多问什么,喜欢就好。

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