目次
前
次
エクスカベータシミュレータ(Arduino)
Arduinoは、アナログジョイスティックの状態を読み込み
該当する数値に変換します。さらに、Processingからの
指示で、数値になったアナログジョイスティックの状態
を転送します。
各動作は、イベントドリブンで扱うようにして
開発時間を短くします。
4つのジョイスティックの状態を入力するための
イベントには、タイマー割込みを使います。
MsTimer2を利用して、ジョイスティックの状態を入力する
タイミングをフラグで通知して貰うことにします。
loopの中に、タイマー割込みのイベントフラグを扱う
ブロックを用意します。
if ( aflag == ON ) {
/* clear event flag */
aflag = OFF ;
/* get value */
/* convert value */
/* update state */
}
イベントフラグで全ジョイスティックの状態を
入力するとチャタリング等の処理が面倒なので
ステートマシンを利用して、1個ずつ状態値を
入力していきます。
関数analogReadで、電圧換算値を取得します。
A/D変換のチャネルは、次のように割当てます。
- PC3 (input) left leaver
- PC2 (input) left paddle
- PC1 (input) right paddle
- PC0 (input) right leaver
変数stateで、チャネルを指定するとして定義。
if ( aflag == ON ) {
/* clear event flag */
aflag = OFF ;
/* get value */
*(adv+state) = analogRead(state);
/* convert value */
*(swv+state) = *(adv+state) / 100 ;
/* update state */
state++ ;
if ( state == LASTSTATE ) { state = 0 ; }
}
Processingスケッチからの指定で、周期的にジョイスティックの
状態をシリアル転送します。こちらもイベントフラグを用意して
周期的に転送と考えると、次のように定義すればよいでしょう。
if ( tflag == ON ) {
/* clear event flag */
tflag = OFF ;
/* generate code */
/* judge transfer */
}
Processingスケッチからの指示がないとき、転送しないように
フラグを用意して対応します。
if ( tflag == ON ) {
/* clear event flag */
tflag = OFF ;
/* generate code */
for ( int i = 0 ; i < LASTSTATE ; i++ ) {
*(msg+i) = conv_code( *(swv+i) );
}
/* judge transfer */
if ( eflag == ON ) {
rs_puts( msg );
crlf();
}
}
MsTimer2では、100msごとにA/D変換処理のイベント通知を
1000msごとにジョイスティックの状態を転送するようにと
イベント通知をするとします。
void update_trigger(void)
{
/* set flag */
aflag = ON ;
/* update counter */
xcnt++ ;
/* judge */
if ( xcnt == LASTXCNT ) {
xcnt = 0 ;
tflag = ON ;
}
}
Arduinoが動作していることがわかるように
D13に標準で接続されているLEDを点滅させる
処理を加えておきます。
void update_trigger(void)
{
/* led handling */
send_led(xcnt & ON);
/* set flag */
aflag = ON ;
/* update counter */
xcnt++ ;
/* judge */
if ( xcnt == LASTXCNT ) {
xcnt = 0 ;
tflag = ON ;
}
}
LEDの点灯は、次の関数に任せておきます。
void send_led(byte x)
{
if ( x ) { PORTB |= (1 << LED_BIT) ; }
else { PORTB &= ~(1 << LED_BIT) ; }
}
MsTimer2は、関数update_triggerを利用するとして
setup内での初期化を入れます。
/* trigger period */
MsTimer2::set(NPER,update_trigger);
/* enable */
MsTimer2::start();
Processingスケッチからの指示を捌くために、受信
割込みを使います。受信割込みでは、デリミタ到着
まで、バッファに文字列を格納していき、デリミタ
が着たなら、イベントフラグで通知します。
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 ;
}
}
}
イベントフラグで通知されるので、loopの中に
コマンドインタプリタを用意して対応。
if ( uflag == ON ) {
/* clear event flag */
uflag = OFF ;
/* new line */
/* get command */
cmd = *(sbuf+0) ;
}
コマンドインタプリタで扱うコマンドを考えます。
今回は、以下としました。
- ? ヘルプ
- E ジョイスティックの状態を周期的に転送する
- I ジョイスティックの状態を周期的に転送しない
- S データの周期的転送をしているか否かの確認
シリアルインタフェースで、1文字、文字列を転送
するためのラッパー関数を定義しておきます。
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');
}
ArduinoからProcessingには、文字だけを送ることに
して、数値から数字への変換が必要です。専用関数を
定義します。
char get_asc(byte x)
{
byte result ;
/* default */
result = '0' ;
/* judge */
if ( x < 10 ) { result = x + '0' ; }
if ( 9 < x ) { result = x - 10 + 'A' ; }
return result ;
}
最下位レベルで動作する関数を定義したので
コマンド'?'を検出したときの文字列出力関数
を定義。
void show_help()
{
rs_puts("? help"); crlf();
rs_puts("E execute"); crlf();
rs_puts("I idle"); crlf();
rs_puts("S show status"); crlf();
}
コマンドインタプリタは、次のように記述。
if ( uflag == ON ) {
/* clear event flag */
uflag = OFF ;
/* new line */
crlf();
/* get command */
cmd = *(sbuf+0) ;
/* help */
if ( cmd == '?' ) { show_help() ; }
/* execute */
if ( cmd == 'E' ) { eflag = ON ; }
/* idle */
if ( cmd == 'I' ) { eflag = OFF ; }
/* show status */
if ( cmd == 'S' ) {
/* message */
if ( eflag == ON ) { rs_puts("run"); }
else { rs_puts("idle"); }
/* new line */
crlf();
}
}
シリアルとI/Oの初期化は、次のように定義。
/* initialize serial */
Serial.begin(9600);
sindex = 0 ;
/* initialize */
for ( state = 0 ; state < LASTSTATE ; state++ ) {
*(adv+state) = 0 ;
*(swv+state) = 0 ;
}
state = 0 ;
xcnt = 0 ;
*(msg+4) = '\0' ;
/* initialize port values */
PORTB = 0xf0 ;
PORTC = 0x00 ;
PORTD = 0x01 ;
/* initialize port direction */
DDRB = 0xff ;
DDRC = 0xf0 ;
DDRD = 0xfe ;
スケッチとしてまとめます。
#include <MsTimer2.h>
#define OFF 0
#define ON OFF+1
#define LASTSTATE 4
#define LASTXCNT 10
#define LED_BIT 5
#define NPER 100
/* variables */
byte uflag ;
byte tflag ;
byte aflag ;
byte eflag ;
byte cmd ;
byte sindex ;
byte sbuf[8] ;
byte state ;
word adv[4] ;
byte swv[4] ;
byte xcnt ;
char msg[5];
/* function prototype */
void show_help();
char get_asc(byte x);
void rs_putchar(char x);
void rs_puts(char *ptr);
void crlf();
void send_led(byte x);
void update_trigger(void);
byte conv_code(byte x);
void setup()
{
/* initialize serial */
Serial.begin(9600);
sindex = 0 ;
uflag = OFF ;
tflag = OFF ;
aflag = OFF ;
eflag = OFF ;
/* initialize */
for ( state = 0 ; state < LASTSTATE ; state++ ) {
*(adv+state) = 0 ;
*(swv+state) = 0 ;
}
state = 0 ;
xcnt = 0 ;
*(msg+4) = '\0' ;
/* initialize port values */
PORTB = 0xf0 ;
PORTC = 0x00 ;
PORTD = 0x01 ;
/* initialize port direction */
DDRB = 0xff ;
DDRC = 0xf0 ;
DDRD = 0xfe ;
/* trigger period */
MsTimer2::set(NPER,update_trigger);
/* enable */
MsTimer2::start();
}
void loop()
{
/* scan A/D converter */
if ( aflag == ON ) {
/* clear event flag */
aflag = OFF ;
/* get value */
*(adv+state) = analogRead(state);
/* convert value */
*(swv+state) = *(adv+state) / 100 ;
/* update state */
state++ ;
if ( state == LASTSTATE ) { state = 0 ; }
}
/* transfer switch state */
if ( tflag == ON ) {
/* clear event flag */
tflag = OFF ;
/* generate code */
for ( int i = 0 ; i < LASTSTATE ; i++ ) {
*(msg+i) = conv_code( *(swv+i) );
}
/* judge transfer */
if ( eflag == ON ) {
rs_puts( msg );
crlf();
}
}
/* command interpreter */
if ( uflag == ON ) {
/* clear event flag */
uflag = OFF ;
/* new line */
crlf();
/* get command */
cmd = *(sbuf+0) ;
/* help */
if ( cmd == '?' ) { show_help() ; }
/* execute */
if ( cmd == 'E' ) { eflag = ON ; }
/* idle */
if ( cmd == 'I' ) { eflag = OFF ; }
/* show status */
if ( cmd == 'S' ) {
/* message */
if ( eflag == ON ) { rs_puts("run"); }
else { rs_puts("idle"); }
/* 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 ;
}
}
}
void show_help()
{
rs_puts("? help"); crlf();
rs_puts("E execute"); crlf();
rs_puts("I idle"); crlf();
rs_puts("S show status"); crlf();
}
char get_asc(byte x)
{
byte result ;
/* default */
result = '0' ;
/* judge */
if ( x < 10 ) { result = x + '0' ; }
if ( 9 < x ) { result = x - 10 + 'A' ; }
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');
}
void send_led(byte x)
{
if ( x ) { PORTB |= (1 << LED_BIT) ; }
else { PORTB &= ~(1 << LED_BIT) ; }
}
void update_trigger(void)
{
/* led handling */
send_led(xcnt & ON);
/* set flag */
aflag = ON ;
/* update counter */
xcnt++ ;
/* judge */
if ( xcnt == LASTXCNT ) {
xcnt = 0 ;
tflag = ON ;
}
}
byte conv_code(byte x)
{
return x ;
}
アナログジョイスティックを入手できないとき、8ピンのDIP
スイッチとデータセレクタを利用し、5ビットでスイッチ情報
を取得できるようにします。回路図は、以下。
対応するスケッチは、以下。
#include <MsTimer2.h>
#define OFF 0
#define ON OFF+1
#define LASTSTATE 2
#define LASTXCNT 10
#define LED_BIT 5
#define NPER 100
#define MASK0F 0x0f
/* variables */
byte uflag ;
byte tflag ;
byte aflag ;
byte eflag ;
byte cmd ;
byte sindex ;
byte sbuf[4] ;
byte state ;
byte xswv[2];
byte swv[2] ;
byte xcnt ;
char msg[5];
byte tmpa ;
byte tmpb ;
/* function prototype */
void show_help();
char get_asc(byte x);
void rs_putchar(char x);
void rs_puts(char *ptr);
void crlf();
void send_led(byte x);
void update_trigger(void);
byte pre_rev(byte x);
byte rev(byte x);
void setup()
{
/* initialize serial */
Serial.begin(9600);
sindex = 0 ;
/* clear flags */
uflag = OFF ;
tflag = OFF ;
aflag = OFF ;
eflag = OFF ;
/* initialize */
for ( state = 0 ; state < LASTSTATE ; state++ ) {
*(xswv+state) = 0 ;
*(swv+state) = 0 ;
}
state = 0 ;
xcnt = 0 ;
*(msg+4) = '\0' ;
/* initialize port values */
PORTB = 0xff ;
PORTC = 0xf8 ;
PORTD = 0x01 ;
/* initialize port direction */
DDRB = 0xff ;
DDRC = 0x07 ;
DDRD = 0xfe ;
/* trigger period */
MsTimer2::set(NPER,update_trigger);
/* enable */
MsTimer2::start();
}
void loop()
{
/* scan switch */
if ( aflag == ON ) {
/* clear event flag */
aflag = OFF ;
/* default */
tmpa = 0 ;
tmpb = 0 ;
/* get value */
for ( int i = 0 ; i < 8 ; i++ ) {
/* impress address */
PORTC = (i & 7) ;
/* delay */
delayMicroseconds(10);
/* get code */
if ( PINC & 0x08 ) { tmpa |= ON ; }
if ( PINC & 0x10 ) { tmpb |= ON ; }
/* shift */
if ( i < 7 ) {
tmpa <<= 1 ;
tmpb <<= 1 ;
}
}
/* store */
*(xswv+0) = rev( tmpa );
*(xswv+1) = rev( tmpb );
}
/* transfer switch state */
if ( tflag == ON ) {
/* clear event flag */
tflag = OFF ;
/* copy */
*(swv+0) = *(xswv+0) ;
*(swv+1) = *(xswv+1) ;
/* generate code */
*(msg+0) = get_asc( *(swv+0) & MASK0F ) ;
*(msg+1) = get_asc( (*(swv+0) >> 4) & MASK0F ) ;
*(msg+2) = get_asc( *(swv+1) & MASK0F ) ;
*(msg+3) = get_asc( (*(swv+1) >> 4) & MASK0F ) ;
/* judge transfer */
if ( eflag == ON ) {
rs_puts( msg );
crlf();
}
}
/* command interpreter */
if ( uflag == ON ) {
/* clear event flag */
uflag = OFF ;
/* new line */
crlf();
/* get command */
cmd = *(sbuf+0) ;
/* help */
if ( cmd == '?' ) { show_help() ; }
/* execute */
if ( cmd == 'E' ) { eflag = ON ; }
/* idle */
if ( cmd == 'I' ) { eflag = OFF ; }
/* show status */
if ( cmd == 'S' ) {
/* message */
if ( eflag == ON ) { rs_puts("run"); }
else { rs_puts("idle"); }
/* 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 ;
}
}
}
char get_asc(byte x)
{
byte result ;
/* default */
result = '0' ;
/* judge */
if ( x < 10 ) { result = x + '0' ; }
if ( 9 < x && x < 16 ) { result = x - 10 + 'A' ; }
return result ;
}
void show_help()
{
rs_puts("? help"); crlf();
rs_puts("E execute"); crlf();
rs_puts("I idle"); crlf();
rs_puts("S show status"); 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');
}
void send_led(byte x)
{
if ( x ) { PORTB |= (1 << LED_BIT) ; }
else { PORTB &= ~(1 << LED_BIT) ; }
}
void update_trigger(void)
{
/* led handling */
send_led(xcnt & ON);
/* set flag */
aflag = ON ;
/* update counter */
xcnt++ ;
/* judge */
if ( xcnt == LASTXCNT ) {
xcnt = 0 ;
tflag = ON ;
}
}
byte pre_rev(byte x)
{
byte result ;
/* default */
result = 0 ;
/* judge */
if ( x & 8 ) { result |= 1 ; }
if ( x & 4 ) { result |= 2 ; }
if ( x & 2 ) { result |= 4 ; }
if ( x & 1 ) { result |= 8 ; }
return result ;
}
byte rev(byte x)
{
byte dh ;
byte dl ;
/* reverse */
dh = pre_rev( (x >> 4) & MASK0F ) ;
dl = pre_rev( x & MASK0F ) ;
return( (dh << 4) | dl ) ;
}
D14(A0)からD16(A2)に、4051のセレクタ信号を出力し、D17(A3)、D18(A4)
で、スイッチの状態を入力します。
目次
前
次