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

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

ただいまの
回答率

88.80%

RaspberryPiでロータリーエンコーダを使う

解決済

回答 4

投稿

  • 評価
  • クリップ 0
  • VIEW 7,792

akaikumako

score 19

前提・実現したいこと

ロボットの動作の処理を行うRasPi2のMainプログラムで
ロータリーエンコーダの信号を
wiringPiISRの割り込みを使って読み取り、回転速度の計算を行おうと思っています。

発生している問題・エラーメッセージ

プログラムを実行したところ、値の読み取りがうまくできませんでした。
どうすればRasPiでロータリーエンコーダの値を読み込めるのでしょうか…?

ソースコード

#include <iostream>
#include <wiringPi.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <math.h>

// ロータリーエンコーダ用割り込みピンの設定
const int rot_pinA = 20;
const int rot_pinB = 18;
signed long int rot_count = 0;

void rotary_changedPinA(void);
void rotary_changedPinB(void);

int main(void){
      wiringPiISR(rot_pinA, INT_EDGE_BOTH, rotary_changedPinA);
      wiringPiISR(rot_pinB, INT_EDGE_BOTH, rotary_changedPinB);

      //STARTボタンとCROSSボタンが押されるまでloopする
      UPDATELOOP(controller, !(controller.button(START) && controller.button(CROSS))){
        //ここにメインの処理を書きます。
  }
}

void rotary_changedPinA(void){
  if(digitalRead(rot_pinA)){
    if(digitalRead(rot_pinB)) ++rot_count;
    else --rot_count;
  }else{
    if(digitalRead(rot_pinB)) --rot_count;
    else ++rot_count;
  }
}

void rotary_changedPinB(void){
  if(digitalRead(rot_pinB)){
     if(digitalRead(rot_pinA)) ++rot_count;
     else --rot_count;
  }else{
     if(digitalRead(rot_pinA)) --rot_count;
     else ++rot_count;
  }
}
  • 気になる質問をクリップする

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

回答 4

checkベストアンサー

+1

ロータリーエンコーダの処理ですが、ラズパイではやったことありませんが、
いわゆる、論理的には以下の方法で検出できるはずです。(言語・変数・命令体系などは適当です。ラズパイ仕様とは違っているかと思いますが、適時読み直してください。)

肝は、A相、B相ともに、GNDレベルになった時に、その前回のA相B相の状態で、回転方向がわかるということです。

よって、こんな感じで検出可能かと思います。(ハードに問題なければです、ハード側はオシロスコープなどを当てるなどして、確認してください。特にロータリーエンコーダにおいて、チャタリング対策は必須です。)


//割り込み前のA相状態
bool oldPinA = false;
//割り込み前のB相状態
bool oldPinB = false;

//
int main(void){
    //割り込みの設定(A相割り込みもB相割り込みも同一処理で可能)
    wiringPiISR(rot_pinA, INT_EDGE_BOTH, rotary_changedPin);
    wiringPiISR(rot_pinB, INT_EDGE_BOTH, rotary_changedPin);
    //
    ・・・
    //
}

//
void rotary_changedPin(void){
    //
    bool sigPinA = digitalRead(rot_pinA);
    bool sigPinB = digitalRead(rot_pinB);

    //A相、B相共にGNDレベルなら
    if(!sigPinA && !sigPinB){

        //ハードでチャタリング対策をしないなら、ここで数〜数十msecのwaitで回避できます
        //しかしエンコードの高速回転についていけなくなります。
        //ハード側でのCRなどでの対策が得策です。
        //wait(10);  //10msec待ちとか。

        if(!oldPinA && oldPinB){
            //正回転
        }else{
            //負回転
        }
    }
    
    //A相、B相の状態を記録
    oldPinA = sigPinA;
    oldPinB = sigPinB;
}

なお、チャタリング対策については、A相、B相ともに、シグナル線を、
・10Kのプルアップ
・0.01uFのコンデンサでGNDに接続
で、かなり良好な結果がでるかと思います。
立ち上がりのエッジが気になるなら、プルアップ抵抗を4.7Kなど、小さくするとオシロでの見た時のシグナルは綺麗に見えてきます。ただ、CPUのトリガーエッジに引っかかるタイミングは、あまり変わらないので、このあたりは、綺麗な方形波を目指すことも無いかと?思います。


投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2015/11/05 18:02

    プログラム参考にさせていただきました(_ _)
    チャタリング対策のWaitは入れていませんが、
    動くようにはなったようです!
    (ちなみに360p/rのロータリーエンコーダを使っています。)
    あまりチャタリングについて詳しくないので
    いろいろ調べつつさらに効率的にロータリーエンコーダを
    利用できるように改善してみます。
    有難うございました!

    キャンセル

0

こんにちは。

RaspberryPiを触ったことはないのですが、ファームウェア開発に慣れてますので、その観点から回答してみます。

割り込みを使われていますが、まずは割り込みを使わないで入力できているか見た方が良いと思います。
I/Oポートで制御できるLEDがついてませんか? もし、あるならまずLEDを点灯/消灯できるようにしてから、rot_pinAのH/LをチェックしてHなら点灯、Lなら消灯するようなソフトを走らせる等により、ちゃんと入力できているのか確認した方が良いと思います。

ここのソースをみると、なんとなくプルアップが必要な気もします。ロータリーエンコーダとRaspberryPiの接続回路によりますが。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2015/11/05 17:52

    回答ありがとうございました。
    プログラムは合ってるのに値が読めない…と思っていたら
    プルアップをプログラムに記述すると上手く読める事が出来ました!

    キャンセル

0

どんなエンコーダーをどの様に接続しているのか分からないのと、Raspberry Piをもっておらず試せないので、外していたらごめんなさい。

akaikumakoさんのプログラムでは、大雑把に言えば L→H ならカウントアップ、 H→L ならカウントダウンのようなロジックになっていますが、ロータリーエンコーダーからの出力は常に H と L を繰り返すものなので、これではいつまでたってもプラスマイナスゼロで変化を検出できません。

まずは下記ページの説明がとても分かりやすいのでご覧ください。

ロータリーエンコーダ
パルスはA相とB相でずれて出力されるようになっていて、回転方向によってずれかたが異なるから、それでどちらに回されたのかを知ることができる。
その説明の少し下にある 信号変化の模式図 を見ると分かるように、A相・B相の信号レベルを単独で取得してもダメで、直前の値との関係 および A・B間の関係 を調べる必要があります。

上記ページにも実装例が載っていますが、ご参考までに他の実装例も幾つかリンクを張っておきます。

Raspberry Piでロータリーエンコーダーの動作確認
Sunfounder スーパーキット For Raspberry Pi Lesson10 ロータリエンコーダ
LPC1114FN28でロータリーエンコーダ制御

どれを見ても基本は同じです。
異なっているのは、チャタリング等のノイズに対してプログラム側でどの程度対応するかと、あとは実装方法の好みの問題だと思います。

なお、Chironianさんもご指摘のように、エンコーダーをダイレクトに接続したのではノイズによる誤動作を回避するのが難しい場合もあり、Raspberry Pi との接続回路を工夫する必要があるかもしれません。
その場合には、下記ページをご参考になさってみてください。

ロータリーエンコーダーの接続

幾らかでもご参考になれば幸いです。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2015/11/05 17:58

    プログラムが間違ってたの気づいてませんでした(・・;)
    ご指摘ありがとうございます。
    ノイズの影響が大きいところでロータリーエンコーダを使うので
    回路の方も参考にしてみます!

    キャンセル

0

つい先日、ラズベリーパイ(※1の方)でエンコーダ入力ソフトを
作ったばかりなので、私のソフトの違いを記載させていただきます。
これが原因かどうかはわかりませんが

1.wiringPiSetupGpio()で初期化をしていない
2.入力端子のプルアップ、プルダウンの設定をしていない
pullUpDnControl()で設定できます
3.rotary_changedPinA,rotary_changedPinBの中で
それぞれの端子が前回の値から本当に変化したかを確認してない
結構、GPIOの割り込みは値が変わってないのに発生します

これで変わるかわかりませんが、試してみてください

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

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

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

関連した質問

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