第 4 章 より高度な処理を行おう 27
4.3 DaStartSamplingを使ったアナログ出力更新
先程、DaOutputDAEx関数を使って、アナログ出力更新を実現しました。
実は、同じサンプリングだけを行うなら、DaStartSampling関数を使う方が、簡単に処理を実現で きます。
ここでは、DaStartSampling関数を使い、永続的なサンプリングを実現します。
以下の4つのファイルを作成します。
ファイル名 備 考
sample3.h LinuxプロセスとRTLinuxモジュールの双方で共有する定義ファイル module3.c RTLinuxモジュールのソースコード
sample3.c Linuxプロセスのソースコード
makefile 上記ソースコードをコンパイルするためのmakefile
下記Listは、
LinuxプロセスとRTLinuxモジュール間で、共通で使う定義ファイルです。ファイル名
を「sample3.h」として保存してください。sample3.h
12 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
/*
sample3.h 共通定義ヘッダファイル
Copyright 2002 Interface Corporation. All rights reserved.
*/
#if !defined(___SAMPLE3_H)
#define ___SAMPLE3_H
#include "fbida.h"
/* Constants --- */
#define FIFO_COMMAND 1 /* Linuxプロセスから指令を受け取るRT-FIFO */
#define FIFO_THRU_CMD 2 /* ハンドラからRTLinuxスレッドへ指令を受け渡しするRT-FIFO
*/
#define DEVICE_ID 3329 /* 製品を特定する一意のID値(PCI-3329を表す) */
#define SUB_SYSTEM_ID 0x0001 /* 製品を特定する一意のID値(PCI-3329を表す) */
#define RSW_NO 0 /* 同一型式の製品を区別するRSW1設定値 */
#define CH_NUM 2 /* 1件のDA出力の総チャンネル数 */
#define SMP_NUM 100 /* DA出力のサンプリング件数 */
#define BUFF_SIZE 1000 /* ドライバが使用する内部バッファの大きさ */
/* DAデータ配列にアクセスする関数マクロ */
#define DA_PTR(ptr, num, ch) ((ptr) + (CH_NUM * (num)) + (ch))
typedef unsigned short DA_TYPE; /* DAデータの型 */
#define MAX_DATA 0xfffU /* DAデータの最大値 */
#define MIN_DATA 0x000U /* DAデータの最小値 */
#define MID_DATA ((MAX_DATA + MIN_DATA) / 2) /* DAデータの中間値 */
#define PI (3.1415926) /* パイの値 */
/* Command ID --- */
/* コマンドID群(周期制御&監視用スレッドへの指示用) */
enum CMD_IDS {
ID_START, /* アナログ出力更新のスタート指示 */
ID_STOP, /* アナログ出力更新のストップ指示 */
ID_CLEAR_DATA, /* アナログ出力更新データのクリア */
ID_SET_DATA /* アナログ出力更新データのセット */
};
49 50 51 52 53 54 55 56 57 58 59
/* コマンド指示用の構造体 */
struct CMD_STRUCT {
enum CMD_IDS id; /* コマンドID */
/* 各コマンドに対する設定パラメータ */
union {
long smp_period_ms; /* ID_START時使用:周期監視の設定(ms単位) */
enum DATA_KIND_IDS data_kind; /* ID_SET_DATA時使用:データの設定 */
} opt;
};
#endif
下記Listは、RTLinuxモジュールのソースファイルです。ファイル名を「module3.c」として保存し てください。
module3.c
12 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
/*
module3.c RTLinuxモジュールのソースコード
Copyright 2002 Interface Corporation. All rights reserved.
*/
#include <rtl.h>
#include <rtl_sched.h>
#include <rtl_fifo.h>
#include <math.h>
#include "sample3.h"
pthread_t my_task_info;
int g_device_no = -1; /* DA製品のデバイス番号 */
DA_TYPE inner_da_buff[CH_NUM * BUFF_SIZE]; /* ドライバ内部が使用するバッファ */
DA_TYPE da_data[CH_NUM * SMP_NUM]; /* アナログ出力更新を行うバッファ */
DA_TYPE da_sin_wave_data[CH_NUM * SMP_NUM]; /* 参照用:サイン波データ */
DA_TYPE da_triangle_wave_data[CH_NUM * SMP_NUM]; /* 参照用:三角波データ */
DA_TYPE da_teeth_saw_wave_data[CH_NUM * SMP_NUM];/* 参照用:のこぎり波データ */
DA_TYPE da_rectangle_wave_data[CH_NUM * SMP_NUM];/* 参照用:矩形波データ */
/* アナログ出力更新時のイベント発生時に呼ばれるコールバック関数 */
void my_smp_callback(int user_arg) {
rtl_printf("my_smp_callback called user_arg=%d¥n", user_arg);
}
/* 状態監視および制御を行うRTLinuxスレッド */
void* my_task(void* arg) {
int ret;
struct CMD_STRUCT cmd;
DA_TYPE* p_src_data = NULL;
unsigned long smp_stat, smp_count, avail_count, avail_repeat;
rtl_printf("my_task called arg=%d¥n", arg);
/* RTLinuxスレッドが組み込み中、動作する永久ループ */
while(1){
/* 自スレッドを、次の周期までスリープさせる */
pthread_wait_np();
/* RT-FIFOからの指示があれば取り込み */
ret = rtf_get(FIFO_THRU_CMD, &cmd, sizeof(cmd));
if(ret == sizeof(cmd)){
rtl_printf("my_task: get command id=%d¥n", cmd.id);
switch(cmd.id){
case ID_START: /* アナログ出力更新のスタート */
rtl_printf("my_task: start output!!¥n");
/* アナログ出力更新の開始 */
ret = DaStartSampling(g_device_no, FLAG_ASYNC);
if(ret){
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124
pthread_make_periodic_np(pthread_self(), gethrtime(), cmd.opt.smp_period_ms * 1000 * 1000);
break;
case ID_STOP: /* アナログ出力更新のストップ */
rtl_printf("my_task: stop output!!¥n");
/* アナログ出力更新の停止 */
ret = DaStopSampling(g_device_no);
if(ret){
rtl_printf("DaStopSampling error [ret=%x]¥n", ret);
}
pthread_suspend_np(pthread_self());
break;
case ID_CLEAR_DATA: /* アナログ出力更新用データのクリア */
rtl_printf("my_task: clear output data!!¥n");
/* バッファのクリア */
ret = DaClearSamplingData(g_device_no);
if(ret){
rtl_printf("DaClearSamplingData error [ret=%x]¥n", ret);
}
break;
case ID_SET_DATA: /* アナログ出力更新用データのセット */
rtl_printf("my_task: set output data!!¥n");
switch(cmd.opt.data_kind){
case ID_SIN_WAVE_DATA: p_src_data = da_sin_wave_data; break;
case ID_TRIANGLE_WAVE_DATA: p_src_data = da_triangle_wave_data; break;
case ID_TEETH_SAW_WAVE_DATA: p_src_data = da_teeth_saw_wave_data; break;
case ID_RECTANGLE_WAVE_DATA: p_src_data = da_rectangle_wave_data; break;
default: rtl_printf("unknown data_kind!!¥n"); break;
}
/* バッファのセット */
ret = DaSetSamplingData(g_device_no, p_src_data, SMP_NUM);
if(ret){
rtl_printf("DaSetSamplingData error [ret=%x]¥n", ret);
}
break;
default: rtl_printf("unknown id!!¥n"); break;
}
}
/* アナログ出力更新の状態を取得する */
ret = DaGetStatus(g_device_no, &smp_stat, &smp_count, &avail_count, &avail_repeat);
if(ret){
rtl_printf("DaGetStatus error [ret=%d]¥n", ret);
}
rtl_printf("my_task: smp_stat:%ld smp_count:%ld avail_count:%ld avail_repeat:%ld¥n", smp_stat, smp_count, avail_count, avail_repeat);
} return 0;
}
/* Linuxプロセスからの指令を受け取るハンドラ */
int my_handler(unsigned int fifo) {
int ret;
struct CMD_STRUCT cmd;
rtl_printf("my_handler called fifo=%d¥n", fifo);
/* Linuxプロセスの指示に従って、RTLinuxスレッド等の制御を行う */
while((ret = rtf_get(FIFO_COMMAND, &cmd, sizeof(cmd))) == sizeof(cmd)){
rtf_put(FIFO_THRU_CMD, &cmd, sizeof(cmd));
rtl_printf("my_handler: get command id=%d¥n", cmd.id);
pthread_wakeup_np(my_task_info);
134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206
void make_sampling_data(DA_TYPE* dst_data, enum DATA_KIND_IDS id, int max_num) {
int ch, num;
rtl_printf("make_sampling data called dst_data=%p id=%d max_num=%d¥n", dst_data, id, max_num);
switch(id){
case ID_SIN_WAVE_DATA: /* サイン波データの作成 */
for(num = 0; num < max_num; num++){
for(ch = 0; ch < CH_NUM; ch++){
*DA_PTR(dst_data, num, ch) =
MID_DATA * sin(num * 2 * PI / max_num) + MID_DATA;
}
}
break;
case ID_TRIANGLE_WAVE_DATA: /* 三角波データの作成 */
for(num = 0; num < max_num; num++){
for(ch = 0; ch < CH_NUM; ch++){
if(num <= (max_num / 2)){
*DA_PTR(dst_data, num, ch) =
(num * MAX_DATA / (max_num / 2));
} else {
*DA_PTR(dst_data, num, ch) =
MAX_DATA - (num * MAX_DATA / (max_num / 2));
}
}
}
break;
case ID_TEETH_SAW_WAVE_DATA: /* のこぎり波データの作成 */
for(num = 0; num < max_num; num++){
for(ch = 0; ch < CH_NUM; ch++){
*DA_PTR(dst_data, num, ch) = (num * MAX_DATA / max_num);
}
}
break;
case ID_RECTANGLE_WAVE_DATA: /* 矩形波データの作成 */
for(num = 0; num < max_num; num++){
for(ch = 0; ch < CH_NUM; ch++){
if(num <= (max_num / 2)){
*DA_PTR(dst_data, num, ch) = MAX_DATA;
} else {
*DA_PTR(dst_data, num, ch) = MIN_DATA;
}
}
}
break;
default: rtl_printf("unknown id!!¥n"); break;
} }
/* 初期化関数(モジュールが組み込まれた時、呼ばれる関数) */
int init_module(void) {
int i, ret;
DASMPLREQ smp_req; /* アナログ出力更新の条件設定を行う構造体 */
rtl_printf("init_module called¥n");
EXPORT_NO_SYMBOLS;
/* Linuxプロセスからの指示を受け取るハンドラとRT-FIFOを生成する */
rtf_destroy(FIFO_COMMAND);
rtf_create(FIFO_COMMAND, sizeof(struct CMD_STRUCT));
rtf_create_handler(FIFO_COMMAND, my_handler);
/* ハンドラとRTLinuxスレッドの間のRT-FIFOを生成する */
rtf_destroy(FIFO_THRU_CMD);
rtf_create(FIFO_THRU_CMD, sizeof(struct CMD_STRUCT));
/* DA製品のオープン */
ret = DaOpenEx(DEVICE_ID, SUB_SYSTEM_ID, RSW_NO, &g_device_no);
if(ret){
rtl_printf("DaOpenEx error [ret=%x]¥n", ret);
210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276
}
/* イベント検出時のコールバック関数の登録 */
ret = DaSetBoardConfig(g_device_no, BUFF_SIZE, NULL, my_smp_callback, 0);
if(ret){
DaClose(g_device_no);
rtl_printf ("DaSetBoardConfig error [ret=%x]¥n", ret);
return -2;
}
/* ドライバが使用する内部バッファ領域を指定する */
ret = DaSetBoardConfigEx(g_device_no, BUFF_SIZE, 0, inner_da_buff);
if(ret){
DaClose(g_device_no);
rtl_printf ("DaSetBoardConfigEx error [ret=%x]¥n", ret);
return -3;
}
/* デフォルトのアナログ出力更新の条件設定を読み取る */
ret = DaGetSamplingConfig(g_device_no, &smp_req);
if(ret){
DaClose(g_device_no);
rtl_printf ("DaGetSamplingConfig error [ret=%d]¥n", ret);
return -4;
}
/* アナログ出力更新の条件設定を変更 */
smp_req.ulChCount = CH_NUM;/* 1件のDA出力時の総チャンネル数の指定 */
smp_req.fSmplFreq = 100.0; /* アナログ出力更新の周波数の設定 */
smp_req.ulSmplRepeat = 0; /* アナログ出力更新の繰り返し回数を指定無しに設定 */
for(i = 0; i < CH_NUM; i++){
smp_req.SmplChReq[i].ulChNo = 1 + i; /* チャンネルを指定(1,2を順に指定しています) */
smp_req.SmplChReq[i].ulRange = DA_0_5V; /* レンジ:0〜+5Vを指定 */
}
/* 書き換えたアナログ出力更新の条件設定を設定する */
ret = DaSetSamplingConfig(g_device_no, &smp_req);
if(ret){
DaClose(g_device_no);
rtl_printf ("DaSetSamplingConfig error [ret=%d]¥n", ret);
return -5;
}
/* 参照用データの作成 */
make_sampling_data(da_sin_wave_data, ID_SIN_WAVE_DATA, SMP_NUM);
make_sampling_data(da_triangle_wave_data, ID_TRIANGLE_WAVE_DATA, SMP_NUM);
make_sampling_data(da_teeth_saw_wave_data, ID_TEETH_SAW_WAVE_DATA, SMP_NUM);
make_sampling_data(da_rectangle_wave_data, ID_RECTANGLE_WAVE_DATA, SMP_NUM);
/* RTLinuxスレッドを作成、起動する */
return pthread_create(&my_task_info, NULL, (void*)my_task, 0);
}
/* 終了関数(モジュールが取り外される時、呼ばれる関数) */
void cleanup_module(void) {
int ret;
rtl_printf("cleanup_module called¥n");
/* DA製品をクローズする */
ret = DaClose(g_device_no);
if(ret){
rtl_printf("DaClose error [ret=%d]¥n", ret);
}
/* RT-FIFOを閉じる */
下記Listは、Linuxプロセスのソースファイルです。ファイル名を「sample3.c」として保存してく ださい。
sample3.c
12 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
/*
sample3.c Linuxプロセスのソースコード
Copyright 2002 Interface Corporation. All rights reserved.
*/
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <rtl_fifo.h>
#include "sample3.h"
/* set output data時の、選択メニュー */
enum DATA_KIND_IDS select_wave_type(void) {
int cmd_data_no;
while(1){
printf( "¥n --- select a wave type ---"
"¥n 1: sine wave"
"¥n 2: triangle wave"
"¥n 3: sawteeth wave"
"¥n 4: rectangle wave"
"¥n ---"
"¥n enter a wave type number:");
cmd_data_no = 0;
if(scanf("%d", &cmd_data_no) <= 0){
getchar(); continue; /* 読み取れない入力を読み捨てる */
}
switch(cmd_data_no){
case 1: return ID_SIN_WAVE_DATA;
case 2: return ID_TRIANGLE_WAVE_DATA;
case 3: return ID_TEETH_SAW_WAVE_DATA;
case 4: return ID_RECTANGLE_WAVE_DATA;
default:
printf("invalid command!!¥n");
continue;
}
} }
/* Linuxプロセスのメインルーチン */
int main(void) {
int cmd_no;
int fd_cmd;
struct CMD_STRUCT cmd;
char rt_fifo_name[80];
/* RTLinuxモジュールに指令を送るFIFOチャネルのオープン */
sprintf(rt_fifo_name, "/dev/rtf%d", FIFO_COMMAND);
if((fd_cmd = open(rt_fifo_name, O_WRONLY)) < 0){
fprintf(stderr, "Failed to open %s¥n", rt_fifo_name);
return -1;
}
/* RTLinuxモジュールに対する指示(99を入力すると、ループを抜けます) */
while(1){
printf( "¥n ===== select a command number ====="
"¥n 1: start output"
"¥n 2: stop output"
"¥n 3: clear output data"
"¥n 4: set output data"
"¥n ---"
71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115
cmd_no = 0;
if(scanf("%d", &cmd_no) <= 0){
getchar(); continue; /* 読み取れない入力を読み捨てる */
}
if(cmd_no == 99) break; /* 選択メニューから抜ける */
switch(cmd_no){ /* 選択メニューの処理 */
case 1: /* start output */
cmd.id = ID_START;
cmd.opt.smp_period_ms = 100; /* 100ms周期の監視設定 */
break;
case 2: /* stop output */
cmd.id = ID_STOP;
break;
case 3: /* clear output data */
cmd.id = ID_CLEAR_DATA;
break;
case 4: /* set output data */
cmd.id = ID_SET_DATA;
cmd.opt.data_kind = select_wave_type(); /* セットする波形データの選択 */
break;
default:
printf("invalid command!!¥n");
continue;
}
/* 入力された命令の実行 */
if(write(fd_cmd, &cmd, sizeof(cmd)) < 0){
fprintf(stderr, "Failed to send the command.¥n");
return -1;
}
}
/* アナログ出力更新のストップ指示(確実に止める) */
cmd.id = ID_STOP;
if(write(fd_cmd, &cmd, sizeof(cmd)) < 0){
fprintf(stderr, "Failed to send the stop command.¥n");
return -1;
}
printf("fd_cmd:%d¥n", close(fd_cmd));
printf("The Linux process is successfully completed.¥n");
return 0;
}
下記Listは、上記ファイルをコンパイルするメイクファイルです。ファイル名を「makefile」とし て保存してください。
makefile
12 3 4 5 6 7 8 9 10 11
include /usr/include/rtlinux/rtl.mk all: module3.o sample3
sample3: sample3.c
$(CC) $(INCLUDE) $(USER_CFLAGS) -O2 -Wall -o sample3 sample3.c temp.o: module3.c
$(CC) $(INCLUDE) $(CFLAGS) -o temp.o -c module3.c module3.o: temp.o
$(LD) -r -o module3.o temp.o /usr/lib/libm.a
次に、Linuxプロセスを実行します。
# ./sample3 ← Linuxプロセスを実行しています
実行すると、選択メニューが表示されます。
選択メニューと処理は、先の『27ページ
4.1 DaOutputDAExを使ったアナログ出力更新』と同
じです。アナログ出力更新のスタート,ストップ,データのクリアとセットが選択できます。===== select a command number =====
1: start output 2: stop output
3: clear output data 4: set output data
--- 99: exit program
=================================
enter a command number:4
4を入力してEnterキーを押します。
すると、次のメニューが表示されます。
ここでは、セットするデータの波形タイプを選択します。
--- select a wave type --- 1: sine wave
2: triangle wave 3: sawteeth wave 4: rectangle wave
--- enter a wave type number:1
ここでは1を入力してEnterキーを押してください。
再び、最初の選択メニューが表示されます。
===== select a command number =====
1: start output 2: stop output
3: clear output data
4: set output data
--- 99: exit program
=================================
enter a command number:1
ここでは1を入力してEnterキーを押してください。
すると、アナログ出力更新が始まります。下図は、アナログ出力更新している様子を、オシロス コープを使って確認している画面例です。
sample3を終了するには、選択メニューが表示されている状態で、 99を入力してEnterキーを押しま
す。
===== select a command number =====
1: start output 2: stop output
3: clear output data 4: set output data
--- 99: exit program
=================================
enter a command number:99
各メニューの項目の動きについて、下表に示します。
項目名 内 容
1: start output
アナログ出力更新を開始します。この項目を選択する前に、必ず1回「4: setoutput data」で出力するデータをセットしてください。
2: stop output
アナログ出力更新を停止します。3: clear output data
アナログ出力更新するデータの内容をクリアします。「4: set output data」でデータを2回以上セットする時、一旦クリアして、
セットしてください。
4: set output data
アナログ出力更新するデータの内容をセットします。セットするデータの種類を、下表にて選択可能です。
「4: set output data」を選択すると、下表のメニューが表示されます。
ここでは、セットするデータの種類が設定可能です。
項目名 内 容