目次

ArduinoMega2560サーボモータ処理

 ArduinoMega2560を使ってサーボモータを動かしました。

 利用したサーボモータは、MiniS RB303



 手元にあったので、ArduinoMega2560からの5Vで
 動作することを確認しました。

 ArduinoMega2560の接続ピンは、11ピンとしました。
 14、15ピンには、スイッチをつけ、押すと指定した
 角度10度だけ増減するようにします。

 スイッチの接続回路は、以下。



 負論理で動作するようにしてみました。

 入力と出力を決めたので、関数pinModeで入出力
 方向を指定します。

#define POUT 11

#define RBIN 14
#define LBIN 15

/* direction */
pinMode(POUT,OUTPUT);
pinMode(RBIN,INPUT_PULLUP);
pinMode(LBIN,INPUT_PULLUP);

/* value */
digitalWrite(POUT,LOW);

 サーボモータを利用するので、用意されている
 ライブラリを使い、クラスとして認識させます。

#include <Servo.h>

Servo xSrv ;

 2個のスイッチで、サーボモータの角度を増減
 させるだけでなく、端末から角度を指定できる
 ようにもしたいので、シリアル通信ライブラリ
 を利用します。

 関数setupの中は、次のように定義。

void setup()
{
  Serial.begin(9600);
  rs_puts("Hello !");
  crlf();
  show_help();
  /* direction */
  pinMode(POUT,OUTPUT);
  pinMode(RBIN,INPUT_PULLUP);
  pinMode(LBIN,INPUT_PULLUP);
  /* value */
  digitalWrite(POUT,LOW);
  /* flags */
  uflag = OFF ;
  tflag = OFF ;
  /* */
  xcnt = 0 ;
  lsft = 0 ;
  rsft = 0 ;
  /* initialize servo motor angle */
  xSrv.attach(POUT,1000,2000);
  xangle = 90 ;
  xSrv.write(xangle);
  /* 10ms period */
  MsTimer2::set(10,update_trigger);
  MsTimer2::start();
}

 サーボモータは、1msから2msのパルス幅を
 与えると、0度から180度まで回転するとし
 クラスメソッドで、ピン番号と動作範囲を指定
 しています。

 ボタンの状態値が変化したことを捉えて、回転
 角度を増減したいので、シフトレジスタでの
 変化をキャッチします。シフトレジスタ用変数
 をlsft、rsftとしています。

 シフトレジスタで、信号の変化をキャッチする
 には、クロックが必要。タイマー割込みでの
 キャッチができるように、MsTimer2を活用して
 みます。
 割込み周期を10msにして、基板にあるLEDを
 点滅させることを考えました。

 MsTimer2から呼び出される関数は、以下。

void update_trigger()
{
  /* set flag */
  tflag = ON ;
  /* inrement */
  xcnt++ ;
  /* flashing */
  digitalWrite(POUT,LOW) ;
  if ( xcnt > 0x80 ) { digitalWrite(POUT,HIGH) ; }
}

 10msごとに、関数loopの中にある
 ブロックに、フラグtflagで通知します。

 イベント通知フラグで呼び出されたときには
 次のようにシフトレジスタのLSBに、スイッチ
 の状態を保存。

  if ( tflag == ON ) {
    /* clear */
    tflag = OFF ;
    /* shift */
    lsft <<= 1 ;
    rsft <<= 1 ;
    /* mask */
    lsft &= 3 ;
    rsft &= 3 ;
    /* get both switch */
    tmp = digitalRead(LBIN) ;
    tmp <<= 1 ;
    tmp |= digitalRead(RBIN) ;
    /* judge */
    if ( bitRead(tmp,1) == 0 ) { lsft++ ; }
    if ( bitRead(tmp,0) == 0 ) { rsft++ ; }
    /* update servo motor angle */
    if ( lsft == 1 && rsft == 0 ) { xangle += 10 ; }
    if ( lsft == 0 && rsft == 1 ) { xangle -= 10 ; }
    /* range check */
    if ( xangle > 180 ) { xangle = 180 ; }
    if ( xangle < 0   ) { xangle = 0   ; }
    /* update */
    xSrv.write(xangle);
  }

 シフトレジスタは、2つあるので、シフトとマスクを
 実行後、LSBに各々のスイッチの状態を格納します。
 押されたボタンに応じて、角度を増減してからレンジ
 をチェックし、サーボモータの角度処理メソッドへと
 角度を転送。

 端末ソフトでも、サーボモータを動かしたいので
 1文字コマンドを、次のように定義。

 コマンドを定義したなら、コマンドインタプリタを作成。

  if ( uflag == ON ) {
    /* clear flag */
    uflag = OFF ;
    /* get command */
    cmd = *(sbuf+0) ;
    /* help */
    if ( cmd == '?' ) { show_help(); }
    /* set angle */
    if ( cmd == 'A' ) { set_angle(); }
    /* show angle */
    if ( cmd == 'a' ) { get_angle(); }
    /* new line */
    crlf() ;
  }

 コマンド処理を担当する関数は、次のように定義。

void show_help()
{
  rs_puts("? help")      ; crlf();
  rs_puts("A set angle") ; crlf();
  rs_puts("a get angle") ; crlf();
}

void set_angle()
{
  byte aa;
  byte result ;
  byte ii ;
  /* default */
  result = 0 ;
  /* calulate */
  for ( ii = 0 ; ii < 3 ; ii++ ) {
    /* get ascii code */
    aa = *(sbuf+1+ii) ;
    /* judge */
    if ( aa == '\r' ) break ;
    /* calculate */
    result = result * 10 + get_hex( aa ) ;
  }
  /* range check */
  if ( result > 180 ) { result = 180 ; }
  /* set */
  xangle = result ;
  xSrv.write( xangle ) ;
}

void get_angle()
{
  byte aa[4];
  byte tmp ;
  byte ii ;
  /* default */
  *(aa+3) = '\0' ;
  /* get current angle */
  tmp = xSrv.read();
  /* separate */
  *(aa+2) = tmp % 10 ; tmp /= 10 ;
  *(aa+1) = tmp % 10 ; tmp /= 10 ;
  *(aa+0) = tmp % 10 ;
  /* convert */
  for ( ii = 0 ; ii < 3 ; ii++ ) {
    *(aa+ii) = get_asc( *(aa+ii) );
  }
  /* zero surpress */
  if ( *(aa+0) == '0' ) {
    *(aa+0) = ' ' ;
    if ( *(aa+1) == '0' ) { *(aa+1) = ' ' ; }
  }
  /* show */
  rs_puts( aa );
  /* new line */
  crlf();
}

 コマンドに対応する関数内では、サーボモータ用クラス
 を利用して、角度指定か、角度取得かを設定するだけ。
 複雑にすると、バグが入りやすくなるので、短いコード
 で対応しています。

 必要な関数を追加して、次のようにまとめられます。

#include <MsTimer2.h>
#include <Servo.h>

#define OFF 0
#define ON  OFF+1

#define LED_BIT 5

#define POUT 11

#define RBIN 14
#define LBIN 15

Servo xSrv ;

/* variables */
boolean uflag ;
boolean tflag ;

byte xcnt ;
char sbuf[8] ;
byte sindex ;

byte cmd ;

int xangle ;

byte lsft ;
byte rsft ;

void update_trigger();
void show_help();
void rs_putchar(char x);
void rs_puts(char *ptr);
void crlf();
void show_val();
void rs_print(byte x);
byte get_hex(char x);
char get_asc(byte x);
void set_angle();
void get_angle();

void setup()
{
  Serial.begin(9600);
  rs_puts("Hello !");
  crlf();
  show_help();
  /* direction */
  pinMode(POUT,OUTPUT);
  pinMode(RBIN,INPUT_PULLUP);
  pinMode(LBIN,INPUT_PULLUP);
  /* value */
  digitalWrite(POUT,LOW);
  /* flags */
  uflag = OFF ;
  tflag = OFF ;
  /* */
  xcnt = 0 ;
  lsft = 0 ;
  rsft = 0 ;
  /* initialize servo motor angle */
  xSrv.attach(POUT,1000,2000);
  xangle = 90 ;
  xSrv.write(xangle);
  /* 10ms period */
  MsTimer2::set(10,update_trigger);
  MsTimer2::start();
}

void loop()
{
  byte tmp ;
  /* command interpreter */
  if ( uflag == ON ) {
    /* clear flag */
    uflag = OFF ;
    /* get command */
    cmd = *(sbuf+0) ;
    /* help */
    if ( cmd == '?' ) { show_help(); }
    /* set angle */
    if ( cmd == 'A' ) { set_angle(); }
    /* show angle */
    if ( cmd == 'a' ) { get_angle(); }
    /* new line */
    crlf() ;
  }
  /* timer handling */
  if ( tflag == ON ) {
    /* clear */
    tflag = OFF ;
    /* shift */
    lsft <<= 1 ;
    rsft <<= 1 ;
    /* mask */
    lsft &= 3 ;
    rsft &= 3 ;
    /* get both switch */
    tmp = digitalRead(LBIN) ;
    tmp <<= 1 ;
    tmp |= digitalRead(RBIN) ;
    /* judge */
    if ( bitRead(tmp,1) == 0 ) { lsft++ ; }
    if ( bitRead(tmp,0) == 0 ) { rsft++ ; }
    /* update servo motor angle */
    if ( lsft == 1 && rsft == 0 ) { xangle += 10 ; }
    if ( lsft == 0 && rsft == 1 ) { xangle -= 10 ; }
    /* range check */
    if ( xangle > 180 ) { xangle = 180 ; }
    if ( xangle < 0   ) { xangle = 0   ; }
    /* update */
    xSrv.write(xangle);
  }
}

void update_trigger()
{
  /* set flag */
  tflag = ON ;
  /* inrement */
  xcnt++ ;
  /* flashing */
  digitalWrite(POUT,LOW) ;
  if ( xcnt > 0x80 ) { digitalWrite(POUT,HIGH) ; }
}

void show_help()
{
  rs_puts("? help")      ; crlf();
  rs_puts("A set angle") ; crlf();
  rs_puts("a get angle") ; 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');
}

byte get_hex(char 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 ;
}

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

  return result ;
}

void set_angle()
{
  byte aa;
  byte result ;
  byte ii ;
  /* default */
  result = 0 ;
  /* calulate */
  for ( ii = 0 ; ii < 3 ; ii++ ) {
    /* get ascii code */
    aa = *(sbuf+1+ii) ;
    /* judge */
    if ( aa == '\r' ) break ;
    /* calculate */
    result = result * 10 + get_hex( aa ) ;
  }
  /* range check */
  if ( result > 180 ) { result = 180 ; }
  /* set */
  xangle = result ;
  xSrv.write( xangle ) ;
}

void get_angle()
{
  byte aa[4];
  byte tmp ;
  byte ii ;
  /* default */
  *(aa+3) = '\0' ;
  /* get current angle */
  tmp = xSrv.read();
  /* separate */
  *(aa+2) = tmp % 10 ; tmp /= 10 ;
  *(aa+1) = tmp % 10 ; tmp /= 10 ;
  *(aa+0) = tmp % 10 ;
  /* convert */
  for ( ii = 0 ; ii < 3 ; ii++ ) {
    *(aa+ii) = get_asc( *(aa+ii) );
  }
  /* zero surpress */
  if ( *(aa+0) == '0' ) {
    *(aa+0) = ' ' ;
    if ( *(aa+1) == '0' ) { *(aa+1) = ' ' ; }
  }
  /* show */
  rs_puts( aa );
  /* 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 ;
    }
  }
}


目次

inserted by FC2 system