外部スイッチをつける


パソコンに、外部スイッチを付ける事を考えてみます。
スイッチのON/OFF状態をリアルタイムで知りたい用途にはパラレルポートを使いますが、スイッチが押されたことがわかれば良いという用途(たとえばカ メラのシャッター ボタンなど)には、シリアルポートが便利そうです。それは、パラレルポートの場合は、スイッチのセンシ ングをするために頻繁にポーリングが必要ですが、シリアルポートの場合はバッファが使えますので、プログラムの都合が良い時にポーリングすれば良いので、 楽できるはずです。

ハードウェアの仕様

PC/AT互換機のシリアル(RS-232C)ポートを使用し、データ入力ピン(RxD)をスイッチ入力として使用します。PC/AT互換機の場合、RS-232Cポートは、9pin D-sub オスコネクタで構成されています。
本来なら、外部回路を用いて、正規のデータを送るところですが、たかがスイッチ一つですので、もっと簡単な回路を考えてみます。
RS-232Cの仕様では、±12VのNRZ符合なので、単純なスイッチでそれを実現させようとした場合、ひと工夫必要です。
配線図
初期化を終えて、データ通信ができる状態になると、TxDは-12V、DTRは+12Vなるので、これを利用します。
通常は、抵抗を通してRxDを-12Vに固定しておき、スイッチを入れた時に、+12Vに落すことで、疑似的にデータを受信したように見せかけ、スイッチが押されたことを検出します。

サンプルプログラム

ソフトウェアは、通常のシリアル通信を行なう時とほぼ同じです。
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <termios.h>
#include <sys/types.h>
#include <sys/time.h>
#include <unistd.h>
#include <string.h>

int fd;

//==========================================================================
// 初期化
//--------------------------------------------------------------------------
// arg: none
// ret: none
//--------------------------------------------------------------------------
// シリアルデバイスをオープンして、非カノニカルモードに設定。
void initialize()
{
const char *node = "/dev/cuaa0";
struct termios t;

if( (fd = open( node, O_RDWR|O_NONBLOCK|O_NOCTTY )) < 0 ) goto ERROR;
if( tcgetattr( fd, &t ) < 0 ) goto ERROR;
t.c_iflag = 0;
t.c_oflag = 0;
t.c_cflag &= ~(CSIZE|CSTOPB|PARENB);
t.c_cflag |= (CS6|CLOCAL|CREAD);
t.c_lflag &= ~(ICANON);
t.c_cc[VTIME] = 0;
t.c_cc[VMIN] = 1;
if( cfsetspeed( &t, B2400 ) < 0 ) goto ERROR;
if( tcsetattr( fd, TCSANOW, &t ) < 0 ) goto ERROR;
return;

ERROR:
perror( "" );
exit( 1 );
}

//==========================================================================
// キーが押されたか?
//--------------------------------------------------------------------------
// arg: none
// ret: int 1=押された, 0=押されていない
//--------------------------------------------------------------------------
// selectシステムコールを使って、シリアルポートにデータが有るかチェック。
// データがあれば、スイッチが押されたものとし、バッファをフラッシュしておく。
int isKeyPushed()
{
fd_set fdset;
struct timeval timeout;
char buffer[32];
int ret = 0;

while( 1 )
{
timeout.tv_sec = 0;
timeout.tv_usec = 0;
FD_ZERO( &fdset );
FD_SET( fd, &fdset );
if( select( fd+1, &fdset, NULL, NULL, &timeout ) != 1 ) break;

read( fd, buffer, sizeof buffer );
ret = 1;
}

return ret;
}

//==========================================================================
// main
//--------------------------------------------------------------------------
int main()
{
initialize();
while( 1 )
{
sleep( 1 );
if( isKeyPushed() )
printf( "Push" );
else
printf( "." );
fflush( stdout );
}

return 0;
}

sleep()コール中スイッチが押されたとしても、落すことなく検出します。
キーボード以外のスイッチが欲しい時、簡単な回路で実現できるので、便利ではないかと思います。