本教程不是0基础的Rust嵌入式编程,需要有一定的Rust裸机编程的基础知识。
作为一个比较接近C的例子,适合入门,代码比较容易理解。本次例子使用的是target = “thumbv8m.main-none-eabihf” # Cortex-M33 (with FPU)平台进行编译,生成的二进制文件在Qemu模拟器中运行。
嵌入式系统编程是不能使用Rust的Std库的,只能使用它的子库:core。以及一些cortex库还有导入的一些外部库,看代码例子即可。
首先就是安装qemu模拟器和一些Rust编译的工具,这部分可以参考网上教程,有点麻烦。
下面直接附上内存分配器的代码,这个部分挺重要的,定义之后就可以使用vec、string、hashmap等数据结构,这对于裸机Rust编程是非常重要的,代码里面有详细的注释。下面对动态分配内存和无堆分配内存两种模式进行介绍:
#![feature(alloc_error_handler)]
#![no_main]
#![no_std]
#![feature(alloc)]
extern crate alloc;
use panic_halt as _;
use alloc::vec;
use core::alloc::Layout;
use alloc::string::String;
use alloc_cortex_m::CortexMHeap;
use cortex_m::asm;
use cortex_m_rt::entry;
use cortex_m_semihosting::{hprintln,debug};
//全局分配器定义
//为了能够在单片机上开辟一块作为堆的内存,首先要调用cortex_m_rt::heapstart()函数方法
//分配指定大小HEAP_SIZE的内存,本程序HEAP_SIZE大小定义为1024字节。
#[global_allocator]
static ALLOCATOR:CortexMHeap=CortexMHeap::empty();
const HEAP_SIZE:usize=1024; //byte
#[entry]
fn main()->!{
// 初始化内存分配器
unsafe{ALLOCATOR.init(cortex_m_rt::heap_start() as usize,HEAP_SIZE)}
//字符串定义,导入之后和std中用法类似。
let s = String::from("hello");
for c in s.chars() {
hprintln!("{}", c);
}
//Vec类型,使用方法一致。
let mut xs=vec![0,1,2];
xs.push(3);
//简单的函数调用
let s=add(1,2);
hprintln!("{}","qemu vec:").unwrap();
hprintln!("{:?}",xs).unwrap();
hprintln!("{:?}",s).unwrap();
//只有在qemu中模拟才用下面这句,在真机环境不需要。退出qemu
debug::exit(debug::EXIT_SUCCESS);
loop{}
}
//内存分配错误处理
#[alloc_error_handler]
fn alloc_error(_layout:Layout)->!{
asm::bkpt();
loop{}
}
fn add(a:i32,b:i32)->i32{
a+b
}
编译命令(已配置过):
cargo build --example allocator2
qemu运行命令:
qemu-system-arm -cpu cortex-m3 -machine lm3s6965evb -nographic -semihosting-config enable=on,target=native -kernel target/thumbv8m.main-none-eabihf/debug/examples/allocator2
代码运行结果如下:

啥叫无堆分配内存呢?heapless不需要设置,因为它的集合不依赖于全局内存分配器,必须预先声明集合的容量。 不重新分配,并具有固定容量,此容量是集合类型签名的一部分。在这种情况下,我们已经声明具有8个元素的容量,即向量最多可以容纳8个元素。上面的动态分配是可以改变内存大小的,和C语言里面的malloc函数一致理解。但是这里不可以,分配了就是固定的,这种缺点显而易见:由于集合具有固定容量,因此将元素插入集合中的所有操作都可能失败,错误处理也是一个比较麻烦的事。
这里还引入一个概念:最坏情况执行时间(WCET)。
如果您正在构建时间敏感型应用程序或硬实时应用程序,那么您可能非常关心程序不同部分的最坏情况执行时间。
有堆分配集合可以重新分配,因此可能增加集合的操作的WCET还将包括重新分配集合所需的时间,这本身取决于集合的运行时容量。这使得很难确定操作的WCET,因为它取决于正在使用的分配器及其运行时容量。
另一方面,固定容量集合不重新分配,因此所有操作都有可预测的执行时间,例如,在常量时间内执行。
以下是无堆分配的详细代码:
#![no_main]
#![no_std]
extern crate heapless; // v0.4.x
use cortex_m_rt::entry;
use heapless::Vec;
use heapless::consts::*;
use panic_halt as _;
use cortex_m_semihosting::{hprintln,debug};
#[entry]
fn main() -> ! {
let mut xs: Vec<_, U8> = Vec::new();
xs.push(42).unwrap();
hprintln!("{:?}",xs);
debug::exit(debug::EXIT_SUCCESS);
loop{}
}