目次

パルス出力試験

 ArduinoのデジタルI/Oを利用して、パルス出力試験を
 してみました。

 次のタイミングチャートで、動作する装置がありました。



 手動でトリガーを与えると、パルスを出力すると動いたので
 パルス幅が最低どれだけあればよいのかを調べることに。

 シリアルでパルス幅を与え、動作確認します。

 利用するArduinoは、MakerNanoを選定。




 MakerNanoは、GPIOのうち0から12にLEDが接続されているので
 動作を目視できるので、今回の用途には最適と判断。

 パルス幅は、1msの整数倍として、次のようにシリアル
 インタフェースを利用して設定することに。



 シリアルインタフェースを使うので、操作を1文字コマンドで
 指定します。
 コマンドリストを、以下に挙げます。

 各コマンドの処理を定義していきます。

? コマンドリスト一覧表示
 コマンドリストを表示するので、関数を使って
 まとめてシリアル出力するようにします。

void show_help()
{
  Serial.println("? help");
  Serial.println("E send pluse");
  Serial.println("M set interval count");
  Serial.println("m show interval count");
}

E 指定した時間幅(msの倍数)で、パルスを出力
 イベント通知フラグを利用し、ステートマシンで
 指定した時間幅で、パルスを出力するように関数
 を定義すればよいでしょう。

 ステートマシンは、次のステートダイアグラムで実現。



 ステートマシンを、関数の中に入れます。

void do_pluscode()
{
  switch( state ) {
    /* wait trigger */
    case 0 : state = 0 ;
             if ( eflag == ON ) {
               state = 1 ;
               eflag = OFF ;
             }
             break ;
    /* set interval and set H */
    case 1 : state = 2 ;
             bcur = millis() + myinterval;
             digitalWrite(XLED,ON) ;
             break ;
    /* delay and set L */
    case 2 : state = 2 ;
             if ( millis() >= bcur ) {
               state = 3 ; 
               digitalWrite(XLED,OFF) ;
             }
             break ;
    /* return first state */
    case 3 : state = 0 ;
             break ;
    /* others */
    default : state = 0 ;
              break ;
  }
}

 Arduinoには、システムタイマーの値を取得する関数millis()が
 あるので、現在値に時間間隔に相当する値を加算して、その値を
 超えたかで、時間経過を判定。

 ステート値で該当状態にあるときに、何をするかを
 記述しておけば、機械的にプログラムができます。

 ステートマシンを動かすための初期化を担当する
 関数を定義しておきます。

void init_pluscode()
{
  digitalWrite(XLED,OFF);
  pinMode(XLED,OUTPUT);
  state = 0 ;
  eflag = OFF ;
  myinterval = 500 ;
}

 パルス出力のピンは、XLEDとしてマクロ定義して
 後から変更可能にしておきましょう。

#define XLED 12

 ステートマシンを動かすために、イベント通知フラグを
 利用しているので、コマンド'E'を与えられたときに
 イベント通知フラグをセット。

 eflag = ON ;

M 時間幅を設定
 時間幅は、変数myintervalに設定するとして、受信
 バッファ中の文字列を、整数にして格納することに。

 myinterval = atoi( &sbuf[1] );

m 時間幅を表示
 数myintervalの値を、表示すればよいので
 標準バンドリングされている関数を利用。

 Serial.println(myinterval);

 ここまでで、シリアルインタフェースを使って動作する
 コマンドインタプリタから呼出す処理を定義したので
 コマンドインタプリタそのものを定義。

void cmd_handling()
{
  /* judge */
  if ( uflag == OFF ) return ;
  /* clear */
  uflag = OFF ;
  /* command interpreter */
  cmd = *(sbuf+0) ;
  /* help */
  if ( cmd == '?' ) { show_help(); }
  /* event */
  if ( cmd == 'E' ) { eflag = ON ; }
  /* set counter */
  if ( cmd == 'M' ) { myinterval = atoi( &sbuf[1] ); }
  /* show counter */
  if ( cmd == 'm' ) { Serial.println(myinterval); }
}

 コマンドインタプリタを起動するには、受信割込みで
 イベント通知フラグを利用すると考えます。
 受信割込み処理を定義。

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を点滅させます。
 LED点滅を扱う関数は、以下。

void sys_timer_handling()
{
  /* get current system timer */
  cur = millis();
  /* calculate interval */
  sdiff = cur - pre ;
  /* judge */
  if ( sdiff < SDIFF_MAX ) return ;
  /* update */
  pre = cur ;
  /* impress */
  digitalWrite(LEDP,xcnt & ON );
  /* update */
  xcnt++ ;
}

 システムタイマーのカウンタ値を常に入力して
 記憶している値の差を計算して、規定値超えと
 なっているのかを調べます。

 規定値を超えていれば、変数xcntのLSBが1か0かを
 取り出して、基板上のLEDに転送。

 システムタイマーの値やLED点滅に使う変数の初期化
 をしておきます。

void init_sys_timer()
{
  pre = millis();
  cur = pre ;
  xcnt = 0 ;
}

 Arduinoのプログラムは、2関数setup()、loop()で
 構成するので、これらの関数を定義。

void setup()
{
  /* pin value */
  digitalWrite( LEDP , OFF );
  /* data direction */
  pinMode( LEDP , OUTPUT );
  /* initialize system timer */
  init_sys_timer();
  /* Debug */
  Serial.begin(9600);
  Serial.println("Hello !");
  uflag = OFF ;
  init_pluscode();
}

void loop()
{
  /* timer counter */
  sys_timer_handling();
  /* command interpreter */
  cmd_handling();
  /* perform each mode */
  do_pluscode();
}

 まとめます。

#include <stdio.h>
#include <stdlib.h>

#define OFF 0
#define ON  OFF+1

#define LEDP 13

#define XLED 12

#define SDIFF_MAX 500

unsigned long pre ;
unsigned long cur ;
word          sdiff ;

byte xcnt ;

unsigned long bcur ;

char sbuf[16];
boolean uflag ;
char cmd ;
byte sindex ;
word myinterval ;
byte state ;
boolean eflag ;

/* function prototype */
void init_sys_timer();
void sys_timer_handling();
void init_pluscode();
void do_pluscode();
void cmd_handling();
void show_help();

void setup()
{
  /* pin value */
  digitalWrite( LEDP , OFF );
  /* data direction */
  pinMode( LEDP , OUTPUT );
  /* initialize system timer */
  init_sys_timer();
  /* Debug */
  Serial.begin(9600);
  Serial.println("Hello !");
  uflag = OFF ;
  init_pluscode();
}

void loop()
{
  /* timer counter */
  sys_timer_handling();
  /* command interpreter */
  cmd_handling();
  /* perform each mode */
  do_pluscode();
}

void init_sys_timer()
{
  pre = millis();
  cur = pre ;
  xcnt = 0 ;
}

void sys_timer_handling()
{
  /* get current system timer */
  cur = millis();
  /* calculate interval */
  sdiff = cur - pre ;
  /* judge */
  if ( sdiff < SDIFF_MAX ) return ;
  /* update */
  pre = cur ;
  /* impress */
  digitalWrite(LEDP,xcnt & ON );
  /* update */
  xcnt++ ;
}

void init_pluscode()
{
  digitalWrite(XLED,OFF);
  pinMode(XLED,OUTPUT);
  state = 0 ;
  eflag = OFF ;
  myinterval = 500 ;
}

void do_pluscode()
{
  switch( state ) {
    /* wait trigger */
    case 0 : state = 0 ;
             if ( eflag == ON ) {
               state = 1 ;
               eflag = OFF ;
             }
             break ;
    /* set interval and set H */
    case 1 : state = 2 ;
             bcur = millis() + myinterval;
             digitalWrite(XLED,ON) ;
             break ;
    /* delay and set L */
    case 2 : state = 2 ;
             if ( millis() >= bcur ) {
               state = 3 ; 
               digitalWrite(XLED,OFF) ;
             }
             break ;
    /* return first state */
    case 3 : state = 0 ;
             break ;
    /* others */
    default : state = 0 ;
              break ;
  }
}

void cmd_handling()
{
  /* judge */
  if ( uflag == OFF ) return ;
  /* clear */
  uflag = OFF ;
  /* command interpreter */
  cmd = *(sbuf+0) ;
  /* help */
  if ( cmd == '?' ) { show_help(); }
  /* event */
  if ( cmd == 'E' ) { eflag = ON ; }
  /* set counter */
  if ( cmd == 'M' ) { myinterval = atoi( &sbuf[1] ); }
  /* show counter */
  if ( cmd == 'm' ) { Serial.println(myinterval); }
}

void show_help()
{
  Serial.println("? help");
  Serial.println("E send pluse");
  Serial.println("M set interval count");
  Serial.println("m show interval count");
}

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

 ブレッドボードで、動作実験すると以下。



 ブレッドボード上の別のLEDを用意したのは
 次の利用から。

 大きめのLEDなら、パルス幅分の信号が
 出力されていることを、目視しやすい。


目次

inserted by FC2 system