目次
PVME-332は、インターニックス株式会社殿が開発したVMEバス対応のシングルエンド入力時64チャンネル/差動入力時32チャンネルのアナログ入力、スループットレート15マイクロ秒/チャンネルの16ビット分解能A/D変換ボードです。
A/D変換器自体はボード上に1つしか載っていませんので、多チャンネルの読み込みはシーケンシャルオートスキャニングによって実現しています(読みとりチャンネル数は指定します)。A/D変換のトリガーは、外部入力(TTL立ち下がりエッジ)、ソフトウエア設定トリガー及びそれらの後のインターバルタイマーを使った自動トリガーが可能です。
これから紹介するデバイスサポートは、このボードを「とりあえず」EPICSで使うためにtobiyamaが書いたものです。既に多くの偉大な先達がお書きになったデバイスサポートを参考にしています。本ボードは色々な機能を持っていますが、以下のデバイスサポートは全てのモード変更には対応していません。
- 入力はsingle-end、+-10Vモード
- トリガーは外部トリガーあるいはソフトウエアのトリガー。インターバルタイマーは使用しない。
- チャンネルスキャンが終了したら割り込みをかけ、FIFOメモリーを読む。
- アクセスモードはA24 Supervisor-Dataのみ。
- ベースアドレス、割り込みレベル、割り込みベクトルは1枚目についてはスタートアップファイルで指定。2枚目以降については自動設定。
上記と違うモードで使用するためには、デバイスサポートを変更する必要があります。多分簡単です。
PVME-332を操作する上で必要な各レジスタについて紹介します。詳しくはPVME-332のユーザーズ・マニュアルをご覧下さい。ベースアドレス(BASE_IO)は省略しますが、あるアドレス(例えば0x01)が書いてある場合、BASE_IO+0x01だと思って見て下さい。特に断らない限り、unsigned charアクセスをしていると思って下さい。
- trg_cntrl_reg(0x01)
Bit | I/O | 機能
|
---|
D7 | I | トリガー(0:内部、1:外部)
|
D6 | I | インターバルタイマー(0:不使用、1:使用)
|
| |
|
| |
|
| |
|
| |
|
| |
|
D0 | I | トリガー許可(0:不許可、1:許可)
|
- trg_strt_reg(0x03)
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(0x05)
Bit | I/O | function
|
---|
| |
|
| |
|
| |
|
D4 | O | スキャン終了(0:スキャン中あるいは動作停止中、1:スキャン終了)
|
D3 | O | FIFO full flag(0:negate、1:assert)
|
D2 | O | FIFO half flag(0:negate、1:assert)
|
D1 | O | FIFO empty flag(0:negate、1:assert)
|
D0 | O | A/D conversion busy(0:A/D stop、1:busy)
|
ボードの状態を示します。が、普通に使う以上、デバッグ以外にはあまり使い道はありまへん。
- scn_cnt_reg(0x07)
Bit | I/O | function
|
---|
| |
|
| |
|
D5 | I | scan channel
|
D4 | I | scan channel
|
D3 | I | scan channel
|
D2 | I | scan channel
|
D1 | I | scan channel
|
D0 | I | scan channel
|
スキャン動作を行うチャンネルの数を設定します。例えば64チャンネルなら(64-1)=0x3fを書き込みます。チャンネル0より順次シーケンシャル動作を行います。なお、差動入力モードにしているときは0から31チャンネルまでの設定しか受け付けません。
- mode_reg(0x09)
Bit | I/O | function
|
---|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
D0 | I | Analog input mode(0:single-end、1:differencial mode)
|
アナログ入力モードの設定を行います。スキャン動作中に変更してはいけません。
- mode_reg(0x0B)
Bit | I/O | function
|
---|
D7 | I | A/D resolution(0:16-bit、1:12-bit)
|
| |
|
| |
|
| |
|
D3 | I | Analog input range(should be 0)
|
D2 | I | Analog input range(0:bipolar、1:unipolar)
|
D1 | I | Analog input range(should be 0)
|
D0 | I | Analog input range(0:10V、1:5V)
|
アナログ入力レンジ及び変換分解能を設定します。バイポーラモードの時はオフセットバイナリ、ユニポーラモードの時はストレートバイナリです。
- rst_reg(0x0D)
Bit | I/O | function
|
---|
| |
|
| |
|
| |
|
| |
|
D3 | O | FIFO re-transmit(0:negate、1:assert)
|
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)
|
各種リセットを行います。Local I/Oリセットをしたあとは最低でも1秒間のウエイトが必要だそうです。A/Dのcalibration後も必要と思いますが....(PVME-331は1.4msウエイトが必要らしい)
- int_cntrl_reg(0x0f)
Bit | I/O | function
|
---|
| |
|
| |
|
D5 | O | interrupt selection
|
D4 | O | interrupt selection
|
| |
|
D2 | O | interrupt level
|
D1 | O | interrupt level
|
D0 | O | interrupt level
|
D5 | D4 | Interrupt
|
0 | 0 | disable
|
0 | 1 | scan end
|
1 | 0 | FIFO half flag
|
0 | 1 | FIFO full flag
|
Interrupt levelについては、0がdisable、IRQ1が1、IRQ2が2、...となり、IRQ7が7です。
- int_vct_reg(0x11)
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(0x13)
Bit | I/O | function
|
---|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
D0 | I | interrupt status(0:negate、1:assert)
|
割り込み用ステータスレジスタです。このボードはROAKなので、このレジスターを読むことによって割り込みを解除します。
- intvl_tm_reg(0x14):short access
インターバルタイマーの時間設定を行うレジスタです。0x0000で50μ秒、0x0001で100μ秒で0xffffで3.2768秒だそうで。
- data_reg(0x16):short access
FIFO内にあるA/Dデータを読み出すレジスタです。スキャン終了後1回目のアクセスでch0のデータが、2回目でch1のデータが...とスキャンチャンネル分readを繰り返す必要があります。
本デバイスサポートは、EPICS R313用です(R312で動かない理由はないと思いますが)。EPICSそのものに対する説明、入門出家入道については専門家に帰依するなり、コントロールグループのページをご参照されるなり、各自ご努力ください。R312とR313はディレクトリ構造、ライブラリ構造とも大きく変わっていますのでご注意下さい。また、IOCは68k40及びPPC750で動作確認を行っています。
せっかちな人のために、以下にこれから紹介するデバイスサポートのsource fileを示します。
PVME-332は、インターバルタイマーを設定していない場合、トリガーがかかるまで待機をしており、トリガーにより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>
アドレスマップにそって、DPvme332構造体を定義します。
struct DPvme332 {
unsigned char dummy0;
unsigned char trg_cntrl_reg;
unsigned char dummy1;
unsigned char trg_strt_reg;
unsigned char dummy2;
unsigned char sts_reg;
unsigned char dummy3;
unsigned char scn_cnt_reg;
unsigned char dummy4;
unsigned char mode_reg;
unsigned char dummy5;
unsigned char ana_con_reg;
unsigned char dummy6;
unsigned char rst_reg;
unsigned char dummy7;
unsigned char int_cntrl_reg;
unsigned char dummy8;
unsigned char int_vct_reg;
unsigned char dummy9;
unsigned char int_sts_reg;
unsigned short intvl_tm_reg;
unsigned short data_reg;
unsigned short dummys[116];
};
カード毎の占有アドレススペースは0xffとります。
このデバイスサポートで使用する関数のプロトタイプ宣言をしておきます。あまりお行儀の良くない書き方で恐縮ですが、
int devPvme332Config();
static long init_all();
static long init_wf_record();
static long init_mi_record();
static long init_bo_record();
static long read_wf_record();
static long get_wf_int_info();
static long read_mbbiDirect();
static long write_bo_record();
static void pvme332_isr();
static int checkLink();
を宣言しています。devPvme332Configはスタートアップルーチンで呼びますので、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;
}devWfPvme332 ={
6,
NULL,
NULL,
init_wf_record,
get_wf_int_info,
read_wf_record,
NULL
};
です。
struct ioCard {
volatile struct DPvme332 *card; /* address of this card */
FAST_LOCK lock; /* semaphore */
IOSCANPVT ioscanpvt;/* list or records processed upon interrupt */
};
と、通常の構造体のメンバーにIOSCANPVTを付け加える必要があります。また、これを使ったカード用変数は
static struct ioCard cards_332[CONST_NUM_LINKS];
です。
int devPvme332Config(ncards,a24base,irq,intvecbase,Nchannel)
int ncards;
long a24base;
int irq;
int intvecbase;
int Nchannel;
{
pvme332_num_links = ncards;
Base_IO = a24base;
int_level = irq;
INT_VEC_BASE = intvecbase;
pvme_adc = Nchannel;
logMsg("Pvme332 NumLink= %d BaseIO= %x INTVECBASE= %x ADC= %d Ch\n",pvme332_num_links,Base_IO,INT_VEC_BASE,pvme_adc);
init_all(0);
}
何枚PVME-332を使うか、Base_IOアドレス、interruptレベル、interruptベクター、スキャンチャンネルを設定するファイルです。スタートアップファイルの中で、
devPvme332Config(1,0xaf6000,0x05,0xef,64)
iocInit
の様に、iocInitの前に呼びます。
カード存在の確認、各レジスタの初期化を行います。
static long init_all(after)
int after;
{
short cardNum, chanNum;
int i,j;
unsigned char probeVal;
unsigned short p1;
volatile struct DPvme332 *p;
char temp;
if (init_flag != 0 )
return(OK);
init_flag = 1;
if (sysBusToLocalAdrs(VME_AM_STD_SUP_DATA,(char *)Base_IO,(char **)&p) == ERROR)
{
logMsg("VmeP332: cannot find A24 address space\n");
return(ERROR);
}
for (cardNum=0; cardNum< pvme332_num_links; cardNum++)
{
tg_mode = 0x81; /*external trigger enable afterwards*/
probeVal = TG_INT_NO; /* INT trigger -- trigger disable */
if (vxMemProbe((char*) &(p->trg_cntrl_reg), WRITE, 1, &probeVal)!=OK)
{
if (debug_flag >0 )
logMsg("No PVME332 with cardNum= %d\n probe= %x\n",cardNum,p);
cards_332[cardNum].card = NULL;
}
else
{
probeVal=0x06; /* adc calib */
if ((vxMemProbe((char*) &(p->rst_reg), WRITE,1,&probeVal)==OK)&&(debug_flag >5))
logMsg("ADC calibration for card %d\n",cardNum);
nanosleep(4000000);
......中略......
probeVal = INT_VEC_BASE+cardNum;
if ((vxMemProbe((char*) &(p->int_vct_reg), WRITE,1,&probeVal)==OK)&&(debug_flag >5))
logMsg("Int vector %x = %x\n",cardNum,probeVal);
if (debug_flag >0)
logMsg("Found PVME332 with cardNum= %d address= %x\n",cardNum,p);
cards_332[cardNum].card = p; /* Remember address of the board */
scanIoInit(&(cards_332[cardNum].ioscanpvt));
cards_332[cardNum].card->int_cntrl_reg = (0x10) | (unsigned char)(int_level);
FASTLOCKINIT(&(cards_332[cardNum].lock));
FASTUNLOCK(&(cards_332[cardNum].lock)); /* Init the board lock */
}
p++;
}
return(OK);
}
以下の設定をします。
- trg_cntrl_regに0x00を(内部トリガー、トリガー禁止)。これは、この時点でトリガーがかかると処理できないためです。
- scn_cnt_regにスキャンチャンネル数-1
- mode_regに0x00(シングルエンド入力モード)
- 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)pvme332_isr,
(int)pwf) != OK)
logMsg("devPVME332: Interrupt connect failed for card %d\n",pwf->inp.value.vmeio.card);
logMsg("intConnect 0x%X\n",pwf);
sysIntEnable(int_level);
logMsg("pvme332 int set for int 0x%x\n",int_level);
cards_332[cardNum].card->trg_cntrl_reg = 0x81;
return(0);
}
intConnectで割り込みベクターと割り込みハンドラの関連づけを行います。その後sysIntEnableで割り込みを許可し、カードに対してはトリガーを外部トリガー、トリガー許可のモード(0x81)とします。
割り込みがかかったとき、割り込みの処理をするルーチンを記述します。このカードは割り込み処理がROAKなので、まずint_sts_regを読むことで割り込みを解除します。その後、scanIoRequestで実際の読みとり処理を行います。
static void pvme332_isr(pwf)
struct waveformRecord *pwf;
{
unsigned char mode;
short cardN;
cardN = pwf->inp.value.vmeio.card;
/* logMsg("pvme332_isr int called \n");*/
mode = cards_332[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;
unsigned char stat;
cardN = pwf->inp.value.vmeio.card;
*ppvt = cards_332[cardN].ioscanpvt;
/* logMsg("pvme332 intInfo. 0x%X\n",*ppvt); */
cards_332[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の場合、相手はオフセットバイナリですから、読みとった値-32767を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_332[cardN].card->trg_cntrl_reg = 0x80 ; /* disable trigger */
switch(pwf->ftvl)
{
case DBF_USHORT:
/* unsigned short* us_thing = (unsigned short*)pwf->bptr; */
for (i=0; i<pwf->nelm;i++)
{
samples=cards_332[cardN].card->data_reg;
us_thing[i]=(unsigned short)(samples);
}
pwf->nord=i;
cards_332[cardN].card->trg_cntrl_reg = tg_mode | 0x01;
break;
case DBF_SHORT:
/* short* s_thing = (short*)pwf->bptr; */
/* logMsg("short called \n"); */
for (i=0; i<pwf->nelm;i++)
{
samples=cards_332[cardN].card->data_reg;
s_thing[i]=(short)(samples-32767);
/* logMsg("FIFO status = %x\n",cards_332[cardN].card->sts_reg);*/
}
pwf->nord=i;
cards_332[cardN].card->trg_cntrl_reg = tg_mode |0x01;
break;
default:
logMsg("devPVME332: Invalid data type\n");
}
cards_332[cardN].card->rst_reg = 0x04;
cards_332[cardN].card->trg_cntrl_reg = (unsigned char)((tg_mode)|( 0x01));
return(0);
}
このデバイスサポートで使うデータベースをまとめると、以下の様になります。
Database | Name | C | S | function | SCAN | DTYP | その他
|
---|
mbbi direct | status | 0 | 0 | status read | 10 second | PVME332 |
|
bo | trigger | 0 | 0 | trigger | Passive | PVME332 |
|
bo | tg_inout | 0 | 1 | tg_inout | Passive | PVME332 | ZNAM="EXT",ONAM="INT"
|
bo | adc_calib | 0 | 2 | A/D calib | Passive | PVME332 |
|
waveform | adc_in | 0 | 0 | adc in | I/O Intr | PVME332 | FTVL="SHORT",NELM=64
|
subarray | a00〜a63 | 0 | 0 | adc | Passive | Soft channel | MALM=64,NELM=1,INDXを設定
|
なお、subarrayを使うためには、トリガー分配のためfanoutもたくさん必要になります。medmでデータを読んだりしない限り、subarrayはいらないかも知れません。(しかしデータベースで一般的に使うためには、どこかでwaveformをばらさなければならないので、結局は同じことになると思われる)。capfastで書いたデータベースを下に示します。
Makefile.Vxを変更し、それぞれ
SRCS.C += ../devPVME332.c
と
LIBOBJS += devPVME332.o
を付け加え、gmakeでコンパイルします。capfastのschファイルもsch2dbでdbファイルにコンバートします。また、新しく加わったデータベースを"なんとかinclude.dbd"の定義に加えておきます。
device(waveform,VME_IO,devWfPvme332,"PVME332")
device(bo,VME_IO,devBoPvme332,"PVME332")
device(mbbiDirect,VME_IO,devMbbiPvme332,"PVME332")
スタートアップファイルは、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_blm.db","user=tobiyama")
#dbLoadRecords("feedbackApp/Db/dbExample.db","user=tobiyama")
devPvme332Config(1,0xaf6000,0x05,0xef,64)
iocInit
#seq &snctest
iocInitの前にdevPvme332Configでパラメータを渡すのを忘れないようにして下さい。
テストのためmedmで制御をしてみました。

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