2008-11-02
今回取り上げるのは、インクリメンタル型のロータリーエンコーダーです。アブソリュート型という回転軸が360度のどの位置にあるのかがわかるものもあります。インクリメンタル型は1クリック右に回った、1クリック左に回ったということまでは解りますが、何クリック回ったのかをMPUに数えさせないと現在どの位置にいるのかが解りません。ステレオの音量調節のように銘板が必要な物には使えませんが、初期値が常にゼロで有るような、例えば電子レンジの時間設定のようなものであればインクリメンタル型を使用するのが適当です。エンコーダーのツマミに目盛りが必要なければ、それはインクリメンタル型を使うべきと言うことです。
蛇足、補足:
秋月電子で販売されているロータリーエンコーダーがALPS製であると解説しているサイトが多数有りますが。ALPS電気のホームページを見る限り、このエンコーダーは見あたりません。製品の裏側にはALPHAと書かれています。ここの製品ではないでしょうか。中国の会社のようです。
擦動式のロータリーエンコーダー EC16B。秋月電子で¥200。擦動式なのでチャタリングがある。
付属のデータシートは何故かEC11Bの信号の例がある。EC16Bとは異なるので注意!!
2012年末から秋月電子で販売されているEC12E2420801(右の小さい方)も端子の並び順が異なるだけで互換性があります
先にまとめを書いておきます
・どんなロータリーエンコーダーにも万能に使用できるアルゴリズムは存在しません。
同じインクリメンタル型のエンコーダーでも信号の出方が違うのです。
・回転を検知するアルゴリズムは無数にあります。当然、一長一短があります。
・手動で回すロータリーエンコーダーにはクリック感があります。
クリック感のある場所以外で回転軸が止まることもありますが、その位置で止めることは神経を使わないと無理です。
1回転の分解能はクリック感<->クリック感の回転角度で決まります。
・モーターや機械に取り付けるタイプのエンコーダーにはクリック感がありません。
分解能はクリック感<->クリック感ではなく、A端子の変化点<->B端子の変化点になります。
(A端子、B端子については、以下を見れば何だかわかります)
・擦動式のエンコーダーはチャタリングがあります。
通常、ソフトでこれを除去します。(ハードで除去することも出来ます)
EC16Bに添付されているデータシートにはEC16Bの仕様が何故か掲載されていないので以下の図を見てください。
・エンコーダーのA,B端子は抵抗でプルアップします、C端子はGNDに直結して使用します。
・クリック感のあるところではA端子、B端子が共に5Vになっています。つまり各端子が互いに全く導通していない状態です。
導通していないのでプルアップ抵抗を介してGNDに電流が流れないため消費電力を抑えられます。
■回転を検知する方法
右回転の検知
・A端子が 5V->0V に変化したとき B端子が5V ならば右回転 (図の青い線) ---- ロジックA
もしくは
・A端子が 0V->5V に変化したとき B端子が0V ならば右回転 (図の赤い線) ---- ロジックB
左回転の検知
・A端子が 5V->0V に変化したとき B端子が0V ならば左回転 (図の青い線) ---- ロジックC
もしくは
・A端子が 0V->5V に変化したとき B端子が5V ならば左回転 (図の赤い線) ---- ロジックD
以上は、A端子の電圧変化を捉えていますがB端子の変化を捉えても同様に回転を検知できます。
ロジックの組み合わせにも注意する必要があります。
正しく検知するためには
右回転を立ち上がりで検知したら、左回転は立ち下がりで検知する <-- ロジックB,Cを使う
右回転を立ち下がりで検知したら、左回転は立ち上がりで検知する <-- ロジックA,Dを使う
このようにします。
但しチャタリングがない場合はこの組み合わせに注意は不要です。
もし、チャタリングがなければ上のロジックは正しく動作します。以下は右回りの例ですが、オシロスコープで計測すれば波形にはチャタリングが含まれてこの図のようになります。
上に
A端子が 5V->0V に変化したとき B端子が5V ならば右回転 (図の青い線) ---- ロジックA
と書きましたが、実際にこの条件を満足する場所は以下の図の場合、2カ所有ります。青い線で示した部分です。チャタリングを考慮しないと2クリック分回ったと誤解してしまいます。チャタリングを除去するためには最初の青い線のところで右回転を検知したら直ぐに10ms待ちます、チャタリングの時間は5ms程度なので10ms待てば2つめの青い線の所を通り過ぎるので2クリック分回ったと誤解することがありません。
ロジックAとCを組み合わせてはいけない理由も、この図から解ります。ロジックCは以下の通りですが
A端子が 5V->0V に変化したとき B端子が0V ならば左回転 (図の青い線) ---- ロジックC
この図は右回りの時の信号例ですが図の赤い線はロジックCを満足するため、左回りと誤解してしまいます。
初めてロータリーエンコーダーを使ったプログラムを書くとこの辺でかなり苦労すると思います。
実験回路の回路図。以下のサンプルはこの回路を動かす物です。ロータリーエンコーダーを回すと1クリックにつき10msだけLEDがピカッと光ります。
ご参考までに基板パターン。
実験の様子
0001 '------------------------------------------------------ 2008-10-29 -------------
0002 ' EC16B ROTARY_ENCODER
0003 '-------------------------------------------------------------------------------
0004 program ROTARY_ENCODER
0005
0006 symbol PORT_OUTPUT = 0
0007 symbol PORT_INPUT = 1
0008
0009 '------------ ROTARY ENCODER ------------------------------
0010 symbol ENCODER_A_TRIS = TRISB
0011 symbol ENCODER_A_PORT = PORTB
0012 symbol ENCODER_A_BIT = 0
0013
0014 symbol ENCODER_B_TRIS = TRISB
0015 symbol ENCODER_B_PORT = PORTB
0016 symbol ENCODER_B_BIT = 1
0017
0018 symbol LED_RIGHT_TRIS = TRISB
0019 symbol LED_RIGHT_PORT = PORTB
0020 symbol LED_RIGHT_BIT = 2
0021
0022 symbol LED_LEFT_TRIS = TRISB
0023 symbol LED_LEFT_PORT = PORTB
0024 symbol LED_LEFT_BIT = 3
0025
0026 symbol CW = 1
0027 symbol CCW = 2
0028 symbol ENCODER_STOP = 0
0029
0030 dim Rotary_Enc_A as byte
0031
0032 '===============================================================================
0033 ' Rotary encoder
0034 '
0035 ' RETURN
0036 ' CW / CCW / ENCODER_STOP
0037 '===============================================================================
0038 sub function rotary_encoder() as byte
0039 dim temp_A as byte
0040 dim temp_B as byte
0041
0042 temp_A = ENCODER_A_PORT.ENCODER_A_BIT
0043 temp_B = ENCODER_B_PORT.ENCODER_B_BIT
0044
0045 result = ENCODER_STOP
0046
0047 if ( Rotary_Enc_A = 1 ) and ( temp_A = 0 ) then
0048 if temp_B = 1 then
0049 result = CW
0050 Delay_ms(10)
0051 end if
0052 end if
0053
0054 if ( Rotary_Enc_A = 0 ) and ( temp_A = 1 ) then
0055 if temp_B = 1 then
0056 result = CCW
0057 Delay_ms(10)
0058 end if
0059 end if
0060
0061 Rotary_Enc_A = temp_A
0062 end sub
0063
0064
0065 '*******************************************************************************
0066 ' MAIN
0067 '*******************************************************************************
0068 main:
0069 dim turn as byte
0070
0071 '---- port A digital INPUT-OUTPUT (comparetor off)--------
0072 CMCON.CM2 = 1
0073 CMCON.CM1 = 1
0074 CMCON.CM0 = 1
0075
0076 '---- disable port B pull-up -------------
0077 OPTION_REG.NOT_RBPU = 1
0078
0079 '------- Rotary Encoarder --------------------------------
0080 ENCODER_A_TRIS.ENCODER_A_BIT = PORT_INPUT
0081 ENCODER_B_TRIS.ENCODER_B_BIT = PORT_INPUT
0082
0083 '------- LED ---------------------------------------------
0084 LED_RIGHT_TRIS.LED_RIGHT_BIT = PORT_OUTPUT
0085 LED_LEFT_TRIS.LED_LEFT_BIT = PORT_OUTPUT
0086 LED_RIGHT_PORT.LED_RIGHT_BIT = 0
0087 LED_LEFT_PORT.LED_LEFT_BIT = 0
0088
0089 Delay_ms(1000)
0090
0091 Rotary_Enc_A = ENCODER_A_PORT.ENCODER_A_BIT
0092
0093 while(TRUE)
0094 turn = rotary_encoder()
0095
0096 if turn = CW then
0097 LED_RIGHT_PORT.LED_RIGHT_BIT = 1
0098 Delay_ms(10)
0099 LED_RIGHT_PORT.LED_RIGHT_BIT = 0
0100 end if
0101
0102 if turn = CCW then
0103 LED_LEFT_PORT.LED_LEFT_BIT = 1
0104 Delay_ms(10)
0105 LED_LEFT_PORT.LED_LEFT_BIT = 0
0106 end if
0107 wend
0108 end.
0109
0110 '-------------- E N D O F P R O G R A M -----------------------------------
上に示した、待ち時間を入れる方法はメインループの処理がロータリーエンコーダーのために待たされるのが難点です。10msの待ち時間を入れればメインルーチンは1秒間に僅か100回しかLOOP出来ません。
待ち時間が入らない方法を以下に示します。ロジックは図中の解説の通りですが、ソースリストを見た方が理解しやすいかもしれません。
0001 '------------------------------------------------------ 2008-10-31 -------------
0002 ' EC16B ROTARY_ENCODER
0003 '-------------------------------------------------------------------------------
0004 program ROTARY_ENCODER
0005
0006 symbol PORT_OUTPUT = 0
0007 symbol PORT_INPUT = 1
0008
0009 '------------ ROTARY ENCODER ------------------------------
0010 symbol ENCODER_A_TRIS = TRISB
0011 symbol ENCODER_A_PORT = PORTB
0012 symbol ENCODER_A_BIT = 0
0013
0014 symbol ENCODER_B_TRIS = TRISB
0015 symbol ENCODER_B_PORT = PORTB
0016 symbol ENCODER_B_BIT = 1
0017
0018 symbol LED_RIGHT_TRIS = TRISB
0019 symbol LED_RIGHT_PORT = PORTB
0020 symbol LED_RIGHT_BIT = 2
0021
0022 symbol LED_LEFT_TRIS = TRISB
0023 symbol LED_LEFT_PORT = PORTB
0024 symbol LED_LEFT_BIT = 3
0025
0026 symbol CW = 1
0027 symbol CCW = 2
0028 symbol ENCODER_STOP = 0
0029
0030 symbol ROTARY_LOCK = 1
0031 symbol ROTARY_UNLOCK = 0
0032
0033 dim Rotary_Enc_A as byte
0034 dim Rotary_Enc_B as byte
0035
0036 dim Rotary_A_Lock as boolean
0037 dim Rotary_B_Lock as boolean
0038
0039 dim Rotary_Stat as byte
0040
0041 '===============================================================================
0042 ' Rotary encoder
0043 '
0044 ' RETURN
0045 ' CW / CCW / ENCODER_STOP
0046 '===============================================================================
0047 sub function rotary_encoder() as byte
0048 dim temp_A as byte
0049 dim temp_B as byte
0050
0051 temp_A = ENCODER_A_PORT.ENCODER_A_BIT
0052 temp_B = ENCODER_B_PORT.ENCODER_B_BIT
0053
0054 result = ENCODER_STOP
0055
0056 if (Rotary_Enc_A = 1) and (temp_A = 0) and (temp_B = 1) then
0057 Rotary_Stat = CW
0058 Rotary_A_Lock = True
0059 end if
0060 if Rotary_A_Lock = True then
0061 if (temp_A = 0) and (temp_B = 0) then
0062 result = Rotary_Stat
0063 Rotary_A_Lock = False
0064 end if
0065 end if
0066
0067 if (Rotary_Enc_B = 1) and (temp_B = 0) and (temp_A = 1) then
0068 Rotary_Stat = CCW
0069 Rotary_B_Lock = True
0070 end if
0071 if Rotary_B_Lock = True then
0072 if (temp_A = 0) and (temp_B = 0) then
0073 result = Rotary_Stat
0074 Rotary_B_Lock = False
0075 end if
0076 end if
0077
0078 Rotary_Enc_A = temp_A
0079 Rotary_Enc_B = temp_B
0080 end sub
0081
0082
0083 '*******************************************************************************
0084 ' MAIN
0085 '*******************************************************************************
0086 main:
0087 dim turn as byte
0088
0089 '---- port A digital INPUT-OUTPUT (comparetor off)--------
0090 CMCON.CM2 = 1
0091 CMCON.CM1 = 1
0092 CMCON.CM0 = 1
0093
0094 '---- disable port B pull-up -------------
0095 OPTION_REG.NOT_RBPU = 1
0096
0097 '------- Rotary Encoarder --------------------------------
0098 ENCODER_A_TRIS.ENCODER_A_BIT = PORT_INPUT
0099 ENCODER_B_TRIS.ENCODER_B_BIT = PORT_INPUT
0100
0101 '------- LED ---------------------------------------------
0102 LED_RIGHT_TRIS.LED_RIGHT_BIT = PORT_OUTPUT
0103 LED_LEFT_TRIS.LED_LEFT_BIT = PORT_OUTPUT
0104 LED_RIGHT_PORT.LED_RIGHT_BIT = 0
0105 LED_LEFT_PORT.LED_LEFT_BIT = 0
0106
0107 Delay_ms(1000)
0108
0109 Rotary_Enc_A = ENCODER_A_PORT.ENCODER_A_BIT
0110 Rotary_Enc_B = ENCODER_B_PORT.ENCODER_B_BIT
0111 Rotary_A_Lock = False
0112 Rotary_B_Lock = False
0113
0114 while(TRUE)
0115 turn = rotary_encoder()
0116
0117 if turn = CW then
0118 LED_RIGHT_PORT.LED_RIGHT_BIT = 1
0119 Delay_ms(10)
0120 LED_RIGHT_PORT.LED_RIGHT_BIT = 0
0121 end if
0122
0123 if turn = CCW then
0124 LED_LEFT_PORT.LED_LEFT_BIT = 1
0125 Delay_ms(10)
0126 LED_LEFT_PORT.LED_LEFT_BIT = 0
0127 end if
0128 wend
0129 end.
0130
0131 '-------------- E N D O F P R O G R A M -----------------------------------
ハードウェアーによるチャタリング除去は抵抗2本、コンデンサ2個が増えます。コンデンサは0.1μFで良いのでそれほど基板上の実装面積は増えません。
ハードウェアーによるチャタリング除去回路の実験。
上記2つのロジックは人間相手の処理なので、それほど高速に処理できなくても問題有りません。しかし相手がモーターだったり、極端に分解能の高いエンコーダーの場合、処理速度が問題になってきます。一般的にMPUではif文を使うと条件分岐が発生して処理時間が遅くなります。また、条件に一致する場合と、一致しない場合とでは処理時間が異なります。処理時間の違いは「あるケースだと処理が間に合うが、有るケースでは間に合わない」という問題を起こすため限界性能の見極めが実験で確認しにくくなります。
以下は、この問題を解決したロジックです。この方法以外にもIF文を使用しない計算ロジックは存在しますので、これは一例です。要求仕様がMPUの限界性能に近くなる場合はアセンブラで記述しMPUの性能を引き出してやる必要があります。
チャタリングがある物には使用できませんが、高速動作させても大丈夫なロータリーエンコーダーには、そもそもチャタリングがないので、それを気にする必要はありません。
0001 '------------------------------------------------------ 2008-11-03 -------------
0002 ' ROTARY_ENCODER
0003 '-------------------------------------------------------------------------------
0004 program ROTARY_ENCODER
0005
0006 symbol PORT_OUTPUT = 0
0007 symbol PORT_INPUT = 1
0008
0009 '------------ ROTARY ENCODER ------------------------------
0010 symbol ENCODER_A_TRIS = TRISB
0011 symbol ENCODER_A_PORT = PORTB
0012 symbol ENCODER_A_BIT = 0
0013
0014 symbol ENCODER_B_TRIS = TRISB
0015 symbol ENCODER_B_PORT = PORTB
0016 symbol ENCODER_B_BIT = 1
0017
0018 symbol LED_RIGHT_TRIS = TRISB
0019 symbol LED_RIGHT_PORT = PORTB
0020 symbol LED_RIGHT_BIT = 2
0021
0022 symbol LED_LEFT_TRIS = TRISB
0023 symbol LED_LEFT_PORT = PORTB
0024 symbol LED_LEFT_BIT = 3
0025
0026 symbol CW = 1
0027 symbol CCW = -1
0028 symbol ROTARY_STOP = 0
0029
0030 dim NowA as byte
0031 dim NowB as byte
0032 dim PrevA as byte
0033 dim PrevB as byte
0034
0035 '===============================================================================
0036 ' Rotary encoder
0037 '
0038 ' RETURN
0039 ' CW / CCW / ROTARY_STOP
0040 '===============================================================================
0041 sub function rotary_encoder() as short
0042 NowA = ENCODER_A_PORT.ENCODER_A_BIT
0043 NowB = ENCODER_B_PORT.ENCODER_B_BIT
0044
0045 result = ( PrevB xor NowA ) - ( PrevA xor NowB )
0046
0047 PrevA = NowA
0048 PrevB = NowB
0049 end sub
0050
0051
0052 '*******************************************************************************
0053 ' MAIN
0054 '*******************************************************************************
0055 main:
0056 dim turn as byte
0057
0058 '---- port A digital INPUT-OUTPUT (comparetor off)--------
0059 CMCON.CM2 = 1
0060 CMCON.CM1 = 1
0061 CMCON.CM0 = 1
0062
0063 '---- disable port B pull-up -------------
0064 OPTION_REG.NOT_RBPU = 1
0065
0066 '------- Rotary Encoarder --------------------------------
0067 ENCODER_A_TRIS.ENCODER_A_BIT = PORT_INPUT
0068 ENCODER_B_TRIS.ENCODER_B_BIT = PORT_INPUT
0069
0070 '------- LED ---------------------------------------------
0071 LED_RIGHT_TRIS.LED_RIGHT_BIT = PORT_OUTPUT
0072 LED_LEFT_TRIS.LED_LEFT_BIT = PORT_OUTPUT
0073 LED_RIGHT_PORT.LED_RIGHT_BIT = 0
0074 LED_LEFT_PORT.LED_LEFT_BIT = 0
0075
0076 Delay_ms(100)
0077
0078 while(TRUE)
0079 turn = rotary_encoder()
0080
0081 if turn = CW then
0082 LED_RIGHT_PORT.LED_RIGHT_BIT = 1
0083 Delay_ms(10)
0084 LED_RIGHT_PORT.LED_RIGHT_BIT = 0
0085 end if
0086
0087 if turn = CCW then
0088 LED_LEFT_PORT.LED_LEFT_BIT = 1
0089 Delay_ms(10)
0090 LED_LEFT_PORT.LED_LEFT_BIT = 0
0091 end if
0092 wend
0093 end.
0094
0095 '-------------- E N D O F P R O G R A M -----------------------------------
これはアルプスのロータリーエンコーダーのものです。こういう物があるので万能な処理ロジックは存在しないのです。クリック安定点ではBsignalがバタつくのでAsignalの変化を捉えるロジックしか採用できません。ツマミにちょっと触れただけでBsignalが変化してしまいます。
0001 '===============================================================================
0002 ' Rotary ENCODER
0003 '
0004 ' RETURN
0005 ' CW / CCW / ROTARY_STOP
0006 '===============================================================================
0007 sub function rotary_encoder() as byte
0008 dim temp_A as byte
0009 dim temp_B as byte
0010
0011 temp_A = ENCODER_A_PORT.ENCODER_A_BIT
0012 temp_B = ENCODER_B_PORT.ENCODER_B_BIT
0013
0014 result = ROTARY_STOP
0015
0016 if Rotary_Enc_A <> temp_A then
0017 if temp_A <> temp_B then
0018 result = CW
0019 else
0020 result = CCW
0021 end if
0022 Rotary_Enc_A = temp_A
0023 Rotary_Enc_B = temp_B
0024 Delay_ms(10)
0025 end if
0026 end sub