目次

DACテスト

 Arduinoには、DAC(D/Aコンバータ)がないので
 外付けでDACを用意して、動作テストしました。




 DACは、MAXIM社のMAX525で4チャネル分の出力
 を持ちます。分解能は12ビットで、シリアルで
 情報を転送。

 ArduinoのポートCに、制御信号を接続します。



 ポートCを利用するので、初期化は
 次のように簡単に済ませます。

  /* set I/O value */
  PORTC = 0x08 ;
  /* set port directions */
  DDRC = 0xfe ; 
  /* enable seria

 制御情報を転送するために、タイミングチャート
 を見ておきましょう。



 タイミングチャートから、シリアルの16ビットで
 制御情報転送ということがわかるので、16ビット
 のデータを転送する関数を定義。

#define BIT_CS  3
#define BIT_SCK 2
#define BIT_DAI 1

void  send_dac(word x)
{
   byte i ;
   word xx ;
   /* copy */
   xx = x ;
   /* enable nCS */
   PORTC &= ~(1 << BIT_CS);
   /* loop */
   for ( i = 0 ; i < 16 ; i++ ) {
     /* data */
     PORTC &= ~(1 << BIT_DAI) ;
     if ( xx & MASK8000 ) { PORTC |= (1 << BIT_DAI) ; }
     /* clock : H */
     PORTC |=  (1 << BIT_SCK) ;
     /* shift */
     xx <<= 1 ;
     /* clock : L */
     PORTC &= ~(1 << BIT_SCK) ;
   }
   /* disable nCS */
   PORTC |=  (1 << BIT_CS);
}

 各チャネルから、電圧を出力するために、どのような
 情報を送信すればよいか、リストになっているので
 確認しておきます。



 チャネルごとに、チャネル番号、モード、DAC値を出力
 するのが、最も簡単として、ラッパー関数を定義して
 各チャネルへのデータ転送を楽にしておきましょう。

#define DAC0 0x3000
#define DAC1 0x7000
#define DAC2 0xb000
#define DAC3 0xf000

void  dac0_write(word x) { send_dac(DAC0 | x); }

void  dac1_write(word x) { send_dac(DAC1 | x); }

void  dac2_write(word x) { send_dac(DAC2 | x); }

void  dac3_write(word x) { send_dac(DAC3 | x); }

 MAX525のDOUTからの出力を、どう受信するのかと
 チャネルに対応したレジスタにデータを送信した
 ときの動作仕様があるので、それらを指定。

void  init_dac()
{
  send_dac(0x4000);
  send_dac(0xa000);
  dac0_write(0);
  dac1_write(0);
  dac2_write(0);
  dac3_write(0);
}

 ここまでで、DACに関係する操作で使える関数を
 定義したので、コマンドインタプリタを考えて
 いきます。

 コマンドは、以下としました。

 コマンドの一覧を表示する関数は、以下。

void  show_help()
{
  rs_puts("? help");                   crlf();
  rs_puts("D set value with channel"); crlf();
  rs_puts("d show values");            crlf();
  rs_puts("B set flag");               crlf();
  rs_puts("b clear flag");             crlf();
  rs_puts("Y set dac with channel");   crlf();
  rs_puts("y show dac values");        crlf();
}

 1文字表示、文字列表示、改行指定などの関数は
 以下としておきます。

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

void rs_puts(char *x)
{
  while ( *x != '\0' ) { 
    rs_putchar( *x ) ;
    x++ ;
  }
}

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

byte  get_hex(char x)
{
  byte result ;
  /* default */
  result = 0 ;
  /* judge and convert */
  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' ;
  /* convert */
  if ( 0 <= x && x <= 9 ) { result = x + '0' ; }

  return result ;
}

 コマンドインタプリタを動かすために、受信割込み
 ハンドラが必要になります。

 ハンドラは、次のように定義。

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

 割込みハンドラを定義したので、コマンドインタプリタ
 をまとめておきます。

  /* command interpreter */
  if ( uflag == ON ) {
    uflag = OFF ;
    /* new line */
    crlf();
    /* get command */
    cmd = *(sbuf+0) ;
    /* help */
    if ( cmd == '?' ) { show_help(); }
    /* test DAC */
    if ( cmd == 'D' ) { dac_handle(); }
    /* show current dac value */
    if ( cmd == 'd' ) { show_dac(); }
    /* set flag */
    if ( cmd == 'B' ) {
      state = 0 ;
      bflag = ON ;
    }
    /* clear flag */
    if ( cmd == 'b' ) {
      bflag = OFF ; 
      dac0_write( 0 );
    }
    /* set dac value */
    if ( cmd == 'Y' ) { put_sxseq(); }
    /* show dac value */
    if ( cmd == 'y' ) { show_sxseq(); }
  }

 1文字ごとのコマンドに対応した関数を
 定義していきます。

 コマンド'D'の関数は、以下。

void  dac_handle()
{
  byte xcn ;
  word xval ;
  byte i ;
  byte xtmp ;
  /* get channel number */
  xcn = get_hex( *(sbuf+1) );
  /* judge */
  if ( xcn > 3 ) {
    rs_puts("channel error !");
    crlf();
  } else {
    xval = 0 ;
    for ( i = 0 ; i < 4 ; i++ ) {
      xtmp = *(sbuf+2+i) ;
      if ( xtmp == '\r' ) break ;
      xval = xval * 10 + get_hex( xtmp );
    }
    /* check */
    if ( xval > 4095 ) {
      xval = 4095 ;
      rs_puts("out of range !");
      crlf();
    }
    /* store */
    *(xdac+xcn) = xval ;
    /* send */
    if ( xcn == 3 ) {  dac3_write( xval ); }
    else {
      if ( xcn == 2 ) {  dac2_write( xval ); }
      else {
        if ( xcn == 1 ) {  dac1_write( xval ); }
        else            {  dac0_write( xval ); }
      }
    }
  }
}

 受信バッファに格納されているコマンドの次には
 チャネル番号があり、番号に続けて10進数の数字
 が並んでいるので、変換しています。

 変換後、内部の配列に格納すると同時に、MAX525に
 転送します。

 コマンド'd'の関数は、以下。

void  show_val(word x)
{
  char msg[5] ;
  word xx ;
  byte i ;
  /* copy */
  xx = x ;
  /* default */
  *(msg+4) = '\0' ;
  /* separate */
  for ( i = 0 ; i < 4 ; i++ ) {
    *(msg+3-i) = xx % 10 ;
    xx /= 10 ;
  }
  /* convert */
  for ( i = 0 ; i < 4 ; i++ ) {
    *(msg+i) = get_asc( *(msg+i) ) ;
  }
  /* zero surpress */
  if ( *(msg+0) == '0' ) {
    *(msg+0) = ' ' ;
    if ( *(msg+1) == '0' ) {
      *(msg+1) = ' ' ;
      if ( *(msg+2) == '0' ) {
        *(msg+2) = ' ' ;
      } 
    } 
  } 
  /* show */
  rs_puts( msg );
}

void  show_dac()
{
  byte i ;
  for ( i = 0 ; i < 4 ; i++ ) {
    show_val( *(xdac+i) );
    crlf();
  }
}

 コマンド'B'、'b'は、単にフラグとカウンタの
 初期化をしているだけなので、残りの1文字の
 コマンドを定義します。

 コマンド'Y'の関数は、以下。

void  put_sxseq()
{
  byte xcn ;
  word xval ;
  byte i ;
  byte xtmp ;
  /* get channel number */
  xcn = get_hex( *(sbuf+1) );
  /* judge */
  if ( xcn > 7 ) {
    rs_puts("out of sequence error !");
    crlf();
  } else {
    xval = 0 ;
    for ( i = 0 ; i < 4 ; i++ ) {
      xtmp = *(sbuf+2+i) ;
      if ( xtmp == '\r' ) break ;
      xval = xval * 10 + get_hex( xtmp );
    }
    /* check */
    if ( xval > 4095 ) {
      xval = 4095 ;
      rs_puts("out of range !");
      crlf();
    }
    /* store */
    *(ydac+xcn) = xval ;
  }
}

 殆ど、dac_handle関数と同じですが
 指定値を格納する配列が異なります。

 コマンド'Y'の関数は、以下。

void  show_sxseq()
{
  byte i ;
  for ( i = 0 ; i < 8 ; i++ ) {
    show_val( *(ydac+i) );
    crlf();
  }
}

 シーケンス動作では、シーケンス番号とDAC値を
 表示します。これで、処理がわかりやすくなり
 ます。

  /* perform */
  if ( bflag == ON && tflag == ON ) {
    /* clear flag */
    tflag = OFF ;
    /* get value from array */
    yval = *(ydac+state);
    /* send */
    dac0_write( yval );
    /* show */
    show_val( state );
    rs_putchar(' ');
    show_val( yval );
    crlf();
    /* update */
    state++ ;
    state &= 7 ;
  }

 2つのフラグを使います。

 bflagは、シーケンス動作実行か停止かを指定。
 tflagは、1秒経過を通知するイベントフラグで
 利用。

 フラグは、もうひとつ用意しておき、これで
 起動時のコンフィグレーションに使います。

  /* opening messeage */
  if ( oflag == ON ) {
    oflag = OFF ;
    rs_puts("Hello , Darling !");
    crlf();
    show_help();
    init_dac();
  }

 シリアルインタフェースのプロトコルは、以下。

 Arduinoには、1個LEDがついているので
 これを1秒周期で点滅させて、動作中と
 わかるようにします。

 まとめると、以下。

#include <MsTimer2.h>

#define OFF    0
#define ON     OFF+1
#define MASK3F 0x3f
#define MASK8000 0x8000

#define LED_BIT 7

#define BIT_CS  3
#define BIT_SCK 2
#define BIT_DAI 1

#define DAC0 0x3000
#define DAC1 0x7000
#define DAC2 0xb000
#define DAC3 0xf000

volatile byte xcnt;
volatile byte uflag;
volatile byte bflag;
volatile byte tflag;
volatile byte lflag;
volatile byte oflag;
volatile char cmd ;
volatile char sbuf[16] ;
volatile byte sindex ;
volatile word xdac[4] ;
volatile byte state ;
volatile word ydac[8] ;
volatile word yval ;

void  send_dac(word x)
{
   byte i ;
   word xx ;
   /* copy */
   xx = x ;
   /* enable nCS */
   PORTC &= ~(1 << BIT_CS);
   /* loop */
   for ( i = 0 ; i < 16 ; i++ ) {
     /* data */
     PORTC &= ~(1 << BIT_DAI) ;
     if ( xx & MASK8000 ) { PORTC |= (1 << BIT_DAI) ; }
     /* clock : H */
     PORTC |=  (1 << BIT_SCK) ;
     /* shift */
     xx <<= 1 ;
     /* clock : L */
     PORTC &= ~(1 << BIT_SCK) ;
   }
   /* disable nCS */
   PORTC |=  (1 << BIT_CS);
}

void  init_dac()
{
  send_dac(0x4000);
  send_dac(0xa000);
  dac0_write(0);
  dac1_write(0);
  dac2_write(0);
  dac3_write(0);
}

void  dac0_write(word x) { send_dac(DAC0 | x); }

void  dac1_write(word x) { send_dac(DAC1 | x); }

void  dac2_write(word x) { send_dac(DAC2 | x); }

void  dac3_write(word x) { send_dac(DAC3 | x); }

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

void  update_trigger(void)
{
  /* update counter */
  xcnt++ ;
  /* enable */
  tflag = ON ;
  lflag = ON ;
}

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

void rs_puts(char *x)
{
  while ( *x != '\0' ) { 
    rs_putchar( *x ) ;
    x++ ;
  }
}

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

void  show_help()
{
  rs_puts("? help");                   crlf();
  rs_puts("D set value with channel"); crlf();
  rs_puts("d show values");            crlf();
  rs_puts("B set flag");               crlf();
  rs_puts("b clear flag");             crlf();
  rs_puts("Y set dac with channel");   crlf();
  rs_puts("y show dac values");        crlf();
}

byte  get_hex(char x)
{
  byte result ;
  /* default */
  result = 0 ;
  /* judge and convert */
  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  dac_handle()
{
  byte xcn ;
  word xval ;
  byte i ;
  byte xtmp ;
  /* get channel number */
  xcn = get_hex( *(sbuf+1) );
  /* judge */
  if ( xcn > 3 ) {
    rs_puts("channel error !");
    crlf();
  } else {
    xval = 0 ;
    for ( i = 0 ; i < 4 ; i++ ) {
      xtmp = *(sbuf+2+i) ;
      if ( xtmp == '\r' ) break ;
      xval = xval * 10 + get_hex( xtmp );
    }
    /* check */
    if ( xval > 4095 ) {
      xval = 4095 ;
      rs_puts("out of range !");
      crlf();
    }
    /* store */
    *(xdac+xcn) = xval ;
    /* send */
    if ( xcn == 3 ) {  dac3_write( xval ); }
    else {
      if ( xcn == 2 ) {  dac2_write( xval ); }
      else {
        if ( xcn == 1 ) {  dac1_write( xval ); }
        else            {  dac0_write( xval ); }
      }
    }
  }
}

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

  return result ;
}

void  show_val(word x)
{
  char msg[5] ;
  word xx ;
  byte i ;
  /* copy */
  xx = x ;
  /* default */
  *(msg+4) = '\0' ;
  /* separate */
  for ( i = 0 ; i < 4 ; i++ ) {
    *(msg+3-i) = xx % 10 ;
    xx /= 10 ;
  }
  /* convert */
  for ( i = 0 ; i < 4 ; i++ ) {
    *(msg+i) = get_asc( *(msg+i) ) ;
  }
  /* zero surpress */
  if ( *(msg+0) == '0' ) {
    *(msg+0) = ' ' ;
    if ( *(msg+1) == '0' ) {
      *(msg+1) = ' ' ;
      if ( *(msg+2) == '0' ) { *(msg+2) = ' ' ; }
    } 
  } 
  /* show */
  rs_puts( msg );  
}

void  show_dac()
{
  byte i ;
  for ( i = 0 ; i < 4 ; i++ ) {
    show_val( *(xdac+i) );
    crlf();
  }
}

void  put_sxseq()
{
  byte xcn ;
  word xval ;
  byte i ;
  byte xtmp ;
  /* get channel number */
  xcn = get_hex( *(sbuf+1) );
  /* judge */
  if ( xcn > 7 ) {
    rs_puts("out of sequence error !");
    crlf();
  } else {
    xval = 0 ;
    for ( i = 0 ; i < 4 ; i++ ) {
      xtmp = *(sbuf+2+i) ;
      if ( xtmp == '\r' ) break ;
      xval = xval * 10 + get_hex( xtmp );
    }
    /* check */
    if ( xval > 4095 ) {
      xval = 4095 ;
      rs_puts("out of range !");
      crlf();
    }
    /* store */
    *(ydac+xcn) = xval ;
  }
}

void  show_sxseq()
{
  byte i ;
  for ( i = 0 ; i < 8 ; i++ ) {
    show_val( *(ydac+i) );
    crlf();
  }
}

void  setup(void)
{
  byte i ;
  /* set I/O values */
  PORTD = 0x03 ;
  PORTB = 0x00 ;
  PORTC = 0x08 ;
  /* set port directions */
  DDRD = 0xfe ;
  DDRB = 0xff ;
  DDRC = 0xfe ; 
  /* enable serial port */
  Serial.begin(9600);
  /* clear flags */
  tflag = OFF ;
  lflag = OFF ;
  bflag = OFF ;
  uflag = OFF ;
  oflag = ON ;
  /* set others */
  xcnt = 0 ;
  sindex = 0 ;
  for ( i = 0 ; i < 8 ; i++ ) {
    if ( i < 4 ) { *(xdac+i) = 0 ; }
    *(ydac+i) = 500 ;
  }
  /* 1000ms period */
  MsTimer2::set(1000,update_trigger);
  /* enable */ 
  MsTimer2::start();
}

void loop(void)
{
  /* opening messeage */
  if ( oflag == ON ) {
    oflag = OFF ;
    rs_puts("Hello , Darling !");
    crlf();
    show_help();
    init_dac();
  }
  /* LED handling */
  if ( lflag == ON ) {
    /* clear flag */
    lflag = OFF ;
    /* update */
    send_led( xcnt & ON );
  }
  /* perform */
  if ( bflag == ON && tflag == ON ) {
    /* clear flag */
    tflag = OFF ;
    /* get value from array */
    yval = *(ydac+state);
    /* send */
    dac0_write( yval );
    /* show */
    show_val( state );
    rs_putchar(' ');
    show_val( yval );
    crlf();
    /* update */
    state++ ;
    state &= 7 ;
  }
  /* command interpreter */
  if ( uflag == ON ) {
    uflag = OFF ;
    /* new line */
    crlf();
    /* get command */
    cmd = *(sbuf+0) ;
    /* help */
    if ( cmd == '?' ) { show_help(); }
    /* test DAC */
    if ( cmd == 'D' ) { dac_handle(); }
    /* show current dac value */
    if ( cmd == 'd' ) { show_dac(); }
    /* set flag */
    if ( cmd == 'B' ) {
      state = 0 ;
      bflag = ON ;
    }
    /* clear flag */
    if ( cmd == 'b' ) {
      bflag = OFF ; 
      dac0_write( 0 );
    }
    /* set dac value */
    if ( cmd == 'Y' ) { put_sxseq(); }
    /* show dac value */
    if ( cmd == 'y' ) { show_sxseq(); }
  }
}

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

 接続して動かすと、次のようにメッセージが
 表示されます。



 コマンド'D'と'd'の動作をチェックすると、以下。



 このとき、マルチメータで電圧を測定します。

 コマンド'Y'と'y'の動作をチェックすると、以下。



 コマンド'B'と'b'の動作をチェックすると、以下。



 コマンド'B'と'b'を使うと、VCOを使って
 いろいろな周波数の信号を生成できます。

 VCOとして、NE555を使うときには、次の
 ような基板を利用します。




 スピーカから、VCOで生成した音がでるので
 アナログシンセサイザーを使っているのと
 等価。


目次

inserted by FC2 system