目次
PVME-331は、インターニックス株式会社殿が開発したVMEバス対応のシングルエンド入力時16チャンネル/差動入力時8チャンネルのアナログ入力、スループットレート10マイクロ秒/チャンネルの16ビット分解能A/D変換ボードです。
A/D変換器自体はボード上に1つしか載っていませんので、多チャンネルの読み込みはシーケンシャルオートスキャニングによって実現しています(読みとりチャンネル数は指定します)。A/D変換のトリガーは、外部入力(TTL立ち下がりエッジ)、ソフトウエア設定トリガー及びそれらの後のインターバルタイマーを使った自動トリガーが可能です。
これから紹介するデバイスサポートは、このボードを「とりあえず」EPICSで使うためにtobiyamaが書いたものです。既に多くの偉大な先達がお書きになったデバイスサポートを参考にしています。本ボードは色々な機能を持っていますが、以下のデバイスサポートは全てのモード変更には対応していません。
- 入力はsingle-end、+-10Vモード
- トリガーは外部トリガーあるいはソフトウエアのトリガー。インターバルタイマーは使用しない。
- チャンネルスキャンが終了したら割り込みをかけ、FIFOメモリーを読む。
- アクセスモードはA24 Supervisor-Dataのみ。
- ベースアドレス、割り込みレベル、割り込みベクトルは1枚目についてはスタートアップファイルで指定。2枚目以降については自動設定。
上記と違うモードで使用するためには、デバイスサポートを変更する必要があります。多分簡単です。
PVME-331を操作する上で必要な各レジスタについて紹介します。詳しくはPVME-331のユーザーズ・マニュアルをご覧下さい。ベースアドレス(BASE_IO)は省略しますが、あるアドレス(例えば0x01)が書いてある場合、BASE_IO+0x01だと思って見て下さい。特に断らない限り、unsigned charアクセスをしていると思って下さい。
- scanfile0〜scanfile15
A/D変換を行うチャンネル、レンジ、変換フォーマットを指定するレジスタで、16個指定する必要があります。
D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0
|
* | I | I | I | I | I | I | I
|
D3 | D2 | D1 | D0 | チャンネル番号
|
0 | 0 | 0 | 0 | チャンネル0
|
0 | 0 | 0 | 1 | チャンネル1
|
0 | 0 | 1 | 0 | チャンネル2
|
0 | 0 | 1 | 1 | チャンネル3
|
0 | 1 | 0 | 0 | チャンネル4
|
0 | 1 | 0 | 1 | チャンネル5
|
0 | 1 | 1 | 0 | チャンネル6
|
0 | 1 | 1 | 1 | チャンネル7
|
1 | 0 | 0 | 0 | チャンネル8
|
1 | 0 | 0 | 1 | チャンネル9
|
1 | 0 | 1 | 0 | チャンネル10
|
1 | 0 | 1 | 1 | チャンネル11
|
1 | 1 | 0 | 0 | チャンネル12
|
1 | 1 | 0 | 1 | チャンネル13
|
1 | 1 | 1 | 0 | チャンネル14
|
1 | 1 | 1 | 1 | チャンネル15
|
D6 | D5 | 変換コード
|
0 | 0 | オフセットバイナリ
|
0 | 1 | ストレートバイナリ
|
1 | 0 | 2の補数
|
1 | 1 | 使用禁止
|
- trg_cntrl_reg(0x21)
Bit | I/O | 機能
|
---|
D7 | I | トリガー(0:内部、1:外部)
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
D0 | I | トリガー許可(0:不許可、1:許可)
|
- trg_strt_reg(0x23)
Bit | I/O | function
|
---|
D7 | I | internal trigger start
|
D6 | I | internal trigger start
|
D5 | I | internal trigger start
|
D4 | I | internal trigger start
|
D3 | I | internal trigger start
|
D2 | I | internal trigger start
|
D1 | I | internal trigger start
|
D0 | I | internal trigger start
|
トリガーモードが内部トリガーモードで、トリガー許可になっている時、このレジスターに何かを書くとトリガースターとします。
- sts_reg(0x25)
Bit | I/O | function
|
---|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
D0 | O | スキャン終了(0:スキャン中あるいは動作停止中、1:スキャン終了)
|
ボードの状態を示します。が、普通に使う以上、デバッグ以外にはあまり使い道はありまへん。
- scn_cnt_reg(0x27)
Bit | I/O | function
|
---|
| |
|
| |
|
| |
|
| |
|
D3 | I | scan channel
|
D2 | I | scan channel
|
D1 | I | scan channel
|
D0 | I | scan channel
|
スキャン動作を行うチャンネルの数を設定します。例えば16チャンネルなら(16-1)=0x0fを書き込みます。チャンネル0より順次シーケンシャル動作を行います。なお、差動入力モードにしているときは0から15チャンネルまでの設定しか受け付けません。
- mode_reg(0x29)
Bit | I/O | function
|
---|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
D0 | I | Analog input mode(0:single-end、1:differencial mode)
|
アナログ入力モードの設定を行います。スキャン動作中に変更してはいけません。
- rst_reg(0x2B)
Bit | I/O | function
|
---|
| |
|
| |
|
| |
|
| |
|
| |
|
D2 | O | FIFO reset(0:negate、1:assert)
|
D1 | O | ADC calibration(0:negate、1:assert)
|
D0 | O | Local I/O reset(0:negate、1:assert)
|
各種リセットを行います。A/Dのcalibration後は1.4msウエイトが必要です。
- int_cntrl_reg(0x2D)
Bit | I/O | function
|
---|
| |
|
| |
|
| |
|
| |
|
D3 | O | interrupt selection(0:disable, 1:enable)
|
D2 | O | interrupt level
|
D1 | O | interrupt level
|
D0 | O | interrupt level
|
Interrupt levelについては、0がdisable、IRQ1が1、IRQ2が2、...となり、IRQ7が7です。
- int_vct_reg(0x2F)
Bit | I/O | function
|
---|
D7 | O | interrupt vector
|
D6 | O | interrupt vector
|
D5 | O | interrupt vector
|
D4 | O | interrupt vector
|
D3 | O | interrupt vector
|
D2 | O | interrupt vector
|
D1 | O | interrupt vector
|
D0 | O | interrupt vector
|
割り込みベクターを設定します。OSや他のボードが使っていない番号にしないと確実にハングアップします。
- int_sts_reg(0x31)
Bit | I/O | function
|
---|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
D0 | I | interrupt status(0:negate、1:assert)
|
割り込み用ステータスレジスタです。このボードはROAKなので、このレジスターを読むことによって割り込みを解除します。
- intvl_tm_reg(0x34):short access
インターバルタイマーの時間設定を行うレジスタです。0x0000で1μ秒、0x0001で2μ秒で0xffffで65.5m秒だそうで。但し、最小設定時間は10マイクロ秒ですので、最小値は0x000Aとなります。
- data_reg(0x36):short access
FIFO内にあるA/Dデータを読み出すレジスタです。スキャン終了後1回目のアクセスでch0のデータが、2回目でch1のデータが...とスキャンチャンネル分readを繰り返す必要があります。
本デバイスサポートは、EPICS R313用です(R312で動かない理由はないと思いますが)。EPICSそのものに対する説明、入門出家入道については専門家に帰依するなり、コントロールグループのページをご参照されるなり、各自ご努力ください。R312とR313はディレクトリ構造、ライブラリ構造とも大きく変わっていますのでご注意下さい。また、IOCは68k40及びPPC750で動作確認を行っています。
せっかちな人のために、以下にこれから紹介するデバイスサポートのsource fileを示します。
PVME-331は、インターバルタイマーを設定していない場合、トリガーがかかるまで待機をしており、トリガーにより1チャンネルずつA/D変換、FIFOへのストアーを行います。全てのスキャンが終了した時点でデータを読み込むためには、sts_regを常に監視していてスキャン終了フラグが立つのを見るか、スキャン終了時に割り込みをかけ、割り込みサービスとしてデータ読み込みを行うか、ということになります。言うまでもなくマルチジョブ環境でスキャン終了フラグをモニターするのは論外ですから、割り込みをつかうことになります。
EPICS/VxWorksで割り込みを使う場合、以下のルーチンを用意する必要があります。
- データ構造
card構造体に、IOSCANPVTというメンバーを用意します。この部分でinterrupt情報のやりとりをします。
- 割り込みサービスルーチン
割り込みが起きたとき呼ばれるルーチン。intConnectで割り込みベクターに関連づけられます。この中で、ROAKならまず割り込みステータスレジスタを読み(これによりirqが解除される)、本当はこれが正しいかチェックし、正しければscanIoRequestを出して割り込みで動くべきプロセスに制御を渡します。正しくないなら、エラー処理をし、callbackを要求します。本デバイスサポートはインチキをしてこのエラー処理をしていません。
- 割り込みを使うデータベースでの割り込みの初期化
割り込みベクターと割り込みサービスルーチンを関連づけるため、データベースの初期化(init)の所でintConnectを呼びます。但し、この前に既にボードレジスタの設定はすんでいる必要があります。また、sysIntEnableで割り当てたIRQに対する割り込みを許可します。
- データベースに対する割り込みの設定
get_ioint_infoのところで、そのデータベースとioscanpvtの関連づけをします。
このデバイスサポートでは、データをwaveformレコードにいれますので、waveformの定義にこの割り込み部分を付け加える必要があります。
基本的なデバイスサポートの構造については、TD-4Vのデバイスサポートの項目で十分に?説明してありますので、ここでは目新しい物のみを説明します。
インクルードファイルでは、割り込み処理に関連して以下のheaderが新たに必要になります。(実はいらないかもしれない)
#include <iv.h>
#include <dbScan.h>
アドレスマップにそって、DPvme331構造体を定義します。
struct DPvme331 {
unsigned char dummy0;
unsigned char scan_file0;
unsigned char dummy1;
unsigned char scan_file1;
unsigned char dummy2;
unsigned char scan_file2;
unsigned char dummy3;
unsigned char scan_file3;
unsigned char dummy4;
unsigned char scan_file4;
unsigned char dummy5;
unsigned char scan_file5;
unsigned char dummy6;
unsigned char scan_file6;
unsigned char dummy7;
unsigned char scan_file7;
unsigned char dummy8;
unsigned char scan_file8;
unsigned char dummy9;
unsigned char scan_file9;
unsigned char dummy10;
unsigned char scan_file10;
unsigned char dummy11;
unsigned char scan_file11;
unsigned char dummy12;
unsigned char scan_file12;
unsigned char dummy13;
unsigned char scan_file13;
unsigned char dummy14;
unsigned char scan_file14;
unsigned char dummy15;
unsigned char scan_file15;
unsigned char dummy16;
unsigned char trg_cntrl_reg;
unsigned char dummy17;
unsigned char trg_strt_reg;
unsigned char dummy18;
unsigned char sts_reg;
unsigned char dummy19;
unsigned char scn_cnt_reg;
unsigned char dummy20;
unsigned char mode_reg;
unsigned char dummy21;
unsigned char rst_reg;
unsigned char dummy22;
unsigned char int_cntrl_reg;
unsigned char dummy23;
unsigned char int_vct_reg;
unsigned char dummy24;
unsigned char int_sts_reg;
unsigned short dummy25;
unsigned short intvl_tm_reg;
unsigned short data_reg;
unsigned short dummys[100];
};
カード毎の占有アドレススペースは0xffとります。
このデバイスサポートで使用する関数のプロトタイプ宣言をしておきます。
int devPvme331Config();
static long init_all();
static long init_wf_record();
static long init_bi_record();
static long init_bo_record();
static long read_wf_record();
static long get_wf_int_info();
static long read_bi_record();
static long write_bo_record();
static void pvme331_isr();
static int checkLink();
devPvme331Configはスタートアップルーチンで呼びますので、staticにしてはいけません。
このデバイスサポートでは、waveform、bo、mbbiDirectのデータベースを使います。割り込みに関連しているのはwaveform recordです。
waveformレコードの定義は次の通りです。
struct {
long number;
DEVSUPFUN report;
DEVSUPFUN init;
DEVSUPFUN init_record;
DEVSUPFUN get_ioint_info;
DEVSUPFUN read_write;
DEVSUPFUN conv;
}devWfPvme331 ={
6,
NULL,
NULL,
init_wf_record,
get_wf_int_info,
read_wf_record,
NULL
};
です。
struct ioCard {
volatile struct DPvme331 *card; /* address of this card */
FAST_LOCK lock; /* semaphore */
IOSCANPVT ioscanpvt;/* list or records processed upon interrupt */
};
と、通常の構造体のメンバーにIOSCANPVTを付け加える必要があります。また、これを使ったカード用変数は
static struct ioCard cards[CONST_NUM_LINKS];
です。
int devPvme331Config(ncards,a24base,intvecbase,Nchannel)
int ncards;
long a24base;
int intvecbase;
int Nchannel;
{
pvme331_num_links = ncards;
Base_IO = a24base;
INT_VEC_BASE = intvecbase;
pvme_adc = Nchannel;
logMsg("Pvme331 NumLink= %d BaseIO= %x INTVECBASE= %x ADC= %d Ch\n",pvme331_num_links,Base_IO,INT_VEC_BASE,pvme_adc);
init_all(0);
}
何枚PVME-331を使うか、Base_IOアドレス、interruptベクター、スキャンチャンネルを設定するファイルです。スタートアップファイルの中で、
devPvme331Config(1,0x800000,0xe1,16)
iocInit
の様に、iocInitの前に呼びます。
カード存在の確認、各レジスタの初期化を行います。
static long init_all(after)
int after;
{
int cardNum, chanNum;
int i,j;
unsigned char probeVal;
unsigned short p1;
volatile struct DPvme331 *p;
if (init_flag != 0 )
return(OK);
init_flag = 1;
if (sysBusToLocalAdrs(VME_AM_STD_SUP_DATA,(char *)Base_IO,(char **)&p) == ERROR)
{
logMsg("VmeP331: cannot find A24 address space\n");
return(ERROR);
}
for (cardNum=0; cardNum< pvme331_num_links; cardNum++)
{
if (vxMemProbe((char*) &(p->rst_reg), READ, 1, &probeVal)!=OK)
{
if (debug_flag >0 )
logMsg("No PVME331 with cardNum= %d\n probe= %x\n",cardNum,p);
cards[cardNum].card = NULL;
}
else
{
probeVal=0x0f;
if ((vxMemProbe((char*) &(p->rst_reg), WRITE,1,&probeVal)==OK)&&(debug_flag >5))
logMsg("System Reset for card %d\n",cardNum);
nanosleep(4000000);
probeVal=BIP10 | 0x00;
if ((vxMemProbe((char*) &(p->scan_file0), WRITE,1,&probeVal)==OK)&&(debug_flag >5))
logMsg("Scan file 0 set %d\n",cardNum);
probeVal=BIP10 | 0x01;
if ((vxMemProbe((char*) &(p->scan_file1), WRITE,1,&probeVal)==OK)&&(debug_flag >5))
logMsg("Scan file 1 set %d\n",cardNum);
probeVal=BIP10 | 0x02;
・・・中略・・・
cards[cardNum].card = p; /* Remember address of the board */
scanIoInit(&cards[cardNum].ioscanpvt);
logMsg("pvme331 ioInit. 0x%X\n",cards[cardNum].ioscanpvt);
FASTLOCKINIT(&(cards[cardNum].lock));
FASTUNLOCK(&(cards[cardNum].lock)); /* Init the board lock */
}
p++;
}
return(OK);
}
以下の設定をします。
- trg_cntrl_regに0x00を(内部トリガー、トリガー禁止)。これは、この時点でトリガーがかかると処理できないためです。
- scn_cnt_regにスキャンチャンネル数-1
- ana_con_regに0x00(16ビット、+/-10V)
- ADCのキャリブレーション(rst_regに0x02)、約4msウエイト
- int_cntrl_regに割り込みレベル及び割り込みセレクトを設定
- int_vct_regに割り込みベクターを設定
- intvl_tm_regに0xffffを設定(インターバルタイマーは使わないので、適当で良いはずですが)
- カード構造体のioscanpvtをscanIoinitで初期化
その後、fastlock等の初期化をします。
iocInitの前に割り込みがかかってしまうとosが死んでしまいますので、iocinitのあと、各データベースを初期化しているところでOSの割り込み関係の設定をします。
static long init_wf_record(pwf)
struct waveformRecord *pwf;
{
short cardNum;
cardNum = pwf->inp.value.vmeio.card;
if (intConnect(INUM_TO_IVEC(INT_VEC_BASE + cardNum),
(VOIDFUNCPTR)pvme331_isr,
(int)pwf) != OK)
logMsg("devPVME331: Interrupt connect failed for card %d\n",pwf->inp.value.vmeio.card);
logMsg("intConnect 0x%X\n",pwf);
sysIntEnable(int_level);
logMsg("pvme331 int set for int 0x%x\n",int_level);
/* cards[cardNum].card->trg_cntrl_reg = 0x81;*/
tg_mode = 0x80;
tg_enable = 0x01;
return(0);
}
intConnectで割り込みベクターと割り込みハンドラの関連づけを行います。その後sysIntEnableで割り込みを許可します。
割り込みがかかったとき、割り込みの処理をするルーチンを記述します。このカードは割り込み処理がROAKなので、まずint_sts_regを読むことで割り込みを解除します。その後、scanIoRequestで実際の読みとり処理を行います。
static void pvme331_isr(pwf)
struct waveformRecord *pwf;
{
unsigned char mode;
short cardN;
cardN = pwf->inp.value.vmeio.card;
/* logMsg("pvme331_isr int called \n");*/
mode = cards[cardN].card->int_sts_reg;
scanIoRequest(cards_332[cardN].ioscanpvt);
/* logMsg("pvme332 int called \n");*/
return;
}
なお、この割り込みハンドラ中にprintfを書くとうまく動かないようです。メッセージが必要な場合はlogMsgを使います。但し、これはbufferedですので、例えばここでこけてもそのメッセージが表示されるかは不明です。
scanIoRequestが動いたときに、waveformを読みに来る様に、ioscanpvtを設定します。
static long get_wf_int_info(cmd,pwf,ppvt)
int cmd;
struct waveformRecord *pwf;
IOSCANPVT *ppvt;
{
short cardN;
cardN = pwf->inp.value.vmeio.card;
*ppvt = cards[cardN].ioscanpvt;
logMsg("pvme331 intInfo. 0x%X\n",*ppvt);
cards[cardN].card->trg_cntrl_reg = (unsigned char)((tg_mode)|(0x01));
if (debug_flag >5) logMsg("WF_INT CALLED\n");
return(0);
}
waveformにFIFOバッファーの内容を読みとっていきます。まず、トリガーを禁止し、ftvlのタイプを確認します。DBF_USHORTの場合、読みとるべき相手はストレートバイナリですから、そのままの値を読みとっていきます。DBF_SHORTの場合、設定により相手は2の補数ですから、読みとった値をそのままwaveformに入れていきます。いずれもnelmまで入れます。その後、FIFOバッファをクリアし、トリガーを許可します。
static long read_wf_record(pwf)
struct waveformRecord *pwf;
{
short cardN;
int i;
unsigned short samples;
unsigned short* us_thing = (unsigned short*)pwf->bptr;
short* s_thing = (short*)pwf->bptr;
if (debug_flag >5)
logMsg("read_wf_record called...\n");
cardN = pwf->inp.value.vmeio.card;
if (debug_flag >5)
logMsg("read_wf_record called with card number of %d\n",cardN);
if (checkLink(cardN) == ERROR)
return(ERROR);
cards[cardN].card->trg_cntrl_reg = 0x80;
switch(pwf->ftvl)
{
case DBF_USHORT:
/* unsigned short* us_thing = (unsigned short*)pwf->bptr; */
for (i=0; i<pwf->nelm;i++)
{
samples=cards[cardN].card->data_reg;
us_thing[i]=(unsigned short)(samples);
}
pwf->nord=i;
cards[cardN].card->trg_cntrl_reg = (tg_mode | tg_enable);
break;
case DBF_SHORT:
/* short* s_thing = (short*)pwf->bptr; */
for (i=0; i<pwf->nelm;i++)
{
samples=cards[cardN].card->data_reg;
s_thing[i]=(short)(samples);
}
pwf->nord=i;
cards[cardN].card->trg_cntrl_reg = (tg_mode|tg_enable);
break;
default:
logMsg("devPVME331: Invalid data type\n");
}
cards[cardN].card->rst_reg=0x04;
cards[cardN].card->trg_cntrl_reg = (tg_mode|tg_enable);
return(0);
}
このデバイスサポートで使うデータベースをまとめると、以下の様になります。
Database | Name | C | S | function | SCAN | DTYP | その他
|
---|
bi | status | 0 | 0 | status read | 10 second | PVME331 |
|
bo | trigger | 0 | 0 | trigger | Passive | PVME331 |
|
bo | tg_inout | 0 | 1 | tg_inout | Passive | PVME331 | ZNAM="EXT",ONAM="INT"
|
bo | adc_calib | 0 | 2 | A/D calib | Passive | PVME331 |
|
waveform | adc_in | 0 | 0 | adc in | I/O Intr | PVME331 | FTVL="SHORT",NELM=16
|
subarray | a00〜a15 | 0 | 0 | adc | Passive | Soft channel | MALM=16,NELM=1,INDXを設定
|
なお、subarrayを使うためには、トリガー分配のためfanoutもたくさん必要になります。medmでデータを読んだりしない限り、subarrayはいらないかも知れません。(しかしデータベースで一般的に使うためには、どこかでwaveformをばらさなければならないので、結局は同じことになると思われる)。capfastで書いたデータベースを下に示します。
Makefile.Vxを変更し、それぞれ
SRCS.C += ../devPVME331.c
と
LIBOBJS += devPVME331.o
を付け加え、gmakeでコンパイルします。capfastのschファイルもsch2dbでdbファイルにコンバートします。また、新しく加わったデータベースを"なんとかinclude.dbd"の定義に加えておきます。
device(waveform,VME_IO,devWfPvme331,"PVME331")
device(bo,VME_IO,devBoPvme331,"PVME331")
device(bi,VME_IO,devbiPvme331,"PVME331")
スタートアップファイルは、iocBootの下のiocなんとかの所に作ります。
# Example vxWorks startup file
#Following must be added for many board support packages
cd "/users/tobiyama/epics_r313/iocBoot/iocfeedback"
#ld < bin/enableA24_frc64.o
ld < bin/kekRouteSet_frc40.o
#ld < bin/kekRouteSet_frc64.o
ld < bin/iocCore
ld < bin/seq
ld < bin/feedbackLib
dbLoadDatabase("dbd/feedbackApp.dbd")
dbLoadDatabase("feedbackApp/Db/fb_ar_adc.db","user=tobiyama")
#dbLoadRecords("feedbackApp/Db/dbExample.db","user=tobiyama")
devPvme332Config(1,0x800000,0xe1,16)
iocInit
#seq &snctest
iocInitの前にdevPvme331Configでパラメータを渡すのを忘れないようにして下さい。
テストのためmedmで制御をしてみました。

入力はch0にのみfunction generatorからのサイン波を入れ、他のチャンネルはオープンになっています(こういう使い方はとてもよくない)。triggerはexternal triggerで50Hzでかけています。表示はch0のみを見ています。
汎用16チャンネル16ビットADC-PVME331のデバイスサポート及びEPICSデータベース等の説明を行いました。このデバイスサポートの開発においては、コントロールグループの皆様に相談に乗っていただき、色々なご協力を頂きました。感謝いたします。また、製造元のインターニックス殿及び販売代理店のロジックハウス殿にも色々ご協力を賜りました。感謝いたします。
Makoto Tobiyama
15/Sep/98
Return to FB Home Page...