文章目录
- esp-rs 简介
- Github
- Rust 包仓库
- Wokwi 电子模拟器
- 开发环境
- Rust 环境
- esp-rs 环境
- 创建 ESP32C3 项目
- 项目结构
- 编译项目命令
- 运行模拟器
- ESP32C3 烧录
esp-rs 简介
esp-rs 是一个专注于为 Espressif 系列芯片(如 ESP32、ESP32-S2、ESP32-C3 等)提供 Rust 语言支持的社区和项目。它的目标是为开发者提供一个高效、安全且易于使用的 Rust 开发环境,以便在 Espressif 芯片上进行嵌入式系统开发。
- 构建工具
存储库 | 描述 |
---|---|
esp-rs/rust | 带有 Xtensa 支持的 Rust 编译器分支 |
esp-rs/rust-build | Rust 编译器 fork 的预构建二进制文件以及安装脚本 |
- 硬件抽象层
存储库 | 描述 |
---|---|
esp-rs/esp-idf-hal | 支持 Rust 标准库(std) |
esp-rs/esp-hal | 不支持 Rust 标准库 ( no_std) |
Github
- esp-rs:https://github.com/esp-rs
- esp-hal 非标准库:https://github.com/esp-rs/esp-hal
- 本章示例项目源码:https://github.com/WuFengSheng/esp-rs-demo
Rust 包仓库
- https://crates.io/
Wokwi 电子模拟器
- https://wokwi.com/projects/410182337086340097
开发环境
Rust 环境
- 参考我的这篇文章 《使用 Rustup 管理 Rust 版本》
# 安装 nightly 版本
rustup install nightly
# 设置默认 Rust 版本
rustup default nightly
# 当前 Rust 版本
rustc --version
esp-rs 环境
- espup安装
用于安装和维护使用 Rust 为 Espressif SoC 开发应用程序所需的工具链的工具。https://github.com/esp-rs/rust-build
cargo install espup
espup install
. $HOME/export-esp.sh
- RISC-V 安装
以下指令专门针对基于 RISC-V 架构的 ESP32-C
rustup target add riscv32imc-unknown-none-elf
rustup component add rust-src --toolchain nightly
# 安装 cargo-generate
cargo install cargo-generate
cargo generate -h
# 安装 espflash
cargo install espflash
espflash --help
espflash flash --help
创建 ESP32C3 项目
cargo generate -a esp-rs/esp-template
项目结构
- Cargo.toml
[dependencies]
esp-backtrace = { version = "0.14.0", features = [
"esp32c3",
"exception-handler",
"panic-handler",
"println",
] }
esp-hal = { version = "0.20.1", features = [ "esp32c3" ] }
esp-println = { version = "0.11.0", features = ["esp32c3", "log"] }
log = { version = "0.4.21" }
- main.rs
#![no_std]
#![no_main]
use esp_backtrace as _;
use esp_hal::{
clock::ClockControl,
delay::Delay,
peripherals::{Peripherals, I2C0},
Blocking,
prelude::*,
system::SystemControl,
gpio::{Io, Level, Output},
i2c::I2C
};
const I2C_ADDR: u8 = 0x27; // LCD1602的I2C地址
// LCD 指令
const LCD_CMD_CLEAR: u8 = 0x01;
const LCD_CMD_HOME: u8 = 0x02;
const LCD_CMD_ENTRY_MODE: u8 = 0x04;
const LCD_CMD_DISPLAY_CONTROL: u8 = 0x08;
const LCD_CMD_FUNCTION_SET: u8 = 0x20;
const LCD_CMD_SET_DDRAM_ADDR: u8 = 0x80;
// LCD 控制位
const LCD_BACKLIGHT: u8 = 0x08;
const ENABLE: u8 = 0x04;
const RW_WRITE: u8 = 0x00;
const RS_DATA: u8 = 0x01;
const RS_COMMAND: u8 = 0x00;
#[entry]
fn main() -> ! {
let peripherals = Peripherals::take();
let system = SystemControl::new(peripherals.SYSTEM);
let clocks = ClockControl::max(system.clock_control).freeze();
let delay = Delay::new(&clocks);
esp_println::logger::init_logger_from_env();
// Set GPIO0 as an output, and set its state high initially.
let io = Io::new(peripherals.GPIO, peripherals.IO_MUX);
let mut led = Output::new(io.pins.gpio0, Level::High);
// 初始化 I2C
let mut i2c: I2C<I2C0, Blocking> = I2C::new(peripherals.I2C0, io.pins.gpio6, io.pins.gpio5, 400.kHz(), &clocks);
log::info!("The type of i2c is: {}", core::any::type_name_of_val(&i2c));
// 初始化 LCD
lcd_init(&mut i2c);
lcd_write_string(&mut i2c, "Hello, World!");
loop {
log::info!("Hello world! \n");
led.toggle();
delay.delay(500.millis());
}
}
fn lcd_init(i2c: &mut I2C<I2C0, Blocking>) {
// 设置 4-bit 模式
lcd_command(i2c, 0x33); // 初始化指令
lcd_command(i2c, 0x32); // 设置4-bit模式
lcd_command(i2c, LCD_CMD_FUNCTION_SET | 0x08); // 2行显示
lcd_command(i2c, LCD_CMD_DISPLAY_CONTROL | 0x0F); // 打开显示,有光标,会闪烁
lcd_command(i2c, LCD_CMD_CLEAR); // 清屏
lcd_command(i2c, LCD_CMD_ENTRY_MODE | 0x02); // 设置光标移动方向
}
fn lcd_command(i2c: &mut I2C<I2C0, Blocking>, command: u8) {
lcd_write(i2c, command, RS_COMMAND);
}
fn lcd_data(i2c: &mut I2C<I2C0, Blocking>, data: u8) {
lcd_write(i2c, data, RS_DATA);
}
fn lcd_write(i2c: &mut I2C<I2C0, Blocking>, data: u8, mode: u8) {
let high_nibble = data & 0xF0;
let low_nibble = (data << 4) & 0xF0;
lcd_send_nibble(i2c, high_nibble | mode);
lcd_send_nibble(i2c, low_nibble | mode);
}
fn lcd_send_nibble(i2c: &mut I2C<I2C0, Blocking>, nibble: u8) {
// 发送高4位
let data = nibble | LCD_BACKLIGHT;
i2c.write(I2C_ADDR, &[data | ENABLE]);
i2c.write(I2C_ADDR, &[data & !ENABLE]);
}
fn lcd_write_string(i2c: &mut I2C<I2C0, Blocking>, s: &str) {
for c in s.chars() {
lcd_data(i2c, c as u8);
}
}
- diagram.json
{
"version": 1,
"editor": "wokwi",
"author": "WuFengSheng <469742978@qq.com>",
"parts": [
{
"type": "board-esp32-c3-devkitm-1",
"id": "esp",
"top": 0.59,
"left": 0.67,
"attrs": {
"flashSize": "16"
}
},
{
"type": "wokwi-led",
"id": "led1",
"top": -20,
"left": -50,
"attrs": {
"color": "red"
}
},
{
"type": "wokwi-resistor",
"id": "r1",
"top": 50,
"left": -54.5,
"rotate": 90,
"attrs": {}
},
{
"type": "wokwi-lcd1602",
"id": "lcd1",
"top": 46,
"left": 132.07,
"attrs": {
"pins": "i2c"
}
},
{
"type": "wokwi-vcc",
"id": "vcc1",
"top": 20,
"left": 105,
"attrs": {}
}
],
"connections": [
[
"esp:TX",
"$serialMonitor:RX",
"",
[]
],
[
"esp:RX",
"$serialMonitor:TX",
"",
[]
],
[
"esp:GND.4",
"led1:C",
"black",
[
"h0"
]
],
[
"led1:A",
"r1:1",
"green",
[
"v0"
]
],
[
"r1:2",
"esp:0",
"green",
[
"h0",
"v38"
]
],
[
"vcc1:VCC",
"lcd1:VCC",
"red",
[
"v0"
]
],
[
"lcd1:GND",
"esp:GND.8",
"black",
[
"h0"
]
],
[
"lcd1:SDA",
"esp:6",
"green",
[
"h0"
]
],
[
"lcd1:SCL",
"esp:5",
"green",
[
"h0"
]
]
],
"serialMonitor": {
"display": "terminal",
"convertEol": true
}
}
编译项目命令
cd esp-rs-demo
# 默认 debug
cargo build
# 或指定 release
cargo build --release
运行模拟器
注: VSCode 需要安装 wokwi 插件
ESP32C3 烧录
espflash flash --monitor target/riscv32imc-unknown-none-elf/debug/esp-rs-demo