Raspberry Pi Pico(Pico W や Pico 2 も含む)を買ってみて、とりあえずLチカはできたけれど、「この先どう発展させればいいんだろう?」と感じたことはありませんか?
私も最初は、MicroPythonでLEDをチカチカさせて満足していましたが、だんだん「ずっと while True でLEDだけ点滅させているのはもったいないな」と思うようになりました。
そんなときに便利なのがタイマー割り込みです。タイマー割り込みを使うと、メイン処理とは別のところでLEDを自動的に点滅させることができます。しかも、Raspberry Pi Pico / Pico W / Pico 2 どれでもほぼ同じコードで動かせます。
この記事では、
- 割り込みなしのLチカ
- タイマー割り込みで直接LEDをトグルするLチカ
- フラグ方式で安全に扱うタイマー割り込みLチカ
- 割り込みの基本ルールと3パターンの比較
という流れで、MicroPythonでタイマー割り込みによるLチカを初心者向けに分かりやすく解説します。
まずは実際にどういう動きをするかイメージしておきましょう。LED(発光ダイオード)がゆっくり点灯・消灯を繰り返して「チカチカ」します。
視覚的にパチッと反応があるので、初めての電子工作でも手応えを感じやすい実験です。
完成イメージ
見た目の動きは、ふつうのLチカとほとんど同じです。違いは「どう制御しているか」のという中身です。
この記事では、同じ動きでも中身の仕組みを変えていくから、じっくり読んでいってね。ちょいむずかも。
準備:必要な道具と環境
パーツ一覧
| 品名 | 役割 | 代替案 |
|---|---|---|
| Raspberry Pi Pico W | マイコン本体 | Pico(無印)でも可 |
| Micro USBケーブル | データ通信・電源供給 | Type-A to Micro USB(データ通信対応) |
| ブレッドボード | 配線用の土台 | 小型でもOK |
| ジャンパワイヤ(オス-オス) | 接続ケーブル | 色違いで用意すると便利 |
| LED(赤・黄・緑など) | 点灯テスト用 | 単色1つでも可 |
| 抵抗(220〜330Ω) | 電流制限 | 470Ωでも可 |
動作環境(筆者の検証環境)
- PC:MacBook Air (M1, macOS 15.5 arm64)
- エディタ:Thonny 4.1.6(Python 3.10.11, Tk 8.6.13)
- デバイス:Raspberry Pi Pico W(MicroPython)
もちろん、Windows や Linux 環境でも動作します。MicroPythonを導入していれば同じコードで実行可能です。
配線図


安全上の注意
- LEDには極性(+と−)があります。長い脚が+側です。
- 抵抗を必ず直列に入れましょう。 入れ忘れるとLEDが焼ける危険があります。
- 静電気が強い日や金属テーブル上では、手で基板を触る前に放電しておきましょう。
まずは割り込みなしでLチカしてみよう
まずは一番基本となる割り込みなしのLチカから復習しておきます。ここが分かっていると、タイマー割り込み版の理解もスムーズになります。
今回は、外付けLEDをGPIO 15番ピンに接続した例で書いてみます。Pico / Pico W / Pico 2 どれでも、配線とピン番号が合っていれば同じように動作します。
from machine import Pin
from time import sleep_ms
# ==========================
# 設定値(あとで変えるならここだけ)
# ==========================
LED_PIN = 15 # LED をつないだ GPIO 番号
TIMER_INTERVAL_MS = 500 # 点滅間隔(ミリ秒)
# ==========================
# メイン処理
# ==========================
def main():
led = Pin(LED_PIN, Pin.OUT)
led_state = 0
led.value(led_state)
while True:
# LED の ON/OFF をトグル
led_state = 1 - led_state
led.value(led_state)
# 一定時間待つ(これで点滅間隔を決める)
sleep_ms(TIMER_INTERVAL_MS)
このコードはとても素直で、一定時間待ってLEDの状態を反転させるだけです。MicroPython入門としては、このスタイルがまず押さえておきたい形です。
ただしこの方法は、プログラムがずっとLチカだけを担当している状態になります。他の処理を入れたいときには、同じループの中にコードを詰め込む必要があり、だんだん見通しが悪くなりがちです。
ただ待っているだけが
このコードだよ。
そこで次は、この「待ち時間」をタイマー割り込みに任せる形に変えていきます。
タイマー割り込みでLEDを直接トグルするLチカ
次のステップは、Raspberry Pi Pico のハードウェアタイマーを使って、一定周期ごとにLEDを点滅させる方法です。MicroPythonではmachine.Timerクラスを使います。
ここではまず、タイマー割り込みのコールバック関数の中で直接LEDのON/OFFを切り替えるシンプルなパターンを見ていきます。
from machine import Pin, Timer
# ==========================
# 設定値(あとで変えるならここだけ)
# ==========================
LED_PIN = 15 # LED をつないだ GPIO 番号
TIMER_INTERVAL_MS = 500 # 点滅間隔(ミリ秒)
# ==========================
# グローバル変数
# ==========================
led = None
led_state = 0
timer = None
# ==========================
# タイマー割り込みハンドラ
# ==========================
def toggle_led(timer_obj):
# timer_obj には、この割り込みを発生させた Timer オブジェクトが渡される
global led_state
led_state = 1 - led_state # 0/1 を切り替え
led.value(led_state)
# ==========================
# メイン処理
# ==========================
def main():
global led, timer
# LED 初期化
led = Pin(LED_PIN, Pin.OUT)
led.value(0)
# タイマー初期化(周期ごとに toggle_led を呼ぶ)
timer = Timer()
timer.init(
period=TIMER_INTERVAL_MS, # 割り込み周期 [ms]
mode=Timer.PERIODIC, # 周期モード(繰り返し)
callback=toggle_led # 呼び出す関数
)
# メイン側は他の処理を書ける(ここでは何もしないで放置)
while True:
pass
ここでの主役は、toggle_led() 関数と timer.init() のセットです。
def toggle_led(timer_obj):
タイマー割り込みが発生したときに自動で呼ばれる関数(割り込みハンドラ)です。ここに「一定時間ごとにやってほしい処理」を書きます。この例ではLEDのON/OFFトグルだけを行っています。timer.init(..., callback=toggle_led)periodで「何ミリ秒ごとに」、modeで「一回きり or 周期的」、callbackで「どの関数を呼ぶか」を指定しています。
「このタイマーが鳴ったらtoggle_led()を呼んでね」と登録しているイメージです。
timer_obj って何?
toggle_led(timer_obj) の timer_obj は、MicroPython の Timer がコールバック関数に自動で渡してくる引数です。
timer = Timer()で作ったTimerオブジェクトそのものが、timer_objとして渡されます。- タイマーを複数使う場合は、
if timer_obj is timer0:のようにして、「どのタイマーからの割り込みか」を判別するのに使えます。 - 今回のようにタイマーは1つだけで、ハンドラの中で特にタイマー情報を使わない場合は、「形だけ受け取っている引数」と考えてOKです。
ポイントとして、MicroPython の Timer は 「引数を1つ取る関数」をコールバックとして受け取る仕様になっています。そのため、
def toggle_led():のように引数なしにするとエラーになる- たとえ中で使わなくても、
def toggle_led(timer_obj):のように引数を1つ受け取る形にしておく必要がある
もし「使わない引数があるのは気持ち悪いな…」と感じる場合は、timer_obj の代わりに _timer のような名前にして、「使わないけど形だけ受け取っているよ」という意図を表現する書き方もよく使われます。
ここまで来ると、メイン側のwhile True: passは、極端な例として「何もしていない」状態です。それでもLEDはタイマー割り込みだけでチカチカし続けるので、LED点滅をバックグラウンドに丸投げしているイメージがつかみやすいと思います。
timer_obj は「どのタイマーから来たか」の情報だよ。今回みたいに1個しか使わないなら、あまり気にせず書式どおり受け取るだけでもOK!
上記の「タイマー割り込みでLEDを直接トグルするLチカ」には注意点があります。それは割り込みハンドラ内で直接LEDを制御していることです。LED程度なら良いのですが、ここに処理を盛り込みすぎると、割り込みが長くなりすぎて不安定の原因になります。
そこで登場するのが、次に紹介するフラグ方式です。
フラグ方式で安全にタイマー割り込みLチカ
割り込みの世界でよく言われる基本ルールのひとつが、「割り込みハンドラは短く、すぐ返す」です。タイマー割り込みの中で重い処理をすると、他の割り込みやメイン処理に悪影響が出ることがあります。
そこでおすすめなのが、割り込みハンドラの中では「やってほしいことのフラグだけを立てる」という書き方です。LEDのON/OFFはメインループ側で行います。
from machine import Pin, Timer
# ==========================
# 設定値
# ==========================
LED_PIN = 15
TIMER_INTERVAL_MS = 1000 # 少しゆっくり 1秒間隔
# ==========================
# グローバル変数
# ==========================
led = Pin(LED_PIN, Pin.OUT)
led_state = 0
# 割り込みで触るのは「フラグだけ」
led_toggle_requested = False
# ==========================
# タイマー割り込みハンドラ
# ==========================
def timer_isr(timer_obj):
global led_toggle_requested
# ← ここでは「トグルしてね」というフラグを立てるだけ
led_toggle_requested = True
# ==========================
# メイン処理
# ==========================
def main():
global led_state, led_toggle_requested
timer = Timer()
timer.init(
period=TIMER_INTERVAL_MS,
mode=Timer.PERIODIC,
callback=timer_isr
)
while True:
# フラグが立っていたら、ここで実際の処理を行う
if led_toggle_requested:
led_toggle_requested = False # フラグをクリア
led_state = 1 - led_state
led.value(led_state)
# ここに他の処理を書いてもOK
# 例: ボタン入力やセンサー読み取りなど
# 必要なら軽く sleep を入れてもよい
このパターンでは、timer_isr()は本当に短い処理しかしていません。フラグをTrueにするだけなので、すぐにreturnしてくれます。
一方、LEDのON/OFFはメインループ側でまとめて行います。将来的に「ログを取る」「別のLEDも同時に制御する」「状態変数を増やす」といった拡張も、メイン側を編集するだけで済むので、とても保守しやすい構成です。
センサーやボタンにも応用しやすいよ!
タイマー割り込みを本格的に使っていきたいなら、このフラグ方式をベースに考えるのがおすすめです。
割り込みを使うときの基本ルール
ここで、Raspberry Pi Pico に限らず、マイコンで割り込みを扱うときの基本ルールを整理しておきます。Pico / Pico W / Pico 2 どれでも同じ考え方でOKです。
- 割り込みハンドラは「即返す」が基本
長い処理を入れず、できるだけ短く終わらせること。 - sleep系関数は使わない
sleep()やsleep_ms()などの待ち処理は、割り込み内ではNGです。 - printは最小限に
デバッグ用のprint()も時間がかかることがあるので、常用は避けた方が無難です。 - 共有するデータはシンプルに
真偽値のフラグや、小さなカウンタなどにしておくと安全です。 - 複雑な処理はメインループで
センサーの処理、表示の更新、状態管理などはメイン側に集約します。
設計で迷いにくいよ!
今回のフラグ方式は、まさにこのルールを守りやすくするための書き方です。最初は少しコードが長く見えるかもしれませんが、慣れるとこちらの方が安心して拡張できます。
3つのLチカを比較してみよう
ここまでで、
- ① 割り込みなしのLチカ
- ② タイマー割り込みで直接LEDをトグル
- ③ フラグ方式のタイマー割り込み
という3パターンを見てきました。それぞれの特徴をざっくり比較してみます。
① 割り込みなしのLチカ
- メリット: コードが短くて直感的。MicroPython入門として最適。
- デメリット:
sleep_ms()で待っている間は何もできない。並行処理には向かない。
② 直接トグルするタイマー割り込み
- メリット: LED点滅をハードウェアタイマーに任せられる。メイン側に別の処理を書きやすい。
- デメリット: 調子に乗って割り込み内に処理を詰め込むと、不安定になりやすい。
③ フラグ方式のタイマー割り込み
- メリット: 割り込みはフラグだけを扱うので安全。メインループでまとめて状態管理しやすい。
- デメリット: コード量は増える。最初は構造を理解するのに少し慣れが必要。
あとから機能追加しやすいよ!
Pico / Pico W / Pico 2 どれを使っていても、この考え方はそのまま応用できます。ハードが変わっても、MicroPythonのコードはほぼ流用できるのも嬉しいポイントです。
まとめ:タイマー割り込みLチカから次のステップへ
今回は、Raspberry Pi Pico(Pico W / Pico 2 含む)とMicroPythonを使って、
- 割り込みなしの基本Lチカ
- タイマー割り込みで直接トグルするLチカ
- フラグ方式で安全なタイマー割り込みLチカ
- 割り込み使用時の基本ルールと3パターン比較
という流れで、タイマー割り込みによるLチカを一通り体験しました。
「割り込み」と聞くと難しそうに感じるかもしれませんが、実際には「一定時間ごとに呼ばれる関数を登録するだけ」と考えると、かなりハードルが下がると思います。
次の一歩としては、
- タイマー割り込みとPWMを組み合わせて、明るさ変化+点滅を同時に制御する
- ボタンやセンサーと組み合わせて、一定間隔で状態をチェックする仕組みを作る
- 複数のタイマーを使って、違う周期の処理を同時に走らせてみる
といったチャレンジもおすすめです。
まずはこの記事の3パターンを全部動かしてみて、周期やピン番号を自分なりに変えて遊んでみてください。タイマー割り込みの感覚がつかめてくると、Raspberry Pi Pico で作れる電子工作のアイデアが一気に広がっていきますよ。

コメント