tunozemichanの日記 / tunozemichan's diary

SORD社のコンピューターM68やM68MXの解析についての備忘録です。This blog is a memorandum about the analysis of SORD's computers M68 and M68MX.

Raspberry Pi PICOのPIO(Programmable I/O)のコードをArduino IDE経由でSDKの関数を使って書く(その1)

Raspberry Pi PICOのPIOをArduino IDEを使って書く方法のメモです。

 

PIOでLチカっぽいことをする方法

まず、PIOのアセンブラを書きます。この時、PIOに関するCのコードを併記しておくと、設定の修正があった時、1つのファイルにまとまって情報があって楽になります。

test_signal.pioと言う名前で以下のコードを書きました。


.program test_signal

// ここから、PIOアセンブラコード

loop: // ループの起点。解説書だと.wrapとも書いてある。
    wait 1, gpio 0 // GPIO0がHIGHになるまで待つ
    set pins, 0  [10] // set pinとして設定した名前pinsのピンを0にして、10遅延(その状態を維持する)
    set pins, 0  [10]
    set pins, 0  [10]
    set pins, 0  [6]
    set pins, 1  [10]
    set pins, 0  [7]
    set pins, 1  [3]
    set pins, 0  [5]
    set pins, 1  [11]
    wait 0, gpio 0

// ここまで

% c-sdk {

// 初期化関数

static inline void test_signal_program_init(PIO pio, uint sm, uint offset, uint pin) {
    pio_sm_config c = test_signal_program_get_default_config(offset);

 // Set pinをセット

    sm_config_set_set_pins(&c, pin, 1);

   // GPIOを初期化

    pio_gpio_init(pio, pin);

 // pinの方向を決める。set pinで設定したら出力に決まっているが、設定した方がよいみたい

    pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, true);

 

    //  state machineを初期化
    pio_sm_init(pio, sm, offset, &c);

    // state machine起動

   pio_sm_set_enabled(pio, sm, true);
}
%}

 

遅延と書いたのは、その状態をクロック数分維持するという意味です。PICOのシステムクロックの速度に依存します。PIOのstate machineをシステムクロックの何分の一で動かすのかは、

sm_config_set_clkdiv(&c, 6.25);

と、初期化関数の中で記載します。上記では6.25分の1で動かします。

 

このコードをpioasm Onlineのページの左の枠にコピペします。即座にアセンブルされ、右の枠に出力されます。また、% c-sdk { %}で囲った範囲はCのコードとして扱われて、やはり成形されて一緒に出力されます。

Cコード部分に以下の情報が付加されます。

 
static const struct pio_program test_signal_program = {
    .instructions = test_signal_program_instructions,
    .length = 9,
    .origin = -1,
};
PIOアセンブラコードの起点originと長さlengthの情報です。

static inline pio_sm_config test_signal_program_get_default_config(uint offset) {
    pio_sm_config c = pio_get_default_sm_config();
    sm_config_set_wrap(&c, offset + test_signal_wrap_target, offset + test_signal_wrap);
    return c;
}
PIOのループwrapの定義です。

 

右の枠に出たアセンブラコードとCのコードを例えば、test_signal.pio.hとしてinoファイルのあるフォルダに保存します。

 

ここまで来たら、Arduino IDEのコード、例えばtest_signal.inoを書きます。


// Our assembled program:
#include "test_signal.pio.h"

void setup() {
    PIO pio = pio0;
    uint offset = pio_add_program(pio, &test_signal_program);
// 使っていないstate machineの番号を得る。0番号が明白なら、sm = 0;とか書いてもよい。
    uint sm = pio_claim_unused_sm(pio, true);
// test_signal.pio.hに書かれている通り、最後の引数はset pinに割り当てるGPIOの番号
    test_signal_program_init(pio, sm, offset, 15);
    while (true) {
    }
}

void loop() {
}
setup()関数内で、test_signal_program_init()に具体的なピン番号を渡すだけです。
loop()で何か処理をするなら、setup()内のwhileを消し忘れないようにしましょう。
 
お勧め参考書
参考書として有名なのはインターフェース2021年8月号がありますが、その後に出版された「ラズベリーパイ Pico/Pico W攻略本」の方がサンプルコード(特にUART)が整理されていて、自分としては読みやすかったし、得るものが多かったです。
 
MicroPythonやCircuitPythonでもPIOのコードを書けますが、SDKのCの関数を知らないと混乱します。今回に限って言えば、敷居が低いのはSDK(またはArduino IDE))です。
 
Set pinとOut pin
上記書籍のUARTのサンプルコードにはっきり書いてありますが、Set pinとOut pinは同一ピンに割り当てられます。UARTでは、Set pinでスタートビットを出力して、その後、Out pinでデータを送るコードになっています。
Set pin、Out pinを定義するには、初期化関数の引数に加えるだけです。例えば、上記の例の初期化関数の定義のところで
 test_signal_program_init(PIO pio, uint sm, uint offset, uint set_pins, uint out_pins)などと修正して、 
    sm_config_set_set_pins(&c, set_pins, 1);
    sm_config_set_oput_pins(&c, out_pins, 1);
とするだけです。inoファイルのsetup()内でset_pinsとout_pinsに同じGPIO番号を割り当てれば、両方使えるピンになります。GPIO15番を割り当てるなら以下のようなこんな感じです。
 
 test_signal_program_init(pio, sm, offset, 15, 15);