====== 複数のロータリエンコーダを繋ぐ ======
===== 複数個繋ぎたい時にはどうすれば良いのでしょう? =====
H8/3052のハードでサポートされているのは、1個のロータリエンコーダだけです。\\
秋月のロータリエンコーダは1回転で24パルス。監視すべきタイミングはA相とB層の立ち上がりと、\\
立下りなので24x4=96。1秒間に3回転したとしても96x3=288Hz=3.5ms間隔でチェックすれば良い。\\
ソフトで書いても全然余裕です。\\
\\
接続は以下のようにします。
^ ポート ^ エンコーダ ^ 相 ^
| PA-0 |ロータリエンコーダ 0|A 相|
| PA-1 |ロータリエンコーダ 0|B 相|
| PA-2 |ロータリエンコーダ 1|A 相|
| PA-3 |ロータリエンコーダ 1|B 相|
| PA-4 |ロータリエンコーダ 2|A 相|
| PA-5 |ロータリエンコーダ 2|B 相|
| PA-6 |ロータリエンコーダ 3|A 相|
| PA-7 |ロータリエンコーダ 3|B 相|
===== 仕組みを考える =====
ロータリエンコーダはパルスの変化を見れば良いのだから、前回の値と今回の値を調べればよい。\\
表にまとめると以下のようになる。
^前回の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 と思ったより小さめです。
;******************************************************************************
; ソフトウェアロータリエンコーダ処理
; (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
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 の処理
}
}
}
速度が遅いものは外部ハードウェアが無くても何とかなります。\\
コストを下げるためには、ソフトでできることはソフトでやりましょう。