RP2040のPIOのJMP, MOV, PUSHとかの解説(C言語向け)

RP2040(Raspberry Pi Pico)のPIOのJMPやMOVといったアセンブラのような命令について解説します。

1. JMP

条件によって設定したラベル位置に処理を移動させます。

JMP 例
loop:               ; ラベルloopを設定(名前は自由に付けれる)
    ~その他処理~
    jmp !x loop     ; レジスタXが0の場合、ラベルloopに処理をジャンプします。

命令は次のフォーマットで記述します。

jmp (<条件>) <ラベル>

かっこ()は省略可を意味します。(『jmp <ラベル>』と書くことも可能ということ)

条件が成立(true)する場合に、ラベルにジャンプします。条件は次の8パターンの書き方があります。

条件 動作
記載なし 条件は常に成立(true)となり、常にラベルにジャンプします。
!x レジスタXが0の場合、ラベルにジャンプします。
x-- レジスタXが0では無い場合、ラベルにジャンプします。ついでにレジスタxの値を1引きます。
!y レジスタYが0の場合、ラベルにジャンプします。
y-- レジスタYが0では無い場合、ラベルにジャンプします。ついでにレジスタyの値を1引きます。
x!=y レジスタXとYが違う値の場合、ラベルにジャンプします。
pin あらかじめsm_config_set_jmp_pin関数で設定しておいたGPIOピンがHレベルの場合、ラベルにジャンプします。
!OSRE OUTシフトレジスタが空でなければ、ラベルにジャンプします。(OUTシフトレジスタにはPULL命令によってTX FIFOから32bitのデータが格納される)



2. WAIT

条件が成立するまで待機します。遅延命令を付けた場合は、条件成立直後に遅延処理が実施されます。
WAIT 例

wait 1 gpio 28     ; GPIO28が1(Hレベル)になるまで待機します。

命令は次のフォーマットで記述します。

wait <polarity> gpio <GPIO番号>
又は
wait <polarity> pin <pin番号>
又は
wait <polarity> irq <irq番号> (rel)


polarityには1か0が入ります。
1の場合、指定のGPIO等がHレベル(1)となるまで待機します。
0の場合、指定のGPIO等がLレベル(0)となるまで待機します。
かっこ()は省略可を意味します。

対象 動作
gpio 指定のGPIO番号の入力値(1:Hレベル 0:Lレベル)によって待機するか否かが決まります。
pin あらかじめsm_config_set_in_pin関数で設定した入力ピン番号の値(1:Hレベル 0:Lレベル)によって、待機するか否かが決まります。
例えば次のようにGPIO16をベースの入力ピンと設定していた場合、

sm_config_set_in_pin(&c, 16);

『wait 1 pin 2』によってGPIO18がHレベルになるまで待機します。(ベースをGPIO16としたので、16 + 2 = でGPIO18が対象となる)
irq IRQ番号のフラグよって、待機するか否かが決まります。IRQフラグはIRQ命令で立てたりクリアします。
  • "1"を待っていて"1"になった場合、ステートマシンによってIRQフラグはクリアされます。
  • IRQ番号は0-7までを指定します。relがある場合、ステートマシン番号が足され、その値を4で割った余りの数が対象の番号となります。例えば、ステートマシン2番を使用している場合で、IRQ番号が1の場合は、(2 + 1) % 4 = 3 でIRQ3番が、ステートマシン3番を使用している場合で、IRQ番号が2の場合は、(3 + 2) % 4 = 1 で IRQ1番が、対象のIRQ番号になります。



3. IN

あらかじめステートマシン用に設定しておいたGPIOやX,Yレジスタ等から、INシフトレジスタに値を移します。
IN 例

in pins 1           ; sm_config_set_in_pin関数で設定したGPIOピンの値(H:1 or L:0)を1bit読み、
                    ; ISR(INシフトレジスタ)に値を格納します。
                    ; sm_config_set_in_shift関数で、INシフトレジスタに
                    ; 右シフトで入れるか、左シフトで入れるかを設定しておきます。

命令は次のフォーマットで記述します。

in <読込元>, <読込ビット数>

あらかじめsm_config_set_in_shift関数で読込元のデータをINシフトレジスタへ右シフトで入れるか、左シフトで入れるかを設定しておきます。

読込元は次の6パターンの書き方があります。

読込元 動作
pins あらかじめsm_config_set_in_pin関数で設定しておいたGPIOピンからINシフトレジスタに指定のビット数だけ読み込みます。
x レジスタXからINシフトレジスタに指定のビット数だけ読み込みます。
y レジスタYからINシフトレジスタに指定のビット数だけ読み込みます。
null 指定のビット数分、ゼロを読み込みます。INシフトレジスタを好きなビット数だけ0ビットで埋めることができます。
isr INシフトレジスタからINシフトレジスタに指定のビット数だけ読み込みます。
osr OUTシフトレジスタからINシフトレジスタに指定のビット数だけ読み込みます。

sm_config_set_in_shift関数で自動pushを有効にできます。この自動pushが有効の場合、IN命令で各読込元から読み込んで、push閾値(これもsm_config_set_in_shift関数で設定)に達すると、INシフトレジスタの内容をRX FIFOに移動します(pushの動作)。自動pushが行われてもIN命令は関係なく1クロックで動作します。また、この自動pushの際にRX FIFOの空きが無い場合は、空きができるまで待機状態となります。自動pushではINシフトレジスタの値を全てゼロにクリアします(入力シフトカウントもゼロクリアします)。



4. OUT

あらかじめステートマシン用に設定しておいたGPIOやX,Yレジスタ等へ、OUTシフトレジスタの値を移します。
OUT 例

out pins 3           ; OSR(OUTシフトレジスタ)の最下位3ビットを、
                     ; sm_config_set_out_pin関数で設定したGPIOピンへ書き込みます。
                     ; 例えば、あらかじめsm_config_set_out_pin関数で
                     ; GPIO0~2までを使用すると設定しておけば
                     ; GPIO0~2に最下位3ビットの値(H/L)が出力されます。

命令は次のフォーマットで記述します。

out <書込先>, <書込ビット数>

書込先は次の8パターンの書き方があります。

書込先 動作
pins OUTシフトレジスタから指定のビット数だけ、あらかじめsm_config_set_out_pin関数で設定しておいたGPIOピンへデータを移します。
x OUTシフトレジスタから指定のビット数だけ、レジスタXへデータを移します。
y OUTシフトレジスタから指定のビット数だけ、レジスタYへデータを移します。
null 指定のビット数分、破棄できます。
pindirs 指定のビット数分、PINの入力/出力設定(pio_sm_set_pindirs_with_mask関数等で実施)にデータを移します。
pc 指定のビット数分、pc(program counter)にデータを移すので、そのアドレスに処理がジャンプします。
isr OUTシフトレジスタから指定のビット数だけ、isr(INシフトレジスタ)にデータを移します。
EXEC OUTシフトレジスタの値が命令データとして取り出され、次のクロックで実行されます。

sm_config_set_out_shift関数で自動pullを有効にできます。この自動pullが有効の場合、OUT命令で各書込先へデータを移して、pull閾値(これもsm_config_set_out_shift関数で設定)に達すると、TX FIFOからOUTシフトレジスタに値を移動します(pullの動作)。自動pullが行われてもOUT命令は関係なく1クロックで動作します。また、この自動pullの際にTX FIFOが空だった場合、データが入るまで待機状態となります。(また、出力シフトカウントはゼロクリアされます)。



5. PUSH

ISR(INシフトレジスタ)の内容をRX FIFOに移動します。(INシフトレジスタはゼロになります)
PUSH 例

push            ; 現状のISR(INシフトレジスタ)の値をRX FIFOに移動します。
                ; ただし、RX FIFOの空きがない場合は、空きができるまで待機状態となります。

命令は次のフォーマットとなります。

push (iffull)

push (iffull) block

push (iffull) noblock

かっこ()は省略可を意味します。(『push』と書くことも可能ということ)

次のような動作となります。

書き方 動作
push 現状のISR(INシフトレジスタ)の値をRX FIFOに移動します。ただし、RX FIFOの空きがない場合は、空きができるまで待機状態となります。
push iffull sm_config_set_in_shift関数で設定したpush閾値を超えていない場合、何もしません。push閾値を超えている場合に、現状のISR(INシフトレジスタ)の値をRX FIFOに移動します。ただし、RX FIFOの空きがない場合は、空きができるまで待機状態となります。
push block 『push』と同様です。blockはデフォルトで有効状態なので、push実行時、RX FIFOの空きがない場合は、空きができるまで待機状態となります。
push noblock 現状のISR(INシフトレジスタ)の値をRX FIFOに移動します。ただし、RX FIFOの空きがない場合は、値を破棄して次の命令に進みます。

sm_config_set_in_shift関数で自動pushを有効にできます。この自動pushが有効の場合、push閾値(これもsm_config_set_in_shift関数で設定)に達すると、INシフトレジスタの内容をRX FIFOに移動します(pushの動作)。



6. PULL

TX FIFOから32bitのデータをOSR(OUTシフトレジスタ)に移動します。
PULL 例

pull            ; TX FIFOから32bitのデータをOSR(OUTシフトレジスタ)に移動します。
                ; ただし、TX FIFOが空の場合、データが入るまで待機状態となります。

命令は次のフォーマットとなります。

pull (ifempty)

pull (ifempty) block

pull (ifempty) noblock

かっこ()は省略可を意味します。(『pull』と書くことも可能ということ)

次のような動作となります。

書き方 動作
pull TX FIFOから32bitのデータをOSR(OUTシフトレジスタ)に移動します。ただし、TX FIFOが空の場合、データが入るまで待機状態となります。
pull ifempty sm_config_set_out_shift関数で設定したpull閾値を超えていない場合、何もしません。pull閾値を超えている場合に、TX FIFOから32bitのデータをOSR(OUTシフトレジスタ)に移動します。
pull block 『pull』と同様です。blockはデフォルトで有効状態なので、pull実行時、TX FIFOが空の場合、データが入るまで待機状態となります。
pull noblock TX FIFOから32bitのデータをOSR(OUTシフトレジスタ)に移動します。ただし、TX FIFOが空の場合は、レジスタXの値が入ります。(TX FIFOが空の場合、『mov x, osr』と同じ効果が得られる)

sm_config_set_out_shift関数で自動pullを有効にできます。この自動pullが有効の場合、pull閾値(これもsm_config_set_out_shift関数で設定)に達すると、TX FIFOからOUTシフトレジスタに値を移動します(pullの動作)。



7. MOV

データを指定の読込元から指定の宛先にコピーします。
MOV 例

mov pins, x     ; あらかじめsm_config_set_out_pin関数で設定しておいた
                ; GPIOピンに、Xレジスタの値をコピーします。

mov x, pins     ; あらかじめsm_config_set_in_pin関数で設定しておいた
                ; GPIOピンの値(H:1/L:0)を、Xレジスタにコピーします。

命令は次のフォーマットとなります。

mov 宛先, (op) 読込元

かっこ()は省略可を意味します。(『mov 宛先, 読込元』と書くことも可能ということ)


宛先には次が入ります。

宛先 宛先/動作
pins sm_config_set_out_pin関数で設定しておいたGPIO
x レジスタX
y レジスタY
exec コピーしたデータを命令として実行します。MOVの次のクロックで命令が実行されます。
pc pc(program counter)にコピーします。そのアドレスに処理が移動(JMP)します。
isr isr(INシフトレジスタ)
osr osr(OUTシフトレジスタ)

(op) には次が入ります。

(op) 動作
(省略) 値をそのままコピーします。
『!』 または 『~』 ビット反転してコピーします。
『::』 ビット順を逆に並び変えてコピーします。

読込元には次が入ります。

読込元 読込元/動作
pins sm_config_set_in_pin関数で設定しておいたGPIO
x レジスタX
y レジスタY
null 0がコピーされます。
status ? (本家pdfを参照してください)
isr isr(INシフトレジスタ)
osr osr(OUTシフトレジスタ)



8. IRQ

割り込みフラグを立てます。またはフラグを下げます(クリア)。
IRQ 例

irq 2     ; IRQ2番のフラグを立てます。

命令は次のフォーマットとなります。

irq IRQ番号 (_rel)

irq set IRQ番号 (_rel)

irq nowait IRQ番号 (_rel)

irq wait IRQ番号 (_rel)

irq clear IRQ番号 (_rel)

かっこ()は省略可を意味します。(『irq IRQ番号』と書くことも可能ということ)

IRQ番号は0-7の間で指定します。0-7はステートマシン間で共有できます。また、0-3はシステムレベルで共有できます。(irq_set_exclusive_handler関数、pio_set_irq0_source_enabled関数等で設定。詳細は別で記述予定です)

命令分 動作
irq IRQ番号 IRQ番号のフラグを立てます。IRQ番号は0-7までを指定します。
irq IRQ番号 _rel IRQ番号のフラグを立てます。IRQ番号は0-7までを指定します。_relがある場合、ステートマシン番号が足され、その値を4で割った余りの数が対象の番号となります。例えば、ステートマシン2番を使用している場合で、IRQ番号が1の場合は、(2 + 1) % 4 = 3 でIRQ3番が、ステートマシン3番を使用している場合で、IRQ番号が2の場合は、(3 + 2) % 4 = 1 で IRQ1番が、対象のIRQ番号になります。
irq set IRQ番号 『irq IRQ番号』と同様です。
irq nowait IRQ番号 『irq IRQ番号』と同様です。
irq wait IRQ番号 IRQ番号のフラグを立てます。この後、このフラグが下がるまで待機します。
irq clear IRQ番号 IRQ番号のフラグを下げます。(クリアします)



9. SET

5bitの値(0~31)を指定の宛先に書き込みます。
SET 例

set x, 27     ; レジスタXに27を入れます。

命令は次のフォーマットとなります。

set 宛先 値

値は0-31の間で指定します。
指定可能な宛先は次のようになります。

宛先(書き方) 宛先/動作
pins あらかじめsm_config_set_set_pin関数で設定しておいたGPIOピン
x レジスタX
y レジスタY
pindirs PINの入力/出力設定(pio_sm_set_pindirs_with_mask関数等で実施)



すべてのPIO命令は1クロックで実行されます。Picoのクロックスピードは125Mhzですが、ステートマシンには分周期があり、最大65536除算した値[(125Mhz / 65536) = 1907Hz]まで速度を落とすことができます。(sm_config_set_clkdiv関数で設定)

すべてのPIO命令には最後尾に遅延命令[数値]が付けられます。(例:『out x, 1 [7]』:7クロック分何もしない)


コメント

このブログの人気の投稿

v4l2-ctlで行うUSBカメラ設定方法まとめ

【Windows11】簡単にできるRaspberry Pi Pico 開発環境構築 (VSCode, C言語, PicoProbe)

Raspberry Piでシリアル通信する方法(通信設定・通信確認)