Raspberry Pi Pico W(以下 PicoW)にはメインプロセッサとは独立して動作する、Programmable Input/Output(以下 PIO)と呼ばれる2機のプロセッサがあります。
それぞれのPIOにはステートマシンと呼ばれる、4個の独立して動作する処理ユニットがあり、PIO用のアセンブラ(以下 PIOASM)を使ってGPIOを制御できます。
センサー等の電子部品をPicoWに複数接続して処理する場合、一部の電子部品の制御に処理時間を取られ、別の電子部品の処理に支障をきたす場合があります。
たとえば、LEDとスイッチをPicoWに接続し、LEDの制御に時間を取られた場合、スイッチからの入力を検知できないことが考えられます。
PIOでスイッチ等の一部の電子部品を制御することで、この問題を解決することができます。
このようにPIOはI/Oに特化したプロセッサと言えます。
詳細はRP2040 Datasheet: A microcontroller by Raspberry Piを参照してください。
この記事では、GPIOピン(以下 GPピン)に赤、緑、青の3つのLEDを接続し、PIOを使って赤と緑のLEDを500ms間隔で交互に点灯、消灯させながら、メインの処理で青のLEDを1000ms間隔で点灯、消灯させるプログラムをC/C++で作成します。
実験準備
実験に必要な機器と部品を準備します。
機器
「Windows PCとC/C++でRaspberry Pi Pico Wの開発を始めよう。」記事で書きましたラズパイPicoWと、Visual Studio Code(以下 VSCode)をインストールしたWindows PCを準備します。
使う部品
LED(発光ダイオード)、固定抵抗など実験に使う部品を準備します。
| 部 品 名 | 規 格 | 数 量 | 取扱い店(参考) | 
| LED | 5mm 赤色 | 1 | 電子工作ステーション | 
| LED | 5mm 緑色 | 1 | 電子工作ステーション | 
| LED | 5mm 青色 | 1 | 電子工作ステーション | 
| カーボン抵抗 | 1/4W 330Ω | 3 | 電子工作ステーション | 
| ブレッドボード | 830穴 (訳アリ品を除く) | 1 | 電子工作ステーション | 
| ジャンパーワイヤ | オス-メス(約20cm) | 1 | 電子工作ステーション | 
配線
GPIOピン(以下 GPピン)に接続された2つのLEDを、PIOを使って交互に点灯、消灯させるプログラムをC/C++で作成します。
配線リスト
ラズパイPicoWのGPピンと抵抗を通して、LEDを接続します。
| ラズパイPicoW | 部 品 | 部 品 | 
| GP26 | カーボン抵抗 R1 | LED1 アノード(足の長い方) | 
| GP27 | カーボン抵抗 R2 | LED2 アノード(足の長い方) | 
| GP16 | カーボン抵抗 R3 | LED3 アノード(足の長い方) | 
| GND | LED1 カソード(足の短い方) | ー | 
| GND | LED2 カソード(足の短い方) | ー | 
| GND | LED3 カソード(足の短い方) | ー | 
配線図

ライセンス
プロジェクトのビルドに必要なソースファイル、ソースコードをビルドするための設定ファイル等は、3条項BSDライセンスのもとで配布します。
3条項BSDライセンスの概要(意訳)は次のとおりです。
- サンプルファイルのソースコードまたはバイナリ形式の再配布時には、著作権表示とライセンス条文を記載(SPDX-License-Identifier: BSD-3-Clauseなど)してください。
- サンプルファイルはコピー/配布/変更/変更後の配布/商用利用/有料販売など、自由に使うことができます。
- 書面による許可なしに、サンプルファイルから派生した製品の宣伝や販促に、本ライセンス記載の組織や貢献者(ラズパイ実験室)の名前を使うことはできません。
- サンプルファイルにはなんの保証もついていません。
 サンプルファイルを利用し、なにか問題が発生した場合でも、作者(ラズパイ実験室)は一切の責任を負いませんので、使用に伴うリスクを理解し、自己責任で利用してください。
プロジェクト
GPIOに接続した2つのLEDを交互に点灯・消灯させるプロジェクト(pio_led_toggle)をVSCodeのC/C++を使って作成します。
pio_led_toggleプロジェクトは、コンパイルに必要な、ソースファイル(pio_led_toggle.c)、ソースコードをビルドするための設定ファイル(CMakeLists.txt)、PicoのSDKインポート用のファイル(pico_sdk_import.cmake)で構成されています。
ソースコードをVSCodeにコピーしてビルドします。
プロジェクト・フォーマットの作成
プロジェクト作成のためのフォーマットを作成します。
VSCodeを起動し、「Rasoberry Pi Pico Project」アイコンをクリックし、「New C/C++ Project」をクリックします。

Raspberry Pi Pico ダイアログが表示されますので、「Basic Setting」を選択します。

以下のように設定します。
| 項 目 | 説 明 | 
| Name | pio_led_toggl | 
| Board type | Pico W | 
| Location | 任意(全角文字、半角カタカナが含まれていないこと。) ※自身のユーザ内に任意のファルダを作成します。 | 
| Select Pico SDK version | v2.1.1 | 
| Features | PIO interface :✔ | 
| Stdio support | Console over UART :✔ | 
設定終了後、「Create」をクリックします。

保存するフォルダ内のファイル作成者の信頼についての、ダイアログが表示される場合がありますので、「はい、作成者を信頼します」をクリックします。

プロジェクトが読み込まれます。(数分~数十分かかることがあります。)
「自動補完システム」が準備完了になるまで待ち、準備完了マーク({})が表示された後、「エクスプローラー」を選択します。
「build」を開き、「build.ninja」が存在していることを確認します。
2つのソースコード、「pio_led_toggle.c」と、「blink.pio」が作成されています、
pio_led_toggle.cを選択すると、ソースコードがエディタエリアに表示されます。

blink.pioを選択すると、ソースコードがエディタエリアに表示されます。

プロジェクトの作成
作成したプロジェクト・フォーマットに、ソースコードを入力またはコピーして、プロジェクトを作成します。
▶️pio_led_toggle.cの作成
- 「エクスプローラー」アイコンをクリックして、「pio_led_toggle.c」を表示します
- 現在、VSCodeに表示されている内容をすべて削除します
- 下記「pio_led_toggle.c」のソースコードを入力またはコピー/貼り付けし保存します
【pio_led_toggle.c】
//
// Copyright (c) 2025 ラズパイ実験室
//
// SPDX-License-Identifier: BSD-3-Clause
//
//********************************
//
//PIO機能でLEDを交互に点灯・消灯
//
//********************************
#include <stdio.h>
#include <stdbool.h>
#include "pico/stdlib.h"
#include "hardware/pio.h"
#include "mypio1.pio.h"
//LED接続 GPIOピン基準番号
#define OUT_PIN_BASE 26
//LED接続 GPIOピン数
#define NUM_OUT_PIN 2
//クロック分周値(125000000Hz / 62500 = 2000Hz)
#define CLOCK_DIV 62500
//点滅間隔(ms)
#define BLINK_INTERVAL 500
//メイン処理GPIOピン
#define LED_PIN 16
int main()
{
    stdio_init_all();
    //メイン処理LEDピン 初期化
    gpio_init(LED_PIN);
    //メイン処理LEDピンを出力ピンに設定
    gpio_set_dir(LED_PIN, GPIO_OUT);    
    //使用するPIO番号を指定
    PIO pio = pio0;
    
    //使用するステートマシン番号を指定
    uint state_machine_id = 0;
    //指定したPIOの命令モリにプログラムをロードする
    uint offset = pio_add_program(pio, &mypio1_program);
    
    //PIOプログラムの呼び出しと起動
    mypio1_program_init(pio, state_machine_id, offset, OUT_PIN_BASE, NUM_OUT_PIN, CLOCK_DIV);
    //TX FIFOに点滅間隔(ms)を設定
    //PIOのカウンタ・プログラムは、設定した点滅間隔よりも合計で3サイクル多くかかるので-3する
    pio->txf[state_machine_id] = BLINK_INTERVAL - 3;
   
    
    //メイン処理
    while (true) {
        //LED点灯
        gpio_put(LED_PIN, 1);
        sleep_ms(1000);
        //LED消灯
        gpio_put(LED_PIN, 0);
        sleep_ms(1000);
    }
}
▶️ mypio1.pioの作成
- 「エクスプローラー」に表示されている「blink.pio」の名前を、右クリックして「mypio1.pio」に変更します
- 現在、VSCodeに表示されている「mypio1.pio」の内容をすべて削除します
- 下記「mypio1.pio」のソースコードを入力またはコピー/貼り付けし保存します
【mypio1.pio】
;
; Copyright (c) 2025 ラズパイ実験室
; 
; SPDX-License-Identifier: BSD-3-Clause
;
.program mypio1
    //TX FIFOから32bitのデータ(点滅間隔(ms))をOSR(OUTシフトレジスタ)に移動
    pull block
    //OSRから32ビット、スクラッチレジスタyへデータを移動
    out y, 32
//.wrap_target?.wrap間の処理を繰り返し実行
.wrap_target
  //スクラッチレジスタyをスクラッチレジスタxにコピー    
    mov x, y        ; 1サイクル遅延
    //指定したGPIOピン26をHigh、27をLowに設定
    set pins, 0b01     ; 1サイクル遅延
lp1:
    //Xレジスタが0でなければXレジスタの内容を減算し、lp1にジャンプする。(0ならば次の処理へ)
    jmp x-- lp1     ; x+1サイクル遅延
    //スクラッチレジスタyをスクラッチレジスタxにコピー    
    mov x, y        ; 1サイクル遅延
    //指定したGPIOピン26をLow、27をHighに設定
    set pins, 0b10     ; 1サイクル遅延
lp2:
    jmp x-- lp2     ; x+1サイクル遅延
.wrap               ; 点滅を繰り返す
% c-sdk {
//GPIO出力を設定して、SMが指定されたピンに出力するように設定
 static inline void mypio1_program_init(PIO pio, uint sm, uint offset, uint ouT_pin, uint out_pin_num, float clkdiv){
   
    //指定したPIOステートマシンの初期設定情報をメモリに格納する
    pio_sm_config config = mypio1_program_get_default_config(offset);
    
    //ステートマシンのクロック分周値を設定する。125000000Hz / clkdiv(62500) = 2000Hz
    sm_config_set_clkdiv(&config, clkdiv);
    //LEDピン(GPIO26、GPIO27GPIO27)設定
    {
        int     i;
        for (i = 0; i < out_pin_num; i++){
                //GPIOをPIOに割り当てる
                pio_gpio_init(pio, ouT_pin + i);
                //指定したPIOで使用するステートマシンのGPIOピンの方向を出力に設定する
                pio_sm_set_consecutive_pindirs(pio, sm, ouT_pin + i, 1, true);
        }
        //ステートマシンの「set」ピンを設定する
        sm_config_set_set_pins(&config, ouT_pin, out_pin_num);
    }
    //指定したPIOで使用するステートマシンにメモリに格納された初期設定情報を適用する
    pio_sm_init(pio, sm, offset, &config);
    //指定したPIOで使用するステートマシンを起動する
    pio_sm_set_enabled(pio, sm, true);
 
}
%}▶️ CMakeLists.txtの変更
- 「エクスプローラー」に表示されている「CMakeLists.txt」をクリックすると、エディタエリアに内容が表示されます。
- 「Generate PIO header」のフィル名を、「blink.pio」から「mypio1.pio」に変更し、保存します。
# Generate PIO header
pico_generate_pio_header(pio_led_toggle ${CMAKE_CURRENT_LIST_DIR}/blink.pio)
pico_generate_pio_header(pio_led_toggle ${CMAKE_CURRENT_LIST_DIR}/mypio1.pio)
プログラムの実行
Raspberry Pi Pico Wに接続されているUSBケーブルを外し、「BOOTSEL」ボタンを押しながら、USBケーブルを差し込みます。

「F5」キーを押して実行すると、途中、Temporary breakpointsで一時停止します。

もう一度、「F5」キーを押して続行します。
赤LEDと緑LEDが500ms間隔で交互に点灯・消灯し、青LEDが1000ms間隔で点灯・消灯します。
確認後、「Shift + F5」キーを押して、停止します。
まとめ
GPピンに赤、緑、青の3つのLEDを接続し、PIOを使って赤と緑のLEDを500ms間隔で交互に点灯、消灯させながら、メインの処理で青のLEDを1000ms間隔で点灯、消灯させました。
3つのLEDがスムーズに点灯・消灯することで、メインプロセッサとは独立して動作する、Programmable Input/Output(PIO)のディジタル信号出力機能を確認することができました。
おすすめの一冊
 
 
