====== 複数のロータリエンコーダを繋ぐ ====== ===== 複数個繋ぎたい時にはどうすれば良いのでしょう? ===== 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 の処理 } } } 速度が遅いものは外部ハードウェアが無くても何とかなります。\\ コストを下げるためには、ソフトでできることはソフトでやりましょう。