前回でようやく指定回数CW/CCWで回すということが(分解能1/4だが)一応できた。
今回は仕事投げっぱなしのダメ上司ムーブをIRQを使って少しマシにしようと思う。
IRQとは
IRQとはハード関連のプログラムやマイコンのファームウェアを書く人にはおなじみの割り込み要求(Interrupt ReQuest)と呼ばれるものである。
CPUが何かのトリガで動く際に、ずっと無限ループなどで監視(ポーリング)していると他の作業ができなくなる。
そのため、以下のような仕組みで「割り込み作業」を実行することでCPUの使える時間を増やす。
割り込み元が割り込み要求(IRQ)を発行する
↓
CPUは割り込みが入った際に一旦メインの作業を停止し、作業中の情報を退避する
↓
CPU側で割り込み関数を実行する
↓
CPUが作業中の情報を元に戻し、割り込みが入った際に止めていた場所から再開する
割り込み処理はマイコンを使う上で必須の機能だが、複雑になるため可能ならば余り使いたくない機能でもある。
今回のように処理待ちでは使わざるおえないため仕方なしに使っていく。
コード
実際のコードは下記になる。
from rp2 import PIO, StateMachine, asm_pio
import time
from machine import Pin
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(1)
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
def set_flag():
global irq_flag
irq_flag = True
print("set_flag:" + str(irq_flag))
PIO(0).irq(lambda pio: set_flag())
cw_ccw = StateMachine(0, cw_ccw, freq=2000, set_base=Pin(2))
cw_ccw.active (1)
cw_ccw.put(0x100) # CW
while True:
if irq_flag:
irq_flag = False
break
cw_ccw.put((0b1 << 31) + 0x100) # CCW
while True:
if irq_flag:
irq_flag = False
break
cw_ccw.active(0)
動作させると回転が終わったと同時に逆回転が始まるという制御になる。
「終わりました」と報告されたらすぐに次の仕事をアサインするようになりちょっとはダメ上司感が減った。
内容としては回転が終了した時点でirq(1)で割り込み要求を発行し、PIO(0).irq(lambda pio: set_flag())でset_flag()を割り込み関数として登録している。
割り込み関数set_flag()内でglobal宣言したirq_flagを変更している。
メインループ内で無限ループを作成し、irq_flagがTrueになるまで待っている。
注意したいのは同時に割り込みが発生するなどしてirq_flagに同時にアクセスに行くと値が壊れること。
今回は同時に割り込みが入ることは無いため問題ないが、実用上は排他制御が必要になることに注意したい。