H8 MCUボードにイロイロつないでみる/1 - マザーボード?にMCUボードを乗せる
H8 MCUボードにイロイロつないでみる/2 - I/Oポートを増やす+温度センサーを付ける
H8 MCUボードにイロイロつないでみる/3 - ロータリーエンコーダをつけてみる
H8 MCUボードにイロイロつないでみる/4 - 赤外線リモコン受信素子をつけてみる
H8 MCUボードにイロイロつないでみる/5 - 周波数カウンタにしてみる
H8 MCUボードにイロイロつないでみる/6 - ・・・
ありきたりですが、次にロータリーエンコーダをつないでみました。
ロータリーエンコーダ(以下、エンコーダと訳)の動作原理は、ネット上にたくさんあるので、そちらを参照して頂くとして・・・
ではなく、ちょっとだけ書いておきます。
まず、エンコーダは、軸の回転した量をパルスで出力するもの*1なので、単純に考えれば、以下のとおり、軸を回した時にパルスを出力すれば良いわけです。
軸を回せば回しただけのパルスがでますので、それを数えれば回転量が分かるというわけです。
しかし、この場合、回転量が増加する一方向だけの動作しかできません。
軸を右に回しても、左に回しても、それを知る術がないのです。
そこで、その問題を解決する為に、エンコーダは、下図のようにA相・B相という2種類のパルスを出すようになっています。
どうして、これで左右、どちらへ回転しているのが分かるのか?
とりあえず、回転量はパルスを数えるとして、B相信号の立ち上がり部分に注目してみます。
すると、
(1)右へ向かう場合、B相が立ち上がる時は「必ずA相はHレベルである」
(2)左へ向かう場合、B相が立ち上がる時は「必ずA相はLレベルである」
という事になります。
これで、軸がどちらの方向に回されたのか?を知る事ができます。
実際は、立下りエッジも見たりとか、時と場合によってイロイロな方法や検出回路があるのですが、簡単な考え方としては以上です。
例によって、部品箱を覗くと、以下のように2個のエンコーダがみつかりました。
ひとつは、COPALの光学式のもの*2、もうひとつは何かの基板から外した接点式のもの*3です。
配線の途中にあるのは、以下のような(ありきたりの)I/F回路を組んだ基板です。
親基板に乗せても良かったのですが、I/F回路が違うのと、どうせ使い回すだろう…ということで、こういう形になってしまいました。
H8 3048Fなどは、位相カウンタ内臓ですので、その入力である、TCLKAとTCLKBにA相・B相の信号線をつなげばOKです。
他に、光学式は電源がいるのとプルアップやHC14*4などの為に電源をつなぎます。
※ちなみに、接点式の信号端子は、秋葉原などで出回っているモノのように「COM-A-B」ではなく「A-COM-B」でした。
素性の知れないモノは要チェックです。
ところで、最初、接点式のものは手抜きして回路図A部分のプルアップ部分だけを配線して動かしてみたのです。
結果、カウントアップ/ダウンは行えました。
それで、「んー、チャタリングは意外と少ないのねぇ」と気を抜いてしまったのでした。
が、やはり甘い考えでしたね。はい。
カウンタのオーバーフロー/アンダーフローの処理で割り込みを使うようにソフトを書いたのですが、割り込みが発生すると、カウンタ値がとんでもない値に飛んでいってしまうのでした。
光学式では起こらないので、やはりチャタリングで割り込みが多数入っているんだろう、という結果になったわけです。
そこで、またもや手抜きで回路図B部分の積分回路まで組んで、あとはH8の入力回路に頼ろうと・・・
見事に裏切られましたね。結果変わらずです*5。
あ~あ、やっぱりな。…と、部品箱からHC14を取り出して増設となりました。
結果は、さすがシュミットトリガ!でした。
手抜きはしないよーに!
そもそも、内臓カウンタで機能を提供しているので、処理を書くという程のものではありません。
short tmr2ovf; /************************************************************************/ /* */ /* ロータリーエンコーダ用に2相カウンタを初期化 */ /* */ /************************************************************************/ void init_ctr2(void) { tmr2ovf = 0; /* オーバーフローカウンタ */ ITU.TMDR.BIT.MDF = 1; /* 位相計数モード設定 */ ITU.TMDR.BIT.FDIR = 0; /* Overflow/Underflowでflag set */ ITU2.TIER.BIT.OVIE = 1; /* OVF での割り込み許可 */ ITU2.TCNT = 0; /* カウンタ初期値 */ ITU.TSTR.BIT.STR2 = 1; /* カウンタのスタート */ } /************************************************************************/ /* */ /* ロータリーエンコーダのカウンタ値を取得 */ /* */ /************************************************************************/ long get_ctr2(void) { #if 1 #define ENC_TYPE_COPAL (1) #endif long ctr; ctr = ITU2.TCNT; /* カウンタの値を取得 */ ctr += tmr2ovf * 0x10000; /* オーバーフロー分を加える */ #ifdef ENC_TYPE_COPAL return ctr; /* 戻り値として返す(光学式) */ #else return ctr / 4; /* 戻り値として返す(接点式) */ #endif }
初期化処理とカウンタ値取得処理です。
初期化処理は、コメントのとおりでマニュアルの写しみたいなものです。
カウンタ値取得処理は、16ビットカウンタ値を32ビットカウンタ値に拡張する処理が入っています。
必要なのかは微妙*6ですが、-2147483648~0~+2147483647まで使えます*7。
変数 tmr2ovf は、ハードカウンタのオーバーフロー/アンダーフローの数を保持している変数です。
/************************************************************************/ /* vector 34 OVI2 */ /************************************************************************/ __interrupt(vect=34) void INT_OVI2(void) { extern short tmr2ovf; /* TSRのOVFをクリア(読み込んでから書き込む) */ if(ITU2.TSR.BIT.OVF) ITU2.TSR.BIT.OVF = 0; /* OverFlowかUnderFlowか判断して、その処理を行う */ if(ITU2.TCNT & 0x8000) --tmr2ovf; else ++tmr2ovf; }
オーバーフロー/アンダーフロー時に起きる割り込み処理です。
割り込み要因フラグをクリアしてから、オーバーフローなのかアンダーフローなのかの判断をしてオーバーフローカウンタを増減しています。
オーバーフロー/アンダーフローの判断は、割り込みが入った時のハードカウンタ値を取得して、MSBが立っていれば、0→0xffffへのアンダーフロー、MSBが立っていなければ、0xffff→0へのオーバーフローとしています。
普通は、割り込み処理に飛んでくるまでに、0x8000もカウントしない筈なので…。
あ、長時間の割り込み禁止ならば、ありえますね。
どんなシステムか知りませんが。
ちなみに、接点式のものは、以下の図の通り、クリック間で四つのエッジが出力されます。
H8 3048Fの位相カウンタは4逓倍で全てのエッジをカウントするので、1クリックで4カウントされます。