ロータリーエンコーダ-について
秋月のロータリーエンコーダー使用です。(メカ式、原点無し)
ロータリーエンコーダーは可変抵抗とは違い回転の範囲がなくどこまでも回転します。
このような原点情報を持たず、どこまでもクルクル回るようなものは、スタート地点がわからないので、通常、実使用で機器に用いるときは開始時に原点復帰のような原点を覚えこませる動作が必要です。
ここで使用するプログラムでは、
プログラム実行したロータリーエンコーダー位置が原点になります。
キザミ
このロータリーエンコーダーはクボミがあり、その間でも信号は出力するのですが、止めにくいです。
ロータリーエンコーダーには種類がいろいろとあるので、目的、必要に応じて選択する必要があります。
LCD表示
結果をLCD表示にしました。
LCD表示を加えましたが、やはり、マイコン内部の情報が表示できるLCDはいいですね。
回路図
5kΩでプルアップしロータリーエンコーダーを接続します。
LEDは2本 -- 左、右回転のとき、それぞれ、LEDを点灯させます。
チャタリングの問題も考える必要があります。(これはメカ式接点のため)
ロータリーエンコーダーの仕様書をざっと見ると、5kΩでプルアップした場合 チャタリング時間が2msとありました。ここでは回路でチャタリング対策はしないので、ソフト側で割り込み間隔を 3ms にすることで対処しました。
プログラム
FQ_RoEncLCD_new
/* ロータリーエンコーダーの回転判別
LED点灯 & Rエンコーダー値 LCD表示
*/
#include <3664.h>
#include "myfunc.h"
unsigned char ft; //記憶値
//unsigned char cnt=0; //カウンタ
signed char cnt=0; //カウンタ
unsigned char fD(unsigned char ft0,unsigned char ft1){
/* 回転方向 判別式関数 引数 ft0: 1つ前の状態値 */
/* ft1: 現在地 */
/* 返値 (01=)1 or (10=)2 */
unsigned char D;
ft = ft1; /* ftに保存し 現在値とする */
/* ft と ft0 は 別変数 */
ft0 = ft0 << 1 ; /* 1つ前の状態値 をビットシフト */
ft0 = ft0 & 3 ; // 0000 0011 でマスク(AND)し、下位2ビットだけ取り出す
ft1 = ft1 & 3 ; // 0000 0011 でマスク(AND)、下位2ビットだけ取り出す
D = (ft0 + ft1) & 3 ; //2つを+し判別式の結果、マスク
return(D);
}
void int_timerw(void){
unsigned char pb; //回転判別式 値
unsigned char PD; //Renc現在値
PD=IO.PDR8.BYTE >> 6; //Renc現在値 読み取り
/* 記憶値(ft) と 現在値(PD)=Rencのビット状態(IO.PDR8.BYTE) が違う*/
if(ft != PD){ // → 回転した
pb = fD(ft,PD); //回転判別式
//回転方向によって、LEDでお知らせ#0,#1&カウンタ累積
if(pb>=2){ //左回転のとき
IO.PDR1.BIT.B4=1;
IO.PDR1.BIT.B5=0;
cnt++;
}else{ //右回転のとき
IO.PDR1.BIT.B4=0;
IO.PDR1.BIT.B5=1;
cnt--;
}
}
TW.TSRW.BIT.IMFA = 0; // 検知フラグを戻して再開
}
void main(void){
DI;
IO.PCR8=0x3F; /* Bit0,1だけは0:入力 他は全て1にセットし出力に設定 0011 1111 */
IO.PCR1=0xFF;
/*タイマーw 約3msごとに割り込み */
TW.TCRW.BYTE=0xB0; /*1011 0000*/
TW.GRA = 4000; /*0.003(3ms) */
TW.TIERW.BIT.IMIEA=1; //タイマー割り込み あり
ft= IO.PDR8.BYTE >> 6; /* 初期化 : 現在値をグローバル変数に保存 */
LCD_init( 16 );
TW.TMRW.BIT.CTS=1; // ITU0 TCNTカウント開始
EI;
while(1) {
//LCD表示
LCD_locate(2,1); //
LCD_dtoutRsgn(3,cnt); //右詰めで数値表示 桁数変化処理を対処した表示関数 追加
}
}
ロータリーエンコーダーを使う
で作成した、判定関数を適用し、回転方向がわかります。記憶値はすべてグローバル変数にしました。
ほとんどの処理をタイマ関数Wの中で記述しています。タイマー関数int_timerw(void) で3msごとに定期的にロータリーエンコーダーのビット情報を監視します。そこで読み取ったRエンコーダー状態の現在値を、PDとします。
すでに記憶されているRエンコーダー状態値 と比較します
if(ft != PD){
この2つの値が異なっていれば、回転したと判断する。この処理をタイマW関数で3msごとに毎回しています。
回転したと判断した場合に
(記憶されているRエンコーダー状態値ft) と (Rエンコーダー状態の現在値PD) を回転判別式に放り込むと
pb = fD(ft,PD); //回転判別
判別式の結果値pbで回転した方向がわかります。
判別式の結果値pb(左回転で2以上になる、右回転では1以下)によって、各回転方向の処理します。
if(pb>=2){ //左回転のとき
IO.PDR1.BIT.B4=1;
IO.PDR1.BIT.B5=0;
cnt++;
}else{ //右回転のとき
IO.PDR1.BIT.B4=0;
IO.PDR1.BIT.B5=1;
cnt--;
}
main関数でカウンタ変数(回転度)をLCD液晶に表示します。
- 反時計まわり回転で カウンタが増える
- 時計まわり回転で カウンタ減る
内容になっています。
実行結果
ロータリーエンコーダーを回転させると、LEDが点灯し、回転させた方向で、点灯するLEDが変わります。
同時に回転度がLCDに表示されます。このエンコーダはクリックキザミがあり、4飛び値になります。
ロータリーエンコーダーを素早く回転させると、タイマで定期的に見に行く読み込み時間が3msなので読みこぼしがでます。もう少し監視をマメにし、早くす るとすばやい回転にも耐えられるでしょうがチャタリングの問題で、正確な数を累積できない問題がでるでしょう。
ここではプログラムを実行したときのロータリーエンコーダー位置が原点になります。
最初はカウンタが0です。
いきなり、カウンタをマイナスにする時計回りにすると、-減少、反対回しで+増加になります。
- twtter
- google+
- hatena