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