跳过正文

开发 Rust no_std 应用

·2324 字
Rust Esp32 Rust Esp32
目录
rust-esp32 - 这篇文章属于一个选集。
§ 6: 本文

no_std 应用是 bare-metal 实现,不依赖 esp-idf 及其提供的 FreeRTOS 操作系统和 Rust std 标注库,而是使用 std 的一个子集 core 库,core 库不支持 heap 内存分配和线程。当前支持 HAL/WIFI/BLE/ESP-NOW/Backtrace/Storage 等。

no_std 不依赖于 C/C++ 开发的 esp-idf 及其提供的 FreeRTOS 操作系统环境,而是基于 esp-pacs/esp-hal 开发的 xtensa-esp32s3-none-elf 应用。

esp-alloc crate 为 no_std 提供了 heap 内存分配的支持;例子:esp-examples/alloc

no_std 相关的库:

  • esp-pacs : Peripheral access crates,是根据处理的 SVD 描述文件生成的 Peripheral Access Crates (PACs) ,它是低级的处理器寄存器定义 unsafe 封装。理论上可以直接基于 esp-pacs 开发 no_std 应用,但是因为层次太低级,开发效率不高,所以一般使用 esp-hal 等高层封装来开发。

  • esp-hal : Hardware abstraction layer. async traits from the various packages in the embedded-hal repository.

    • 相比 esp-pacs crate,esp-hal 是高层次的封装,一般实现了 embedded-hal 和 async trait,底层基于 esp-pacs;
    • esp-hal 为 embassy 提供了实现,可以通过 embassy async task 来实现 并发任务
    • 2024.03.08 发布的 0.16.0 版本开始,将以前芯片相关的 esp32-hal, esp32c3-hal 等 crate 合并为一个单独的 esp-hal crate。使用时在 features 中指定 CPU 类型,如: esp-hal = { version = "0.17.0", features = [ "esp32s3" ] }

说明:esp-pacs 和 esp-hal 是 no_std 应用开发的基础。

  • esp-wifi : Wi-Fi, BLE and ESP-NOW support

  • esp-alloc : Simple heap allocator. This allocator is built on top of the phil-opp/linked-list-allocator crate, which does most of the heavy lifting. While it’s often advisable to avoid allocations in such limited environments, there are scenarios when an allocator is still required and/or desirable.

  • esp-println : print!, println!. esp-println allows for printing over UART, USB Serial JTAG, or RTT without any required dependencies. 实现 log crate 的 trait,用于打印日志到串口;

  • esp-backtrace : Exception and panic handlers. As the name implies, esp-backtrace enables backtraces in no_std applications. It additionally provides a panic handler and exception handler , both behind features. This crate makes debugging issues much easier.

    • 为了正确显示 panic 的行号和函数符号,需要确保在 release profile 中配置 debug = true 参数(dev profile 缺省是该值)。
  • esp-storage : Embedded-storage traits to access unencrypted flash memory

  • esp-ieee802154 : Low-level IEEE802.15.4 driver for the ESP32-C6 and ESP32-H2

  • esp-openthread : A bare-metal Thread implementation using esp-ieee802154

对比:

  1. std 的 esp-idf-hal 实现了 embeded-hal 和 async trait,底层基于 C/C++ esp-idf;
  2. no_std 的 esp-hal 实现了 embeded-hal 和 async trait,底层基于 esp-pacs;

其它开源的 no_std 库(embedded-* 是 Rust 嵌入式工作组或社区提供的 no_std 应用项目):

  1. embedded-graphics: Embedded-graphics is a 2D graphics library that is focused on memory constrained embedded devices.
  2. embedded-layout: Simple layout/alignment functions
  3. embedded-text: TextBox with text alignment options

embassy 是支持 async 的 no_std 库。

使用 esp-rs/esp-template 模板来快速创建 no_std 类型项目:

  • 为了在 panic 时打印代码行和符号,需要在 release profile 中添加配置 debug = 2 # 2/full/true :full debug info, 虽然二进制包含 debuginfo,但是烧写时会被去掉,并不会增加 flash app 体积。

创建一个 no_std Bare-Metal 项目, 自定义是否使用 WiFi/Bluetooth/ESP-NOW via the esp-wifi crate;

zj@a:~/code/esp32$ cargo generate esp-rs/esp-template

# no_std 应用需要声明 no_std 和 no_main 宏,这样编译器才不会导入 std 库。
# #![no_std] 告诉编译器不导入和链接 libstd 库。
# #![no_main] 告诉编译器不使用标准的 main 接口,而是使用 esp 提供的 main 入口。
zj@a:~/code/esp32/non_std$ cat myesp-nonstd/src/main.rs
#![no_std]
#![no_main]

# in a bare-metal environment, we need a panic handler that runs if a panic occurs in code There are
# a few different crates you can use (e.g panic-halt) but esp-backtrace provides an implementation
# that prints _the address of a backtrace_ - together with espflash these addresses can get decoded
# into source code locations
use esp_backtrace as _;

use esp_hal::{clock::ClockControl, peripherals::Peripherals, prelude::*, delay::Delay};

#[entry]
fn main() -> ! {
    let peripherals = Peripherals::take();
    let system = peripherals.SYSTEM.split();

    let clocks = ClockControl::max(system.clock_control).freeze();
    let delay = Delay::new(&clocks);

    esp_println::logger::init_logger_from_env();

    loop {
        log::info!("Hello world!");
        delay.delay(500.millis());
    }
}

按需配置 Cargo.toml:

zj@a:~/code/esp32/non_std$ cat myesp-nonstd/Cargo.toml
[package]
name = "myesp-nonstd"
version = "0.1.0"
authors = ["alizj"]
edition = "2021"
license = "MIT OR Apache-2.0"

[dependencies]
# esp-has 是 no_std 类型 crate, features 中指定了 CPU 类型
esp-hal = { version = "0.17.0", features = [ "esp32s3" ] }

# bare-metal 环境下,当程序 panic 时打印调用栈
esp-backtrace = { version = "0.11.0", features = [
    "esp32s3",
    "exception-handler",
    "panic-handler",
    "println",
] }

# esp-println 启用 log feature 后,为 log 提供具体的实现
esp-println = { version = "0.9.0", features = ["esp32s3", "log"] }

# 向终端打印日志。esp_println 提供了 log 的具体实现
log = { version = "0.4.20" }

[profile.dev]
# Rust debug is too slow.  For debug builds always builds with some optimization
opt-level = "s" # optimize for binary size

# dev profile 的 debug 参数默认为 2,表示 full debug info,
# release profile 的 debug 参数默认为 0,表示关闭 debug info;

[profile.release]
codegen-units = 1 # LLVM can perform better optimizations using a single thread
debug = 2 # 2/full/true:full debug info, 虽然二进制包含 debuginfo,但是烧写时会被去掉,所以不会增加 flash app 体积
debug-assertions = false
incremental = false
lto = 'fat'
opt-level = 's'
overflow-checks = false

按需配置 rust-toolchain.toml :

zj@a:~/code/esp32/myesp-nonstd$ cat rust-toolchain.toml
[toolchain]
channel = "esp" # 使用 esp channel 工具链

按需配置 .cargo/config.toml :

zj@a:~/code/esp32/myesp-nonstd$ cat .cargo/config.toml
[target.xtensa-esp32s3-none-elf]
runner = "espflash flash --monitor"  # cargo run 会烧录 flash 和读终端日志

[env]
ESP_LOGLEVEL="INFO"

[build]
rustflags = [
  "-C", "link-arg=-nostartfiles",
]

target = "xtensa-esp32s3-none-elf" # 使用不链接 esp-idf 的 none-elf 工具链

[unstable]
build-std = ["core"]  # 使用 core 库而非 std 库!

构建和烧录:

zj@a:~/code/esp32$ cd myesp-nonstd/

zj@a:~/code/esp32/myesp-nonstd$ source ~/esp/export-esp.sh

# 构建,只使用 --release profile
zj@a:~/code/esp32/myesp-nonstd$ cargo build --release

# cargo 会运行 espflash 来烧写 binary
zj@a:~/code/esp32/myesp-nonstd$ cargo run

no_std 构建结果 只有二进制 myesp-nonstd, 不包含构建 std 应用时生成的 bootloader.bin 和 partition-table.bin:

zj@a:~/code/esp32/non_std$ ls -l target/xtensa-esp32s3-none-elf/debug/
total 2.0M
drwxr-xr-x  27 alizj  864  5  9 12:20 build/
drwxr-xr-x 338 alizj  11K  5 10 15:27 deps/
drwxr-xr-x   2 alizj   64  5  8 15:06 examples/
drwxr-xr-x   5 alizj  160  5  9 12:21 incremental/
-rwxr-xr-x   1 alizj 2.0M  5 10 15:27 myesp-nonstd*
-rw-r--r--   1 alizj  194  5  8 15:06 myesp-nonstd.d

参考:

  1. 官方文档:Embedded Rust (no_std) on Espressif
  2. 官方 non_std 示例:https://github.com/esp-rs/no_std-training
  3. https://apollolabsblog.hashnode.dev/series/esp32c3-embedded-rust-hal 强烈推荐。
  4. https://github.com/apollolabsdev/ESP32C3
  5. Bare-Metal Rust on ESP32: A Brief Overview
  6. https://apollolabsblog.hashnode.dev/the-embedded-rust-esp-development-ecosystem

1 在 Rust no_std 应用中使用 defmt 日志框架
#

defmt 是一种 no_std 应用的 logging framework,它将 ESP32 芯片中应用打印的日志延迟到 host server 上格式化,从而降低 ESP32 芯片应用的内存开销。

ESP32 no_std book 的 defmt 例子:https://docs.esp-rs.org/no_std-training/03_7_defmt.html

对于 ESP32 no_std 应用来说, esp-println, esp-backtrace and espflash/cargo-espflash provide mechanisms to use defmt :

  1. espflash has support for different logging formats, one of them being defmt.
    • espflash requires framming bytes as when using defmt it also needs to print non-defmt messages, like the bootloader prints. It’s important to note that other defmt-enabled tools like probe-rs won’t be able to parse these messages due to the extra framing bytes. Uses rzcobs encoding
  2. esp-println has a defmt-espflash feature, which adds framming bytes so espflash knows that is a defmt message.
  3. esp-backtrace has a defmt feature that uses defmt logging to print panic and exception handler messages.

在代码里使用 defmt::println!() 等宏来打印日志。

If you want to use any of the logging macros like info, debug

  • Enable the log feature of esp-println
  • When building the app, set DEFMT_LOG level.

defmt-rtt:Transmit defmt log messages over the RTT (Real-Time Transfer) protocol https://github.com/knurling-rs/defmt/tree/main/firmware/defmt-rtt

embassy 依赖于 defmt 和 defmt-rtt:

  1. https://embassy.dev/book/dev/project_structure.html
  2. https://embassy.dev/book/dev/basic_application.html
rust-esp32 - 这篇文章属于一个选集。
§ 6: 本文

相关文章

Rust 驱动 Audio - 播放和录音
·6323 字
Rust Esp32 Rust Esp32
Rust 驱动 Camera - 采集和播放
·7304 字
Rust Esp32 Rust Esp32
Rust 驱动 LCD - 显示中英文
·818 字
Rust Esp32 Rust Esp32
Rust 驱动 LCD - 显示图片
·4232 字
Rust Esp32 Rust Esp32