Linux でハードなポートからデータをとってくる方法

数秒単位ならば、通常の方法があるので( sleep() とかね)、もっと
シビアな場合の方法は、RT-Linux を使うのが最良でしょう。
#シビアな場合とは、10mS以下の場合かな?

お約束
RT-Linux が走っていること。

手順その0(前提)
サンプルコード ( 2task ) をネタにして、実現方法を決める
方針1:実際にリアルタイムで動作する部分はモジュールとして後から追加する
方針2:メインからの入出力は /dev/rtf0,1,2,3 で行う
方針3:/dev/rtf0, /dev/rtf1 はデータの入出力
方針4:/dev/fd2 はリアルタイムタスクの制御用に使う

手順その1
モジュールを作る(リアルタイムで動作する部分)
サンプル(2task) に手を加えてみました。

#define MODULE
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/version.h>
#include <linux/errno.h>

#include <linux/rt_sched.h>
#include <linux/rtf.h>
#include "control.h"

RT_TASK tasks[2]; /* 二個のタスクを作るので */

/* t -- the fifo number */
void fun( int t ) {
while(1) {
/* FIFO にデータを入れる */
/* 実際にデータをとってくる所は get_data() にしてます */
/* ということで get_data() は作ってくださいな */
rtf_put( t, get_data(), sizeof( int ) );
/* 次の用があるまでお休み */
rt_task_wait();
}
}

/* task controle function */
/* /dev/rtf2 にデータが書き込まれれば自動的にこの関数が起動します */
/* 書き込むデータは、 my_msg_struct型で、その中身は */
/* コマンドとタスク番号と動作間隔が入っています */
int my_handler( unsigned int fifo ) {
struct my_msg_struct msg;
int err;
RTIME now;

/* FIFO からデータを取ってきて、希望するデータならばコマンドを実行する */
while(( err = rtf_get(2, &msg, sizeof(msg))) == sizeof(msg)){
switch( msg.command ){
case START_TASK:
/* タスクを起動する開始時間として、今の時間をとってきて */
now = rt_get_time();
/* タスクを指定時間間隔で起動します */
rt_task_make_periodic( &tasks[msg.task], now, msg.periodic );
break;
case STOP_TASK:
/* 指定したタスクを止めます */
rt_task_suspend( &tasks[msg.task] );
break;
default:
return -EINVAL;
}
}
if( err != 0 ){
return -EINVAL;
}
return 0;
}


/* モジュールの初期化関数 */
int init_module( void ) {
/* データを受け渡す FIFO を作る */
/* rtf_create( FIFOの番号、確保するFIFOのサイズ ) */
rtf_create( 0, 4000 );
rtf_creatr( 1, 4000 );
rtf_create( 2, 100 );
/* タスクの初期化 */
/* rt_task_init( RT_TASKへのポインター, 実行する関数, 実行する時に渡される引き数、スタックサイズ、優先度 */
rt_task_init( &tasks[0], fun, 0, 3000, 4);
rt_task_init( &tasks[1], fun, 1, 3000, 5 );
/* 2番のFIFO にデータが書き込まれたら起動する関数の登録 */
/* rtf_create_handler( FIFO の番号、起動する関数 */
rtf_create_handler( 2, &my_handler );
return 0;
}

/* 終了時の処理 */
void cleanup_module(void) {
/* FIFO の解放 */
rtf_destroy(0);
rtf_destroy(1);
rtf_destroy(2);
/* タスクの削除 */
rt_task_delete( &tasks[0] );
rt_task_delete( &tasks[1] );
}


手順その2
できたモジュールを組み込む。
コンパイルした時に、エラーが無いことを確認して、例えば rt_process.o を
組み込む時には insmod rt_process.o を実行して下さい。
当然、こんな危ないことをするので root 権限で実行します。

手順その3
スペシャルファイルを作る
通常のカーネルプロセスとのデータのやり取りに使う FIFO のスペシャルファイル
を作ります。 この、作業は1回行えば良いです。
作り方は、簡単で、この際ですから4個、一度に作っておきましょう。
for i in 0 1 2 3; do mknod /dev/rtf$i c 63 $i; done
を実行すればできます。(当然 root 権限が必要になります)

手順その4
メイン部分を作る。

サンプルソース task2 から
/* おきまりのヘッダーファイルのインクルード */
#include <stdio.h>
#include <errno.h>
#include <sys/time.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/rtf.h>
#include <asm/rt_time.h>
#include "control.h"

#define BUFFSIZE 70

char buff[ BUFFSIZE ];

int main() {
fd_set rfds;
struct timeval tv;
int retval;
int fd0, fd1, ctl;
int n;
int i;
struct my_msg_struct msg;

/* /dev/rtf0 のオープン、実際にオープンされるのは1番の FIFO */
/* こちら側(通常のプロセス)からの見えかたはキャラクターデバイス */
/* open で開いたので読み書きは read/write を使います */
fd0 = open( "/dev/rtf0", O_RDONLY );
if( fd0 < 0 ){
fprintf( stderr, "Error opening /dev/rtf0\n" );
exit(1);
}
fd1 = open( "/dev/rtf1", O_RDONLY );
if( fd1 < 0 ){
fprintf( stderr, "Error opening /dev/rtf1\n" );
exit(1);
}
ctl = open( "/dev/rtf2", O_WRONLY);
if( ctl < 0 ){
fprintf( stderr, "Error opening /dev/rtf2\n" );
exit(1);
}

/* 今までは FIFO のオープンで、ここから下がタスクの制御になります */
/* このサンプルでは、ctl に構造体を渡すことによりタスクの開始/停止を */
/* 実現しています */ /* now start the task */
msg.command = START_TASK;
msg.task = 0:
msg.period = ( RT_TICKS_PER_SEC * 5000 ) / 1000000;
if( wtrite( ctl, &msg, sizeof(msg) ) < 0 ){
fprintf( stderr, "Can't send a command to RT-task\n" );
exit(1);
}

msg.task = 1;
msg.period = ( RT_TICKS_PER_SEC * 2000) / 1000000;
if( write( ctl, &msg, sizeof( msg ) ) < 0 ){
fprintf( stderr, "Can't send a command to RT-task\n" );
exit(1);
}

/* ここまでがタスクの開始でした。 */
/* ここから下がタスクからデータを吸い上げる部分です */
/* データを吸い上げる方法は select() を使ってデータが現れるのを */
/* read( int, void*, int ) で読み込んでいます。 */
/* select() の使い方は man select を参照のこと */
for( i = 0; i < 100; i++ ){
FD_ZERO( &rfds );
FD_SET( fd0, &rfds );
FD_SET( fd1, &rfds );
tv.tv_sec = 1;
tv.tv_usec = 0;

retval = select( FD_SETSIZE, &rfds, NULL, NULL, &tv );
if( retval > 0 ) {
if( FD_ISSET( fd0, &rfds ) ){
n = read( fd0, buf, BUFSIZE-1 );
buf]n] = 0;
printf( "FIFO 0: %s\n", buf );
}
if( FD_ISSET( fd1, &rfds ) ){
n = read( fd1, buf, BUFSIZE-1 );
buf[0] = 0;
printf( "FIFO 1: %s\n", buf );
}
}
}

/* ここまでが、データのサンプリング部分でした */
/* ここからが、終了処理です */
/* このサンプルでは ctl に終了コマンドを送ってタスクを終了(実際は停止)*/
/* 本当にタスクが削除されるのはこのプログラムが終わった時から */
/* しばらくして、モジュールがアンロードされる時です(?) */
/* stop the tasks */
msg.command = STOP_TASK;
msg.task = 0;
if( write( ctl, &msg, sizeof(msg) ) < 0 ){
fprintf( stderr, "Can't send a command to RT-task\n" );
exit(1 );
}

msg.task = 1;
if( write( ctl, &msg, sizeof( msg ) ) < 0 ) {
fprintf( stderr, "Can't send a command to RT-task\n" );
exit(1);
}
return 0;
}


例えば、このメインのプログラムが app という名前だとすると
./app
と打って、実行すれば動作するはずです。
幸運を祈ります。

Written by H.Tokumoto (Orange System HIROSHIMA)