GPIO入力を割り込み信号として最速応答速度(最小遅延)でアプリ利用
問題発覚:GPIO入力の応答時間は遅延する
GPIO入力の変化に応じて何らかのアクションを起こすプログラムを作る場合、定期的に(適当な時間sleepして)Readするか、poll等のイベントドリブン(この関数をコールすると、変化が発生するまで返ってこない)を用いると思います。
GPIO入力が変化してから、それを認識してアプリの動作に反映させるまでの時間にこだわらなければ、これがベストでしょう。
しかし、即 反応しなければならないアプリを作る場合、少々厳しいことになります。
ちょっと複雑なLinux環境でpollを使うと、数ms~数100msも遅れる場合があります。
そして、困ったことに、この遅延時間が安定しません。
数msで反応したと思ったら、次の変化では数100msも遅れた、という状態になるのです。
この現象はLinux等のOSでより顕著であるため、こういった遅延や応答速度のバラツキが問題になる基板や製品では採用できず、RTOSを採用せざるを得ない、というケースが多くあります。
以前携わったプロジェクトでも、同様な条件のためRTOSを採用する予定でしたが、「Linuxでも当社のプラットフォームなら1msで応答できます」「ほら見て!」というベンダーの言葉とデモを鵜呑みにしてLinuxを採用し、後から問題になったことがありました。
おそらく、ベンダーの言やデモは、他に何も邪魔するアプリケーションやドライバが稼働していない状態での話だったのでしょう。
あるいは、1msというのは平均値の話であり、最悪値の話ではなかったのでしょう。
最悪なのは、その問題が発覚したのがリリース目前で今更OS変えるなど考えられない時期だったことです。
そして、その問題を明らかにしたのが私でした。
なぜかというと、その応答部分のプログラムの担当にされたから、というのもありますが、前述の話を聞いた時から薄々気づいていたからです。
しかし、状況は前述のとおり最悪で、担当である私には逃げ場がありませんでした。
実証と現実:実際の応答速度は?
薄々感づいていた私は、できるだけ早くこの問題を明らかにする様に取り組みました。
プログラムが完成し、デバッグ段階に突入してからでは、さすがにどうにもならないだろうと考えたからです。
(もちろん最初に警告は発しましたが、のれんに腕押しだったので、証拠を突きつけるしかないのです)
方法は特記するほどのモノではなく、古典的にLEDに出す、というものです。
結果は、平均値は3ms~5msくらい、最速は約1ms、最悪値は100msを超えました。
実際、その基板のパフォーマンスはかなり高いので(少なくとも私が触れたことのある基板では最速)、ラズパイ等の汎用基板ではもっと悪い数値になるでしょう。
それに対する、要求仕様はというと…
「1msくらい?」
…
話にならない。
試行錯誤:応答速度を改善するには?
文句を言っても始まらないので、あの手この手で改善を試みました。
そもそも、この遅延の原因は何だろうか?
基板のパフォーマンスもLinuxのパフォーマンスもそこまで悪くはない。
そう、だから私も証拠無く反対できなかったのですが、その結果は私の予想よりも更に悪いものでした。
各プログラム(プロセスやスレッド)は、OS(Linuxカーネル)のスケジューラによって、順番に、時には交互に、実行されるようになっています。
GPIO入力ドライバが入力の変化をイベントとして発行したとき、何らかのプログラムが実行中の場合、そのブログラムが一旦止まるまで目的のプログラムが動けないので、イベントを受け取るタイミングが遅れてしまうのです。
しかし、優先順位という仕組みがあります。
あるプログラム(プロセスやスレッド)が動作中でも、より優先度の高いプログラムが条件を満たすと(イベント受け取りが発生すると)動作中のプログラムを止めて、優先プログラムが動作するわけです。
通常は優先順位はOSが適切な形になるようにコントロールしていますが、これを操作できます。
とりあえず、目的のプログラム(スレッド)の優先順位だけ上げてみたところ一定の効果を得ることができました。
数値にするのは難しいのですが、頻繁に10msを超えていた状況が改善され、概ね10ms以内に収まる様になった、という感じです。
しかし、これでは不十分です。
「時々、10msを超える」
「頻繁に数ms~10msくらいの遅延が出る」
これではまだ、だめなのです。
割込み応答速度(遅延)改善の切り札
追い詰められた私を救ったのは、あるエンジニアにもらったヒントでした。
シグナルというソフト割り込みの方が早いかも、と。
そして、人のよい彼はそのドライバをわざわざ公開してくれたのです。
gpiisig.c make.zip by ジン(team-ebiメンバー)
結果は劇的でした。
ほとんどの遅延が1ms以内に収まったのです。
そうして山を越え、無事にリリースにこぎつけることができました。
シグナルについては本にも載っていましたが、アプリ側(プロセスやスレッド)にばかり目が行っていました。
1つに集中して周りが見えなくなる悪い例だと思います。
やはり、一人で根を詰めるのはよくありませんね。
助け合いましょう!
というわけで、ドライバを動かすアプリも公開!
お試しあれ!