R8C インターバルタイマーを使ったエンコーダー入力(スイッチ入力)

ロータリーエンコーダーを使った入力装置は、設定範囲が広く、厳密な値と、
広い範囲(レンジ)を網羅できる入力デバイスとして、組み込み機器では重宝
する入力デバイスです。
しかし、その入力を正しくエンコードして、読み取るには、多少の工夫が必要
です。

エンコーダー入力の前に、シンプルなスイッチ入力を考えてみます。
機械式接点では、チャタリングがあるので、それを除去する必要があります。
一つの方法として、サンプリング理論に沿ったデジタルフィルターを使って行
う方法があります。
これは、もっとも一般的な方法です。

サンプリング方式では、周期的に割り込みなどをかけて、入力信号を間欠的に
取り込みます。
サンプリング理論では、周期的に、状態を取り込む事で、ローパスフィルター
を通した信号とほぼ同じような状態となり、チャタリングの高い周波数を除去
したのと同じ効果となります。
サンプリング周期より速い(短い)信号は除去されます。
これにより、サンプリングするだけで、チャタリングを除去できます。

また、チャタリングが発生している期間は、機械的な構造による特性があり、
それにより周期を選ぶ必要があります。
通常、押しボタンスイッチのようなもので、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");
        }
    }

SWITCH_test

では、次はエンコーダーのデコードです。
基本的に、エンコーダーのデコードはスイッチの入力と基本的に変わらない。
エンコードする方法は、デバイスの分解能などの物理的要因により、いくつ
かの方法があるのですが、今回1回転辺り24ステップ(クリック付き)
エンコーダーを使い、サンプリングによる方法を行ってみました。

機械式接点による、ロータリーエンコーダーの場合は、かなり短い周期でパルス
が発生する為、製品のスペックに合わせたサンプリング周期を設定する必要があ
ります。
エンコーダーの回転軸を速く回した場合にも追従出来るようにするには、ギリ
ギリまでサンプリング周波数を高くする必要があります。
このエンコーダーのチャタリングは3.5ms以下のようなので、240Hzを採用
しました。

IMG_0755

光学式や磁気式のエンコーダーで、より高分解の場合は、チャタリングが無いの
と、周波数が高いので、サンプリングとは別の方法が用いられますが、それは、
又、別の機会とします。

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)を検出します。

ENCODER_test