Rust 语言开发 ESP32C3 并在 Wokwi 电子模拟器上运行(esp-hal 非标准库、LCD1602、I2C)

文章目录

  • [esp-rs 简介](#esp-rs 简介)
  • Github
  • [Rust 包仓库](#Rust 包仓库)
  • [Rust 教程](#Rust 教程)
  • [Wokwi 电子模拟器](#Wokwi 电子模拟器)
  • 开发环境

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

Rust 包仓库

Rust 教程

Wokwi 电子模拟器

开发环境

Rust 环境

bash 复制代码
# 安装 nightly 版本
rustup install nightly
# 设置默认 Rust 版本
rustup default nightly
# 当前 Rust 版本
rustc --version

esp-rs 环境

  • espup安装

用于安装和维护使用 Rust 为 Espressif SoC 开发应用程序所需的工具链的工具。https://github.com/esp-rs/rust-build

bash 复制代码
cargo install espup
espup install
. $HOME/export-esp.sh
  • RISC-V 安装

以下指令专门针对基于 RISC-V 架构的 ESP32-C

bash 复制代码
rustup target add riscv32imc-unknown-none-elf
bash 复制代码
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 项目

bash 复制代码
cargo generate -a esp-rs/esp-template

项目结构

  • Cargo.toml
bash 复制代码
[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" }
rust 复制代码
#![no_std]
#![no_main]
#![allow(dead_code)]

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;
    let _ = i2c.write(I2C_ADDR, &[data | ENABLE]);
    let _ = 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
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
    }
}

编译项目命令

bash 复制代码
cd esp-rs-demo
# 默认 debug
cargo build
# 或指定 release
cargo build --release

运行模拟器

注: VSCode 需要安装 wokwi 插件

ESP32C3 烧录

bash 复制代码
espflash flash --monitor target/riscv32imc-unknown-none-elf/debug/esp-rs-demo
相关推荐
AitTech2 分钟前
深入理解C#中的TimeSpan结构体:创建、访问、计算与格式化
开发语言·数据库·c#
向上的车轮2 分钟前
ASP.NET Zero 多租户介绍
后端·asp.net·saas·多租户
yz_518 Nemo21 分钟前
django的路由分发
后端·python·django
A 八方38 分钟前
【漏洞复现】泛微OA E-Office do_excel.php 任意文件写入漏洞
开发语言·安全·php
kuan_li_lyg1 小时前
MATLAB - 机械臂手眼标定(眼在手内) - 估计安装在机器人上的移动相机的姿态
开发语言·人工智能·matlab·机器人·ros·机械臂·手眼标定
觉醒法师1 小时前
Vue3+TS项目 - ref和useTemplateRef获取组件实例
开发语言·前端·javascript
ac-er88881 小时前
PHP__sleep和__wakeup有什么区别
开发语言·php
老章学编程i1 小时前
Vue工程化开发
开发语言·前端·javascript·vue.js·前端框架
秀聚1 小时前
C++初始化列表 initializer_list 介绍
开发语言·c++
水饺编程1 小时前
【英特尔IA-32架构软件开发者开发手册第3卷:系统编程指南】2001年版翻译,1-2
linux·嵌入式硬件·fpga开发