🕌

RustによるESP32のデュアルコアの使用法

2024/11/25に公開

はじめに

なんとなく、RustでESP32のデュアルコアを触りたいなと思って、色々と調べながらやってみようとしたのですが、記事が全くと言ってよいほどなかったのでまとめました。

一応、私の作成したリポジトリを載せておきます。

FreeRTOS

今回、デュアルコアでプログラムを作成するにあたって、ESP32のFreeRTOSを使います。

FreeRTOSについては、Lang-shipさんなどに詳しく記載されているので、気になった方はそちらを参照してください。

タスク作成

デュアルコアを扱うには、それぞれにタスクを渡して実行する形をとります。タスクの作成には、C言語から関数を呼び出して使用します。

unsafe extern "C" fn task1(_: *mut core::ffi::c_void) {
    loop {
        for i in 0..10 {
            log::info!("Task 1 Entered {}", i);
            FreeRtos::delay_ms(1000);
        }
    }
}

もう一つのコアには同様の内容で、task2という関数名を定義しておきます。

タスクの割り当て

static TASK1_NAME: &str = "Task 1";
let task1_name = CString::new(TASK1_NAME)?;

上記で、Rustの文字列スライスをCString::newを使用して、C言語の文字列に変換したものをtask1_nameという変数に代入します。

    unsafe {
        xTaskCreatePinnedToCore(
            Some(task1),
            task1_name.as_ptr(),
            4096,
            std::ptr::null_mut(),
            10,
            std::ptr::null_mut(),
            0,
        );
    }

ここでは、タスクを特定のコアに割り当てています。第一引数には、上で定義したtask1関数を渡します。第二引数では先程のタスク名を、第三引数にはタスクのスタックサイズ、第四引数にはタスクに渡す引数をここで書きます。第五引数はタスクの優先度を、第六引数にはタスクハンドル(タスクの管理や操作等)を格納し、第七引数で使用するCPUのコアを定義します。

もうひとつのタスクの割り当てでは、CPUのコア1に割り当てて二つのコアを動かしています。

プログラム全体

ソースコードもここに載せておきます。
ふたつのタスクでdelayの時間を変えて、両方のコアが起動していることが確認できます。

use esp_idf_hal::delay::FreeRtos;
use esp_idf_sys::{self as _};
use esp_idf_sys::xTaskCreatePinnedToCore;
use std::ffi::CString;
use esp_idf_svc::sys::link_patches;
static TASK1_NAME: &str = "Task 1";
static TASK2_NAME: &str = "Task 2";


unsafe extern "C" fn task1(_: *mut core::ffi::c_void) {
    loop {
        for i in 0..10 {
            log::info!("Task 1 Entered {}", i);
            FreeRtos::delay_ms(1000);
        }
    }
}
unsafe extern "C" fn task2(_: *mut core::ffi::c_void) {
    loop {
        for i in 0..10 {
            log::info!("Task 2 Entered {}", i);
            FreeRtos::delay_ms(3000);
        }
    }
}

fn main() -> anyhow::Result<()> {
    link_patches();

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

    // ログレベルを警告レベルに設定
    unsafe {
        esp_idf_sys::esp_log_level_set(
            b"uart\0".as_ptr() as *const _,
            esp_idf_sys::esp_log_level_t_ESP_LOG_WARN,
        );
    }
    
    let task1_name = CString::new(TASK1_NAME)?;
    unsafe {
        xTaskCreatePinnedToCore(
            Some(task1),
            task1_name.as_ptr(),
            4096,
            std::ptr::null_mut(),
            10,
            std::ptr::null_mut(),
            0,
        );
    }
    let task2_name = CString::new(TASK2_NAME)?;
    unsafe {
        xTaskCreatePinnedToCore(
            Some(task2),
            task2_name.as_ptr(),
            4096,
            std::ptr::null_mut(),
            9,
            std::ptr::null_mut(),
            1,
        );
    }
    for i in 0..10 {
        log::info!("Main Entered {}", i);
        FreeRtos::delay_ms(5000);
    }
    Ok(())
}

余談

本当はupsafeを使いたくないのですが、呼び出しているのがC言語ということもあって、Rustの安全性の担保ができないのでは?と思ってます。
分かり次第、追記します...

GitHubで編集を提案

Discussion