前回で分解能の問題はあるが、とりあえず一通りの機能は実装できた。
なので今回は一旦の仕上げとして並列化してみようと思う。
並列化
Raspberry Pi PicoのPIOはインストラクションメモリを共用して0~3,4~7のステートマシンを同時に動作させることができる。
これは2種のプロトコルを各4chまで並列に動かせるということに他ならない。
2種と書いているが当然同じプロトコルでも良く、最大8chの実装が可能になる。
前回のコードを変更して0~3のステートマシンを並列に動作させると下記のようになる。
注:配線が面倒なためSM1~SM3のモータの動作確認は行っていません
from rp2 import PIO, StateMachine, asm_pio
import time
from machine import Pin
@asm_pio(set_init=(PIO.OUT_LOW, PIO.OUT_LOW, PIO.OUT_LOW, PIO.OUT_LOW), out_shiftdir=PIO.SHIFT_LEFT, autopull=False)
def cw_ccw():
wrap_target()
pull()
out(y, 1)
out(x, 31)
label("continue")
jmp(not_y, "cw")
jmp("ccw")
label("return")
jmp(x_dec, "continue")
irq(0x11)
wrap()
label("cw")
set(pins, 0b1001)[3]
set(pins, 0b0011)[3]
set(pins, 0b0110)[3]
set(pins, 0b1100)[3]
jmp("return")
label("ccw")
set(pins, 0b1100)[3]
set(pins, 0b0110)[3]
set(pins, 0b0011)[3]
set(pins, 0b1001)[3]
jmp("return")
irq_flag = [False] * 4
def set_flag(flags):
global irq_flag
if flags == 0x200:
irq_flag[0] = True
elif flags == 0x400:
irq_flag[1] = True
elif flags == 0x800:
irq_flag[2] = True
elif flags == 0x100:
irq_flag[3] = True
PIO(0).irq(lambda pio: set_flag(pio.irq().flags()))
sm = []
for i in range(4):
sm.append(StateMachine(i, cw_ccw, freq=2000, set_base=Pin(2 + i * 4)))
for i in range(4):
sm[i].active(1)
cw_flag = [True] * 4
for i in range(4):
sm[i].put((i + 1) << 8) # CW
while True:
for i in range(4):
if irq_flag[i]:
irq_flag[i] = False
if cw_flag[i]:
cw_flag[i] = False
print("SM" + str(i) + " CCW:" + hex(((i + 1) << 8)))
sm[i].put((0b1 << 31) + ((i + 1) << 8)) # CCW
else:
cw_flag[i] = True
print("SM" + str(i) + " CW:" + hex(((i + 1) << 8)))
sm[i].put((i + 1) << 8) # CW
ポイントは割り込みの処理。
データシートの下記図を見ても分かる通り、各0~3のステートマシンでIRQは共用されている。

このため、どのステートマシンから発行された割り込みかを判別しなければならない。
発行時にステートマシン情報を付加しているのが
irq(0x11)
の部分。
irq()の引数はデータシートで見ると5bitのindexとなっており0x00~0x1fまでの値を取りうるが、MSB(最上位ビット)とそれ以外で扱いが異なる。
MSBに1が立つとステートマシン判別用の計算を行い、呼び出し元に計算後の値を割り込みフラグとして渡す。
受け取り側は4の剰余加算をindexに加算した値(?)を受け取るらしいが、計算が面倒なのでPIO(0).irq(lambda pio: print(pio.irq().flags())を実行して確認した。
具体的にirq(0x11)を行った場合、SM0:0x200,SM1:400,SM2:0x800,SM3:0x100の値の割り込みフラグとなる。
それを割り込み関数内で判別して各変数に振り分けている。
PIO雑感
これまでのコードにより実現した機能は、例えばXYZ軸のモーターを並列に動かしながら別の作業(UARTの待受など)を実行することができるという意味で非常に強力である。
今後のマイコンの方向性としてこういったIO用ステートマシンと独自命令を入れておくというのはトレンドになるのかもしれない。
ARM自体もそういう方向性(Custum Instructions)に見えないこともないし。
PIOは優れた機能なのだが、正直もう少しインストラクションメモリとスクラッチレジスタが欲しかった。
価格との兼ね合いなんで仕方ないんでしょうけど。
もうちょっとPIO周りの仕様が豪華で¥100ぐらい値上げされたVerとか作ってくれませんかねぇ…。
このステッピングモータを使った内容はここまでで終了し、次回は一旦これまでのPIOで得られた知識をまとめた記事を投稿しようかと思う。
その後は別のステッピングモータとモータドライバで動かしていく予定。