esp32笔记[7]-使用rust+zig开发入门

摘要

使用rust(no-std)+zig⚡️开发esp32c3入门,测试例程,实现rust调用zig中的加法函数并通过串口打印.

平台信息

  • esp32c3
  • rust
  • zig

esp32c3简介

[https://docs.espressif.com/projects/esp-idf/zh_CN/latest/esp32c3/get-started/index.html]

  • riscv32imc-unknown-none-elf

ESP32-C3 SoC 芯片支持以下功能:

  • 2.4 GHz Wi-Fi
  • 低功耗蓝牙
  • 高性能 32 位 RISC-V 单核处理器
  • 多种外设
  • 内置安全硬件
    ESP32-C3 采用 40 nm 工艺制成,具有最佳的功耗性能、射频性能、稳定性、通用性和可靠性,适用于各种应用场景和不同功耗需求。

rust+zig⚡️开发esp32简介

zig⚡️语言

[https://ziglang.org]
Zig is a general-purpose programming language and toolchain for maintaining robust, optimal and reusable software.
Maintain it with Zig
Incrementally improve your C/C++/Zig codebase.
Use Zig as a zero-dependency, drop-in C/C++ compiler that supports cross-compilation out-of-the-box.
Leverage zig build to create a consistent development environment across all platforms.
Add a Zig compilation unit to C/C++ projects; cross-language LTO is enabled by default.
A fresh approach to metaprogramming based on compile-time code execution and lazy evaluation.

Call any function at compile-time.
Manipulate types as values without runtime overhead.
Comptime emulates the target architecture.
Focus on debugging your application rather than debugging your programming language knowledge.

No hidden control flow.
No hidden memory allocations.
No preprocessor, no macros.

rust语言简介

[https://esp-rs.github.io/no_std-training/01_intro.html]
Rust是一种现代的系统编程语言,它具有内存安全、并发性和高性能的特点。在嵌入式领域,Rust也逐渐得到了应用,并且在ESP32开发上也有相应的支持。

rust no-std裸金属开发(no-std方式对比std方式)

[https://github.com/esp-rs/esp-hal]
[https://esp-rs.github.io/no_std-training]
Espressif provides a C-based development framework called ESP-IDF. It has, or will have, support for all Espressif chips starting with the ESP32, note that this framework doesn't support the ESP8266.

ESP-IDF, in turn, provides a newlib environment with enough functionality to build the Rust standard library (std) on top of it. This is the approach that is being taken to enable std support on Epressif devices.

The Espressif products supported for Rust std development are the ones supported by the ESP-IDF framework. For details on different versions of ESP-IDF and support of Espressif chips, see this table.

When using std, you have access to a lot of features that exist in ESP-IDF, including threads, mutexes and other synchronization primitives, collections, random number generation, sockets, etc.

Relevant esp-rs Crates
esp-idf-svc包括全部封装
Repository Description
embedded-svc Abstraction traits for embedded services (WiFi, Network, Httpd, Logging, etc.)
esp-idf-svc An implementation of embedded-svc using esp-idf drivers.
esp-idf-hal An implementation of the embedded-hal and other traits using the esp-idf framework.
esp-idf-sys Rust bindings to the esp-idf development framework. Gives raw (unsafe) access to drivers, Wi-Fi and more.

Rich functionality: If your embedded system requires lots of functionality like support for networking protocols, file I/O, or complex data structures, you will likely want to use hosted-environment approach because std libraries provide a wide range of functionality that can be used to build complex applications.
Portability: The std crate provides a standardized set of APIs that can be used across different platforms and architectures, making it easier to write code that is portable and reusable.
Rapid development: The std crate provides a rich set of functionality that can be used to build applications quickly and efficiently, without worrying, too much, about low-level details.

Using no_std may be more familiar to embedded Rust developers. It doesn't use std (the Rust standard library) but instead uses a subset, the core library. The Embedded Rust Book has a great section on this.

It is important to note that no_std uses the Rust core library. As this library is part of the Rust standard library, a no_std crate can compile in std environment. However, the opposite isn't true: an std crate can't compile in no_std environment. This information is worth remembering when deciding which library to choose.

✅ in Wi-Fi/BLE/ESP-NOW means that the target supports, at least, one of the listed technologies. For details, see Current support table of the esp-wifi repository.
ESP8266 HAL is in maintenance mode and no further development will be done for this chip.

Relevant esp-rs Crates

Repository Description
esp-hal Hardware abstraction layer
esp-pacs Peripheral access crates
esp-wifi Wi-Fi, BLE and ESP-NOW support
esp-alloc Simple heap allocator
esp-println print!, println!
esp-backtrace Exception and panic handlers
esp-storage Embedded-storage traits to access unencrypted flash memory

When You Might Want to Use the Core Library (no_std)

Small memory footprint: If your embedded system has limited resources and needs to have a small memory footprint, you will likely want to use bare-metal because std features add a significant amount of final binary size and compilation time.
Direct hardware control: If your embedded system requires more direct control over the hardware, such as low-level device drivers or access to specialized hardware features you will likely want to use bare-metal because std adds abstractions that can make it harder to interact directly with the hardware.
Real-time constraints or time-critical applications: If your embedded system requires real-time performance or low-latency response times because std can introduce unpredictable delays and overhead that can affect real-time performance.
Custom requirements: bare-metal allows more customization and fine-grained control over the behavior of an application, which can be useful in specialized or non-standard environments.

⚠️ Note: There are several examples covering the use of specific peripherals under the examples' folder of every SoC esp-hal. E.g. esp32c3-hal/examples.

rust-offline:尽量使用离线方式运行cargo

[https://blog.csdn.net/u012067469/article/details/128046844]
[https://github.com/stuartZhang/cargo-offline]
cargo-offline是标准cargo命令的包装器。其被用来,根据·距离cargo-offline命令执行目录最近的Cargo.toml文件是否被修改过,来给被包装的cargo命令条件地增补--offline命令行参数(即,离线编译)。形象地讲,就是将cargo check条件地变形为cargo check --offline。
仅首次编译·或·在依赖项变更时,cargo命令才【连线】编译与同步本地的crates.io-index索引清单 —— 有限且可控的“搬梯子”还是可以经济承受的。
在所有其它时候,cargo命令皆【离线】编译 —— 没事少连线github.com。

#选择缓存Cargo.toml文件【修改时间】至Cargo.toml [metadata]
cargo install cargo-offline --features=cargo-metadata

实现

  1. 使用docker镜像一键搭建开发环境.
    [https://esp-rs.github.io/book/installation/using-containers.html]
    [https://hub.docker.com/r/espressif/idf-rust/tags]
    Depending on your operating system, you can choose any container runtime, such as Docker, Podman, or Lima.

Espressif provides a C-based development framework called ESP-IDF. It has, or will have, support for all Espressif chips starting with the ESP32, note that this framework doesn't support the ESP8266.

#官方镜像有arm64和amd64版本,但是没有zig编译器
docker pull espressif/idf-rust:all_latest

使用笔者的镜像:

#编译机为arm64架构
docker run -it --rm -v $PWD:/srv --network=bridge qsbye/build-env5:v0.5-arm64 bash
export https_proxy=http://192.168.0.103:7890 http_proxy=http://192.168.0.103:7890 all_proxy=socks5://192.168.0.103:7890
  1. 使用模板新建工程
#git clone https://github.com/georgik/esp32c3-rust-zig.git
#容器中
cp /home/esp/esp32c3-rust-zig /srv
cd /srv/esp32c3-rust-zig

工程目录如下:

.
├── Cargo.lock
├── Cargo.toml
├── LICENSE-APACHE
├── LICENSE-MIT
├── README.md
├── rust-toolchain.toml
├── src
│   └── main.rs
├── target
│   ├── CACHEDIR.TAG
│   ├── release
│       ├── build
│       ├── examples
│       └── incremental
│           ├── esp32c3_zig
│           ├── esp32c3_zig.d
│           ├── examples
│           └── incremental
└── ziglib
    ├── build.zig
    ├── libmain.a
    ├── libmain.a.o
    ├── src
    └── main.zig
  1. 关键代码
    src/main.rs
#![no_std]
#![no_main]

//与zig文件通信
#[link(name = "main")]
extern "C" {
    fn add(x: i32, y: i32) -> i32;
}

use esp_println::println;

use esp32c3_hal::{
    clock::ClockControl, peripherals::Peripherals, prelude::*, timer::TimerGroup, Rtc,Delay,
};
use esp_backtrace as _;

#[riscv_rt::entry]
fn main() -> ! {
    let peripherals = Peripherals::take();
    let system = peripherals.SYSTEM.split();
    let clocks = ClockControl::boot_defaults(system.clock_control).freeze();

    // Disable the RTC and TIMG watchdog timers
    let mut rtc = Rtc::new(peripherals.RTC_CNTL);
    let timer_group0 = TimerGroup::new(peripherals.TIMG0, &clocks);
    let mut wdt0 = timer_group0.wdt;
    let timer_group1 = TimerGroup::new(peripherals.TIMG1, &clocks);
    let mut wdt1 = timer_group1.wdt;
    // 延时函数初始化
    let mut delay = Delay::new(&clocks);

    rtc.swd.disable();
    rtc.rwdt.disable();
    wdt0.disable();
    wdt1.disable();

    println!("Compute");
    unsafe {
        println!("{}", add(4,5));
        
    }

    loop {
        println!("Compute");
        unsafe {
            println!("{}", add(4,5));//波特率115200
        }
        delay.delay_ms(2000u32);//延时2000ms
    }

}

ziglib/src/main.zig

const std = @import("std");
const testing = std.testing;

export fn add(a: i32, b: i32) i32 {
    return a + b;
}

test "basic add functionality" {
    try testing.expect(add(3, 7) == 10);
}

ziglib/build.zig

const std = @import("std");

pub fn build(b: *std.build.Builder) void {
    // Standard release options allow the person running `zig build` to select
    // between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall.
    const mode = b.standardReleaseOptions();

    const lib = b.addStaticLibrary("ziglib", "src/main.zig");
    lib.setBuildMode(mode);
    lib.install();

    const main_tests = b.addTest("src/main.zig");
    main_tests.setBuildMode(mode);

    const test_step = b.step("test", "Run library tests");
    test_step.dependOn(&main_tests.step);
}

rust-toolchain.toml

[toolchain]
channel = "nightly"

Cargo.toml

[package]
name = 'esp32c3_zig'
edition = '2021'
version = '0.1.0'
authors = [
    'Juraj Michálek <juraj.michalek@espressif.com>',
    'qsbye',
]
license = 'MIT OR Apache-2.0'

[package.metadata]
last-modified-system-time = 1697730153

[dependencies]
esp32c3-hal = '0.5.0'

[dependencies.esp-backtrace]
version = '0.4.0'
features = [
    'esp32c3',
    'panic-handler',
    'print-uart',
]

[dependencies.esp-println]
version = '0.3.1'
features = ['esp32c3']

[dependencies.riscv-rt]
version = '0.11'
features = []
optional = true

[features]
default = ['rt']
rt = ['riscv-rt']

.cargo/config.toml

[target.riscv32imc-unknown-none-elf]
runner = "espflash flash --monitor"
#runner = "wokwi-server --chip esp32c3"

[build]
rustflags = [
  # enable the atomic codegen option for RISCV
  "-C", "target-feature=+a",

  # Tell the `core` library that we have atomics, even though it's not
  # specified in the target definition
  "--cfg", "target_has_atomic_load_store",
  "--cfg", 'target_has_atomic_load_store="8"',
  "--cfg", 'target_has_atomic_load_store="16"',
  "--cfg", 'target_has_atomic_load_store="32"',
  "--cfg", 'target_has_atomic_load_store="ptr"',
  "--cfg", "target_has_atomic",
  "--cfg", 'target_has_atomic="8"',
  "--cfg", 'target_has_atomic="16"',
  "--cfg", 'target_has_atomic="32"',
  "--cfg", 'target_has_atomic="ptr"',

  # Required to obtain backtraces (e.g. when using the "esp-backtrace" crate.)
  # NOTE: May negatively impact performance of produced code
  "-C", "force-frame-pointers",
  "-C", "link-arg=-Tlinkall.x",
  "-C", "link-arg=-Lziglib",
  "-C", "link-arg=-lmain"
]
target = "riscv32imc-unknown-none-elf"


[unstable]
build-std = ["core"]
  1. 编译及运行工程
cargo install espflash
cargo run
#或者
cargo-offline run

效果

cargo-offline run
Finished dev [unoptimized + debuginfo] target(s) in 0.03s
     Running `espflash flash --monitor target/riscv32imc-unknown-none-elf/debug/esp32c3_zig`
[2023-10-21T03:43:19Z INFO ] Detected 6 serial ports
[2023-10-21T03:43:19Z INFO ] Ports which match a known common dev board are highlighted
[2023-10-21T03:43:19Z INFO ] Please select a port
[2023-10-21T03:43:20Z INFO ] Serial port: '/dev/cu.wchusbserial56910187941'
[2023-10-21T03:43:20Z INFO ] Connecting...
[2023-10-21T03:43:21Z INFO ] Using flash stub
Chip type:         esp32c3 (revision v0.4)
Crystal frequency: 40MHz
Flash size:        4MB
Features:          WiFi, BLE
MAC address:       d4:f9:8d:3e:26:14
App/part. size:    189,952/4,128,768 bytes, 4.60%
[00:00:01] [========================================]      13/13      0x
[00:00:00] [========================================]       1/1       0x
[00:00:05] [========================================]      57/57      0x10000[2023-10-21T03:43:29Z INFO ] Flashing has completed!
Commands:
    CTRL+R    Reset chip
    CTRL+C    Exit

I (34) boot: ESP-IDF v5.1-beta1-378-gea5e0ff298-dirt 2nd stage bootloader
I (34) boot: compile time Jun  7 2023 07:59:10
I (35) boot: chip revision: v0.4
I (39) boot.esp32c3: SPI Speed      : 40MHz
I (44) boot.esp32c3: SPI Mode       : DIO
I (48) boot.esp32c3: SPI Flash Size : 4MB
I (53) boot: Enabling RNG early entropy source...
I (59) boot: Partition Table:
I (62) boot: ## Label            Usage          Type ST Offset   Length
I (69) boot:  0 nvs              WiFi data        01 02 00009000 00006000
I (77) boot:  1 phy_init         RF data          01 01 0000f000 00001000
I (84) boot:  2 factory          factory app      00 00 00010000 003f0000
I (92) boot: End of partition table
I (96) esp_image: segment 0: paddr=00010020 vaddr=3c020020 size=05938h ( 22840) map
I (110) esp_image: segment 1: paddr=00015960 vaddr=3fc80790 size=00038h (    56) load
I (113) esp_image: segment 2: paddr=000159a0 vaddr=40380000 size=00784h (  1924) load
I (122) esp_image: segment 3: paddr=0001612c vaddr=00000000 size=09eech ( 40684) 
I (138) esp_image: segment 4: paddr=00020020 vaddr=42000020 size=1e5b8h (124344) map
I (166) boot: Loaded app from partition at offset 0x10000
I (166) boot: Disabling RNG early entropy source...
Compute
9
Compute
9
Compute
9
posted @ 2023-10-21 13:13  qsBye  阅读(448)  评论(0编辑  收藏  举报