質問: c言語初心者なため、質問させていただきます。 ラズパイで赤外線LEDや赤外線受光モジュール・LCDディスプレイ・タクトスイッチを使って、サバイバルゲーム企画をゼミで進めています。 ラズパイを組み込み機器のようにして、赤外線銃と赤外線受光部を作ろうと思っています。 使用言語はC言語で開発しています。 機能の概要としては、 1. ボタン入力を受け取ったときに赤外線を送信し残段数を減らしLCDに表示 2. 赤外線受光モジュールが赤外線を受光したときに、HPを減らしLCDに表示 3. 1番とは別のボタン入力を受け取ったときに残弾数回復(いわゆるリロードです)そしてLCDに表示 4. サバイバルゲーム1試合を管理するゲームの開始・終了・死亡から復活までの待機などの時間管理 5. 上記イベントが発生したときに,用意しておいた効果音の再生 などを目標にしていました。 1.2.3に関してはwiringPiライブラリの関数であるWiringPiISRで各自GPIOピンに対する割り込みイベントの設定 4に関しては、1秒周期のタイマー割り込みを使用し時刻を更新して条件(開始時刻など)に合致させる 5に関しては、c言語上でsystem関数を使い[aplay]コマンドの実行 などで実装を試みました。 ですが、実際に起動してみると割り込みがそれぞれ衝突してしまうのか、不具合が多く発生してしまいました。 実際に起きている不具合としては * LCDディスプレイで意図しない場所に表示がずれてしまう。(HPの場所に残段数が表示される) * タイマー割り込みが発生するからなのか、sleep関数 1秒以上(おそらくタイマ割り込みの周期)が使えない * タイマ割り込み上でsystem関数を使うと入力を一切受け付けなくなってしまう などが発生してしまいました そこでいくつか質問させていただきます。 質問その1**wiringPiISRで宣言したイベントを一時的に禁止・停止させる方法はありますか?** もしそれがあるのならば、割り込みイベントAが発生したときに残りの割り込みイベントBなどに対して禁止できれば 割り込みが重なることがないのでは?と考えたためです。 質問その2**質問その1の方法ではなくイベントひとつ発生時にほかの割り込みを禁止する方法はありますか?** おなじく、割り込みに重ねた割り込みをとめられるのでは?と思いました。 質問その3**タイマー割り込みを使用している場合、sleep関数は使えないものなのですか?** できないのであれば、sleep関数のみで何とか実装しようと思っています。 質問その4**そもそもが組み込みプログラム上でのsystem関数はナンセンスでしょうか?** 音声の再生時間にCPUが多くとられているのなら、時間的にもったいないのかなと思います。 質問その5**組み込み機器での入力の待機処理というのはwhileやforによる無限ループやpause関数で実現しているのですか?** 本企画がサバゲーなので、どうしても入力が一切ない待機状態(タイマ割り込みを除く)があるため、どう実装しようか模索中です。 現状での問題点は上記になります。 Raspberryで同じく電子工作をしている方や組み込みに携わったことのある方 ご教授いただけたらと思います。 自分でも調べてはいるんですが、知識が圧倒的に乏しく時間に余裕がないため質問させていただきました。 よろしくお願いします。 2/3 11:00ソースコード追記 先にごり押し版を掲載させていただきます。 ir_game.c 2/4 19:36 ソースコード改変版 ir_test_game.c
回答: 割り込みで行うのは最低限のことだけします。つまりフラグを立てる。 例えば、トリガーが引かれたら、トリガー引かれているフラグを立てる。 タイマー割り込みがあったら、やっぱりタイマーフラグを立てる。 条件分岐もしません。粛々とフラグを立てます。 そしてメインループで フラグ群と 現在の状態(HP、残弾数、リロード残時間、発砲クールタイム残時間など)から 次の状態を算出し、 算出した次の状態から外部への出力(LCD、音)を求め(実行)ます。 特に状態遷移と外部出力を分けることで、コードがわかりやすくなります。 ループ処理の状態更新部分は雰囲気こんな感じです。あくまで雰囲気。 ``` if(!System.ShouldUpdate) // 100Hzとかで状態更新 周期はどこかに定数で持っておくと楽 return; if(Input.ButtonReload && !PreviousState.IsReloading){ State.IsReloading = true; State.TimeToReload = (int)(ReloadTime_ms / SystemTimerInterval_ms); // [ms] / [ms/update] } if(PreviousState.IsReloading){ State.TimeToReload = PreviousState.TimeToReload - 1; } /* ... */ PreviousState = State; ResetFlag() ```