目次

メカ動作テスト

 過去に設計、開発したMCRマシンを再利用することにしました。

 産業用モータの採用と電源電圧の安定化にDCDCコンバータを
 使っているので、ファームウエア作成に集中できると判断。

 開発時間短縮も考えました。

 15年近く放置していたので、各ブロックが動作するかを
 Arduinoを接続して確認します。



 10ピンケーブルの各ワイヤーに、LED、スイッチ、モータ駆動
 回路が接続してあります。

 各デバイスは、負論理で動作します。

 モータドライブの回路は、以下。




 Arduinoが持っているPWM波形生成関数では、負論理なので
 DUTY比を100%から引いた値を与えなければなりません。

 端末からArduinoに指示を出して、動作確認するため
 コマンドを決めます。

 1文字コマンドは、次のように決定。

 '?'を除いた各コマンドの仕様を考えます。

 A(show switch state)

  1文字だけの入力で、8ビットの状態を表示します。



 D(display LEDs)

  1文字コマンドに続けて、10進数の0から3のいずれかを入力。
  0から3の意味は、以下。

  操作は、以下。



  制御基板上の2個のLEDの状態を確認できます。



 P(set both duty ratio)

  1文字コマンドに続けて、10進数の2桁のDUTY比を2個入力。
  それ以外の内容は、コマンド'L'、'R'と同じにします。

 L(set left motor duty ratio)

  1文字コマンドに続けて、10進数の2桁のDUTY比を入力。



  モータを接続しないでも、制御基板上のモニタLEDの
  輝度で、PWM波形を出力していることを確認できます。



 R(set right motor duty ratio)

  'L'コマンドと同様に、1文字コマンドに続けて、10進数の
  2桁のDUTY比を入力。



  制御基板上のモニタLED(赤色)の輝度で、PWM波形を出力して
  いることを確認できます。



 r(show duty ratio)

  1文字だけの入力で、左右のモータに設定しているDUTY比を表示。



 コマンドの仕様を決めたので、次に通信プロトコルを考え
 以下としました。

 各ビットの接続は、次のように決めました。

 モータを回転するには、PWM波形を利用したいので
 PB1、PB2を使いました。

 Arduinoスケッチは、以下。

#include <MsTimer2.h>

#define OFF 0
#define ON  OFF+1

#define MASK0F 0x0f
#define MASKF0 0xf0
#define MASKFF 0xff
#define MASK7F 0x7f

#define LED_BIT 5

/* variables */
boolean uflag ;
boolean oflag ;
byte cmd ;
byte sindex ;
byte sbuf[8] ;
byte xcnt ;
char msg[4] ;
byte tmp ;
byte sdat ;
byte lduty ;
byte rduty ;
byte ldutya ;
byte rdutya ;

/* function prototype */
void update_trigger();
void show_help();
byte get_hex(byte x);
char get_asc(byte x);
void rs_putchar(char x);
void rs_puts(char *ptr);
void crlf();
void binary_display(byte x);
byte get_sensor();
void show_value(byte x);
byte calc_duty(byte x);
byte get_duty();

void setup()
{
  /* initialize serial */
  Serial.begin(9600);
  sindex = 0 ;
  /* clear flags */
  uflag  = OFF ;
  oflag  = ON ;
  /* initialize port values */
  PORTB = 0xff ;
  PORTC = 0x0f ; /* pull up */
  PORTD = 0xe9 ;
  /* initialize port direction */
  DDRB = 0xff ;
  DDRC = 0xf0 ; /* lower nibble inputs */
  DDRD = 0xea ;
  /* clear */
  xcnt = 0 ;
  lduty = 0 ;
  rduty = 0 ;
  /* 250ms period */
  MsTimer2::set(250,update_trigger);
  /* enable */ 
  MsTimer2::start();
}

void loop()
{
  /* opening message */
  if ( oflag == ON ) {
    /* new line */
    crlf();
    /* clear flag */
    oflag = OFF ;
    /* message */
    rs_puts("Hello , Darling !");
    /* new line */
    crlf();
  }
  /* serial handling */
  if ( uflag == ON ) {
    /* clear flag */
    uflag = OFF ;
    /* new line */
    crlf();
    /* get command */
    cmd = *(sbuf+0) ;
    /* help */
    if ( cmd == '?' ) { show_help() ; }
    /* show switch state */
    if ( cmd == 'A' ) {
      /* show with binary format */
      binary_display( sdat );
      /* new line */
      crlf();
    }
    /* display LED */
    if ( cmd == 'D' ) {
      /* get code */
      tmp = get_hex( *(sbuf+1) );
      /* shift */
      tmp <<= 6 ;
      /* inverse */
      tmp ^= 0xc0 ;
      /* clear */
      PORTD &= ~0xc0 ;
      /* impress */
      PORTD |= tmp ;
    }
    /* set both duty ratio */
    if ( cmd == 'P' ) {
      /* update */
      lduty = get_duty() ;
      rduty = 10 * get_hex( *(sbuf+3) ) + get_hex( *(sbuf+4) );
      /* send */
      if ( lduty == 0 ) {
        analogWrite( 10 , 255 );
      } else {
        ldutya = calc_duty( lduty ) ;
        analogWrite( 10 , ldutya );
      }
      if ( rduty == 0 ) {
        analogWrite( 9 , 255 );
      } else {
        rdutya = calc_duty( rduty ) ;
        analogWrite( 9 , rdutya );
      }
    }
    /* motor duty ratio */
    if ( cmd == 'L' ) {
      /* update */
      lduty = get_duty() ;
      /* clear */
      if ( lduty == 0 ) {
        analogWrite( 10 , 255 );
      } else {
        ldutya = calc_duty( lduty ) ;
        analogWrite( 10 , ldutya );
      } 
    }
    /* right */
    if ( cmd == 'R' ) {
      /* update */
      rduty = get_duty() ;
      /* clear */
      if ( rduty == 0 ) {
        analogWrite( 9 , 255 );
      } else {
        rdutya = calc_duty( rduty ) ;
        analogWrite( 9 , rdutya );
      } 
    }
    /* show duty ratio */
    if ( cmd == 'r' ) {
      /* left */
      rs_puts("Left :");
      show_value( lduty ) ;
      crlf();
      /* right */
      rs_puts("Right:");
      show_value( rduty ) ;
      crlf();
    }
  }
}

void update_trigger()
{
  /* get sensor state */
  sdat = get_sensor();
  /* impress */
  if ( xcnt & 1 ) { PORTB |=  (1 << LED_BIT); }
  else            { PORTB &= ~(1 << LED_BIT);  }
  /* increment */
  xcnt++ ;
}

void show_help()
{
  rs_puts("? help");                   crlf();
  rs_puts("A switch state");           crlf();
  rs_puts("D display LEDs");           crlf();
  rs_puts("P set both duty");          crlf();
  rs_puts("L left motor duty ratio");  crlf();
  rs_puts("R right motor duty ratio"); crlf();
  rs_puts("r show duty ratio");        crlf();
}

byte  get_hex(byte x)
{
  byte result ;
  /* default */
  result = 0 ;
  /* judge */
  if ( '0' <= x && x <= '9' ) { result = x - '0' ; }
  if ( 'A' <= x && x <= 'F' ) { result = x - 'A' + 10 ; }
  if ( 'a' <= x && x <= 'f' ) { result = x - 'a' + 10 ; }
  
  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');
}

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

  return result ;
}

void binary_display(byte x)
{
  int i ;
  for ( i = 7 ; i > -1 ; i-- ) {
    rs_putchar('0'+((x >> i) & 1));
  }
}

byte get_sensor()
{
  return(PIND & 0x14) ;
}

void show_value(byte x)
{
  *(msg+2) = '\0' ;
  *(msg+1) = get_asc( x % 10 );
  *(msg+0) = get_asc( x / 10 );
  rs_puts( msg );
}

byte calc_duty(byte x)
{
  byte result ;
  /* inverse */
  result = 100 - x ;

  return( (result << 1) + (result >> 1) ) ;
}

byte get_duty()
{
  byte result ;
  /* upper */
  result = get_hex( *(sbuf+1) ) ;
  result *= 10 ;
  /* lower */
  result += get_hex( *(sbuf+2) ) ;

  return result ;
}

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

 動作中は、Arduino基板上のLEDを点滅させています。

 ジャンパーワイヤーを接続できるマイコンやデジタル
 回路であれば、どんなモノを接続してもテスト可能と
 できました。

 IchigoJamのようなBASICでの操作が可能なパーソナル
 コンピュータを使うこともできます。

 IchigoJamは、電源電圧が3.3Vなので、次のような
 電源電圧変換回路を入れて対応します。



 今回、Arduinoを利用したのは、LPC1114で実現できる
 Arduino互換の環境があり、その評価をしたかったと
 いう理由もあります。

 正論理でも扱えるように、インバータを間に入れる
 中継基板も作成しました。




 IchigoJamでもテストできます。
 回路図は、以下。



 IchigoJamのBASICコードは、以下を利用。

10 ' test MCR machine
20 LET S,1
30 GOSUB 200
40 GOSUB 100
50 LET S,S+1
55 IF S=6 LET S,1
60 IF BTN() GOTO 80
70 GOTO 30
80 OUT 2,0:OUT 3,0
85 ? "exit"
90 END
100 ' motor control
110 FOR I=2 TO 3:OUT I,[I]:NEXT
120 ? "S = ";S,[2],[3]
130 ? "wait",[3]:WAIT [4]
140 RETURN
200 ' state machine
210 IF S=1 LET [2],1,0,1200
220 IF S=2 LET [2],1,1,600
230 IF S=3 LET [2],0,1,1200
240 IF S=4 LET [2],1,1,600
250 IF S=5 LET [2],0,0,600
260 RETURN

 状態変数Sにより、左右のモータを回転するか停止するかを
 決めて、それを出力するだけ。正論理で考えられるように
 インバータを入れることに。

 動輪の直径が大きいので、10秒くらいモータを回すと
 1.5mくらいは簡単に移動します。

 緊急停止機能がなかったので、スイッチをつけました。




 DCDCコンバータへの電源供給をカットするので、モータが
 回転していれば、電力供給がなくなり即座に停止します。

 Lアングルの廃材を、適当な長さでカットしてから、穴を
 あけ、スペーサを咬ませてスクリューで固定しています。


目次

inserted by FC2 system