目次
前
次
フォークリフトシミュレータ(Arduino)
フォークリフトのペダル、レバーの情報を定期的に
Processingに送ればよいと考え、スイッチボードを
Arduinoに接続します。
スイッチボードの回路は、以下。
左右に8ピンのDIPスイッチを配置してあるので
ペダルとレバーの対応を仕様として決定。
ペダル
ペダルの操作情報は、左のスイッチを使って表現します。
項目は、以下。
- power on/off
- parking brake set/release
- cratch on/off
- accele up/down
4種の操作をビット割り当てします。
power、parking brake、cratchは論理値でよいので
1、0で表現し1ビット。
acceleはアップ、ダウン、キープなので2ビット利用。
各ペダルの情報は、個別では次の表現とします。
- 10??????(power on)
- 00??????(power off)
- ??10????(parking brake set)
- ??00????(parking brake release)
- ????10??(cratch on)
- ????00??(cratch off)
- ??????10(accele up)
- ??????00(accele no operation)
- ??????01(accele down)
8ビットの状態をリードして、16進数2桁で送信します。
レバー
レバーの操作情報は、右のスイッチを使って表現します。
項目は、以下。
- lift up/down
- tilt forward/backward
- move speed high/low
- move direction forward/backward
4種の操作をビットに割り当てます。
全レバーを2ビットで表示。
各レバーの情報は、個別では次の表現とします。
- 10??????(lift up)
- 00??????(lift no operation)
- 01??????(lift down)
- ??10????(tilt up)
- ??00????(tilt no operation)
- ??01????(tilt down)
- ????11??(speed high)
- ????01??(speed low)
- ????00??(speed neutral)
- ??????10(direction forward)
- ??????00(direction neutral)
- ??????01(direction backward)
8ビットの状態をリードして、16進数2桁で送信します。
仕様を決めたので、スケッチを作成しました。
#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 ) ;
}
要点だけを説明します。
2つのDIPスイッチ状態入力に、タイマー割込みを利用。
MsTimer2を利用して、状態入力のタイミングを
フラグで通知して貰います。
loopの中に、タイマー割込みのイベントフラグを扱う
ブロックを用意します。
if ( aflag == ON ) {
/* clear event flag */
aflag = OFF ;
/* get value */
/* convert value */
/* update state */
}
Processingへ、その情報を送信するかしないかは
別のプロセスに任せるとして、スイッチ情報入力
だけで仕事を完結させます。
現在のスイッチ状態を、マルチプレクサを利用して
1ビットごとに入力し、変数に保存します。その値
のコピーを作っておき、Processingから要求がある
ときに、送信することに。
次のようにコードを書きました。
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 );
スイッチ基板をポートC(D14からD18)に接続。
ポートCは、アナログポートとして使えますが
デジタルポートで利用。
マルチプレクサに対して、どのビットを必要と
するのかを指定し、左右のDIPスイッチ状態を
逐次取得。
人間の操作は、10msよりもはるかに長いので
スイッチ状態を逐次取得しても、問題なしと
考えました。
Processingとはシリアルインタフェースでの情報
交換が最も楽なので、コマンドインタプリタ利用
とします。
コマンドは、以下としました。
- ? ヘルプ
- E スイッチ状態取得転送開始と実行
- I スイッチ状態取得転送中断
- S スイッチ状態取得転送中、停止のどちらかを表示
コマンドインタプリタは、以下。
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();
}
}
関数loopの中のシーケンスを定義後、より下のレベルでの
動作関数を定義していき、スケッチを完成させました。
目次
前
次