The Fool In The Valleyの雑記帳

-- 好奇心いっぱいのおじいちゃんが綴るよしなし事 --

Raspberry Piでstepping motorを動かす 2

Raspberry Piでstepping motorを動かす 1」でモータの正転、逆転、停止ができるようになったので、外部スイッチを使ってそれらの動作を切り換え、更にその時の状態がLEDで表示されるようにしてみました。
以下はそれに関するメモです。

ーーーーーーーーーーーーーーーーー
ステッピング・モータで稼働させる機械をRaspberry Piを使ってコントロールする方法を検討するために次のようなシステムを考える。
【要求仕様】
・ステッピングモータは初期状態で停止。赤スイッチを押すと正転状態になり作動開始。
・正転状態で緑スイッチを押すと逆転。逆転状態で黄スイッチを押すと正転。それを繰り返すことができる。
・正転あるいは逆転状態のときに黒スイッチを押すと停止。
・各スイッチはプッシュ・スイッチで、任意のタイミングで押して離すことができる。
・ステッピングモータの状態がLEDで表示される。

【状態遷移図】
上記要求仕様は、押して離されたスイッチとそれによって遷移する状態の関係から次のような状態遷移図で定義することができる。

f:id:tfitv:20220309080650j:plain

【ハードウェア】
各スイッチが押して離されるのをRaspberry PiのGPIOの入力機能を使って検出する。また、状態を示すLEDをRaspberry PiのGPIOの出力機能を使って点灯・消灯させることにする。そこで、「Raspberry Piでstepping motorを動かす 1」で作ったRaspberry Piの回路に次のような回路を追加する。

f:id:tfitv:20220308105119j:plain

【ソフトウェア】
スイッチは任意のタイミングで押されるので、システムは常にスイッチの状態を監視していなければならない。Pythonではバージョン 3.7 でthreadingが常に利用可能なモジュールとなり、並列処理を行うことができるようになっており、Pythonは3.9がインストールされた最新のRaspberry Pi OSでは当然それが可能になっている。そこで、各スイッチの監視と状態遷移の管理をRaspberry Pi上でスレッドとして実行させることにより上記の仕様を実現できそうである。
下は、スイッチの監視と状態遷移の管理をスレッドで実行させるようにしたPythonプログラムである。4つのスイッチの監視はdef文でそれぞれ、check_red_sw()、check_grn_sw()、check_ylw_sw()、check_blk_sw()で定義され、状態遷移の管理は、state_transition()で定義されている。これらの5つの関数をtaregetとしてthreading.Thread()のインスタンスを作成し、start()で開始させることによりマルチスレッド化を実現することができる。ここで、check_grn_sw()、check_ylw_sw()、check_blk_sw()については、check_red_sw()と類似の処理であるので詳細は省略する。
また、LEDの点灯、消灯は、state_transition()の中でGPIOへの出力として行われるが、煩雑になるためにこれについても下のプログラム例では記述するのを省略している。

import RPi.GPIO as GPIO
import time
import threading

APHASE = 5; AENABLE = 6; BPHASE = 19; BENABLE = 26
SW_RED = 8; SW_GRN = 25; SW_YLW = 24; SW_BLK = 23
LED_RED = 21; LED_GRN = 20; LED_YLW = 16; LED_BLU = 12
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BCM)
GPIO.setup(APHASE, GPIO.OUT); GPIO.setup(AENABLE, GPIO.OUT)
GPIO.setup(BPHASE, GPIO.OUT); GPIO.setup(BENABLE, GPIO.OUT)
GPIO.setup(SW_RED, GPIO.IN); GPIO.setup(SW_GRN, GPIO.IN)
GPIO.setup(SW_YLW, GPIO.IN); GPIO.setup(SW_BLK, GPIO.IN)

GPIO.output(AENABLE,GPIO.HIGH); GPIO.output(BENABLE,GPIO.HIGH)
msec = 0.005        # 5msec      200pulse(1秒)で1回転
pushedSwitch = 3    # 初期状態はSw 3が押された(HALT)状態
motorState = 0      # 0:HALT   1:CW    2:CCW

def CW():
    GPIO.output(APHASE, GPIO.HIGH); time.sleep(msec)
    GPIO.output(BPHASE, GPIO.HIGH); time.sleep(msec)
    GPIO.output(APHASE, GPIO.LOW); time.sleep(msec)
    GPIO.output(BPHASE, GPIO.LOW); time.sleep(msec)

def CCW():
    GPIO.output(BPHASE, GPIO.HIGH); time.sleep(msec)
    GPIO.output(APHASE, GPIO.HIGH); time.sleep(msec)
    GPIO.output(BPHASE, GPIO.LOW); time.sleep(msec)
    GPIO.output(APHASE, GPIO.LOW); time.sleep(msec)

def check_red_sw(): # check if red sw was pushed
    global pushedSwitch #def文内でGlobal変数を読み書きするため
    oldSwStatus = 0
    while True:
        curSwStatus = not GPIO.input(SW_RED)
        if curSwStatus == 0 and oldSwStatus == 1:
            pushedSwitch = 0
        oldSwStatus = curSwStatus
        time.sleep(0.1)

def check_grn_sw(): # check if green sw was pushed
    - 略 -

def check_ylw_sw(): # check if yellow sw was pushed
    - 略 -

def check_blk_sw(): # check if blue sw was pushed
    - 略 -

def state_transition():
    global motorState
    while True:
        if motorState == 0:    # State 1
            if pushedSwitch == 0: # red was pushded
                motorState = 1
        if motorState == 1:   # CW
            CW()
            if pushedSwitch == 1:   # green was pushed
                motorState = 2
            elif pushedSwitch == 3: # black was pushed
                motorState = 0    
        if motorState == 2:   # CCW
            CCW()
            if pushedSwitch == 2:   # yellow was pushed                  
                motorState = 1
            elif pushedSwitch == 3: # black was pushed 
                motorState = 0           
t1 = threading.Thread(target = check_red_sw)
t2 = threading.Thread(target = check_grn_sw)
t3 = threading.Thread(target = check_ylw_sw)
t4 = threading.Thread(target = check_blk_sw)
t5 = threading.Thread(target = state_transition)
t1.start(); t2.start(); t3.start(); t4.start(); t5.start()

【動作テスト】
下のビデオは、上記のプログラムを実行した様子で、下のような流れが確認できる。
◆初期状態から赤いスイッチを押すとCWに回転する。
2相のステッピングモータのそれぞれに流れる電流の様子がオシロで確認できる。
◆緑のスイッチを押すと反転し、CCWに回転する。
2相のステッピングモータのそれぞれに流れる電流が逆相になったのが確認できる。
◆黄色のスイッチを押すと再度反転し、CWに回転する。
◆緑のスイッチを押すと反転し、CCWに回転する。
◆最後に黒のスイッチを押すと停止する。

youtu.be

Raspberry Piでstepping motorを動かす 1