开发环境
硬件
- 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();
    }
}