esp-template
简介
既然我们已经了解了如何生成一个 no_std
项目,让我们看看生成的项目里有哪些东西,
尝试理解它的各个部分,并运行它。
检查生成的项目
从 esp-template
创建项目,使用以下配置:
- Which MCU to target? ·
esp32c3
- Configure advanced template options? ·
false
在本节中,我们使用默认配置。如果需要进一步修改,请参阅 additional prompts。
应该会生成类似这样的文件结构:
├── .cargo
│ └── config.toml
├── src
│ └── main.rs
├── .gitignore
├── Cargo.toml
├── LICENSE-APACHE
├── LICENSE-MIT
└── rust-toolchain.toml
在进一步讨论之前,让我们看看这些文件的用途。
.cargo/config.toml
- Cargo 的配置
- 定义了一些用于正确构建项目的选项
- 包含
runner = "espflash flash --monitor"
- 这意味着你可以用cargo run
来烧写并监视代码
src/main.rs
- 项目的主要源文件
- 关于它的详细信息,请参阅下面的
main.rs
简介一节
.gitignore
- 指示
git
要忽略哪些目录和文件
- 指示
Cargo.toml
- Cargo 清单(manifest),通常声明了一些元数据和项目的依赖项
LICENSE-APACHE
,LICENSE_MIT
- 这些是 Rust 生态中最常用的许可证
- 如果想使用其他许可证,可以删除这些文件,并修改
Cargo.toml
中的许可证
rust-toolchain.toml
- 定义要使用的 Rust 工具链的种类
- 根据目标设备,工具链可以是
nightly
或esp
- 根据目标设备,工具链可以是
- 定义要使用的 Rust 工具链的种类
main.rs
简介
1 #![no_std]
2 #![no_main]
#![no_std]
- 用于告知 Rust 编译器这段代码不使用
libstd
- 用于告知 Rust 编译器这段代码不使用
#![no_main]
no_main
属性表示该程序不使用标准的 main 接口,这通常用在有完整的操作系统的情况下。我们将使用esp-riscv-rt
crate 中的入口(entry)属性来创建一个自定义入口点(entry point),而不是使用标准的 main。在此程序中,我们将入口点命名为main
,但也可以使用任何其他名称。入口点函数必须是发散函数,即具有签名fn foo() -> !
,这种类型表明该函数永远不会返回——这意味着程序永远不会终止。
4 use esp_backtrace as _;
5 use esp_println::println;
6 use esp_hal::{clock::ClockControl, peripherals::Peripherals, prelude::*, timer::TimerGroup, Rtc};
use esp_backtrace as _;
- 由于我们处于裸机环境中,因此需要一个 panic 处理程序,该处理程序在代码发生 panic 时运行
- 有多种不同的 crate 可选(例如
panic-halt
),但是esp-backtrace
提供了一个打印回溯地址的实现——与espflash
配合,这些地址可以被解析为源代码中的位置
use esp_println::println;
- 提供了
println!
的实现
- 提供了
use esp_hal:{...}
- 我们需要导入一些类型,以待后续使用
8 #[entry]
9 fn main() -> ! {
10 let peripherals = Peripherals::take();
11 let system = peripherals.SYSTEM.split();
12 let clocks = ClockControl::max(system.clock_control).freeze();
13
14 println!("Hello world!");
15
16 loop {}
17 }
main
函数中包含:
let peripherals = Peripherals::take()
- HAL 驱动通常会通过 PAC(Peripheral Access Crate,外设访问 crate)获取到外设的所有权
- 这里我们从 PAC 中取出所有外设,之后将他们传递给 HAL 驱动
let mut system = peripherals.SYSTEM.split();
- 有时,外设(此处为 System 外设)是粗粒度的,并不完全适合 HAL 驱动——因此这里我们将 System 外设分割成更小的部分,然后传递给驱动程序
let clocks = ClockControl::max(system.clock_control).freeze();
- 这里配置了系统时钟——本例中,提升到最大值
- 然后我们冻结了时钟,之后就不能再次修改它了
- 某些驱动需要一个时钟的引用,以便知道如何计算速率和时长
println!("Hello world!");
- 打印 “Hello world!”
loop {}
- 因为这个函数不应该返回,我们在一个死循环中不执行任何操作
运行代码
构建和运行这段代码只需:
cargo run
这会根据配置构建代码,并执行 espflash
将其烧写到板子上。
由于 runner
配置还会将 --monitor
参数传递给 espflash
,屏幕上将显示打印的内容。
确保已经安装了 espflash
,否则此步骤会失败。执行此命令以安装 espflash
:
cargo install espflash
屏幕上应该会显示类似这样的内容:
[2023-04-17T14:17:08Z INFO ] Serial port: '/dev/ttyACM0'
[2023-04-17T14:17:08Z INFO ] Connecting...
[2023-04-17T14:17:09Z INFO ] Using flash stub
[2023-04-17T14:17:09Z WARN ] Setting baud rate higher than 115,200 can cause issues
Chip type: esp32c3 (revision v0.3)
Crystal frequency: 40MHz
Flash size: 4MB
Features: WiFi, BLE
MAC address: 60:55:f9:c0:39:7c
App/part. size: 203,920/4,128,768 bytes, 4.94%
[00:00:00] [========================================] 13/13 0x0
[00:00:00] [========================================] 1/1 0x8000
[00:00:01] [========================================] 64/64 0x10000
[2023-04-17T14:17:11Z INFO ] Flashing has completed!
Commands:
CTRL+R Reset chip
CTRL+C Exit
...
Hello world!
这些是第一和第二阶段 bootloader 产生的信息,然后是我们的 “Hello world” 信息!
这就是这段代码做的事情。
可以按 CTRL+R
重启,或按 CTRL+C
退出。
如果在构建项目时遇到了什么问题,请查看 Troubleshooting 章节。