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"

以上都需要:

debian: sudo apt install libudev-dev libssl-dev git pkg-config clang-devel python3.11-venv # python3.11-venv 版本要和电脑上的 python 一致

fedora: sudo dnf install gcc gcc-c++ clang-devel libudev-devel openssl-devel python3-devel

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,false 时 ESP-IDF version 则为稳定版。建议不改,这里只是说怎么改
✔ 🤷   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 # master 多半会因 rust 库适配不及时问题导致工程无法建立或者 api 错误
🔧   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

  • sdkconfig.defaults:
    工程默认是40M晶振,所有使用26M晶振的需要在sdkconfig.defaultsl里添加CONFIG_XTAL_FREQ_26=y,配置生效需要cargo cleancargo run
    建议添加CONFIG_FREERTOS_HZ=1000,默认的是 100
    目前 esp32c2 2MB 版本需要修改.cargo/config.toml里的runner项为runner = "espflash flash --monitor --flash-size 2mb",不然esp32c2会启动报错
    26M 晶振的日志波特率是 74880,40M 的是 115200,若要修改则在程序里添加:unsafe { esp_idf_svc::sys::uart_set_baudrate(esp_idf_svc::sys::uart_port_t_UART_NUM_0, 921600); }

  • espflash.toml:
    在项目下添加,方便更改参数

    baudrate = 2000000 # 烧录用的波特率
    
    [flash]
    mode = "dio"
    size = "4MB"
    frequency = "60MHz"
    

运行

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 里执行一下。

手动打开串口监控:espflash monitor
一般是没有串口权限的,执行:

cat << EOF | sudo tee /etc/udev/rules.d/50-usb-serial.rules
KERNEL=="ttyUSB*", MODE="0666"
KERNEL=="ttyACM*", MODE="0666"
EOF

如果usb已经插上,就重新插一次
编译 esp-idf-sys 时会检查 esp-idf,如果连不上 github/esp… 会报错

其他

使用自定义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 = "*"
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值