esp32 rust linux

官方文档:https://esp-rs.github.io/book/introduction.html

安装 rust

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
如果需要立即使用,使用. "$HOME/.cargo/env"更新命令行环境

工具

risc:

rustup toolchain install nightly --component rust-src
这里不做这个其实也行,编译时会检查 rust-toolchain.toml 文件,会自动执行这个操作

或使用安装工具同时支持 risc 和 xtensa:

cargo install espup

espup install
会在 /home/你的用户名/ 下生成一个 esport-esp.sh,如果要对工程运行 cargo build 或 cargo run 就需要提前引入到命令行里
建议在 ~/.bashrc 的最后新加一行 . $HOME/export-esp.sh
其内容是
export LIBCLANG_PATH="/home/xiaguangbo/.rustup/toolchains/esp/xtensa-esp32-elf-clang/esp-16.0.0-20230516/esp-clang/lib"
export PATH="/home/xiaguangbo/.rustup/toolchains/esp/xtensa-esp-elf/esp-13.2.0_20230928/xtensa-esp-elf/bin:$PATH"
export PATH="/home/xiaguangbo/.rustup/toolchains/esp/riscv32-esp-elf/esp-13.2.0_20230928/riscv32-esp-elf/bin:$PATH"

以上都需要:

sudo apt install libudev-dev libssl-dev git pkg-config clang python3.11-venv
cargo install ldproxy cargo-generate espflash

两种工具链切换需要执行一次cargo clean

创建模板工程

使用 std 方式,不使用 no_std,std 依赖 esp-idf,但不需要手动下载,会在第一次编译时下载
riscv 和 xtensa 架构的都可以,支持列表看官方文档

工程名自己定义,这里以 hhhh 作工程名

xiaguangbo@debian:/media/xiaguangbo/linux_data/project/x/xfoc/project$ cargo generate esp-rs/esp-idf-template cargo
⚠️   Favorite `esp-rs/esp-idf-template` not found in config, using it as a git repository: https://github.com/esp-rs/esp-idf-template.git
🤷   Project Name: hhhh
🔧   Destination: /media/xiaguangbo/linux_data/project/x/xfoc/project/hhhh ...
🔧   project-name: hhhh ...
🔧   Generating template ...
✔ 🤷   Which MCU to target? · esp32c3
✔ 🤷   Configure advanced template options? · true 这里也可以使用默认的 false,不过 ESP-IDF version 则会为稳定版。建议不改,这里只是说怎么改。master的可能出现rust库适配问题导致工程无法建立
✔ 🤷   Enable STD support? · true
? 🤷   Configure project to use Dev Containers (VS Code and GitHub Codespaces)? ✔ 🤷   Configure project to use Dev Containers (VS Code and GitHub Codespaces)? · false
? 🤷   Configure project to support Wokwi simulation with Wokwi VS Code extensio✔ 🤷   Configure project to support Wokwi simulation with Wokwi VS Code extension? · false
✔ 🤷   Add CI files for GitHub Action? · false
✔ 🤷   ESP-IDF version (master = UNSTABLE) · master
🔧   Moving generated files into: `/media/xiaguangbo/linux_data/project/x/xfoc/project/hhhh`...
🔧   Initializing a fresh Git repository
✨   Done! New project created /media/xiaguangbo/linux_data/project/x/xfoc/project/hhhh

工程默认是40M晶振,所有使用26M晶振的需要在sdkconfig.defaultsl里添加CONFIG_XTAL_FREQ_26=y,否则delay不准,正常情况实测延时偏差小于2%。配置生效需要cargo cleancargo run
建议添加CONFIG_FREERTOS_HZ=1000,默认的是100,会让FreeRtos::delay_ms(xxx)最小延时变成10ms
目前 esp32c2 2MB 版本需要修改.cargo/config.toml里的runner项为runner = "espflash flash --monitor --flash-size 2mb",不然esp32c2会启动报错。使用esp32c2 4MB 版本时只会用到2MB,如果需要空间全能用就改成--flash-size 4mb

cd 到工程里

cargo run
第一次会在工程里下载 .embuild/espressif/esp-idf,可能会不断因网络问题失败,再执行。
可能会提示 riscv32-esp-elf-13.2.0_20230928-x86_64-linux-gnu.tar.xz 下载不下来,可以手动下载到 .embuild/espressif/dist 里。
可能会提示 git submodule update --init --recursive,就到 .embuild/espressif/esp-idf/master 里执行一下。

将 esp32c3 连接到电脑上,并把串口的权限改为 777。假设串口是 /dev/ttyUSB0,就执行 sudo chmod 777 /dev/ttyUSB0。每次 usb 断开连上都需要,或者一劳永逸:查看串口设备的用户组`ls -l /dev/ttyUSB0`,应该是dialout,把当前用户添加到dialout `sudo usermod -a -G dialout $USER`,立即生效`su - $USER`

cargo run
就会下载程序到 esp32c3 里并打开串口监控
如果出现打开串口时权限不足 Permission denied:
	$ su - root
	root@debian:~# usermod -aG dialout hhh # ls -l /dev/ttyUSB0 可以看到 用户组是dialout
	root@debian:~# exit
	然后重启

手动打开串口监控:espflash monitor

cargo run 编译 esp-idf-sys 时会检查 esp-idf,如果连不上 github/esp…仓库会报错。。。。vscode 的 rust-analyzer 插件也会因这个原因出问题

其他

可能会用到的命令:

git submodule update --init --recursive
git clone --recursive --depth 1 --shallow-submodules --branch master https://github.com/espressif/esp-idf.git /media/xiaguangbo/linux_data/project/x/xfoc/project/esp32s3/.embuild/espressif/esp-idf/master

使用自定义esp-idf的位置:
.cargo/config.toml里的[env]下添加下面的,位置可以自定义,如果是相对位置则以Cargo.toml所在的位置为原点

ESP_IDF_TOOLS_INSTALL_DIR = "custom:../../env/espressif"

示例

使用 i2c 读取 as5600 的测到的磁铁的角度:

use esp_idf_svc::hal::delay::Delay;
use esp_idf_hal::delay::{FreeRtos, BLOCK};
use esp_idf_hal::i2c::*;
use esp_idf_hal::peripherals::Peripherals;
use esp_idf_hal::prelude::*;

const AS5600_ADDRESS: u8 = 0x36;
const ANGLE_HIGHT_REGISTER_ADDR: u8 = 0x0c;
const ANGLE_LOW_REGISTER_ADDR: u8 = 0x0d;

fn main() {
    // It is necessary to call this function once. Otherwise some patches to the runtime
    // implemented by esp-idf-sys might not link properly. See https://github.com/esp-rs/esp-idf-template/issues/71
    esp_idf_svc::sys::link_patches();

    // Bind the log crate to the ESP Logging facilities
    esp_idf_svc::log::EspLogger::initialize_default();

    log::info!("Hello, world!");

    let peripherals = Peripherals::take().unwrap();
    let i2c = peripherals.i2c0;
    let scl = peripherals.pins.gpio6;
    let sda = peripherals.pins.gpio7;

    let config = I2cConfig::new().baudrate(100.kHz().into());
    let mut i2c = I2cDriver::new(i2c, sda, scl, &config).unwrap();

    loop {
        FreeRtos::delay_ms(1000);

        i2c.write(AS5600_ADDRESS, &[ANGLE_HIGHT_REGISTER_ADDR], BLOCK)
            .unwrap();
        let mut buffer_h: [u8; 1] = [0; 1];
        i2c.read(AS5600_ADDRESS, &mut buffer_h, BLOCK).unwrap();

        i2c.write(AS5600_ADDRESS, &[ANGLE_LOW_REGISTER_ADDR], BLOCK)
            .unwrap();
        let mut buffer_l: [u8; 1] = [0; 1];
        i2c.read(AS5600_ADDRESS, &mut buffer_l, BLOCK).unwrap();

        log::info!(
            "as5600: {}",
            (((buffer_h[0] as u16) << 8 | (buffer_l[0] as u16)) as f32) / 4096.0 * 360.0
        );
    }
}

pin 模拟 onewire 读取 ds18b20 的温度,并从另一个 uart 发出来:

use embedded_hal::digital;

use esp_idf_svc::hal::delay;
use esp_idf_svc::hal::gpio;
use esp_idf_svc::hal::peripherals;
use esp_idf_svc::hal::uart;
use esp_idf_svc::hal::units;

use std::fmt::Write;

const CMD_SKIP: u8 = 0xcc;
const CMD_WRITE_REG: u8 = 0x4e;
const CMD_TEMP_START: u8 = 0x44;
const CMD_READ_REG: u8 = 0xbe;

fn main() {
    // It is necessary to call this function once. Otherwise some patches to the runtime
    // implemented by esp-idf-sys might not link properly. See https://github.com/esp-rs/esp-idf-template/issues/71
    esp_idf_svc::sys::link_patches();

    // Bind the log crate to the ESP Logging facilities
    esp_idf_svc::log::EspLogger::initialize_default();

    log::info!("Hello, world!");

    let peripherals = peripherals::Peripherals::take().unwrap();

    // onewire
    let mut onewire = gpio::PinDriver::input_output_od(peripherals.pins.gpio0).unwrap();
    onewire.set_pull(gpio::Pull::Floating).unwrap(); // use external pull-up

    // uart
    let config = uart::config::Config::default().baudrate(units::Hertz(115_200));
    let mut uart = uart::UartDriver::new(
        peripherals.uart1,
        peripherals.pins.gpio1,
        peripherals.pins.gpio2,
        Option::<gpio::AnyIOPin>::None,
        Option::<gpio::AnyIOPin>::None,
        &config,
    )
    .unwrap();

    // delay
    let delay = delay::Delay::new_default();

    // ds18b20
    // wait reset
    while !onewire_reset(&mut onewire, &delay) {
        delay::FreeRtos::delay_ms(1000);
    }

    // set accuracy
    onewire_write_byte(&mut onewire, &delay, CMD_SKIP);
    onewire_write_byte(&mut onewire, &delay, CMD_WRITE_REG);
    onewire_write_byte(&mut onewire, &delay, 0xff);
    onewire_write_byte(&mut onewire, &delay, 0x00);
    onewire_write_byte(&mut onewire, &delay, 0x7f);

    loop {
        delay::FreeRtos::delay_ms(1000);

        // ds18b20
        // measurement temp
        if !onewire_reset(&mut onewire, &delay) {
            continue;
        }

        onewire_write_byte(&mut onewire, &delay, CMD_SKIP);
        onewire_write_byte(&mut onewire, &delay, CMD_TEMP_START);

        // read temp
        if !onewire_reset(&mut onewire, &delay) {
            continue;
        }

        onewire_write_byte(&mut onewire, &delay, CMD_SKIP);
        onewire_write_byte(&mut onewire, &delay, CMD_READ_REG);

        let temp_l = onewire_read_byte(&mut onewire, &delay);
        let temp_h = onewire_read_byte(&mut onewire, &delay);
        let temp = (((temp_h as i16) << 8 | (temp_l as i16)) as f64) * 0.0625;

        log::info!("temp: {:.1}", temp);

        // uart
        writeln!(uart, "temp: {:.1}", temp).unwrap();
    }
}

fn onewire_reset<T>(pin: &mut T, delay: &delay::Delay) -> bool
where
    T: digital::InputPin + digital::OutputPin,
{
    pin.set_low().unwrap();
    delay.delay_us(600);
    pin.set_high().unwrap();
    delay.delay_us(80);

    if pin.is_low().unwrap() {
        log::info!("onewire: is exist");
        delay.delay_us(900);

        if pin.is_high().unwrap() {
            log::info!("onewire: reset ok");
            true
        } else {
            log::warn!("onewire: reset err");
            false
        }
    } else {
        log::warn!("onewire: reset err");
        false
    }
}

fn onewire_write_byte<T>(pin: &mut T, delay: &delay::Delay, mut byte: u8)
where
    T: digital::InputPin + digital::OutputPin,
{
    for _ in 0..8 {
        let bit = byte & 0x01 != 0;
        byte >>= 1;

        if bit {
            pin.set_low().unwrap();
            delay.delay_us(5);

            pin.set_high().unwrap();
            delay.delay_us(90);
        } else {
            pin.set_low().unwrap();
            delay.delay_us(90);

            pin.set_high().unwrap();
            delay.delay_us(5);
        }
    }
}

fn onewire_read_byte<T>(pin: &mut T, delay: &delay::Delay) -> u8
where
    T: digital::InputPin + digital::OutputPin,
{
    let mut byte: u8 = 0;

    for _ in 0..8 {
        byte >>= 1;

        pin.set_low().unwrap();
        delay.delay_us(5);

        pin.set_high().unwrap();
        delay.delay_us(5);

        if pin.is_high().unwrap() {
            byte |= 0x80;
        }

        delay.delay_us(60);
    }

    byte
}

Cargo.toml:

[dependencies]
...
esp-idf-hal = "*"
embedded-hal = "*"
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值