Rust is used for embedded development. There are some articles on the Internet, but most of them use some ready-made projects. This time I will build an independent project from scratch. This time I use the STM32F103CBT6 chip. The goal of the project is to light up the LED
The first step is to install the rust environment. I choose vscode as the development tool and ST Link V2.1 for simulation.
First: Set up the rust environment
1. rustup update
2. rustup target install thumbv7m-none-eabi
3. cargo install cargo-flash
This tool can directly burn the flash.
Second: Create a project
1. cargo init rusty-blink
Use cargo to create a project, project name <rust-blink>
2. Create a new config file in the .cargo directory, .cargo/config
# .cargo/config
[build]
# Always compile for the instruction set of the STM32F1
target = "thumbv7m-none-eabi"
# use the Tlink.x scrip from the cortex-m-rt crate
rustflags = [ "-C", "link-arg=-Tlink.x"]
3. Create a memory.x file in the project directory
/* memory.x - Linker script for the STM32F103C8T6 */
MEMORY
{
/* Flash memory begins at 0x80000000 and has a size of 64kB*/
FLASH : ORIGIN = 0x08000000, LENGTH = 64K
/* RAM begins at 0x20000000 and has a size of 20kB*/
RAM : ORIGIN = 0x20000000, LENGTH = 20K
}
4. Modify the Cargo.toml file, project root directory
# Cargo.toml
[package]
edition = "2018"
name = "blinky-rust"
version = "0.1.0"
[profile.release]
opt-level = 'z' # turn on maximum optimizations. We only have 64kB
lto = true # Link-time-optimizations for further size reduction
[dependencies]
cortex-m = "^0.6.3" # Access to the generic ARM peripherals
cortex-m-rt = "^0.6.12" # Startup code for the ARM Core
embedded-hal = "^0.2.4" # Access to generic embedded functions (`set_high`)
panic-halt = "^0.2.0" # Panic handler
# Access to the stm32f103 HAL.
[dependencies.stm32f1xx-hal]
# Bluepill contains a 64kB flash variant which is called "medium density"
features = ["stm32f103", "rt", "medium"]
version = "^0.7.0"
The project uses the Rust project stm32f1xx-hal. The current version of the project is 0.9.0. If you want to use the new version, just modify the Cargo.toml file.
5. Modify the main.rs file
// src/main.rs
// std and main are not available for bare metal software
#![no_std]
#![no_main]
use cortex_m_rt::entry; // The runtime
use embedded_hal::digital::v2::OutputPin; // the `set_high/low`function
use stm32f1xx_hal::{delay::Delay, pac, prelude::*}; // STM32F1 specific functions
#[allow(unused_imports)]
use panic_halt; // When a panic occurs, stop the microcontroller
// This marks the entrypoint of our application. The cortex_m_rt creates some
// startup code before this, but we don't need to worry about this
#[entry]
fn main() -> ! {
// Get handles to the hardware objects. These functions can only be called
// once, so that the borrowchecker can ensure you don't reconfigure
// something by accident.
let dp = pac::Peripherals::take().unwrap();
let cp = cortex_m::Peripherals::take().unwrap();
// GPIO pins on the STM32F1 must be driven by the APB2 peripheral clock.
// This must be enabled first. The HAL provides some abstractions for
// us: First get a handle to the RCC peripheral:
let mut rcc = dp.RCC.constrain();
// Now we have access to the RCC's registers. The GPIOC can be enabled in
// RCC_APB2ENR (Prog. Ref. Manual 8.3.7), therefore we must pass this
// register to the `split` function.
let mut gpioc = dp.GPIOC.split(&mut rcc.apb2);
// This gives us an exclusive handle to the GPIOC peripheral. To get the
// handle to a single pin, we need to configure the pin first. Pin C13
// is usually connected to the Bluepills onboard LED.
let mut led = gpioc.pc13.into_push_pull_output(&mut gpioc.crh);
// Now we need a delay object. The delay is of course depending on the clock
// frequency of the microcontroller, so we need to fix the frequency
// first. The system frequency is set via the FLASH_ACR register, so we
// need to get a handle to the FLASH peripheral first:
let mut flash = dp.FLASH.constrain();
// Now we can set the controllers frequency to 8 MHz:
let clocks = rcc.cfgr.sysclk(8.mhz()).freeze(&mut flash.acr);
// The `clocks` handle ensures that the clocks are now configured and gives
// the `Delay::new` function access to the configured frequency. With
// this information it can later calculate how many cycles it has to
// wait. The function also consumes the System Timer peripheral, so that no
// other function can access it. Otherwise the timer could be reset during a
// delay.
let mut delay = Delay::new(cp.SYST, clocks);
// Now, enjoy the lightshow!
loop {
led.set_high().ok();
delay.delay_ms(1_000_u16);
led.set_low().ok();
delay.delay_ms(1_000_u16);
}
}
At this step, the entire project is completed.
let mut gpioc = dp.GPIOC.split(&mut rcc.apb2);
let mut led = gpioc.pc13.into_push_pull_output(&mut gpioc.crh);
These two sentences can be defined as your own pins.
Compile the program cargo build --release
If everything goes well, you will get a target file with debugging, but this file cannot be used and needs to be converted into a bin file
arm-none-eabi-objcopy -O binary target/thumbv7m-none-eabi/release/blinky-rust blinky-rust.bin
Burn the file to the board.
cargo flash --chip stm32f103C8 --release
The whole operation is completed, and you can see the board starts to shine.