皆さん こんにちは。
ポンコツRustacean の moon です。
ここまで、いろいろやってきたのですがタイマー割り込みの存在をすっかり忘れていました。
タイマー割り込みは重要な機能ですから、ここで実装してみることにしました。
この記事は開発環境を構築することを前提にしています。
環境構築について知りたい方は こちらの記事 をご覧になってください。
このサイトは 書籍 基礎から学ぶ組込みRust を参考にしています。
パッケージの複製を作成する
まず私のGitHubリポジトリにあるRustのひな形をベースに別名でパッケージをつくります。
ひな形はデバッグ環境用のものですから、その複製もデバッグできるようになります。
新しいパッケージ名を rp2040-timer-int として git clone します。
xxxxは皆さんのユーザー名です。
カレントディレクトリを rp2040-timer-int に移してから、code . で VSCode を起動します。
C:\Users\xxxx>cd pprp
C:\Users\xxxx\pprp>git clone https://github.com/moons3925/rp2040.git rp2040-timer-int
C:\Users\xxxx\pprp>cd rp2040-timer-int
C:\Users\xxxx\pprp\rp2040-timer-int>code .
launch.jsonを編集する
EXPLORER – ツリーの .vscode 下にある launch.json をクリックして編集します。
executable: の名称を rp2040 から rp2040-timer-int に変更します。
"executable": "./target/thumbv6m-none-eabi/debug/rp2040-timer-int",
Cargo.tomlを編集する
個々のバージョンが違っていたりするとエラーが出たりしてやっかいですので。。
以下をコピペしてください。
[package]
edition = “2021”
name = “rp2040-timer-int”
version = “0.1.0”
license = “MIT OR Apache-2.0”
[dependencies]
cortex-m = “0.7.7”
cortex-m-rt = “0.7.3”
embedded-hal = “0.2.7”
panic-halt = “0.2.0”
rp-pico = “0.8.0”
rp2040-hal = “0.9.2”
fugit = “0.3.7”
critical-section = “1.1.2”
main.rsを編集する
#![no_std]
#![no_main]
use panic_halt as _;
use rp2040_hal as hal;
use rp2040_hal::pac;
use rp_pico::entry;
use rp2040_hal::Clock;
use core::cell::RefCell;
use critical_section::Mutex;
use fugit::MicrosDurationU32;
use embedded_hal::digital::v2::ToggleableOutputPin;
use pac::interrupt;
use rp2040_hal::timer::Alarm;
// (1)
type LedAndAlarm = (
hal::gpio::Pin<hal::gpio::bank0::Gpio25, hal::gpio::FunctionSioOutput, hal::gpio::PullDown>,
hal::timer::Alarm0,
);
// (2)
static mut LED_AND_ALARM: Mutex<RefCell<Option<LedAndAlarm>>> = Mutex::new(RefCell::new(None));
// (3)
const FAST_BLINK_INTERVAL_US: MicrosDurationU32 = MicrosDurationU32::millis(10);
#[entry]
fn main() -> ! {
let mut pac = pac::Peripherals::take().unwrap();
let core = pac::CorePeripherals::take().unwrap();
let mut watchdog = hal::Watchdog::new(pac.WATCHDOG);
let clocks = hal::clocks::init_clocks_and_plls(
rp_pico::XOSC_CRYSTAL_FREQ,
pac.XOSC,
pac.CLOCKS,
pac.PLL_SYS,
pac.PLL_USB,
&mut pac.RESETS,
&mut watchdog,
)
.ok()
.unwrap();
let mut delay = cortex_m::delay::Delay::new(core.SYST, clocks.system_clock.freq().to_Hz());
let sio = hal::Sio::new(pac.SIO);
let pins = rp_pico::Pins::new(
pac.IO_BANK0,
pac.PADS_BANK0,
sio.gpio_bank0,
&mut pac.RESETS,
);
let led_pin = pins.led.into_push_pull_output();
let mut timer = hal::Timer::new(pac.TIMER, &mut pac.RESETS, &clocks); // (4)
critical_section::with(|cs| { // (5)
let mut alarm = timer.alarm_0().unwrap();
let _ = alarm.schedule(FAST_BLINK_INTERVAL_US);
alarm.enable_interrupt();
unsafe {
LED_AND_ALARM.borrow(cs).replace(Some((led_pin, alarm)));
}
});
unsafe {
pac::NVIC::unmask(pac::Interrupt::TIMER_IRQ_0); // (6)
}
loop {
delay.delay_ms(100);
}
}
#[interrupt]
fn TIMER_IRQ_0() { // (7)
critical_section::with(|cs| {
let ledalarm = unsafe { LED_AND_ALARM.borrow(cs).take() };
if let Some((mut led, mut alarm)) = ledalarm {
alarm.clear_interrupt();
let _ = alarm.schedule(FAST_BLINK_INTERVAL_US);
unsafe {
static mut COUNT: u8 = 0;
COUNT += 1;
if 9 < COUNT {
COUNT = 0;
// Blink the LED so we know we hit this interrupt
led.toggle().unwrap();
}
}
// Return LED_AND_ALARM into our static variable
unsafe {
LED_AND_ALARM
.borrow(cs)
.replace_with(|_| Some((led, alarm)));
}
}
});
}
ビルドして実行する
Run - Start Debugging (F5) からプログラムを実行します。
割り込みの周期自体は10msecですが、割り込みサービスルーチン内で10カウントしたらLEDのGPIO出力を反転しているので100msec周期でLEDが点滅します。
Lチカしていれば成功です。
念のためタイマー割り込み fn TIMER_IRQ_0()関数 内にブレークポイントを貼ってプログラムが来ることを確認しておきます。
プログラムの概要
(1)LEDピンとタイマーを制御するアラームをタプルにしたものを type で型にしています。
(2)その型を Mutex, RefCell, Option でラップしています。排他制御の常套手段です。
(3)MicrosDurationU32は期間を表現するのに用意された構造体です。figitクレートを使うのでCargo.tomlに登録しています。
(4)Timer構造体を生成します。
(5)排他制御するためにクリティカルセクションを使います。
(6)タイマー割り込みを使うために割り込みをイネーブルにします。
(7)割り込みサービスルーチンです。
C/C++ SDK の方ではタイマー割り込みを実現する方法が2種類あるようです。
RP2040 HAL のRustのソースを覗いてみたのですが、アラームによる1種類の方法しかないようです。
アラームの方はリスケジュールして使う必要があるので少々手間がかかるようですけど。。
まあ、仕方ないですね。
GitHubにアップ
GitHubの rp2040-timer-int にプロジェクトをアップしましたので参考になさってください。
お疲れさまでした。