ロータリエンコーダの位相信号をソフトウェアでカウントする

20091012
coskx

1.はじめに

H8/3048には位相カウンタが1つしかなく,2つのロータリエンコーダをハードウェアで読みたい場 合は,h8/3069のような2chの位相カウンタを持つCPUに乗り換えることになる。しかし,分解能の少ないロータリエンコーダなら,2相の信号を 2bitの入力で受けて,ソフトウェアでカウントすることができる。この方法によれば4つのロータリエンコーダを読みだすこともできる。

2.ゆっくりカウント値が変化するロータリエンコーダ

例えば100パルス/回転くらいのロータリエンコーダが1RPSで回転したとすると,100パルス/secくらいの信号となり,位相変化はその4倍の400パルス/秒程度になる。このような場合には1msecの割り込みで2つのビットを読んでいれば,カウントできる。

3.ゆっくりカウント値が変化するロータリエンコーダのソフトウェアでの読みとり

正方向に回転した時には次のように2つの信号が変化する。

  時間→
A相 0 1 1 0 0 1 1 0
B相 0 0 1 1 0 0 1 1

逆方向に回転した時には次のように2つの信号が変化する。

  時間→
A相 0 0 1 1 0 0 1 1
B相 0 1 1 0 0 1 1 0

この関係を直前のAB信号と,現在のAB信号を基に,カウンタ値を増やすべきか減らすべきかを考えて表にすると次のようになる。
例えば,前回割り込み時にAB信号が01で現在のAB信号が00ならカウンタ値を1増やすことを示している。
ただし,赤の字のところはあり得ない,または検査周期が長すぎて,データを取り損ねていて+2または-2にすべきところである。

  今回割り込み時のAB信号 

00

01

10

11

前回
割り
込み
時の
AB
信号 

00

0

-1

1

0

01

1

0

0

-1

10

-1

0

0

1

11

0

1

-1

0

前回割り込み時のAB信号をBit3,Bit2,今回割り込み時のAB信号をBit1,Bit0として番号を作ると次のようになる。

AB信号の2進表示

0000 0001 0010 0011 0100 0101 0110 0111 1000 1001 1010 1011 1100 1101 1101 1111

AB信号の10進表示

0

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

カウント増減値

0

-1

1

0

1

0

0

-1

-1

0

0

1

0

1

-1

0


あり得ないかまたは検査周期が長すぎてデータを取り損ねていている組み合わせは,エラーとなるので,エラーを示すテーブルも作っておく。
0は正常,1はエラーを表す。

  今回割り込み時のAB信号 

00

01

10

11

前回
割り
込み
時の
AB
信号 

00

0

0

0

1

01

0

0

1

0

10

0

1

0

0

11

1

0

0

0

前回割り込み時のAB信号をBit3,Bit2,今回割り込み時のAB信号をBit1,Bit0として番号を作ると次のようになる。

AB信号の2進表示

0000 0001 0010 0011 0100 0101 0110 0111 1000 1001 1010 1011 1100 1101 1101 1111

AB信号の10進表示

0

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

カウント増減値

0

0

0

1

0

0

1

0

0

1

0

0

1

0

0

0

 

4. 1つのロータリエンコーダ読み取りプログラムコード

例えば,割り込み関数中でポートBの第0第1ビットにロータリエンコーダからのA相B相の信号が得られるとすると,
次の参考プログラムのようにすれば,ロータリエンコーダの現在位置が,変数counterに保存される。
あとで,errorcountの値が0から増えていなければ,正しく動作していると考えることができる。


参考プログラムの一部

volatile int counter=0; /*ロータリエンコーダを数えるカウンタ,初期値は0*/
volatile int errorcount=0; /*カウントエラーの回数*/


const static int EncoderIndexTable[]={
    0,-1,1,0,  1,0,0,-1,  -1,0,0,1,  0,1,-1,0
}; /*2回のAB信号によるカウンタの増減表*/
const static int EncoderErrorIndexTable[]={
    0,0,0,1,  0,0,1,0,  0,1,0,0,  1,0,0,0
}; /*2回のAB信号によるカウンタのエラー表*/

/*インターバルタイマ割り込み関数*/
void timerIntFunc(void)
{
    /*下位4ビット中をpqrsで表わすと,pqには前回割り込み時のA相B相,rsは今回割り込み時のA相B相*/
    static int index=0;

    index=((index<<2)+(PB.DR.BYTE&0x3))&0xf; /*変数indexの仕様通りのデータが得られる*/
    counter+=EncoderIndexTable[index];
    errorcount+=EncoderErrorIndexTable[index];
}


 

5. 2つのロータリエンコーダ読み取りプログラムコード

2つのロータリエンコーダを使用している時でも同様にプログラミングできる。
例えば,エンコーダ1がポートBの第0第1ビットに,エンコーダ2はポートBの第2第3ビットに得られたとしよう。
次の参考プログラムのようにすれば,2つのロータリエンコーダの現在位置が,それぞれ変数counter1,counter2に保存される。
あとで,errorcount0,errorcount1の値が0から増えていなければ,正しく動作していると考えることができる。

参考プログラムの一部

volatile int counter1=0; /*ロータリエンコーダ1を数えるカウンタ,初期値は0*/
volatile int counter2=0; /*ロータリエンコーダ2を数えるカウンタ,初期値は0*/
volatile int errorcount1=0; /*ロータリエンコーダ1のカウントエラーの回数*/
volatile int errorcount2=0; /*ロータリエンコーダ2のカウントエラーの回数*/

const static int EncoderIndexTable[]={
    0,-1,1,0,  1,0,0,-1,  -1,0,0,1,  0,1,-1,0
} /*2回のAB信号によるカウンタの増減表*/
const static int EncoderErrorIndexTable[]={
    0,0,0,1,  0,0,1,0,  0,1,0,0,  1,0,0,0
} /*2回のAB信号によるカウンタのエラー表*/


/*インターバルタイマ割り込み関数*/
void timerIntFunc(void)
{
    /*下位4ビット中をpqrsで表わすと,pqには前回割り込み時のA相B相,rsは今回割り込み時のA相B相*/
    static int index1=0;
    static int index2=0;

    index1=((index1<<2)+(PB.DR.BYTE&0x3))&0xf; /*変数index1の仕様通りのデータが得られる*/
    counter1+=EncoderIndexTable[index1];
    errorcount1+=EncoderErrorIndexTable[index1];
    index2=((index2<<2)+((PB.DR.BYTE>>2)&0x3))&0xf; /*変数index2の仕様通りのデータが得られる*/
    counter2+=EncoderIndexTable[index2];
    errorcount2+=EncoderErrorIndexTable[index2];

}