ラズペリーパイのGPIO応答速度を測定(Python & CUI イベントコールバック編)
前回のラズベリーパイのGPIO応答速度を測定(Python & GUI編)でGPIO応答速度を測定するとCUI & ポーリング式と比べると50倍くらい遅くなっていました。
遅くなる原因がGUIによるものなのかイベントコールバックによるものなのかを判別するためにCUI版のイベントコールバック式のコードで試してみました。
コードは以下のとおりです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
import RPi.GPIO as GPIO import time ledOut = 17 swIn = 18 def gpio_event_callback(swIn): if GPIO.input(swIn) == False: GPIO.output(ledOut, False) else: GPIO.output(ledOut, True) GPIO.setmode(GPIO.BCM) GPIO.setup(swIn, GPIO.IN, pull_up_down=GPIO.PUD_DOWN) GPIO.setup(ledOut, GPIO.OUT) GPIO.output(ledOut, False) GPIO.add_event_detect(swIn, GPIO.BOTH) GPIO.add_event_callback(swIn, gpio_event_callback) try: while True: time.sleep(1e-3) except KeyboardInterrupt: GPIO.cleanup() |
配線はラズベリーパイのGPIO応答速度を測定(Python編)と同じでnucleoの青いプッシュボタンを押すとPC10(nucleo)→GPIO18(raspberry pi)→GPIO17(raspberry pi)と信号が伝搬していきます。
以前のラズベリーパイのGPIO応答速度を測定(Python編)ではポーリングでGPIO18(swIn)をモニターしてそれをそのままGPIO17(ledOut)に反映していましたが、swInを両エッジイベント(GPIO.BOTH)に登録してコールバック関数gpio_event_callbackでledOutに反映するようにしています。
またラズベリーパイのGPIO応答速度を測定(Python編)ではGPIO23にLowを入れると終了(とgpioクリア)していましたが、今度はCtrl+Cで終了するようにしています。
結果が以下のとおりです。
10回平均で約193usecです。GUI版の約半分くらいです。
GUI関連のイベント待ち処理が約219usec(=412usec – 193usec)くらい、GPIOイベント待ち関連が約185usec(=193usec – 7.6usec)くらいなのでしょうか。
イベント処理関連のオーバーヘッドが結構大きいのかも知れません。
ちなみにtime.sleep(1e-3)は1msec sleepの意味ですが、ここを1(=1sec)、1e-6(=1usec)にすると小さい値ほど小さくなる傾向はあるようですがさほど大差ありませんでした。
time.sleep | GPIO反応時間(10回平均) |
1e-6 | 約160usec |
1e-3 | 約193usec |
1 | 約231usec |
またちょっと本題から外れますがtime.sleepの実力はどの程度かを知るために実時間を計測するために以下のコードを書いて測定してみました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
import RPi.GPIO as GPIO import time ledOut = 17 swIn = 18 GPIO.setmode(GPIO.BCM) GPIO.setup(swIn, GPIO.OUT) GPIO.setup(ledOut, GPIO.OUT) GPIO.output(swIn, False) GPIO.output(ledOut, False) a = input('ready?') GPIO.output(swIn, True) time.sleep(1e-3) GPIO.output(ledOut, True) a = input('finish?') GPIO.cleanup() |
ledOut、swInは名前をそのまま使っていますが、PC10(nucleo)からの接続は外して、swInからHighを出力してtime.sleep後にledOutにHighを出力してその時間差をnucleoで測定しています。
途中、input文で動作を止めているのは一気にこのコードを走らせるとGPIOが不定のときのnucleo測定ファームウェアがインプットキャプチャー割り込みを拾ってしまうので、これを防ぐためいったん’ready?’まで走らせてからnucleo測定ファームウェアを走らせて、’finish?’で結果を読み取るためです。
結果は以下のとおりです。
time.sleep | time.sleep実時間 |
1 | 約1.001398sec |
1e-3 | 約1.292msec |
1e-6 | 約274usec |
1secはかなり正確ですが、実質使えるのは1msecくらい(それでも0.292msecの誤差があります)で、1e-6となると処理が全く間に合っていません。
最初の測定結果からしても1e-6での結果は当然ですね。
終わり