目次
前
次
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に関係する操作で使える関数を
定義したので、コマンドインタプリタを考えて
いきます。
コマンドは、以下としました。
- ? 1文字コマンドのリスト表示
- D チャネル番号に続けて、10進数でDAC値を設定
- d 各チャネルのDAC値を表示
- B シーケンス動作実行
- b シーケンス動作停止
- Y シーケンス動作で出力するDAC値を設定(シーケンス番号は、0から7)
- y シーケンス動作で出力するDCA値を表示
コマンドの一覧を表示する関数は、以下。
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();
}
シリアルインタフェースのプロトコルは、以下。
- データ転送速度 9600bps
- ストップビット 1ビット
- パリティ なし
- フロー制御 なし
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で生成した音がでるので
アナログシンセサイザーを使っているのと
等価。
目次
前
次