项目地址:https://github.com/hangbale/rust-stm32

开发环境

硬件

  • Mac Apple Silicon
  • STM32F103C8T6套件
  • STlink V2

软件

  • OpenOCD

    brew install openocd
    
  • Rust

  • ARM的交叉编译环境

    target要与使用的stm32芯片对应

    rustup target add thumbv7m-none-eabi
    
  • ARM的GCC工具链

    brew install armmbed/formulae/arm-none-eabi-gcc
    
  • VSCode + Cortex Debug插件

依赖

  • cortex-m 提供arm芯片的底层访问方法
  • cortex_m_rt 提供arm芯片的应用层runtime环境
  • nb 提供非阻塞IO
  • panic-halt 由于arm程序中是没有std库的,所以需要引入此库来提供恐慌行为的定义
  • stm32f1xx-hal 提供硬件抽象层

必要文件

memory.x

cortex_m_rt 库所依赖,此文件需要放置在项目根目录下

此文件描述了芯片的内存布局以及链接脚本,比如Flash和RAM空间地址及大小,不同类型的芯片对应的内容也不同,STM32F103C8T6所对应的内容如下

/* Linker script for the STM32F103C8T6 */
MEMORY
{
  FLASH : ORIGIN = 0x08000000, LENGTH = 64K
  RAM : ORIGIN = 0x20000000, LENGTH = 20K
}

以上是关于内存布局的定义,此外,有些芯片提供了除FLASH和RAM之外的内存区域,为了访问这些区域,可以通过定义SECTIONS 段的方式实现:

SECTIONS
{
  .text :
  {
    /* Place code in FLASH */
    *(.text)
  } > FLASH

  .data :
  {
    /* Place initialized data in SRAM */
    *(.data)
  } > SRAM AT > FLASH

  .bss :
  {
    /* Place uninitialized data in SRAM */
    *(.bss)
  } > SRAM
}

具体可参考 https://docs.rs/cortex-m-rt/latest/cortex_m_rt/#memoryx

openocd.cfg

openocd的配置文件,我使用的是stlink,其对应的内容如下


source [find interface/stlink.cfg]

source [find target/stm32f1x.cfg]

vscode调试配置

这里使用的是vscode调试器进行程序烧录调试,其配置文件如下

{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "Cortex Debug",
      "cwd": "${workspaceFolder}",
      "executable": "./target/thumbv7m-none-eabi/debug/rust-embedded", // 对应自己项目编译后的可执行文件
      "request": "launch",
      "type": "cortex-debug",
      "servertype": "openocd",
      "configFiles": [
        "openocd.cfg",
      ],
      "showDevDebugOutput":"raw"
    }
  ]
}

此外需要安装相应上文所述的cortex-debug的插件

然后就通过vscode的debug功能一键运行即可自动下载运行

点灯

关于如何板子和stlink如何接线这里不再赘述,资料很多

下面是一个简单的点灯程序

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

use panic_halt as _;
use nb::block;
use cortex_m_rt::entry;
use stm32f1xx_hal::{pac, prelude::*, timer::Timer};

#[entry]
fn main() -> ! {
    // Get access to the core peripherals from the cortex-m crate
    let cp = cortex_m::Peripherals::take().unwrap();
    // Get access to the device specific peripherals from the peripheral access crate
    let dp = pac::Peripherals::take().unwrap();

    // Take ownership over the raw flash and rcc devices and convert them into the corresponding
    // HAL structs
    let mut flash = dp.FLASH.constrain();
    let rcc = dp.RCC.constrain();

    // Freeze the configuration of all the clocks in the system and store the frozen frequencies in
    // `clocks`
    let clocks = rcc.cfgr.freeze(&mut flash.acr);

    // Acquire the GPIOC peripheral
    // let mut gpioc = dp.GPIOC.split();
    let mut gpioa = dp.GPIOA.split();

    // Configure gpio C pin 13 as a push-pull output. The `crh` register is passed to the function
    // in order to configure the port. For pins 0-7, crl should be passed instead.
    let mut led = gpioa.pa1.into_push_pull_output(&mut gpioa.crl);
    // let mut led = gpioc.pc13.into_push_pull_output(&mut gpioc.crh);
    // Configure the syst timer to trigger an update every second
    let mut timer = Timer::syst(cp.SYST, &clocks).counter_hz();
    timer.start(10.Hz()).unwrap();

    // Wait for the timer to trigger an update and change the state of the LED
    loop {
        block!(timer.wait()).unwrap();
        led.set_high();
        block!(timer.wait()).unwrap();
        led.set_low();
    }
}