Ai-M61-32S-Kit 开发板 Rust 裸机编程指南:PAC
开发资料一览
- Ai-M61 系列模组专题 | 安信可科技 (ai-thinker.com)
- Ai-M61-32S 模组规格书
- Ai-M61-32S-Kit 开发板规格书
- BL616/BL618参考手册 · bouffalolab/bl_docs (github.com)
- BL616/BL618数据手册 · bouffalolab/bl_docs (github.com)
- 文档 | 博流智能开发者社区 (bouffalolab.com)
下文中的大部分内容都会参考以上官方手册的内容,有需要的可以下载到本地看。
了解手上的硬件
手上开发板的基础信息可以通过屏蔽罩丝印表示,例如我手上的这个开发板屏蔽罩上的丝印是 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,执行如下命令安装:
1 | cargo install svdtools |
生成 PAC 需要使用 svd2rust
与 form
,使用如下命令安装:
1 | cargo install svd2rust form |
同时我们还需要用到 SVD 文件,这个文件可以在官方 PAC 的仓库中找到。
修补 SVD 文件
找一个合适的位置执行以下命令来生成一个空的 Rust 库:
1 | cargo new bl61x-pac --lib |
上面的 bl61x-pac
可以是自己觉得方便的名称,在执行完成后会生成该名称的目录。进入该目录后创建 svd 子文件夹,将前面取得的 SVD 文件放入该目录内。
而后创建 bl618-pac/peripherals
文件夹,在里面放入修补文件。然后在 bl61x-pac
下新建文件 bl61x.yaml
作为修补的配置文件即可。
例如,我这边创建了 AON
寄存器的修补文件 AON.yaml
,则配置文件的内容如下:
1 | # Path to the SVD file we're targeting. Relative to this file. |
完成了这些工作后,目录的结构大概是这样:
1 | bl61x-pac |
随后我们便运行如下的命令生成修补后的 SVD 文件即可:
1 | svdtools patch bl61x.yaml |
生成 Rust 文件
编辑一下 Cargo.toml
文件,使得其应该具有如下的内容:[2]
1 | [package] |
并在目录内执行:
1 | svd2rust -i svd/bl616.svd.patched --target=riscv |
到这,你便可以调用该 PAC 库进行开发了。如果你们不想自己生成,也可以使用我上面生成的 PAC 库:wsndshx/bl61x-pac (github.com)。
Hello World!
这里就按照老规矩,先把板子给点亮(物理)。
创建 Rust 项目
随便找个目录,输入如下的命令创建新 Rust 项目:
1 | cargo new blinky --bin |
执行后,会生成具有如下结构的目录:
1 | blinky |
在 blinky 目录内创建 .cargo
文件夹,并在里面创建配置文件 config.toml
,配置文件的内容如下:
1 | [target.riscv32imac-unknown-none-elf] |
上面的配置文件指定了编译时使用 riscv32imac-unknown-none-elf 作为编译时的目标平台,并且使用 memory.x
作为链接脚本。因此接下来在 blinky 目录下新建一个 memory.x
文件,内容如下:
1 | MEMORY |
在链接脚本中我定义了 RAM 和 FLASH 的起始地址与长度为文章所确定的数值。但很明显仅靠这小段脚本还不足以声明好运行所需的所有段,不过 riscv-rt 运行库已经内置了通用的链接脚本,我们仅仅再创建一段编译时自动拷贝 memory.x
的 build script 即可。为此需要新建一个 build.rs
文件,并将如下的内容写入到 build.rs
中:
1 | use std::env; |
处理依赖
该项目需要如下的依赖:
- riscv:CPU 寄存器访问与内联汇编函数
- riscv-rt:启动代码和中断处理程序
- panic-halt:设置恐慌行为为停止
- bl61x-pac:用于 BL616/BL618 微控制器的嵌入式 Rust 外设访问库
执行如下命令添加:
1 | cargo add riscv --features critical-section-single-hart |
编写 PAC 点灯代码
在用 Rust 点灯之前,先去看看在官方 SDK 中是怎么用 C 实现的。在 SDK 中找到 GPIO 口的输入输出例程,可以看见其中关键的代码是如下的几行:
1 | // 获取名为 gpio 的外设 |
分析完上面的例程后,便可以根据例程编写如下的 Rust 代码:
1 |
|
编写完之后,使用如下的命令编译项目:
1 | cargo build -r |
并使用如下的命令生成固件镜像:
1 | 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:
进入软件主界面后,按照下图的说明进行配置:
其中:
- partition table:分区表,这里直接用软件自带的,点击 Browse 弹出来的窗口中选择 partition_cfg_4M
- boot2:boot2 需要去博流的 bouffalo_sdk 处下载,下载地址:bouffalo_sdk/bsp/board/bl616dk/config at master · bouffalolab/bouffalo_sdk (github.com)
- firmware:前面生成的固件镜像,位置在 target/riscv32imac-unknown-none-elf/release/blinky.bin
配置完成后,将开发板使用 USB 连接线连接至电脑,并在按着下图中的③按键的同时短按②按键进入烧录模式。
进入烧录模式后,点击软件的 Refresh
,刷新端口,再点击 Create & Download
开始烧录。当软件里面的那个大大的进度条跑到 100% 并且是绿色的时候,固件便烧录成功了。
再次短按开发板上的复位键,此时就可以观察到开发板上的蓝灯以一秒一次的速度亮起来了~