複数のロータリエンコーダを繋ぐ

複数個繋ぎたい時にはどうすれば良いのでしょう?

H8/3052のハードでサポートされているのは、1個のロータリエンコーダだけです。
秋月のロータリエンコーダは1回転で24パルス。監視すべきタイミングはA相とB層の立ち上がりと、
立下りなので24×4=96。1秒間に3回転したとしても96×3=288Hz=3.5ms間隔でチェックすれば良い。
ソフトで書いても全然余裕です。

接続は以下のようにします。

ポート エンコーダ
PA-0 ロータリエンコーダ 0A 相
PA-1 ロータリエンコーダ 0B 相
PA-2 ロータリエンコーダ 1A 相
PA-3 ロータリエンコーダ 1B 相
PA-4 ロータリエンコーダ 2A 相
PA-5 ロータリエンコーダ 2B 相
PA-6 ロータリエンコーダ 3A 相
PA-7 ロータリエンコーダ 3B 相

仕組みを考える

ロータリエンコーダはパルスの変化を見れば良いのだから、前回の値と今回の値を調べればよい。
表にまとめると以下のようになる。

前回のA相前回のB相 今回のA相 今回のB相 回転変位
0 0 0 0 ありえない
0 0 0 1
0 0 1 0
0 0 1 1 ありえない
0 1 0 0
0 1 0 1 ありえない
0 1 1 0 ありえない
0 1 1 1
1 0 0 0
1 0 0 1 ありえない
1 0 1 0 ありえない
1 0 1 1
1 1 0 0 ありえない
1 1 0 1
1 1 1 0
1 1 1 1 ありえない

プログラムの作成

まずは、ポートが後で変わってもよいように、ポートの定義。

SOFT_ROT_PORT   EQU     PA                      ; ソフトウェアロータリエンコーダの接続ポート
SOFT_ROT_DR     EQU     PADR                    ; ソフトウェアロータリエンコーダの接続ポートの DR
SOFT_ROT_NUM    EQU     4                       ; ソフトウェアロータリエンコーダの接続数

次に上記した表のテーブルと、RAM にワークを作成。

;******************************************************************************
;       ROM 上のデータ
;******************************************************************************
                                                ; Ao Bo An Bn
ENC_CNT_TABLE:  DC.B     0                      ; 0  0  0  0
                DC.B    -1                      ; 0  0  0  1
                DC.B    +1                      ; 0  0  1  0
                DC.B     0                      ; 0  0  1  1
                DC.B    +1                      ; 0  1  0  0
                DC.B     0                      ; 0  1  0  1
                DC.B     0                      ; 0  1  1  0
                DC.B    -1                      ; 0  1  1  1
                DC.B    -1                      ; 1  0  0  0
                DC.B     0                      ; 1  0  0  1
                DC.B     0                      ; 1  0  1  0
                DC.B    +1                      ; 1  0  1  1
                DC.B     0                      ; 1  1  0  0
                DC.B    +1                      ; 1  1  0  1
                DC.B    -1                      ; 1  1  1  0
                DC.B     0                      ; 1  1  1  1
;******************************************************************************
;       RAM 上のデータ(初期化あり)
;******************************************************************************
                segment DATA
ENC_OLD_DATA:   DC.B    0,0,0,0                 ; 各ロータリエンコーダの前回の値
ENC_CNT_DATA:   DC.W    0,0,0,0                 ; 各ロータリエンコーダのカウント値

まずは初期化ルーチン。ポートを入力に切り替えるだけです。

;******************************************************************************
;       ソフトウェアロータリエンコーダの初期化
;
;       _SoftRotEncInit
;         2004/12/21 : 初期バージョン完成
;
;       [書式]  void SoftRotEncInit(void) ;
;
;       [IN]    なし
;
;       [OUT]   なし
;
;       [DED]   
;******************************************************************************
                public  _SoftRotEncInit
_SoftRotEncInit:SUB.B   R0L,R0L                 ; ポートを入力に切り替え
                MOV.B   R0L,@SOFT_ROT_DR
                RTS

ステップ数取得ルーチンはカウンタの値を持ってくるだけです。結果はint型なのでR0に返す。
ステップ数取得とクリアも同じで、カウンタクリアが多いだけ。この行数なのでサブルーチン化はしない。

;******************************************************************************
;       ソフトウェアロータリエンコーダのステップ数取得
;
;       _SoftRotEncCheck
;         2004/12/21 : 初期バージョン完成
;
;       [書式]  int SoftRotEncCheck(int ch) ;
;
;       [IN]    なし
;
;       [OUT]   int     = ステップ数
;
;       [DED]   ER1
;******************************************************************************
                public  _SoftRotEncCheck
_SoftRotEncCheck:
                MOV.W   @(4,ER7),R1
                SHLL.W  R1
                EXTU.L  ER1
                MOV.W   @(ENC_CNT_DATA,ER1),R0
                RTS
;******************************************************************************
;       ソフトウェアロータリエンコーダのステップ数取得とクリア
;
;       _SoftRotEncRead
;         2004/12/21 : 初期バージョン完成
;
;       [書式]  int SoftRotEncRead(int ch) ;
;
;       [IN]    int     ch      = チャンネル
;
;       [OUT]   int     = ステップ数
;
;       [DED]   E0,ER1
;******************************************************************************
                public  _SoftRotEncRead
_SoftRotEncRead:MOV.W   @(4,ER7),R1
                SHLL.W  R1
                EXTU.L  ER1
                MOV.W   @(ENC_CNT_DATA,ER1),R0
                SUB.W   E0,E0
                MOV.W   E0,@(ENC_CNT_DATA,ER1)
                RTS

肝となるのは、このポート監視ルーチン。
アセンブラとCから呼べるようにしてあります。最終的にはタイマ割り込みから呼び出す。

;******************************************************************************
;       ソフトウェアロータリエンコーダの監視
;
;       _SoftRotEncUpdate
;         2004/12/21 : 初期バージョン完成
;
;       [書式]  void SoftRotEncUpdate(void) ;
;
;       [IN]    なし
;
;       [OUT]   なし
;
;       [DED]   ER0,ER1,ER2,ER3
;******************************************************************************
                public  _SoftRotEncUpdate
                public  SOFT_ROTENC_UPDATE
_SoftRotEncUpdate:
SOFT_ROTENC_UPDATE:
                MOV.L   #ENC_OLD_DATA,ER1       ; ER1 = 前回のパルス
                MOV.L   #ENC_CNT_DATA,ER2       ; ER2 = カウント値
 
                MOV.B   @SOFT_ROT_PORT,R0L      ; ポート値の取得
                NOT.B   R0L                     ; 論理を戻す
 
                MOV.B   #SOFT_ROT_NUM,R0H       ; ROH = 回数
 
SOFT_ENC_LOOP:  SUB.L   ER3,ER3                 ; ER3 = 0
                MOV.B   @ER1,R3L                ; ER3 = Ao Bo
                SHLR.B  R0L                     ; CF = An
                ROTXL.B R3L                     ; ER3 = Ao Bo An
                SHLR.B  R0L                     ; CF = Bn
                ROTXL.B R3L                     ; ER3 = Ao Bo An Bn
                AND.B   #00001111B,R3L          ; 不要な Bit を捨てる
 
                MOV.B   R3L,@ER1                ; パルス値を書き戻す
                INC.L   #1,ER1
 
                MOV.B   @(ENC_CNT_TABLE,ER3),R3L; テーブルで増分を取得
                EXTS.W  R3
                MOV.W   @ER2,E0                 ; カウンタに足す
                ADD.W   R3,E0
                MOV.W   E0,@ER2
                INC.L   #2,ER2
 
                DEC.B   R0H
                BNE     SOFT_ENC_LOOP:8
 
                RTS

全ソース

まとめるとこんな感じになります。コードは 130 Byte と思ったより小さめです。

rotenc.asm
;******************************************************************************
;       ソフトウェアロータリエンコーダ処理
;                                                       (C)YdlProg
;******************************************************************************
                INCLUDE (../lib3052/3052.inc)
 
SOFT_ROT_PORT   EQU     PA                      ; ソフトウェアロータリエンコーダの接続ポート
SOFT_ROT_DR     EQU     PADR                    ; ソフトウェアロータリエンコーダの接続ポートの DR
SOFT_ROT_NUM    EQU     4                       ; ソフトウェアロータリエンコーダの接続数
 
                segment TEXT
;******************************************************************************
;       ソフトウェアロータリエンコーダの初期化
;
;       _SoftRotEncInit
;         2004/12/21 : 初期バージョン完成
;
;       [書式]  void SoftRotEncInit(void) ;
;
;       [IN]    なし
;
;       [OUT]   なし
;
;       [DED]   
;******************************************************************************
                public  _SoftRotEncInit
_SoftRotEncInit:SUB.B   R0L,R0L                 ; ポートを入力に切り替え
                MOV.B   R0L,@SOFT_ROT_DR
                RTS
;******************************************************************************
;       ソフトウェアロータリエンコーダのステップ数取得
;
;       _SoftRotEncCheck
;         2004/12/21 : 初期バージョン完成
;
;       [書式]  int SoftRotEncCheck(int ch) ;
;
;       [IN]    なし
;
;       [OUT]   int     = ステップ数
;
;       [DED]   ER1
;******************************************************************************
                public  _SoftRotEncCheck
_SoftRotEncCheck:
                MOV.W   @(4,ER7),R1
                SHLL.W  R1
                EXTU.L  ER1
                MOV.W   @(ENC_CNT_DATA,ER1),R0
                RTS
;******************************************************************************
;       ソフトウェアロータリエンコーダのステップ数取得とクリア
;
;       _SoftRotEncRead
;         2004/12/21 : 初期バージョン完成
;
;       [書式]  int SoftRotEncRead(int ch) ;
;
;       [IN]    int     ch      = チャンネル
;
;       [OUT]   int     = ステップ数
;
;       [DED]   E0,ER1
;******************************************************************************
                public  _SoftRotEncRead
_SoftRotEncRead:MOV.W   @(4,ER7),R1
                SHLL.W  R1
                EXTU.L  ER1
                MOV.W   @(ENC_CNT_DATA,ER1),R0
                SUB.W   E0,E0
                MOV.W   E0,@(ENC_CNT_DATA,ER1)
                RTS
;******************************************************************************
;       ソフトウェアロータリエンコーダの監視
;
;       _SoftRotEncUpdate
;         2004/12/21 : 初期バージョン完成
;
;       [書式]  void SoftRotEncUpdate(void) ;
;
;       [IN]    なし
;
;       [OUT]   なし
;
;       [DED]   ER0,ER1,ER2,ER3
;******************************************************************************
                public  _SoftRotEncUpdate
                public  SOFT_ROTENC_UPDATE
_SoftRotEncUpdate:
SOFT_ROTENC_UPDATE:
                MOV.L   #ENC_OLD_DATA,ER1       ; ER1 = 前回のパルス
                MOV.L   #ENC_CNT_DATA,ER2       ; ER2 = カウント値
 
                MOV.B   @SOFT_ROT_PORT,R0L      ; ポート値の取得
                NOT.B   R0L                     ; 論理を戻す
 
                MOV.B   #SOFT_ROT_NUM,R0H       ; ROH = 回数
 
SOFT_ENC_LOOP:  SUB.L   ER3,ER3                 ; ER3 = 0
                MOV.B   @ER1,R3L                ; ER3 = Ao Bo
                SHLR.B  R0L                     ; CF = An
                ROTXL.B R3L                     ; ER3 = Ao Bo An
                SHLR.B  R0L                     ; CF = Bn
                ROTXL.B R3L                     ; ER3 = Ao Bo An Bn
                AND.B   #00001111B,R3L          ; 不要な Bit を捨てる
 
                MOV.B   R3L,@ER1                ; パルス値を書き戻す
                INC.L   #1,ER1
 
                MOV.B   @(ENC_CNT_TABLE,ER3),R3L; テーブルで増分を取得
                EXTS.W  R3
                MOV.W   @ER2,E0                 ; カウンタに足す
                ADD.W   R3,E0
                MOV.W   E0,@ER2
                INC.L   #2,ER2
 
                DEC.B   R0H
                BNE     SOFT_ENC_LOOP:8
 
                RTS
;******************************************************************************
;       ROM 上のデータ
;******************************************************************************
                                                ; Ao Bo An Bn
ENC_CNT_TABLE:  DC.B     0                      ; 0  0  0  0
                DC.B    -1                      ; 0  0  0  1
                DC.B    +1                      ; 0  0  1  0
                DC.B     0                      ; 0  0  1  1
                DC.B    +1                      ; 0  1  0  0
                DC.B     0                      ; 0  1  0  1
                DC.B     0                      ; 0  1  1  0
                DC.B    -1                      ; 0  1  1  1
                DC.B    -1                      ; 1  0  0  0
                DC.B     0                      ; 1  0  0  1
                DC.B     0                      ; 1  0  1  0
                DC.B    +1                      ; 1  0  1  1
                DC.B     0                      ; 1  1  0  0
                DC.B    +1                      ; 1  1  0  1
                DC.B    -1                      ; 1  1  1  0
                DC.B     0                      ; 1  1  1  1
;******************************************************************************
;       RAM 上のデータ(初期化あり)
;******************************************************************************
                segment DATA
ENC_OLD_DATA:   DC.B    0,0,0,0                 ; 各ロータリエンコーダの前回の値
ENC_CNT_DATA:   DC.W    0,0,0,0                 ; 各ロータリエンコーダのカウント値
 
                END

呼び出し方

Cからの呼び出しは以下のように行います。SoftRotEncUpdateは割り込みで呼んでいないため、
他の処理が重いと読み取りに失敗します。実験時以外はタイマ割り込みからの呼び出しにします。

#include <stdio.h>
 
extern  void    SoftRotEncInit(void) ;          // ロータリエンコーダの初期化
extern  int     SoftRotEncCheck(int ch) ;       // ロータリエンコーダのステップ数取得
extern  int     SoftRotEncRead(int ch) ;        // ロータリエンコーダのステップ数取得とクリア
extern  void    SoftRotEncUpdate(void) ;        // ロータリエンコーダの監視
 
void main(void) {
    SoftRotEncInit() ;                          // ロータリエンコーダ 0~3 の初期化
 
    while(1) {
        SoftRotEncUpdate() ;                    // ロータリエンコーダ 0~3 の監視
 
        if(abs(SoftRotEncCheck(0)) >= 4) {      // ロータリエンコーダ 0 のステップ数取得
            num = SoftRotEncRead(0) ;           // ロータリエンコーダ 0 のステップ数取得とクリア
            // ロータリエンコーダ 0 の処理
        }
        if(abs(SoftRotEncCheck(1)) >= 4) {      // ロータリエンコーダ 1 のステップ数取得
            num = SoftRotEncRead(1) ;           // ロータリエンコーダ 1 のステップ数取得とクリア
            // ロータリエンコーダ 1 の処理
        }
        if(abs(SoftRotEncCheck(2)) >= 4) {      // ロータリエンコーダ 2 のステップ数取得
            num = SoftRotEncRead(2) ;           // ロータリエンコーダ 2 のステップ数取得とクリア
            // ロータリエンコーダ 2 の処理
        }
        if(abs(SoftRotEncCheck(3)) >= 4) {      // ロータリエンコーダ 3 のステップ数取得
            num = SoftRotEncRead(3) ;           // ロータリエンコーダ 3 のステップ数取得とクリア
            // ロータリエンコーダ 3 の処理
        }
    }
}

速度が遅いものは外部ハードウェアが無くても何とかなります。
コストを下げるためには、ソフトでできることはソフトでやりましょう。

h8/複数のロータリエンコーダを繋ぐ.txt · 最終更新: 2018/03/18 09:56 (外部編集)
 
特に明示されていない限り、本Wikiの内容は次のライセンスに従います: CC Attribution-Share Alike 4.0 International
Recent changes RSS feed Donate Powered by PHP Valid XHTML 1.0 Valid CSS Driven by DokuWiki