Ai-M61-32S-Kit 开发板 Rust 裸机编程指南:PAC

[复制链接]
查看1248 | 回复18 | 2024-2-26 19:01:02 | 显示全部楼层 |阅读模式

本帖最后由 instead 于 2024-2-26 19:01 编辑

全文 10574 字,预计阅读时间 7-9 分钟

开发资料一览

下文中的大部分内容都会参考以上官方手册的内容,有需要的可以下载到本地看。

了解手上的硬件

手上开发板的基础信息可以通过屏蔽罩丝印表示,例如我手上的这个开发板屏蔽罩上的丝印是 BLOFN8IR4,对照规格书后可以知道其表示的是:

  • BL:BL618
  • O:外置
  • F:FLASH
  • N:模组温度版本为常温
  • 8:FLASH 大小为 8MB
  • I:内置
  • R:PSRAM
  • 4:PSRAM 大小为 4MB

这代表着我手上这个板子有 4MB 的 PSRAM(不是内存,但可以扩展为内存)和 8MB 的 FLASH。

确定 RAM 和 FLASH 的起始地址

通过查阅参考手册中的 1.4 地址映射,可以得到如下的内存地址映射表:

模块 大小 Cache 开始地址 Non-cache 开始地址
OCRAM 320KB 0x62FC0000 0x22FC0000
WRAM 160KB 0x63010000 0x23010000

以及如下的地址表(部分):

模块 目标 开始地址 大小 描述
FLASH Flash 0xA0000000 128MB 应用程序地址空间
PSRAM pSRAM 0xA8000000 128MB pSRAM 存储器地址空间 (可选项,依赖芯片具体型号)
RAM HBN RAM 0x20010000 4KB HBN RAM, 主要用于超低功耗模式下的数据保存
ROM ROM 0x90000000 128KB Bootrom 区域地址空间
  • OCRAM(On-Chip RAM):OCRAM 是集成在芯片(SoC,System-on-Chip)上的内部 RAM,通常位于处理器或其他核心周围。它的优势在于对于处理器等核心来说,它位于芯片内部,访问速度较快,可以更轻松地与其他芯片内部组件进行通信。由于它是在芯片上的 RAM,因此也称为内嵌式 RAM。

对照一下规格书中的地址映射表可以知道,Flash 的起始地址是 0xA0000000,大小为 8MB;OCRAM 的起始地址是 0x62FC0000,大小为 320KB。

(有人看见映射表里面的 RAM 可能会疑惑为什么不用这个,因为那个 RAM 是 HBN RAM,一般情况下用不上它)

确定外设

Ai-M61-32S-Kit 开发板给的外设其实还挺多的,不过这里因为只是入个门,所以那些复杂的外设我们不碰它,只是玩玩它上面的那个 RGB Logo 灯。通过规格书可知:

  • 红色灯:IO12
  • 绿色灯:IO14
  • 蓝色灯:IO15

这几盏灯都接在 12、14 和 15 GPIO 口上,并且是高电平有效。

PAC

PAC 全称是 Peripheral Access Crate,可以用于访问嵌入式系统外设寄存器。PAC 提供了对硬件寄存器和外设功能的类型安全的抽象,使得在嵌入式系统上进行编程更加方便和可靠。

在嵌入式系统中,通常会有一些外设(如 GPIO、UART、SPI 等),这些外设的控制寄存器位于特定的内存地址。PAC 就是为了方便地访问这些寄存器而创建的。PAC 提供了一个类型安全的 API,使得在编写嵌入式系统代码时,可以避免使用裸指针和不安全的代码。

PAC 通常是由芯片厂商或社区创建的,以支持特定的芯片或开发板。通过使用 PAC,开发者可以使用 Rust 的安全性和表达力,同时又能够方便地访问底层硬件。在 Rust 中,PAC 通常是一个代码生成工具生成的库,根据硬件描述文件自动生成对应的寄存器访问代码。

如何获取 PAC

拿手上的这块 Ai-M61-32S 为例,它所搭载的芯片是 BL618,通过翻阅博通官方库可以知道找到官方发布的bl616-pac - crates.io: Rust Package Registry因此可以直接拿这个来用并不,摸鱼的时候用它写的我焦头烂额,仔细看了看 C 源码和隔壁公司为 bl602 维护的 pac 后发现,里面用于生成的 SVD 文件里寄存器不全,至少我目前看着缺少 AON,以及一部分 GPIO 控制寄存器的,因此我这边要先补上缺失的寄存器再重新用最新的版本来生成一遍。1

生成 PAC

环境准备

由于需要修补厂商提供的 SVD 文件,这里用到的是rust-embedded/svdtools,执行如下命令安装:

cargo install svdtools

生成 PAC 需要使用 svd2rustform,使用如下命令安装:

cargo install svd2rust form

同时我们还需要用到 SVD 文件,这个文件可以在官方 PAC 的仓库中找到。

修补 SVD 文件

找一个合适的位置执行以下命令来生成一个空的 Rust 库:

cargo new bl61x-pac --lib

上面的 bl61x-pac 可以是自己觉得方便的名称,在执行完成后会生成该名称的目录。进入该目录后创建 svd 子文件夹,将前面取得的 SVD 文件放入该目录内。

而后创建 bl618-pac/peripherals 文件夹,在里面放入修补文件。然后在 bl61x-pac 下新建文件 bl61x.yaml 作为修补的配置文件即可。

例如,我这边创建了 AON 寄存器的修补文件 AON.yaml,则配置文件的内容如下:

# Path to the SVD file we're targeting. Relative to this file.
# This must be included only in the device YAML file.
_svd: "./svd/bl616.svd"

# Include other YAML files. Path relative to this file.
_include:
    - "./peripherals/AON.yaml"

完成了这些工作后,目录的结构大概是这样:

bl61x-pac
├─ .gitignore
├─ bl61x.yaml
├─ Cargo.toml
├─ svd
│  └─ bl616.svd
├─ src
│  └─ lib.rs
└─ peripherals
   └─ AON.yaml

随后我们便运行如下的命令生成修补后的 SVD 文件即可:

svdtools patch bl61x.yaml
生成 Rust 文件

编辑一下 Cargo.toml 文件,使得其应该具有如下的内容:2

[package]
name = "bl61x-pac"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
riscv = "0.11.1"
vcell = "0.1.3"

[dependencies.critical_section]
package = "critical-section"
optional = true
version = "1.1.2"

[features]
critical-section = ["critical_section", "riscv/critical-section-single-hart"]
rt = []

并在目录内执行:

svd2rust -i svd/bl616.svd.patched --target=riscv
rm -rf src
form -i lib.rs -o src/ && rm lib.rs
cargo fmt

到这,你便可以调用该 PAC 库进行开发了。如果你们不想自己生成,也可以使用我上面生成的 PAC 库:wsndshx/bl61x-pac (github.com)

Hello World!

这里就按照老规矩,先把板子给点亮(物理)。

创建 Rust 项目

随便找个目录,输入如下的命令创建新 Rust 项目:

cargo new blinky --bin

执行后,会生成具有如下结构的目录:

blinky
├── Cargo.toml
└── src
    └── main.rs

在 blinky 目录内创建 .cargo 文件夹,并在里面创建配置文件 config.toml,配置文件的内容如下:

[target.riscv32imac-unknown-none-elf]
rustflags = [
  "-C", "link-arg=-Tmemory.x",
  "-C", "link-arg=-Tlink.x",
]

[build]
target = "riscv32imac-unknown-none-elf"

上面的配置文件指定了编译时使用 riscv32imac-unknown-none-elf 作为编译时的目标平台,并且使用 memory.x 作为链接脚本。因此接下来在 blinky 目录下新建一个 memory.x 文件,内容如下:

MEMORY
{
    RAM : ORIGIN = 0x62FC0000, LENGTH = 320K
    FLASH : ORIGIN = 0xA0000000, LENGTH = 8M
}

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);

在链接脚本中我定义了 RAM 和 FLASH 的起始地址与长度为文章所确定的数值。但很明显仅靠这小段脚本还不足以声明好运行所需的所有段,不过 riscv-rt 运行库已经内置了通用的链接脚本,我们仅仅再创建一段编译时自动拷贝 memory.x 的 build script 即可。为此需要新建一个 build.rs 文件,并将如下的内容写入到 build.rs 中:

use std::env;
use std::fs::File;
use std::io::Write;
use std::path::PathBuf;

fn main() {
    // Put the linker script somewhere the linker can find it
    let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
    File::create(out.join("memory.x"))
        .unwrap()
        .write_all(include_bytes!("memory.x"))
        .unwrap();
    println!("cargo:rustc-link-search={}", out.display());

    // Only re-run the build script when memory.x is changed,
    // instead of when any part of the source code changes.
    println!("cargo:rerun-if-changed=memory.x");
}

处理依赖

该项目需要如下的依赖:

  • riscv:CPU 寄存器访问与内联汇编函数
  • riscv-rt:启动代码和中断处理程序
  • panic-halt:设置恐慌行为为停止
  • bl61x-pac:用于 BL616/BL618 微控制器的嵌入式 Rust 外设访问库

执行如下命令添加:

cargo add riscv --features critical-section-single-hart
cargo add riscv-rt --features single-hart
cargo add panic-halt
cargo add bl61x-pac --features critical-section

编写 PAC 点灯代码

在用 Rust 点灯之前,先去看看在官方 SDK 中是怎么用 C 实现的。在 SDK 中找到 GPIO 口的输入输出例程,可以看见其中关键的代码是如下的几行:

// 获取名为 gpio 的外设
gpio = bflb_device_get_by_name("gpio");

// 初始化指定 gpio 端口
// 分别对指定的 gpio_config_xx 寄存器中的如下位进行了操作:
// - GPIO_OUTPUT:将 reg_gpio_xx_oe 置 1,使能 gpio 输出
// - GPIO_PULLUP:将 reg_gpio_xx_pu 置 1,使能内部上拉功能
// - GPIO_SMT_EN:将 reg_gpio_xx_smt 置 1,功能不明,使能 SMT
// - GPIO_DRV_0:将 reg_gpio_xx_drv 置 0,输出能力最低
bflb_gpio_init(gpio, GPIO_PIN_0, GPIO_OUTPUT | GPIO_PULLUP | GPIO_SMT_EN | GPIO_DRV_0);

// 指定 gpio 口高电平
// 该函数对寄存器 gpio_cfg138 与 gpio_cfg139 进行操作
// 这两个寄存器共有 35 个有效位,分别设置 35 个 GPIO 口的输出值
// 只有在 GPIO 口处于设置/清除模式时生效
// 若同时进行设置和清除操作,仅设置操作生效
bflb_gpio_set(gpio, GPIO_PIN_0);

// 指定 gpio 口低电平
// 该函数对寄存器 gpio_cfg140 与 gpio_cfg141 进行操作
// 这两个寄存器共有 35 个有效位,分别清除 35 个 GPIO 口的输出值
// 只有在 GPIO 口处于设置/清除模式时生效
// 若同时进行设置和清除操作,仅设置操作生效
bflb_gpio_reset(gpio, GPIO_PIN_0);

分析完上面的例程后,便可以根据例程编写如下的 Rust 代码:

#![no_std]
#![no_main]

extern crate panic_halt;
use riscv_rt::entry;
use bl61x_pac as pac;

#[entry]
fn main() -> ! {
    // 获取外设
    let peripherals = pac::Peripherals::take().unwrap();
    // 配置 GPIO 口第 15 引脚为 GPIO 输出模式,进入 SW-GPIO 模式,使能设置/清除输出模式,并且使能内部上拉功能
    peripherals.GLB.gpio_config(15).write(|w| {
        w.output_function()
            .set_bit()
            .alternate()
            .gpio()
            .pin_mode()
            .set_clear()
            .pull_up()
            .set_bit()
    });
    loop {
        // 将 15 引脚设置为高电平
        peripherals.GLB.gpio_set_0().write(|w| w.gpio_15_set().set_bit());
        peripherals.GLB.gpio_clear_0().write(|w| w.gpio_15_clr().clear_bit());
        // BL618 默认的主频为 320MHz,即每秒 320,000,000 个周期
        // 则如果需要延迟 1 毫秒最多需要消耗 320,000 个周期
        // 并且延迟时间过短会导致常亮
        // 这里延迟 500 毫秒
        for _ in 0..500{
            riscv::asm::delay(320000);
        }
        // 将 15 引脚设置为低电平
        peripherals.GLB.gpio_set_0().write(|w| w.gpio_15_set().clear_bit());
        peripherals.GLB.gpio_clear_0().write(|w| w.gpio_15_clr().set_bit());
        for _ in 0..500{
            riscv::asm::delay(320000);
        }
    }
}

编写完之后,使用如下的命令编译项目:

cargo build -r

并使用如下的命令生成固件镜像:

rust-objcopy --strip-all target/riscv32imac-unknown-none-elf/release/blinky -O binary target/riscv32imac-unknown-none-elf/release/blinky.bin

烧录固件(首次)

为什么是首次呢,因为你此时不清楚板子内部到底烧录了什么程序,因此先完整跑一次烧录流程。3

这里需要用到博流的图形化工具 Bouffalo Lab Dev Cube 烧录编译出来的固件,下载地址见:下载 | 博流智能开发者社区 (bouffalolab.com)

下载并解压后,双击 BLDevCube.exe,选择 BL616/BL618:

BLDevCube 启动界面

进入软件主界面后,按照下图的说明进行配置:

软件主界面.png

其中:

配置完成后,将开发板使用 USB 连接线连接至电脑,并在按着下图中的③按键的同时短按②按键进入烧录模式。

1708919955219.png

进入烧录模式后,点击软件的 Refresh,刷新端口,再点击 Create & Download 开始烧录。当软件里面的那个大大的进度条跑到 100% 并且是绿色的时候,固件便烧录成功了。

再次短按开发板上的复位键,此时就可以观察到开发板上的蓝灯以一秒一次的速度亮起来了~

百回「可愛い」って言うまで放さないい!
回复

使用道具 举报

干簧管 | 2024-2-26 19:02:06 | 显示全部楼层
赞👍
回复

使用道具 举报

7788 | 2024-2-26 20:10:49 | 显示全部楼层
回复

使用道具 举报

物联网 | 2024-2-26 20:22:29 | 显示全部楼层
回复

使用道具 举报

sansui | 2024-2-26 22:39:09 | 显示全部楼层
赞一下
回复

使用道具 举报

bzhou830 | 2024-2-27 08:42:48 | 显示全部楼层
不错不错!
选择去发光,而不是被照亮
回复

使用道具 举报

1084504793 | 2024-2-27 08:47:55 | 显示全部楼层
回复

使用道具 举报

lazy | 2024-2-27 08:57:37 | 显示全部楼层
很专业呀
回复

使用道具 举报

bzhou830 | 2024-2-27 09:08:39 | 显示全部楼层
赞一下
选择去发光,而不是被照亮
回复

使用道具 举报

WT_0213 | 2024-2-27 09:16:39 | 显示全部楼层
赞一下
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则