ESP32 on Rustチートシート(タイマー割込と排他制御)

# ESP32 on Rustチートシート<br> (タイマー割込と排他制御)

タイマー割込を行うプログラムのサンプル

割込を実装する際、ペリファラルに対して割込関数からアクセスするので、ペリフェラルをグローバル変数で宣言し、排他制御でメイン関数と割込関数の両方で書き込みを行うように実装する


## サンプルコード

```toml
# Cargo.toml
[package]
name = "timer-interrupt"
version = "0.1.0"
authors = ["dai_guard"]
edition = "2021"
license = "MIT OR Apache-2.0"

[dependencies]
esp32-hal = "0.7.0"
esp-backtrace = { version = "0.4.0", features = ["esp32", "panic-handler", "print-uart"] }
esp-println = { version = "0.3.0", features = ["esp32"] }
xtensa-lx-rt = { version = "0.14.0", features = ["esp32"], optional = true }
critical-section  = "1.1.1"

[features]
default = ["rt"]
rt = ["xtensa-lx-rt"]

```

```rust
// main.rs
#![no_std]
#![no_main]

// 内部可変パターン
use core::cell::RefCell;
// 排他制御用のMutex
use critical_section::Mutex;

// ESP32用のHALクレート
use esp32_hal::{
    clock::ClockControl,
    pac::{self, Peripherals, TIMG0, TIMG1},
    interrupt::{self, Priority},
    prelude::*,
    timer::{Timer, Timer0, Timer1, TimerGroup},
    Rtc, IO, Delay, 
    gpio::{GpioPin, Output, PushPull, Bank0GpioRegisterAccess, InputOutputPinType}
};

use esp_backtrace as _;
// use xtensa_lx_rt;
use esp_println::println;

// グローバル変数として宣言するペリフェラルを参照する値を宣言
// 1. Mutexで宣言し排他アクセスを保証する
// 2. Optionでグローバル宣言時はNoneで初期化する
//　　あとのメイン関数内でインスタンスを設定する
static TIMER00: Mutex<RefCell<Option<Timer<Timer0<TIMG0>>>>> = Mutex::new(RefCell::new(None));
static TIMER01: Mutex<RefCell<Option<Timer<Timer1<TIMG0>>>>> = Mutex::new(RefCell::new(None));
static GPIO: Mutex<RefCell<Option<GpioPin<Output<PushPull>, Bank0GpioRegisterAccess, InputOutputPinType, 4>>>> = Mutex::new(RefCell::new(None));

// ※ 使用するターゲットボードによってentryのクレートが違います
#[xtensa_lx_rt::entry]
fn main() -> ! {

    let peripherals = Peripherals::take().unwrap();
    let system = peripherals.DPORT.split();
    let clocks = ClockControl::boot_defaults(system.clock_control).freeze();

    // Disable the RTC and TIMG watchdog timers
    let mut rtc = Rtc::new(peripherals.RTC_CNTL);
    let timer_group0 = TimerGroup::new(peripherals.TIMG0, &clocks);
    let mut wdt0 = timer_group0.wdt;
    let mut timer00 = timer_group0.timer0;
    let mut timer01 = timer_group0.timer1;
    let timer_group1 = TimerGroup::new(peripherals.TIMG1, &clocks);
    let mut wdt1 = timer_group1.wdt;

    rtc.rwdt.disable();
    wdt0.disable();
    wdt1.disable();

    // 割込を許可する
    interrupt::enable(pac::Interrupt::TG0_T0_LEVEL, Priority::Priority2).unwrap();
    interrupt::enable(pac::Interrupt::TG0_T1_LEVEL, Priority::Priority2).unwrap();

    //GPIO4を出力として使用する
    let io = IO::new(peripherals.GPIO, peripherals.IO_MUX);
    let mut led = io.pins.gpio4.into_push_pull_output();
    led.set_high().unwrap();

    // タイマーをスタートする
    timer00.start(500u64.millis());
    timer01.start(1000u64.millis());

    // 割込待ちを開始する
    timer00.listen();
    timer01.listen();

    // このクリティカルセッション内での処理はラッピングされます
    // このセッション内の処理が完了するまでは割込が発生しません
    critical_section::with(|cs| {
        TIMER00.borrow_ref_mut(cs).replace(timer00);
        TIMER01.borrow_ref_mut(cs).replace(timer01);
        GPIO.borrow_ref_mut(cs).replace(led);
    });

    loop {
    }
}

// 割込関数を宣言する
#[interrupt]
fn TG0_T0_LEVEL() {

    // このクリティカルセッション内での処理はラッピングされます
    // このセッション内の処理が完了するまでは割込が発生しません
    critical_section::with(|cs| {

        // 参照するタイマーを取得する
        let mut timer = TIMER00.borrow_ref_mut(cs);
        let timer = timer.as_mut().unwrap();
        // 参照するGPIOを取得する
        let mut led = GPIO.borrow_ref_mut(cs);
        let led = led.as_mut().unwrap();

        // I/OのHigh/Lowを切り替える
        led.toggle().unwrap();

        // 割込フラグをリセットし、タイマーを再スタートする
        if timer.is_interrupt_set() {
            timer.clear_interrupt();
            timer.start(500u64.millis());

            println!("Interrupt Level 2 - Timer0");
        }
    });
}

#[interrupt]
fn TG0_T1_LEVEL() {
    critical_section::with(|cs| {
        let mut timer = TIMER01.borrow_ref_mut(cs);
        let timer = timer.as_mut().unwrap();

        if timer.is_interrupt_set() {
            timer.clear_interrupt();
            timer.start(1000u64.millis());

            println!("Interrupt Level 2 - Timer1");
        }
    });
}
```


## 参考リンク

* [timer_interrupt.rs](https://github.com/esp-rs/esp-hal/blob/main/esp32-hal/examples/timer_interrupt.rs)

* [並行性](https://tomoyuki-nakabayashi.github.io/book/concurrency/index.html)

* [RefCell<T>と内部可変パターン](https://doc.rust-jp.rs/book-ja/ch15-05-interior-mutability.html)