目次

ロータリーエンコーダ信号生成

 ロータリーエンコーダの測定キットを半田付けしました。



 ロータリーエンコーダは、回すと位相が
 異なる信号が出力されてきます。
 タイミングチャートで見ると、以下。



 この信号をArduinoの7から10、14から17を利用して
 出力することにします。

  PORTB 13 - 10
    PB5 (output) LED
    PB4 (output) --
    PB3 (output) B phase
    PB2 (output) A phase
    PB1 (output) B phase
    PB0 (output) A phase
 
  PORTC 19 - 14
    PB5 (output) --
    PB4 (output) --
    PB3 (output) B phase
    PB2 (output) A phase
    PB1 (output) B phase
    PB0 (output) A phase

 A、Bの2相が、時計回りと反時計回りにあるので
 ポートB、Cの各4ビットに、割て当します。

 出力は、タイマー割込みを利用して、loopの中で
 次のように定義します。

  if ( eflag == ON ) {
    /* clear flag */
    eflag = OFF ;
    /* blank */
    bport = 0x00 ;
    /* display */
    if ( tflag == ON ) { bport = *(xpat+state) ; }
    /* impress */
    PORTB = bport | (PORTB & 0x20) ;
    PORTC = bport ;
    /* update */
    state++ ;
    state &= 0x03 ;
  }

 タイマー割込み発生を、イベント通知フラグeflagで扱い
 実際にロータリーエンコーダの擬似信号を出力するかは
 制御フラグtflagで指定します。

 制御フラグのセット、クリアは、コマンドインタプリタで
 扱えるようにします。

 コマンドインタプリタは、1文字をコマンドとして
 次の機能を与えます。

 機能を定義したので、loopには、コマンドインタプリタの
 コードを追加。

  if ( uflag == ON ) {
    /* clear flag */
    uflag = OFF ;
    /* new line */
    crlf() ;
    /* get command */
    cmd = *(sbuf+0) ;
    /* help */
    if ( cmd == '?' ) { show_help(); }
    /* enable */
    if ( cmd == 'G' ) {
      state = 0 ;
      tflag = ON ; 
    }
    /* disable */
    if ( cmd == 'g' ) { tflag = OFF; }
    /* show pattern */
    if ( cmd == 'p' ) {
      for ( i = 0 ; i < 4 ; i++ ) {
        show_byte( *(xpat+i) );
        crlf();
      }
    }

 コマンドインタプリタは、受信割込みで扱えるように
 するため、受信バッファにコマンドが格納されたこと
 を、フラグuflagで通知しておきます。

 コマンドインタプリタ内で利用する、通信関連の関数を
 定義しておくと、以下。

void show_help()
{
  rs_puts("? help")         ; crlf();
  rs_puts("G enable")       ; crlf();
  rs_puts("g disable")      ; crlf();
  rs_puts("p show pattern") ; crlf();
}

void rs_putchar(char x)
{
  Serial.write(x);
}

void rs_puts(char *ptr)
{
  while ( *ptr ) {
    rs_putchar( *ptr );
    ptr++;
  }
}

void crlf()
{
  rs_putchar('\r');
  rs_putchar('\n');
}

void show_byte(char x)
{
  char xtmp[5] ;
  /* delimiter */
  *(xtmp+4) = '\0' ;
  /* separate */
  *(xtmp+3) = (x & 1) + '0' ; x >>= 1 ;
  *(xtmp+2) = (x & 1) + '0' ; x >>= 1 ;
  *(xtmp+1) = (x & 1) + '0' ; x >>= 1 ;
  *(xtmp+0) = (x & 1) + '0' ;
  /* show */
  rs_puts( xtmp );
}

 コマンドインタプリタと信号出力の仕様を決めたので
 setupでコンフィグレートしていきます。

void setup()
{
  /* initialize serial */
  Serial.begin(9600);
  sindex = 0 ;
  rs_puts("Hello !");
  crlf();
  /* clear flags */
  tflag = OFF ;
  eflag = OFF ;
  uflag = OFF ;
  /* initialize port values */
  PORTB = 0x00 ;
  PORTC = 0x00 ;
  PORTD = 0x00 ;
  /* initialize port direction */
  DDRB = 0xff ;
  DDRC = 0xff ;
  DDRD = 0xfe ;
  /* variables */
  state = 0 ;
  *(xpat+0) = 15 ;
  *(xpat+1) = 9 ;
  *(xpat+2) = 0 ;
  *(xpat+3) = 6 ;
  xcnt = 0 ;
  /* 5ms period */
  MsTimer2::set(XINTERVAL,update_trigger);
  /* enable */ 
  MsTimer2::start();
}

 ロータリーエンコーダの信号は、配列xpatに格納し
 変数stateの値で、どの格納値を利用するのか指定
 できるように考えました。

 変数xcntは、ポートBの5ビット目に接続されている
 LEDの点滅を制御する目的で利用。

 時間間隔は、タイマー2を利用して生成。
 タイマー2に関係する処理は、ライブラリ
 を使って処理します。

 タイマー2の割込みで呼び出される関数の内容は、以下。

void update_trigger()
{
  /* set flag */
  eflag = ON ;
  /* LED handling */
  PORTB &= ~(1 << 5) ;
  if ( xcnt & ON ) { PORTB = (1 << 5); }
  /* increment timer counter */
  xcnt++ ;
}

 タイマー割込み発生のイベント通知フラグを
 セットするのと、ポートBの5ビットに接続
 しているLEDの点滅を担当させています。

 スケッチとして、まとめると以下。

/*
  rtst.ino

  Pin assignment

  PORTB
    PB5 (output) LED
    PB4 (output) --
    PB3 (output) B phase
    PB2 (output) A phase
    PB1 (output) B phase
    PB0 (output) A phase
 
  PORTC
    PB5 (output) --
    PB4 (output) --
    PB3 (output) B phase
    PB2 (output) A phase
    PB1 (output) B phase
    PB0 (output) A phase
 
  PORTD
    PD7 (output)
    PD6 (output)
    PD5 (output)
    PD4 (output)
    PD3 (output)
    PD2 (output)
    PD1 (output)TxD
    PD0 (input) RxD
*/
#include <MsTimer2.h>

#define OFF 0
#define ON  OFF+1

#define XINTERVAL 50

/* function prototype */
void update_trigger();
void show_help();
void rs_putchar(char x);
void rs_puts(char *ptr);
void crlf();
void show_byte(char x);

/* variables */
boolean tflag ;
boolean eflag ;
boolean uflag ;

byte xpat[4] ;
byte state ;
byte sbuf[8] ;
byte sindex ;

byte cmd ;
byte i ;
byte bport ;
byte xcnt ;

void setup()
{
  /* initialize serial */
  Serial.begin(9600);
  sindex = 0 ;
  rs_puts("Hello !");
  crlf();
  /* clear flags */
  tflag = OFF ;
  eflag = OFF ;
  uflag = OFF ;
  /* initialize port values */
  PORTB = 0x00 ;
  PORTC = 0x00 ;
  PORTD = 0x00 ;
  /* initialize port direction */
  DDRB = 0xff ;
  DDRC = 0xff ;
  DDRD = 0xfe ;
  /* variables */
  state = 0 ;
  *(xpat+0) = 15 ;
  *(xpat+1) = 9 ;
  *(xpat+2) = 0 ;
  *(xpat+3) = 6 ;
  xcnt = 0 ;
  /* 5ms period */
  MsTimer2::set(XINTERVAL,update_trigger);
  /* enable */ 
  MsTimer2::start();
}

void loop()
{
  /* command interpreter */
  if ( uflag == ON ) {
    /* clear flag */
    uflag = OFF ;
    /* new line */
    crlf() ;
    /* get command */
    cmd = *(sbuf+0) ;
    /* help */
    if ( cmd == '?' ) { show_help(); }
    /* enable */
    if ( cmd == 'G' ) {
      state = 0 ;
      tflag = ON ; 
    }
    /* disable */
    if ( cmd == 'g' ) { tflag = OFF; }
    /* show pattern */
    if ( cmd == 'p' ) {
      for ( i = 0 ; i < 4 ; i++ ) {
        show_byte( *(xpat+i) );
        crlf();
      }
    }
  }
  /* handling */
  if ( eflag == ON ) {
    /* clear flag */
    eflag = OFF ;
    /* blank */
    bport = 0x00 ;
    /* display */
    if ( tflag == ON ) { bport = *(xpat+state) ; }
    /* impress */
    PORTB = bport | (PORTB & 0x20) ;
    PORTC = bport ;
    /* update */
    state++ ;
    state &= 0x03 ;
  }
}

void update_trigger()
{
  /* set flag */
  eflag = ON ;
  /* LED handling */
  PORTB &= ~(1 << 5) ;
  if ( xcnt & ON ) { PORTB = (1 << 5); }
  /* increment timer counter */
  xcnt++ ;
}

void show_help()
{
  rs_puts("? help")         ; crlf();
  rs_puts("G enable")       ; crlf();
  rs_puts("g disable")      ; crlf();
  rs_puts("p show pattern") ; crlf();
}

void rs_putchar(char x)
{
  Serial.write(x);
}

void rs_puts(char *ptr)
{
  while ( *ptr ) {
    rs_putchar( *ptr );
    ptr++;
  }
}

void crlf()
{
  rs_putchar('\r');
  rs_putchar('\n');
}

void show_byte(char x)
{
  char xtmp[5] ;
  /* delimiter */
  *(xtmp+4) = '\0' ;
  /* separate */
  *(xtmp+3) = (x & 1) + '0' ; x >>= 1 ;
  *(xtmp+2) = (x & 1) + '0' ; x >>= 1 ;
  *(xtmp+1) = (x & 1) + '0' ; x >>= 1 ;
  *(xtmp+0) = (x & 1) + '0' ;
  /* show */
  rs_puts( xtmp );
}

/* receive interrupt */
void serialEvent()
{
  char ch;
  if ( Serial.available() > 0 ) {
    /* get 1 character */
    ch = Serial.read();
    /* store */
    *(sbuf+sindex) = ch ;
    /* increment */
    sindex++ ;
    /* judge */
    if ( ch == '\r' ) {
      sindex = 0 ; 
      uflag  = ON ;
    }
  }
}

 次のようにロータリーエンコーダドライブ回路に接続。



 ボートB、Cの下位4ビットには、同じ値を出力して
 いるので、LED表示基板を接続すると、Arduinoが転送
 している信号の論理値を格納できます。



 端末ソフトで、Arduinoに接続すると、以下の表示になります。



 コマンドG、gを利用して、ロータリーエンコーダドライブ
 基板の7セグメントLEDの表示が、変化することを確認。



 出力パターンが正しいのかを、コマンドpで確かめられます。



 このスケッチで、基板の半田付け不良と部品位置の間違いを
 発見できました。


目次

inserted by FC2 system