本帖最后由 instead 于 2023-11-23 09:10 编辑
前言
说到嵌入式,就不得不提一嘴Mozilla的Rust。Rust作为一种系统级编程语言,它提供了许多内存安全保证和线程安全保证,虽然自由度比C/C++差了许多,但也比较适合我这种10行代码,9行报错,8个警告的人(
安装Rust环境
Windows上安装Rust需要有C++环境,有两种安装方法,分别是 x86_64-pc-windows-msvc
和 x86_64-pc-windows-gnu
。在这里因为使用习惯上的问题,选用 x86_64-pc-windows-gnu
作为例子。
安装MSYS
这里按照MSYS2官网的说明一路next安装即可,这里就不赘述了。
安装完成后会弹出一个命令行窗口,在里面输入
pacman -S mingw-w64-x86_64-toolchain
安装开发环境 mingw-toolchain
。
安装rustup
在同一个窗口内输入如下的命令来启动安装脚本
curl https://sh.rustup.rs -sSf | sh
回车后,根据下面的输出进行配置
info: downloading installer
Rust Visual C++ prerequisites
Rust requires a linker and Windows API libraries but they don't seem to be
available.
These components can be acquired through a Visual Studio installer.
1) Quick install via the Visual Studio Community installer
(free for individuals, academic uses, and open source).
2) Manually install the prerequisites
(for enterprise and advanced users).
3) Don't install the prerequisites
(if you're targeting the GNU ABI).
>3
## 敏感信息已删除 ##
Current installation options:
default host triple: x86_64-pc-windows-msvc
default toolchain: stable (default)
profile: default
modify PATH variable: yes
1) Proceed with installation (default)
2) Customize installation
3) Cancel installation
>2
I'm going to ask you the value of each of these installation options.
You may simply press the Enter key to leave unchanged.
Default host triple? [x86_64-pc-windows-msvc]
x86_64-pc-windows-gnu
Default toolchain? (stable/beta/nightly/none) [stable]
stable
Profile (which tools and data to install)? (minimal/default/complete) [default]
complete
Modify PATH variable? (Y/n)
y
Current installation options:
default host triple: x86_64-pc-windows-gnu
default toolchain: stable
profile: complete
modify PATH variable: yes
1) Proceed with installation (default)
2) Customize installation
3) Cancel installation
>1
等待上面的操作完成后,如果没有提示什么错误信息,Rust和rustup就已经被成功安装了。
配置RISC-V开发环境
使用Rust在stm32和risc-v的板子上进行开发的流程其实差不多,只要安装对应的target就好了。rust支持的target如下所示
aarch64-apple-darwin
aarch64-apple-ios
aarch64-apple-ios-sim
aarch64-linux-android
aarch64-pc-windows-msvc
aarch64-unknown-fuchsia
aarch64-unknown-linux-gnu
aarch64-unknown-linux-musl
aarch64-unknown-none
aarch64-unknown-none-softfloat
aarch64-unknown-uefi
arm-linux-androideabi
arm-unknown-linux-gnueabi
arm-unknown-linux-gnueabihf
arm-unknown-linux-musleabi
arm-unknown-linux-musleabihf
armebv7r-none-eabi
armebv7r-none-eabihf
armv5te-unknown-linux-gnueabi
armv5te-unknown-linux-musleabi
armv7-linux-androideabi
armv7-unknown-linux-gnueabi
armv7-unknown-linux-gnueabihf
armv7-unknown-linux-musleabi
armv7-unknown-linux-musleabihf
armv7a-none-eabi
armv7r-none-eabi
armv7r-none-eabihf
asmjs-unknown-emscripten
i586-pc-windows-msvc
i586-unknown-linux-gnu
i586-unknown-linux-musl
i686-linux-android
i686-pc-windows-gnu
i686-pc-windows-msvc
i686-unknown-freebsd
i686-unknown-linux-gnu
i686-unknown-linux-musl
i686-unknown-uefi
loongarch64-unknown-linux-gnu
loongarch64-unknown-none
loongarch64-unknown-none-softfloat
mips-unknown-linux-musl
mips64-unknown-linux-muslabi64
mips64el-unknown-linux-muslabi64
mipsel-unknown-linux-musl
nvptx64-nvidia-cuda
powerpc-unknown-linux-gnu
powerpc64-unknown-linux-gnu
powerpc64le-unknown-linux-gnu
riscv32i-unknown-none-elf
riscv32imac-unknown-none-elf
riscv32imc-unknown-none-elf
riscv64gc-unknown-linux-gnu
riscv64gc-unknown-none-elf
riscv64imac-unknown-none-elf
s390x-unknown-linux-gnu
sparc64-unknown-linux-gnu
sparcv9-sun-solaris
thumbv6m-none-eabi
thumbv7em-none-eabi
thumbv7em-none-eabihf
thumbv7m-none-eabi
thumbv7neon-linux-androideabi
thumbv7neon-unknown-linux-gnueabihf
thumbv8m.base-none-eabi
thumbv8m.main-none-eabi
thumbv8m.main-none-eabihf
wasm32-unknown-emscripten
wasm32-unknown-unknown
wasm32-wasi
wasm32-wasi-preview1-threads
x86_64-apple-darwin
x86_64-apple-ios
x86_64-fortanix-unknown-sgx
x86_64-linux-android
x86_64-pc-solaris
x86_64-pc-windows-gnu (installed)
x86_64-pc-windows-msvc
x86_64-sun-solaris
x86_64-unknown-freebsd
x86_64-unknown-fuchsia
x86_64-unknown-illumos
x86_64-unknown-linux-gnu
x86_64-unknown-linux-gnux32
x86_64-unknown-linux-musl
x86_64-unknown-netbsd
x86_64-unknown-none
x86_64-unknown-redox
x86_64-unknown-uefi
已经被安装的target会在后面标记 (installed)
。
输入如下的命令安装 rust-std
rustup target add riscv32imac-unknown-none-elf
(可选)cargo-generate
是一个用于快速生成rust项目的一个工具,如果有需要的话可以使用以下命令安装
cargo install cargo-generate
到这里你会认为环境已经搭建的差不多了吗,确实是差不多了。但这里漏了一个非常关键的部分,那就是 HAL
。HAL
是 Hardware Abstraction Layer
的缩写,即硬件抽象层。它是一种软件抽象层,用于在嵌入式系统中抽象硬件接口,使得应用程序可以独立于底层硬件而编写。
这里我找了一下Rust的支持目录,非常可惜的是没有直接的官方支持库,也没找到有其他具有兼容结构的库可供使用,因此只能等待官方或者其他大手子编写针对Ai-M61-32S的 HAL
库了。
好好好,又看了一遍文档和aithinker_Ai-M6X_SDK,发现板子的处理器是Bouffalo的BL616,顺藤摸瓜找到了他的Embedded Rust Peripheral Access Crate for Bouffalo chips。这就好办了,创建新项目!
Hello, World!——blinky
blinky是一个目标非常简单的项目,只需要让开发板上的板载LED灯以一定的间隔闪烁起来,也是嵌入式领域的"Hello, World!"了。
创建项目
输入如下的命令创建新Rust项目
cargo new ai_m61_32_s_blinky
cd ai_m61_32_s_blinky
执行后,会生成具有如下结构的目录
.
├── Cargo.toml
├── .gitignore
└── src
└── main.rs
在目录内创建 .cargo
文件夹,并在里面创建配置文件 config
[target.riscv32imac-unknown-none-elf]
rustflags = [
"-C", "link-arg=-Tmemory.x",
"-C", "link-arg=-Tlink.x",
]
[build]
target = "riscv32imac-unknown-none-elf"
同时还要在项目的根目录里创建用于描述生成的固件中的内存布局和链接脚本的文件。这里我面向gpt编程,根据sdk的 flash.ld
文件生成了对应的 memory.x
和 link.x
文件。(但大概率会扑街,不要小看人类啊!)
/* memory.x */
MEMORY
{
fw_header_memory (rx) : ORIGIN = 0xA0000000 - 0x1000, LENGTH = 4K;
xip_memory (rx) : ORIGIN = 0xA0000000, LENGTH = 4M;
ram_code (wxa) : ORIGIN = 0xA8000000, LENGTH = 4M;
itcm_memory (rx) : ORIGIN = 0x62FC0000, LENGTH = 20K;
dtcm_memory (rx) : ORIGIN = 0x62FC5000, LENGTH = 4K;
nocache_ram_memory (!rx): ORIGIN = 0x22FC6000, LENGTH = 44K+60K;
ram_memory (!rx): ORIGIN = 0x62FE0000, LENGTH = 320K-20K-4K-44K-60K;
ram_wifi (wxa) : ORIGIN = 0x23010000, LENGTH = 160K - __EM_SIZE;
}
REGION_ALIAS("RAM", ram_memory);
REGION_ALIAS("RAM_CODE", ram_code);
/* Define symbols used in the linker script */
__StackTop = ORIGIN(dtcm_memory) + LENGTH(dtcm_memory);
__StackLimit = __StackTop - SIZEOF(.stack_dummy);
__HeapBase = ORIGIN(ram_memory);
__HeapLimit = ORIGIN(ram_memory) + LENGTH(ram_memory);
__psram_data_start = ORIGIN(ram_code) + SIZEOF(.psram_data);
__psram_data_end = ORIGIN(ram_code) + LENGTH(ram_code);
__psram_heap_base = ORIGIN(ram_code) + SIZEOF(.psram_heap);
__psram_heap_end = ORIGIN(ram_code) + LENGTH(ram_code);
__wifi_bss_start = ADDR(.wifibss);
__wifi_bss_end = ADDR(.wifibss) + SIZEOF(.wifibss);
__LD_CONFIG_EM_SEL = __EM_SIZE;
/* link.x */
ENTRY(__start);
INCLUDE "./memory.x"
SECTIONS
{
. = ORIGIN(xip_memory) + SIZEOF(.fw_header);
.fw_header :
{
KEEP(*(.fw_header))
} > fw_header_memory
.init :
{
*(.text.entry)
KEEP (*(SORT_NONE(.init)))
KEEP (*(SORT_NONE(.vector)))
} > xip_memory
.rftlv.tool :
{
. = ORIGIN(xip_memory) + __RFTLV_SIZE_OFFSET;
PROVIDE( _ld_symbol_rftlv_address = . );
LONG(__RFTLV_HEAD1_H);
LONG(__RFTLV_HEAD1_L);
. = ORIGIN(xip_memory) + __RFTLV_SIZE_OFFSET + __RFTLV_SIZE_HOLE;
} > xip_memory
/* ... (rest of the sections remain the same) ... */
.heap (NOLOAD):
{
. = ALIGN(4);
__HeapBase = .;
KEEP(*(.heap*))
. = ALIGN(4);
__HeapLimit = .;
} > ram_memory
.psram_heap (NOLOAD):
{
. = ALIGN(4);
__psram_heap_base = .;
KEEP(*(.psram_heap*))
. = ALIGN(4);
__psram_heap_end = .;
} > ram_code
.wifibss (NOLOAD) :
{
PROVIDE( __wifi_bss_start = ADDR(.wifibss) );
PROVIDE( __wifi_bss_end = ADDR(.wifibss) + SIZEOF(.wifibss) );
_sshram = . ;
*(SHAREDRAMIPC)
*(SHAREDRAM)
_eshram = . ;
*ipc_shared.o(COMMON)
*sdu_shared.o(COMMON)
*hal_desc.o(COMMON)
*txl_buffer_shared.o(COMMON)
*txl_frame_shared.o(COMMON)
*scan_shared.o(COMMON)
*scanu_shared.o(COMMON)
*mfp_bip.o(COMMON)
*me_mic.o(COMMON)
*(.wifi_ram*)
. = ALIGN(16);
} > ram_wifi
}
之后,我们再进入 Cargo.toml
,编辑我们这个项目的依赖,大概设置成下面这样子:
[package]
name = "ai_m61_32s_blinky"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
bl616-pac = { version = "0.0.0"}
embedded-hal = "0.2.7"
panic-halt = "0.2.0"
riscv-rt = "0.11.0"
riscv = "0.10.1"
编写裸机程序
之后,便可以正式开始对 src\main.rs
进行编写了!
#![no_std]
#![no_main]
use panic_halt as _;
use bl616_pac::Peripherals;
use riscv_rt::entry;
use riscv::asm;
// get_peripherals 获取外设
fn get_peripherals() -> Peripherals {
unsafe {
Peripherals::steal()
}
}
// 延时函数,占用2000个CPU周期
fn delay() {
for _ in 0..2_000 {
unsafe { asm::nop() };
}
}
#[entry]
fn main() -> ! {
// 获取外设
let peripherals = get_peripherals();
// 获取芯片通用全局设定模块
let glb = peripherals.GLB;
// 控制特定gpio脚,这里是IO12,也就是板子上的红色灯
let gpio_12 = &glb.gpio_config[12];
// let gpio_14 = &glb.gpio_config[14];
// let gpio_15 = &glb.gpio_config[15];
// 控制板子上的红色灯闪烁
loop {
unsafe {
// 设置高电平
gpio_12.set_bits(|w|{w.bits(1)});
}
delay();
unsafe {
// 设置低电平
gpio_12.set_bits(|w|{w.bits(0)});
}
delay();
}
}
完成之后,命令行输入如下命令进行编译
cargo build
哦豁,编译报错
修正 memory.x
文件
看起来.....GPT还是不怎么靠谱,还需要对 memory.x
文件进行修改。在阅读了相关资料后,将 memory.x
的内容修改为
/* Linker script for the Ai-M61-32S-Kit */
MEMORY
{
FLASH : ORIGIN = 0xA0000000, LENGTH = 8M
RAM : ORIGIN = 0x62FC0000, LENGTH = 320K
}
REGION_ALIAS("REGION_TEXT", FLASH);
REGION_ALIAS("REGION_RODATA", FLASH);
REGION_ALIAS("REGION_DATA", RAM);
REGION_ALIAS("REGION_BSS", RAM);
REGION_ALIAS("REGION_HEAP", RAM);
REGION_ALIAS("REGION_STACK", RAM);
成功编译
然后执行以下命令清理并编译(你要问我为什么要加 -r
?因为我这不加就会报错找不到文件挺奇怪的......)
cargo clean && cargo build -r
当看到类似如下的输出时,便说明编译成功了!
编译产物位于 target\riscv32imac-unknown-none-elf\release\ai_m61_32s_blinky
终于,终于编译出来了(;´༎ຶД༎ຶ`)
(虽然不知道烧录到板子上后能不能成功跑起来)
参考资料
- bouffalolab/bl_docs: Datasheets and Reference Manual for BL602/BL808 (github.com)
- bouffalolab/bouffalo_sdk: BouffaloSDK is the IOT and MCU software development kit provided by the Bouffalo Lab Team, supports all the series of Bouffalo chips. Also it is the combination of bl_mcu_sdk and bl_iot_sdk (github.com)
- bouffalolab/bl-pac: Embedded Rust Peripheral Access Crate for Bouffalo chips (github.com)
- ai-m61-32s-kit_v1.1.0规格书20230324.pdf (ai-thinker.com)
- Hardware - The Embedded Rust Book (rust-embedded.org)
- BL618评估板教程第一篇:使用GPIO点亮LED灯 - VeriMake
- STM32F4 Embedded Rust at the PAC: GPIO Control - DEV Community
- 关于本书 - Rust语言圣经(Rust Course)
- Rust on RISC-V BL602: Is It Sunny? (lupyuen.github.io)
- henry's blog (henrygressmann.de)
- cargo build - Cargo 手册 中文版 (rustwiki.org)
感谢以上资料的作者所编写的内容,对本次写作做出的帮助与支持!