先日、ネットサーフィンをして調べ物をしている間にふと気になった事が掲示板に書かれていました。 その書き込みでは「PICで2相ロータリーエンコーダの入力ってできない?」というものでした、返答に「H8などのマイコンには標準で命令があり入力ができるので、そちらを使った方がいい」「PICに2相エンコーダの入力はプログラム上絶対に無理」との回答しかなく、へぇ〜そうなんだぁーと思って他にも調べてみたら同様の記事ばかりでしたのでやっぱ無理なのか〜と思いページを閉じた時・・・できんじゃん!簡単にと頭の中では解決しましたw 原理とプログラムは簡単ですが、説明が長いのと履歴に埋もれて書き込みがあった掲示板に辿り着く事ができないのでここに記そうと思います。(元がかなり古い書き込みだったのでもう解決している人がいたらごめんね) まず2相ロータリーエンコーダーについて調べてみます。 こちらのサイトにハード的な原理が詳細に解説されていました。( 勝手にリンクしてごめんちゃいm( __ __ )m ) 正転時、逆転時とも出力信号はA、Bの位相差90°であり回転方向により、ずれ方が異なります。 ロータリーエンコーダーで検知できる動作は、回転方向・回転角度・回転数・回転速度、ですがここではまず回転方向について考えましょう。 左図のabcdの部分に注目してください。 正転、逆転とも4種類の信号状態があり逆になっているだけです。 正逆を判断するのは簡単で、今現在の信号状態を記憶し変化した信号と照らし合わせるだけです。 問題はPICにコンペア命令が無いことですが、状態を記憶するための一時保存レジスタと条件判断命令で簡単に再現できます。 ソースコード上での各フェーズは、A相をフェーズA(PHA)・B相をフェーズB(PHB)として扱いRAポート2をA相・RAポート3をB相の入力として割り当てて扱います。 オープンドレインなのでオン・オフが逆の表記になっています。 頭の中に浮かんだ物を簡単にソースコードにした結果は以下のようなもので片側の相に注目しその時のもう一相の状態を前の状態と比較するというものです。 ;-----------------------低速スクロール------------------------ BTFSS PHB ; PHB=1? GOTO PHBON ; フェーズBオンへ PHBOFF ; フェーズBオフ・ルーチン MOVFW RA ; RA→W MOVWF RATEMP1 ; W→RATEMP1 BTFSS RATEMP1,2 ; RATEMP1<2>=1? GOTO PHAON ; フェーズAオンへ GOTO PHAOFF ; フェーズAオフへ PHBON ; フェーズBオン・ルーチン MOVFW RA ; RA→W MOVWF RATEMP1 ; W→RATEMP1 MOVWF RATEMP2 ; W→RATEMP2 GOTO MAIN ; メインへ PHAON ; フェーズAオン・ルーチン BTFSS RATEMP2,2 ; RATEMP2<2>=1? GOTO MAIN ; メインへ MOVFW RATEMP1 ; RATEMP1→W MOVWF RATEMP2 ; W→RATEMP2 CALL INCDAT ; カウントアップへ GOTO MAIN ; メインへ PHAOFF ; フェーズAオフ・ルーチン BTFSC RATEMP2,2 ; RATEMP2<2>=0? GOTO MAIN ; メインへ MOVFW RATEMP1 ; RATEMP1→W MOVWF RATEMP2 ; W→RATEMP2 CALL DECDAT ; カウントダウンへ GOTO MAIN ; メインへ一応注釈が入っているので追いかければわかると思いますが、原理は簡単です。 B相を基準とし今の状態をRATEMP1に記憶させる、A相の状態で分岐しRATEMP2のものと比較しカウントアップかカウントダウンかを判断させメインに戻るだけです。 このソースを見てあれ?っと思った方は勘がいい!! そうなんです、これでは上の図で正転で言うというa→c、逆転で言うとd→bのようにB相の状態が変化しないと動かない、進角で言うと2進角で判断するので1進角分では動作しないんです。 これでは方向はわかっても正確な回転数や速度は測れないようになってきます。(なので初めの注釈で低速スクロールとなっております) ここで作り直すに当たりもう一度おさらいのため上図abcdの状態をわかりやすく表にしてみましょう。 左表で再確認するとわかるように、今現在の状態は4種類なので4つの分岐先、それに対してA・Bの前の状態を比較するだけなので状態保存は1つでいけます。 4種類の分岐先で判断させるのは状態が変化しなければメインに戻り、変化があれば正転か逆転かを判断し変化した状態を保存するだけの事です。 設定条件は先ほどと同じで、A相をフェーズA(PHA)・B相をフェーズB(PHB)として扱いRAポート2をA相・RAポート3をB相の入力として割り当て状態保存にPHTEMPレジスタを使います。 オープンドレインなのでオン・オフの表記が逆になるのも同じです。 ;-----------------------高速スクロール------------------------ BTFSS PHA ; IF PHA=OFF THEN goto PHAOFF GOTO PHAON ; ELSE GOTO PHAON PHAOFF ; フェーズAオフ・ルーチン BTFSS PHB ; IF PHB=OFF THEN goto A0_B0 GOTO A0_B1 ; ELSE goto A0_B1 GOTO A0_B0 ; PHAON ; フェーズAオン・ルーチン BTFSS PHB ; IF PHB=OFF THEN goto A1_B0 GOTO A1_B1 ; ELSE goto A1_B1 GOTO A1_B0 ; A0_B0 BTFSS PHTEMP,2 ; IF PHTEMP<2>=OFF THEN GOTO CNT_DN ; ELSE goto CNT_DN BTFSS PHTEMP,3 ; IF PHTEMP<3>=OFF THEN goto MAIN GOTO CNT_UP ; ELSE goto CNT_UP GOTO MAIN ; A0_B1 BTFSS PHTEMP,2 ; IF PHTEMP<2>=OFF THEN GOTO CNT_UP ; ELSE goto CNT_UP BTFSS PHTEMP,3 ; IF PHTEMP<3>=OFF THEN goto CNT_DN GOTO MAIN ; ELSE goto MAIN GOTO CNT_DN ; A1_B0 BTFSS PHTEMP,3 ; IF PHTEMP<3>=OFF THEN GOTO CNT_DN ; ELSE goto CNT_DN BTFSS PHTEMP,2 ; IF PHTEMP<2>=OFF THEN goto CNT_UP GOTO MAIN ; ELSE goto MAIN GOTO CNT_UP ; A1_B1 BTFSC PHTEMP,2 ; IF PHTEMP<2>=ON THEN GOTO CNT_DN ; ELSE goto CNT_DN BTFSC PHTEMP,3 ; IF PHTEMP<3>=ON THEN goto MAIN GOTO CNT_UP ; ELSE goto CNT_UP GOTO MAIN ; CNT_UP ; MOVFW RA ; RAポート→W MOVWF PHTEMP ; W→PHTEMP CALL INCDAT ; カウントアップへ GOTO MAIN ; メインへ CNT_DN ; MOVFW RA ; RAポート→W MOVWF PHTEMP ; W→PHTEMP CALL DECDAT ; カウントダウンへ GOTO MAIN ; メインへたったこれだけのソースで2相ロータリーエンコーダー位相差90°の入力が可能になります。 ソースの解説です。 PHAがオンであるかオフであるかを判断させ次にそれぞれでPHBがオンかオフかを判断し4つの状態に分岐させます。 表のとおりA=0・B=0、A=0・B=1、A=1・B=0、A=1・B=1、のそれぞれの状態に分岐したらその中で以前の状態(PHTEMP)とを比較し変化なければメインに戻り、変化があれば正転か逆転かを判断しジャンプします。 ジャンプ先のCNT_U・CNT_DNでは今の状態をPHTEMPに保存しカウントのルーチンを呼び出し処理をメインに戻すだけです。 これで進角1度での判断も可能で正転か逆転かを判断することができるので、カウントアップ・カウントダウンのルーチンを付ければ回転量が判ります。 エンコーダー1回転当たりの進角数で割れば角度や回転数が判り、タイマなどと組み合わせ時間単位で分割すれば回転速度も計数することが可能になります。 ね?PICでも簡単に可能でしょ? チップ特有の機能も使っていないのと50行にも満たないアセンブラ・ソースコードなので動作クロックもさほど気にせずPIC16F84などでも動かせます。 ソースコードのダウンロードはこちらから 次回はこれを使って何か作ってみたいと思います。 |