ロータリーエンコーダーを使った入力装置は、設定範囲が広く、厳密な値と、
広い範囲(レンジ)を網羅できる入力デバイスとして、組み込み機器では重宝
する入力デバイスです。
しかし、その入力を正しくエンコードして、読み取るには、多少の工夫が必要
です。
エンコーダー入力の前に、シンプルなスイッチ入力を考えてみます。
機械式接点では、チャタリングがあるので、それを除去する必要があります。
一つの方法として、サンプリング理論に沿ったデジタルフィルターを使って行
う方法があります。
これは、もっとも一般的な方法です。
サンプリング方式では、周期的に割り込みなどをかけて、入力信号を間欠的に
取り込みます。
サンプリング理論では、周期的に、状態を取り込む事で、ローパスフィルター
を通した信号とほぼ同じような状態となり、チャタリングの高い周波数を除去
したのと同じ効果となります。
サンプリング周期より速い(短い)信号は除去されます。
これにより、サンプリングするだけで、チャタリングを除去できます。
また、チャタリングが発生している期間は、機械的な構造による特性があり、
それにより周期を選ぶ必要があります。
通常、押しボタンスイッチのようなもので、4ms(ミリ秒)程度ですが、
接点の状態が悪いと、それより長くなったりするので、ある程度マージンを取
る必要があります。
通常のスイッチでは、一般的に、60Hz程度が良く選ばれます。
状態変化による、トリガーの生成:
サンプリングでは、別の要件を網羅できます、それは、「トリガー」入力の判断
です。
サンプリングした状態を1つ保持する事で、信号がどう変化したかを確認できま
す。
・前がOFF、今回がONなら、「立ち上がりエッジ」(押した瞬間)。
・間がON、今回がOFFなら、「立ち下がりエッジ」(離した瞬間)。
uint8_t lvl = ~device::P1(); /// 状態を取得 inp_pos_ = ~inp_lvl_ & lvl; /// 立ち上がりエッジ検出 inp_neg_ = inp_lvl_ & ~lvl; /// 立ち下がりエッジ検出 inp_lvl_ = lvl; ///< 状態を退避
・スイッチの状態表示
if(inp_pos_ & device::P1.B0.b()) { sci_puts("SW0 - positive\n"); } if(inp_pos_ & device::P1.B1.b()) { sci_puts("SW1 - positive\n"); } if(inp_neg_ & device::P1.B0.b()) { sci_puts("SW0 - negative\n"); } if(inp_neg_ & device::P1.B1.b()) { sci_puts("SW1 - negative\n"); } if(inp_lvl_ & device::P1.B0.b()) { if((cnt % 10) == 0) { sci_puts("SW0 - ON\n"); } } if(inp_lvl_ & device::P1.B1.b()) { if((cnt % 10) == 0) { sci_puts("SW1 - ON\n"); } }
では、次はエンコーダーのデコードです。
基本的に、エンコーダーのデコードはスイッチの入力と基本的に変わらない。
エンコードする方法は、デバイスの分解能などの物理的要因により、いくつ
かの方法があるのですが、今回1回転辺り24ステップ(クリック付き)
のエンコーダーを使い、サンプリングによる方法を行ってみました。
機械式接点による、ロータリーエンコーダーの場合は、かなり短い周期でパルス
が発生する為、製品のスペックに合わせたサンプリング周期を設定する必要があ
ります。
エンコーダーの回転軸を速く回した場合にも追従出来るようにするには、ギリ
ギリまでサンプリング周波数を高くする必要があります。
このエンコーダーのチャタリングは3.5ms以下のようなので、240Hzを採用
しました。
光学式や磁気式のエンコーダーで、より高分解の場合は、チャタリングが無いの
と、周波数が高いので、サンプリングとは別の方法が用いられますが、それは、
又、別の機会とします。
static uint8_t enc_lvl_ = 0; static uint8_t enc_pos_ = 0; static uint8_t enc_neg_ = 0; static uint16_t enc_count_ = 0; static void encoder_service_() { uint8_t lvl = ~device::P1(); /// 状態の取得 enc_pos_ = ~enc_lvl_ & lvl; /// 立ち上がりエッジ検出 enc_neg_ = enc_lvl_ & ~lvl; /// 立ち下がりエッジ検出 enc_lvl_ = lvl; /// 状態のセーブ if(enc_pos_ & device::P1.B0.b()) { if(enc_lvl_ & device::P1.B1.b()) { --enc_count_; } else { ++enc_count_; } } if(enc_neg_ & device::P1.B0.b()) { if(enc_lvl_ & device::P1.B1.b()) { ++enc_count_; } else { --enc_count_; } } if(enc_pos_ & device::P1.B1.b()) { if(enc_lvl_ & device::P1.B0.b()) { ++enc_count_; } else { --enc_count_; } } if(enc_neg_ & device::P1.B1.b()) { if(enc_lvl_ & device::P1.B0.b()) { --enc_count_; } else { ++enc_count_; } } }
A相、B相、の立ち上がり、立ち下がりを検出して、対になる「相」の
レベルにより、時計周り(CW)、反時計周り(CCW)を検出します。