目次

エクスカベータシミュレータ(Arduino)

 Arduinoは、アナログジョイスティックの状態を読み込み
 該当する数値に変換します。さらに、Processingからの
 指示で、数値になったアナログジョイスティックの状態
 を転送します。

 各動作は、イベントドリブンで扱うようにして
 開発時間を短くします。

 4つのジョイスティックの状態を入力するための
 イベントには、タイマー割込みを使います。

 MsTimer2を利用して、ジョイスティックの状態を入力する
 タイミングをフラグで通知して貰うことにします。

 loopの中に、タイマー割込みのイベントフラグを扱う
 ブロックを用意します。

  if ( aflag == ON ) {
    /* clear event flag */
    aflag = OFF ;
    /* get value */
    /* convert value */
    /* update state */
  }

 イベントフラグで全ジョイスティックの状態を
 入力するとチャタリング等の処理が面倒なので
 ステートマシンを利用して、1個ずつ状態値を
 入力していきます。

 関数analogReadで、電圧換算値を取得します。

 A/D変換のチャネルは、次のように割当てます。

 変数stateで、チャネルを指定するとして定義。

  if ( aflag == ON ) {
    /* clear event flag */
    aflag = OFF ;
    /* get value */
    *(adv+state) = analogRead(state);
    /* convert value */
    *(swv+state) = *(adv+state) / 100 ;
    /* update state */
    state++ ;
    if ( state == LASTSTATE ) { state = 0 ; }
  }

 Processingスケッチからの指定で、周期的にジョイスティックの
 状態をシリアル転送します。こちらもイベントフラグを用意して
 周期的に転送と考えると、次のように定義すればよいでしょう。

  if ( tflag == ON ) {
    /* clear event flag */
    tflag = OFF ;
    /* generate code */
    /* judge transfer */
  }

 Processingスケッチからの指示がないとき、転送しないように
 フラグを用意して対応します。

  if ( tflag == ON ) {
    /* clear event flag */
    tflag = OFF ;
    /* generate code */
    for ( int i = 0 ; i < LASTSTATE ; i++ ) {
      *(msg+i) = conv_code( *(swv+i) );
    }
    /* judge transfer */
    if ( eflag == ON ) {
      rs_puts( msg ); 
      crlf();
    }
  }

 MsTimer2では、100msごとにA/D変換処理のイベント通知を
 1000msごとにジョイスティックの状態を転送するようにと
 イベント通知をするとします。

void  update_trigger(void)
{
  /* set flag */
  aflag = ON ;
  /* update counter */
  xcnt++ ;
  /* judge */
  if ( xcnt == LASTXCNT ) {
    xcnt = 0 ;
    tflag = ON ;
  }
}

 Arduinoが動作していることがわかるように
 D13に標準で接続されているLEDを点滅させる
 処理を加えておきます。

void  update_trigger(void)
{
  /* led handling */
  send_led(xcnt & ON);
  /* set flag */
  aflag = ON ;
  /* update counter */
  xcnt++ ;
  /* judge */
  if ( xcnt == LASTXCNT ) {
    xcnt = 0 ;
    tflag = ON ;
  }
}

 LEDの点灯は、次の関数に任せておきます。

void  send_led(byte x)
{
  if ( x ) { PORTB |=  (1 << LED_BIT) ; }
  else     { PORTB &= ~(1 << LED_BIT) ; }
}

 MsTimer2は、関数update_triggerを利用するとして
 setup内での初期化を入れます。

  /* trigger period */
  MsTimer2::set(NPER,update_trigger);
  /* enable */ 
  MsTimer2::start();

 Processingスケッチからの指示を捌くために、受信
 割込みを使います。受信割込みでは、デリミタ到着
 まで、バッファに文字列を格納していき、デリミタ
 が着たなら、イベントフラグで通知します。

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 ;
    }
  }
}

 イベントフラグで通知されるので、loopの中に
 コマンドインタプリタを用意して対応。

  if ( uflag == ON ) {
    /* clear event flag */
    uflag = OFF ;
    /* new line */
    /* get command */
    cmd = *(sbuf+0) ;
  }

 コマンドインタプリタで扱うコマンドを考えます。
 今回は、以下としました。

 シリアルインタフェースで、1文字、文字列を転送
 するためのラッパー関数を定義しておきます。

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');
}

 ArduinoからProcessingには、文字だけを送ることに
 して、数値から数字への変換が必要です。専用関数を
 定義します。

char get_asc(byte x)
{
  byte result ;
  /* default */
  result = '0' ;
  /* judge */
  if ( x < 10 ) { result = x + '0' ; }
  if ( 9 < x ) { result = x - 10 + 'A' ; }
  
  return result ;
}

 最下位レベルで動作する関数を定義したので
 コマンド'?'を検出したときの文字列出力関数
 を定義。

void show_help()
{
  rs_puts("? help");        crlf();
  rs_puts("E execute");     crlf();
  rs_puts("I idle");        crlf();
  rs_puts("S show status"); crlf();
}

 コマンドインタプリタは、次のように記述。

  if ( uflag == ON ) {
    /* clear event flag */
    uflag = OFF ;
    /* new line */
    crlf();
    /* get command */
    cmd = *(sbuf+0) ;
    /* help */
    if ( cmd == '?' ) { show_help() ; }
    /* execute */
    if ( cmd == 'E' ) { eflag = ON ; }
    /* idle */
    if ( cmd == 'I' ) { eflag = OFF ; }
    /* show status */
    if ( cmd == 'S' ) {
      /* message */
      if ( eflag == ON ) { rs_puts("run"); }
      else               { rs_puts("idle"); }
      /* new line */
      crlf();
    }
  }

 シリアルとI/Oの初期化は、次のように定義。

  /* initialize serial */
  Serial.begin(9600);
  sindex = 0 ;
  /* initialize */
  for ( state = 0 ; state < LASTSTATE ; state++ ) {
    *(adv+state) = 0 ;
    *(swv+state) = 0 ;
  }
  state = 0 ;
  xcnt  = 0 ;
  *(msg+4) = '\0' ;
  /* initialize port values */
  PORTB = 0xf0 ;
  PORTC = 0x00 ;
  PORTD = 0x01 ;
  /* initialize port direction */
  DDRB = 0xff ;
  DDRC = 0xf0 ;
  DDRD = 0xfe ;


 スケッチとしてまとめます。

#include <MsTimer2.h>

#define OFF 0
#define ON  OFF+1

#define LASTSTATE 4
#define LASTXCNT 10

#define LED_BIT 5

#define NPER 100

/* variables */
byte uflag ;
byte tflag ;
byte aflag ;
byte eflag ;
byte cmd ;
byte sindex ;
byte sbuf[8] ;
byte state ;
word adv[4] ;
byte swv[4] ;
byte xcnt ;
char msg[5];

/* function prototype */
void show_help();
char get_asc(byte x);
void rs_putchar(char x);
void rs_puts(char *ptr);
void crlf();
void send_led(byte x);
void update_trigger(void);
byte conv_code(byte x);

void setup()
{
  /* initialize serial */
  Serial.begin(9600);
  sindex = 0 ;
  uflag  = OFF ;
  tflag  = OFF ;
  aflag  = OFF ;
  eflag  = OFF ;
  /* initialize */
  for ( state = 0 ; state < LASTSTATE ; state++ ) {
    *(adv+state) = 0 ;
    *(swv+state) = 0 ;
  }
  state = 0 ;
  xcnt  = 0 ;
  *(msg+4) = '\0' ;
  /* initialize port values */
  PORTB = 0xf0 ;
  PORTC = 0x00 ;
  PORTD = 0x01 ;
  /* initialize port direction */
  DDRB = 0xff ;
  DDRC = 0xf0 ;
  DDRD = 0xfe ;
  /* trigger period */
  MsTimer2::set(NPER,update_trigger);
  /* enable */ 
  MsTimer2::start();
}

void loop()
{
  /* scan A/D converter */
  if ( aflag == ON ) {
    /* clear event flag */
    aflag = OFF ;
    /* get value */
    *(adv+state) = analogRead(state);
    /* convert value */
    *(swv+state) = *(adv+state) / 100 ;
    /* update state */
    state++ ;
    if ( state == LASTSTATE ) { state = 0 ; }
  }
  /* transfer switch state */
  if ( tflag == ON ) {
    /* clear event flag */
    tflag = OFF ;
    /* generate code */
    for ( int i = 0 ; i < LASTSTATE ; i++ ) {
      *(msg+i) = conv_code( *(swv+i) );
    }
    /* judge transfer */
    if ( eflag == ON ) { 
      rs_puts( msg );
      crlf();
    }
  }
  /* command interpreter */
  if ( uflag == ON ) {
    /* clear event flag */
    uflag = OFF ;
    /* new line */
    crlf();
    /* get command */
    cmd = *(sbuf+0) ;
    /* help */
    if ( cmd == '?' ) { show_help() ; }
    /* execute */
    if ( cmd == 'E' ) { eflag = ON ; }
    /* idle */
    if ( cmd == 'I' ) { eflag = OFF ; }
    /* show status */
    if ( cmd == 'S' ) {
      /* message */
      if ( eflag == ON ) { rs_puts("run"); }
      else               { rs_puts("idle"); }
      /* new line */
      crlf();
    }
  }
}

/* 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 ;
    }
  }
}

void show_help()
{
  rs_puts("? help");        crlf();
  rs_puts("E execute");     crlf();
  rs_puts("I idle");        crlf();
  rs_puts("S show status"); crlf();
}

char get_asc(byte x)
{
  byte result ;
  /* default */
  result = '0' ;
  /* judge */
  if ( x < 10 ) { result = x + '0' ; }
  if ( 9 < x ) { result = x - 10 + 'A' ; }
  
  return result ;
}

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  send_led(byte x)
{
  if ( x ) { PORTB |=  (1 << LED_BIT) ; }
  else     { PORTB &= ~(1 << LED_BIT) ; }
}

void  update_trigger(void)
{
  /* led handling */
  send_led(xcnt & ON);
  /* set flag */
  aflag = ON ;
  /* update counter */
  xcnt++ ;
  /* judge */
  if ( xcnt == LASTXCNT ) {
    xcnt = 0 ;
    tflag = ON ;
  }
}

byte conv_code(byte x)
{
  return x ;
}


 アナログジョイスティックを入手できないとき、8ピンのDIP
 スイッチとデータセレクタを利用し、5ビットでスイッチ情報
 を取得できるようにします。回路図は、以下。



 対応するスケッチは、以下。

#include <MsTimer2.h>

#define OFF 0
#define ON  OFF+1

#define LASTSTATE 2
#define LASTXCNT  10

#define LED_BIT 5

#define NPER 100

#define MASK0F 0x0f 

/* variables */
byte uflag ;
byte tflag ;
byte aflag ;
byte eflag ;
byte cmd ;
byte sindex ;
byte sbuf[4] ;
byte state ;
byte xswv[2];
byte swv[2] ;
byte xcnt ;
char msg[5];
byte tmpa ;
byte tmpb ;

/* function prototype */
void show_help();
char get_asc(byte x);
void rs_putchar(char x);
void rs_puts(char *ptr);
void crlf();
void send_led(byte x);
void update_trigger(void);
byte pre_rev(byte x);
byte rev(byte x);

void setup()
{
  /* initialize serial */
  Serial.begin(9600);
  sindex = 0 ;
  /* clear flags */
  uflag  = OFF ;
  tflag  = OFF ;
  aflag  = OFF ;
  eflag  = OFF ;
  /* initialize */
  for ( state = 0 ; state < LASTSTATE ; state++ ) {
    *(xswv+state) = 0 ;
    *(swv+state) = 0 ;
  }
  state = 0 ;
  xcnt  = 0 ;
  *(msg+4) = '\0' ;
  /* initialize port values */
  PORTB = 0xff ;
  PORTC = 0xf8 ;
  PORTD = 0x01 ;
  /* initialize port direction */
  DDRB = 0xff ;
  DDRC = 0x07 ;
  DDRD = 0xfe ;
  /* trigger period */
  MsTimer2::set(NPER,update_trigger);
  /* enable */ 
  MsTimer2::start();
}

void loop()
{
  /* scan switch */
  if ( aflag == ON ) {
    /* clear event flag */
    aflag = OFF ;
    /* default */
    tmpa = 0 ;
    tmpb = 0 ;
    /* get value */
    for ( int i = 0 ; i < 8 ; i++ ) {
      /* impress address */
      PORTC = (i & 7) ;
      /* delay */
      delayMicroseconds(10);
      /* get code */
      if ( PINC & 0x08 ) { tmpa |= ON ; }
      if ( PINC & 0x10 ) { tmpb |= ON ; }
      /* shift */
      if ( i < 7 ) {
        tmpa <<= 1 ;
        tmpb <<= 1 ;
      }
    }
    /* store */
    *(xswv+0) = rev( tmpa );
    *(xswv+1) = rev( tmpb );
  }
  /* transfer switch state */
  if ( tflag == ON ) {
    /* clear event flag */
    tflag = OFF ;
    /* copy */
    *(swv+0) = *(xswv+0) ;
    *(swv+1) = *(xswv+1) ;
    /* generate code */
    *(msg+0) = get_asc( *(swv+0) & MASK0F ) ;
    *(msg+1) = get_asc( (*(swv+0) >> 4) & MASK0F ) ;
    *(msg+2) = get_asc( *(swv+1) & MASK0F ) ;
    *(msg+3) = get_asc( (*(swv+1) >> 4) & MASK0F ) ;
    /* judge transfer */
    if ( eflag == ON ) {
      rs_puts( msg );
      crlf();
    }
  }
  /* command interpreter */
  if ( uflag == ON ) {
    /* clear event flag */
    uflag = OFF ;
    /* new line */
    crlf();
    /* get command */
    cmd = *(sbuf+0) ;
    /* help */
    if ( cmd == '?' ) { show_help() ; }
    /* execute */
    if ( cmd == 'E' ) { eflag = ON ; }
    /* idle */
    if ( cmd == 'I' ) { eflag = OFF ; }
    /* show status */
    if ( cmd == 'S' ) {
      /* message */
      if ( eflag == ON ) { rs_puts("run"); }
      else               { rs_puts("idle"); }
      /* new line */
      crlf();
    }
  }
}

/* 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 ;
    }
  }
}

char get_asc(byte x)
{
  byte result ;
  /* default */
  result = '0' ;
  /* judge */
  if ( x < 10 ) { result = x + '0' ; }
  if ( 9 < x && x < 16 ) { result = x - 10 + 'A' ; }

  return result ;
}

void show_help()
{
  rs_puts("? help");        crlf();
  rs_puts("E execute");     crlf();
  rs_puts("I idle");        crlf();
  rs_puts("S show status"); 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  send_led(byte x)
{
  if ( x ) { PORTB |=  (1 << LED_BIT) ; }
  else     { PORTB &= ~(1 << LED_BIT) ; }
}

void  update_trigger(void)
{
  /* led handling */
  send_led(xcnt & ON);
  /* set flag */
  aflag = ON ;
  /* update counter */
  xcnt++ ;
  /* judge */
  if ( xcnt == LASTXCNT ) {
    xcnt = 0 ;
    tflag = ON ;
  }
}

byte pre_rev(byte x)
{
  byte result ;
  /* default */
  result = 0 ;
  /* judge */
  if ( x & 8 ) { result |= 1 ; }
  if ( x & 4 ) { result |= 2 ; }
  if ( x & 2 ) { result |= 4 ; }
  if ( x & 1 ) { result |= 8 ; }
  
  return result ;
}

byte rev(byte x)
{
  byte dh ;
  byte dl ;
  /* reverse */
  dh = pre_rev( (x >> 4) & MASK0F ) ;
  dl = pre_rev( x & MASK0F ) ;
  
  return( (dh << 4) | dl ) ;
}

 D14(A0)からD16(A2)に、4051のセレクタ信号を出力し、D17(A3)、D18(A4)
 で、スイッチの状態を入力します。


目次

inserted by FC2 system