質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.34%

RaspberryPiで割り込み処理

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 4,772

Yomogi

score 6

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関数を使うと入力を一切受け付けなくなってしまう
    などが発生してしまいました

そこでいくつか質問させていただきます。

質問その1wiringPiISRで宣言したイベントを一時的に禁止・停止させる方法はありますか?
もしそれがあるのならば、割り込みイベント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

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

+2

これは具体的にソースを見せてもらわないと回答は難しいと思います。
が、なんとなく、状態遷移がうまくできていないんじゃないかなという印象があります。

また、割り込み処理内で処理を詰め込みすぎでは?割り込み処理は、できるだけ短時間で済ますべきです。
具体的にはセマフォなどで割り込みが起こったことを通知するだけ、とか、残弾数をデクリメントするだけとかにとどめて、そのほかの処理は全部メインループの中で行うようにすべきだと思います。

こうすれば、状態遷移はメインループ内だけで管理すればよく、また仮に多重割り込みが起こって処理はシリアルなので変なことになりにくくなると思います。

記事を参考に状態遷移図を書いてみるのをお勧めします。
http://www.itmedia.co.jp/keywords/statetransitiontable.html

wiringPiはちょっといじったぐらいなので詳しいことはわからないのですが、
質問その1、質問その2
パッと見で禁止する方法が書いてなさそうだったのですが、割り込み関数にnullを渡すのではだめでしょうか?
ただ、状態遷移がちゃんと管理されていれば別に禁止する必要はなさそうな気がしますし、禁止できたところで状態遷移がちゃんと管理できていなければうまく動かないような気がします。
(不確実な情報ですが、多重割り込みは可能なようでした)

質問その3、質問その4
使えるとは思いますが、割り込み関数の中ではやめたほうが良いでしょう。

質問その5
ウェイトはできるだけタイマー割り込みを使いましょう。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/02/03 08:32

    CodeLabさん、回答ありがとうございます。

    確かに割り込み処理内に詰め込みすぎですね。と、いうと割り込み処理内でフラグを立てて、メイン内でフラグに対応した処理を行いすぐにフラグを倒す感じですかね?
    割り込み処理にnullを渡すのとは違うんですが、一つのイベントが発生した時に、残り二つのイベントを空のメソッドで宣言してから回らせて、処理から抜ける寸前に正しいイベントを宣言
    というゴリ押しをしてみたんですが、まだ少しうまくいかなかったです。

    ご指摘を踏まえ、ソースコードの書き直しと状態遷移図の作成をしてみたいと思います。

    ソースコードを載せずに申し訳ありません。編集後のソースを後ほど載せます。

    キャンセル

  • この投稿は削除されました

  • 2017/02/03 11:26

    コメント上限に引っかかってしまったので、GitHubを使ってみることにしました。
    もしなにか間違いがあればご指摘お願いします。
    https://gist.github.com/Yomogiii/8720110f5dcbc72c23cced04e98f588b

    キャンセル

checkベストアンサー

+1

割り込みで行うのは最低限のことだけします。つまりフラグを立てる。
例えば、トリガーが引かれたら、トリガー引かれているフラグを立てる。
タイマー割り込みがあったら、やっぱりタイマーフラグを立てる。
条件分岐もしません。粛々とフラグを立てます。

そしてメインループで

フラグ群と
現在の状態(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()

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/02/06 10:29

    > 送信直前(digitalWrite)で何度もピンの設定をしてあげると現時点で動くようになりました。

    怖い挙動ですね。
    変なタイミングでピン設定が変わっているのかなんなのか
    回路になんか無理があるのか...

    まあ動いたんなら何よりです。

    キャンセル

  • 2017/03/21 18:27

    遅れて申し訳ありません。
    ゼミ発表でも、なんとか動かすことができました

    本当にありがとうございます!

    キャンセル

  • 2017/03/22 08:23

    間に合ったようでなにより。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.34%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

同じタグがついた質問を見る