MEX関数応用その3....................................................................................................... 63

ハードウェアアクセスゲートウェイ............................................................................. 63

PC内蔵のスピーカ制御................................................................................................ 63

Visual C++による方法............................................................................................ 63

MATLAB MEX関数による方法................................................................................ 64

直接アクセス関数......................................................................................................... 64

inp関数..................................................................................................................... 64

outp関数................................................................................................................... 63

コラム ビットについて.................................................................................................. 63

10進数2進数の変換.................................................................................................... 63

2進数10進数の変換.................................................................................................... 63

外部ポート入出力関数の応用........................................................................................ 63

PIOインターフェース.................................................................................................. 63

8255の操作.............................................................................................................. 64

8255の動作モード.................................................................................................... 64

8255ACWRレジスタ........................................................................................... 64

実際の手順................................................................................................................ 65

AD/DAインターフェース................................................................................................ 65

Tips Figureの保存....................................................................................................... 64

図からのデータ抽出...................................................................................................... 64

演習課題........................................................................................................................... 65

 

 

 

 

 

 

 

 

 

 

 

 

 

 


 

MEX関数応用その3

 これまでの解説では、ベクトル化、MEX関数など特に計算速度にこだわった観点からMATLABを解説してきた。

 MATLABは、インタプリタ型の高級言語であるため、BASIC言語と同じ様に1つ1つ確認しながら、デバッカを使用しているような感覚で実行することが可能である。それらの機能に加え、MATLABには高度な信号処理が容易にできる強力なToolBoxが用意されている。これに入出力系インターフェース(一応現在でもDDE,ActiveX,Javaなどがあるが)が充実すると、入出力処理と信号処理が融合された理想的な研究開発の環境が実現できる。ここでは、MATLABを利用したインタラクティブな開発、開発環境を構築する上で必要な入出力に関連したプログラムをMEX関数により実現していく方法について解説する。

ハードウェアアクセスゲートウェイ

 PC上外部ハードウェアアクセスでもっとも基本となるものがI/Oポートである。外部インターフェースを制御する場合、I/Oポート周りのプログラミングは欠かすことができない。最も、最近のOSでは、直接ハードをアクセスすることはできなくなってしまい、標準的な入出力でさえWIN32APIなど抽象化されたインターフェースなどを通じてアクセスする傾向にある。ここでは、ハードウェアアクセスゲートウェイとして

(1)直接I/Oをアクセスする方法

(2)WIN32APIを利用する方法

2種類について解説する。(1)による方法は、WIN95/98など、直接I/Oアクセスが可能なOSのみで使用することができる。(つまりNTでは不可)この方法は、従来DOSベースのハードウェア周辺のプログラミング技術がそのまま生かせる。また、スクリプトのみでI/O制御が可能なため処理コードが見やすくデバックがし易いなどの特徴を持つ。この応用例として、PCスピーカの制御、ディジタル入出力ボードを利用した外部インターフェースの制御プログラム例、それにPCMCIA AD/DAカードのプログラミング例を扱う。(2)の方法は、WIN32 APIで用意している外部入出力インターフェースをMATLABで使用できるようにするテクニックである。そのため(2)の方法はWIN95/98/NTともに使用可能である。ここでは、具体的な例として、非同期通信のためのシェアードメモリ使用例、(1)では処理不可能な、マルチスレッドを利用したRS232Dインターフェース例、TCP/UDPなどethernet通信インターフェース例、それにVideoForWindowsを利用したFrameGrabberインターフェースの実装例について扱っていこうと考えている。

 

PC内蔵のスピーカ制御

Visual C++による方法

 Visual C++を使い、DOS/Vマシン付属のスピーカのON/OFF制御をしてみよう。スピーカを鳴らすには、8253,8254というプログラマブル・インターバルタイマ・コントローラICを操作する必要がある。最近のコンピュータでは、カスタムICで構成されているため互換の機能が1つのチップの中に含まれている。スピーカは、I/Oポートの0x61の下位2ビットをオン・オフすることで鳴らしたり止めたりすることができる。VisualC++でスピーカをONにするためには、

indata = _inp(0x61) | 0x03;

_outp(0x61,indata);

また、OFFにするためには、

indata = _inp(0x61) & 0xfc;

_outp(0x61,indata);

とする。この時、他のビットの部分を変化しないようにするためONの時には、0x03OFFの時は0xfcでマスクをかけている点に注意してもらいたい。マスクとは、それぞれの数値を2進数に直してみればわかる。

dec2bin(hex2dec('03'))

ans =

11

dec2bin(hex2dec('fc'))

ans =

11111100

となるのでそれぞれor,andをとり他のビットに影響しないようにしている。ここで、03は、11となっているが、これは、00000011のことであり、下位2桁が1であることを意味する。

 

C言語でDOSプロンプト上で実行できる形式にプログラミングすると以下のようになる。


/* beep program

   Programmed by Gerox 1999.Sep.10

   for Windows95/98

*/

#include <stdio.h>

#include <string.h>

#include <conio.h>

#include <stdlib.h>

void main(int argc,char **argv) {

int indata;

printf("Usage : beep 1 \n");

printf("        beep 0 \n");

if((argc == 2) && (atoi(argv[1])== 1)) {

   indata = _inp(0x61) | 0x03;

   _outp(0x61,indata);

} else {

   indata = _inp(0x61) & 0xfc;

   _outp(0x61,indata);

}

return;

}


c:\>beep 1

とするとスピーカから音が鳴り

c:\>beep 0

と音が止まる。

MATLAB MEX関数による方法

このCプログラムをMATLAB MEX関数で記述してみよう。


/* mexbeep program

   Programmed by Gerox 1999.Sep.10

   for Windows95/98

*/

#include <stdio.h>

#include <string.h>

#include <conio.h>

#include <stdio.h>

#include "mex.h"

void mexFunction(int nlhs,mxArray *plhs[],int nrhs,const mxArray * prhs[]) {

 

int indata;

if((nrhs == 1) && (mxGetScalar(prhs[0])== 1)) {

   indata = _inp(0x61) | 0x03;

   _outp(0x61,indata);

} else {

   indata = _inp(0x61) & 0xfc;

   _outp(0x61,indata);

}

return;

}


となる。MEX関数でも同様に_inp,_outp関数が使える。

mex mexbeep.c

としてコンパイル。スピーカをONするには、

mexbeep(1)

OFFするには、

mexbeep(0)

とすればよい。一般的には、いろいろなハードウェアのアクセスに対しMEXでプログラミングすれば良いのであるが、外部アクセスに必要な関数は、_inp,_outpのみである。もし_inp,_outpに対応するMEX関数を記述できるとScriptのみでいろいろなハードウェアにアクセスできる。では、MATLAB_inp,_outpに対応するMEX関数を書いてみよう。

直接アクセス関数

inp関数

MATLABMEX関数を使用する場合には、引数の型や数を指定できるため、様々なパターンについてハンドリングする必要がある。ここでは、使用用途を考え、入力引数に、一般的なdoubleの変数それに2進数の扱いに便利な16進数を扱うために文字列の入力のに対し動作するように設計した。簡略化のためここでの文字列が16進数か10進数かの区別は、2つめの文字列がxであるかどうかで判別している。また、デバックのために[addr,data]=inp('300');のように出力を2つ指定した場合には、入力された文字列が実際にいくら解釈され処理されているかわかるような機能を付けてある。


----------------------------------inp.cpp---------------------------------------

/* inp function for MATLAB 5.x By Gerox

   Programmed by Gerox 1999.July 6

   This is only works under Windows95/98

*/

#include <stdio.h>

#include <string.h>

#include <conio.h>

#include "mex.h"

void errorr(int line){

mexPrintf("Usage:\n");

mexPrintf("  data = inp('0x300'); string hex mode\n");

mexPrintf("  data = inp('300');  string dec mode \n");

mexPrintf("  data = inpp(300);  scalar mode \n");

mexPrintf("  [addr,data]=inp('300');  output check mode \n");

mexPrintf("  Gerox(c) 1999 July 6 All rights reserved. \n");

mexPrintf("   This is only works under Windows95/98\n");

mexPrintf("  [%s]line %d\n",__FILE__,line);

mexErrMsgTxt("");

}

void mexFunction(int nlhs,mxArray *plhs[],int nrhs,const mxArray * prhs[]) {

int i;

char buf[256];

int buflen = 255;

double ioadr,data;

if(nrhs != 1) errorr(__LINE__);

if((nlhs == 0)|| (nlhs == 1)|| (nlhs == 2));

else errorr(__LINE__);

if (mxIsNumeric(prhs[0]) && mxIsDouble(prhs[0])) ioadr = mxGetScalar(prhs[0]);

else if (mxIsChar(prhs[0])) {

   mxGetString(prhs[0],buf,buflen);

   if(buf[1] == 'x') sscanf(buf,"%x",&i);

   else sscanf(buf,"%d",&i);

   ioadr = (double)i;

} else errorr(__LINE__);

if(nlhs == 2) {

   plhs[0] = mxCreateDoubleMatrix(1,1,mxREAL);

   double* pdata = mxGetPr(plhs[0]);

   pdata[0] = ioadr;

   plhs[1] = mxCreateDoubleMatrix(1,1,mxREAL);

   pdata = mxGetPr(plhs[0]);

   pdata[0] = (double)_inp((int)ioadr);

} else if(nlhs < 2) {

   plhs[0] = mxCreateDoubleMatrix(1,1,mxREAL);

   double* pdata = mxGetPr(plhs[0]);

   pdata[0] = (double)_inp((int)ioadr);

}

return;

}


outp関数

 outp関数の実装もinp関数と同様の考え方である。この場合入力が2変数あり、基本的には、出力はない。しかしinp関数等との一貫性を考慮し、右辺に変数を指定した場合DEBUG用に出力がでるように定義した。


----------------------------------outp.cpp---------------------------------------

/* outp function for MATLAB 5.x By Gerox

   Programmed by Gerox 1999.July 6

   This is only works under Windows95/98

*/

#include <stdio.h>

#include <string.h>

#include <windows.h>

#include <conio.h>

#include <io.h>

#include "mex.h"

void errorr(int line){

  mexPrintf("Usage:\n");

  mexPrintf("  outp('0x300','0x20'); string hex mode \n");

  mexPrintf("  outp('300','20'); string dec mode \n");

  mexPrintf("  outp(300,20);  scalar mode \n");

  mexPrintf("  [addr,data]=outp('300','20');  output check \n");

  mexPrintf("  Gerox(c) 1999 July 6 All rights reserved. \n");

  mexPrintf("   This is only works under Windows95/98\n");

  mexPrintf("   [%s]line %d\n",__FILE__,line);

  mexErrMsgTxt("");

}

void mexFunction(int nlhs,mxArray *plhs[],int nrhs,const mxArray * prhs[]) {

  int i;

  char buf[256];

  int buflen = 255;

  double ioadr,data;

  if (nrhs != 2) errorr(__LINE__);

  if ((nlhs == 2) || (nlhs == 0));

  else errorr(__LINE__);

  if (mxIsNumeric(prhs[0]) && mxIsDouble(prhs[0])) ioadr = mxGetScalar(prhs[0]);

  else if (mxIsChar(prhs[0])) {

    mxGetString(prhs[0],buf,buflen);

    if(buf[1] == 'x') sscanf(buf,"%x",&i);

    else sscanf(buf,"%d",&i);

    ioadr = (double)i;

  } else errorr(__LINE__);

  if (mxIsNumeric(prhs[1]) && mxIsDouble(prhs[1])) data = mxGetScalar(prhs[1]);

  else if (mxIsChar(prhs[1])) {

    mxGetString(prhs[1],buf,buflen);

    if(buf[1] == 'x') sscanf(buf,"%x",&i);

    else sscanf(buf,"%d",&i);

    data = (double)i;

  } else errorr(__LINE__);

  _outp((int)ioadr,(int)data);

  if(nlhs == 2) {

    plhs[0] = mxCreateDoubleMatrix(1,1,mxREAL);

    double* pdata = mxGetPr(plhs[0]);

    pdata[0] = ioadr;

    plhs[1] = mxCreateDoubleMatrix(1,1,mxREAL);

    double* pdata2 = mxGetPr(plhs[1]);

    pdata2[0] = data;

  }

  return;

}

----------------------------------------------------------------------------------------


コラム ビットについて

 コンピュータの内部では、情報を0または1の情報で管理している。これがコンピュータの処理の単位で bit (ビット)という。「ビットが立つ」という言葉は、よく使われるがこれは信号が01になることを表す。01のみを組み合わせて表わした数字を2進数と言う。2進数でデータを扱うと桁が大きくなりすぎてわかりづらいため、それら8bitをひとまとめにした単位byte(バイト)が良く使われる。この8bit4bitに区切ると16進数として扱える。これは16進数は、10進数に近く直感的にわかりやすいためである。また16進数は、2進数との対応も桁ごとに割り当てられ明確なことも使われる一つの理由である。特に外部ハードとのアクセスでは、ビット単位でハードウェアが割り当てられている場合がよくあるのでビットと数との対応は、常に考える必要がある。C言語は、元来ハードをより扱いやすくするために開発された処理言語であり、10進数から2進数16進数への変換は、演算子などが用意されているため比較的扱いやすい。MATLABは行列をベースにした言語でありそれらは余り得意とは言えない。そこでMATLAB2進数を上手に扱うにはどのようにするのであろうか?

10進数2進数の変換

10進数を2進数にするときは、2で割っていくことを繰り返す。その時、割り切れたら0割り切れなかったらビットを立て、1を引き、2で割っていく。これを繰り返すことで答えが得られる。MATLABでは、割り算のあまりを求める関数としてrem関数がある。floor関数は、計算結果の小数部分を切り捨てる関数である。例として10進数で122進数で表現する。8ビットの2進数を想定し計算すると

data=12;

fliplr(rem(floor(data*pow2(0:-1:-7)),2))

ans =

     0     0     0     0     1     1     0     0

となる。一番右の桁が一番小さく桁になるのでfliplr関数で、左右のベクトル順序を入れ替え表示している。まず10進の数字で与えられたdataを元にまでのベクトルを作りそれでdataを割る。その割った数を2でもう一度割りその中で割り切れたら0を割り切れなかった場合には、1を表示し、ならべる。スクリプトで書くと以下のようになる。

for data=0:255

 bitdata= rem(floor(data*pow2(0:-1:-7)),2);

 fprintf('%d(%d%d%d%d%d%d%d%d)\n',data,fliplr(bitdata));

end


0(00000000)

1(00000001)

2(00000010)

3(00000011)

4(00000100)

5(00000101)

6(00000110)

7(00000111)

8(00001000)

9(00001001)

10(00001010)

11(00001011)


2進数10進数の変換

2進数から10進数への変換は、逆変換する。つまりそれぞれの2進数での桁を421倍すればよい。上で計算した2進数ベクトルを10進数に変換してみよう。bitdata[1]が下位ビットbitdata[8]が上位ビットに対応するので

sum(bitdata.*[1 2 4 8 16 32 64 128])

となる。簡略化すると

sum(bitdata.*pow2(0:7))

とも記述できる。

MATLAB自体でも10進数2進数に変換する関数dec2bin、その逆のbin2decなどが用意されているのでそちらを用いてもよい。若干使用法が異なるのでhelpでチェックすること。

----------------------------------------------------------------------------------------

外部ポート入出力関数の応用

このプログラムをそれぞれ、inp.cpp,outp.cppで保存し

mex inp.cpp

mex outp.cpp

とすればMEX関数としてMATLAB上で使用可能になる。では、これらの関数を用いて応用先ほどのスピーカをON/OFFするスクリプトを考えてみよう。C言語の例を元に記述すると

スピーカをONする場合

indata = bitor(inp('0x61'),hex2dec('03'));

outp('0x61',indata);

スピーカをOFFする場合

indata = bitand(inp('0x61'),hex2dec('fc'));

outp('0x61',indata);

と記述できる。この場合、C言語のor演算子の替わりにbitor関数を使っている。この場合、通常の&、|は使用できないので注意すること。また、16進数を10進数に変換する関数としてhex2dec関数も使用している。またビット演算を考えて組み直すと以下のように記述することもできる。(分かりづらいかもしれないが、こちらの方が若干効率がよい。)

data=inp('0x61');

bitdata= rem(floor(data*pow2(0:-1:-7)),2);%2進数に変換する。

bitdata(1:2)=1;%下位ビット2ビットのみをオンにする。

data= sum(bitdata.*pow2(0:7));%もう一度10進数に変換する。

outp('0x61',data);

この場合、10進数を2進数に分解しているためデータにマスクをかける必要はない。

スピーカをOFFする場合

data=inp('0x61');

bitdata= rem(floor(data*pow2(0:-1:-7)),2);%2進数に変換する。

bitdata(1:2)=0;%下位ビット2ビットのみをオフにする。

data= sum(bitdata.*pow2(0:7));%もう一度10進数に変換する。

outp('0x61',data);

とすればよい。

まとめて書くと

scriptbeep.m

% scriptbeep program

%   Programmed by Gerox 1999.Sep.10

%   for Windows95/98

%

function scriptbeep(beep)

data=inp('0x61');

bitdata= rem(floor(data*pow2(0:-1:-7)),2);%2進数に変換する。

if(beep == 1) bitdata(1:2)=1;%下位ビット2ビットのみをオンにする。

else bitdata(1:2)=0;%下位ビット2ビットのみをオフにする。

end

data= sum(bitdata.*pow2(0:7));%もう一度10進数に変換する。

outp('0x61',data);

となる。使用法は、従来のものと同様であり

scriptbeep(1)

ON

scriptbeep(0)

OFFになる。

PIOインターフェース

 PIOとは、Parallel Input /Output Interfaceのことで、ディジタル信号つまり0/1のビット操作を行うことができる外部インターフェースである。伝統的に8ビット単位で同時にビット操作ができるためParallel(並列)と言う言葉が使われている。

ここでは、具体的なinp/outpMEX関数関数を使ったIO制御の例として、コンテック社のPIO-120D(PC)を用いる。このIOカードは、ISAスロットのカードで最大120ビットのディジタル信号が扱えるボードである。プログラマブルにディジタルの入出力ビットを変更するLSIとして一般的な8255互換ICを使用している。

8255の操作

 8255には、3つのポートA,B,Cポートがありそれぞれ8ビット単位で入出力設定、操作できる。特にCポートに関しては、4ビットごとに入出力の設定が変更可能である。ここで説明するコンテック社のPIO-120D(PC)では、この82555個搭載されている。つまり、1つの8255でポートA,B,C3×8ビット=24ビットそれが5つあるので最大24×5120ビットの操作が可能である。

8255の動作モード

 8255にはモード0からモード2まで計3種類の動作モードがある。モード12は、主に外部インターフェースとの通信などのために設計されたものであり、ここでは扱わない。詳しい説明は、マニュアルなどを参照してもらいたい。ここで用いるのはモード0と言われる単なるビットの入出力モードである。このモードでは、ポートA、ポートBポートC(上位)、ポートC(下位)をそれぞれ独立に入力ポート、出力ポートとして設定することが可能である。これらを設定・操作するには、8255内部のレジスタを変更・命令する。8255内部には、4つのレジスタA,B,C,CWR(Control Word Register)がある。レジスタA,B,Cは、それぞれ対応するポートに値を保持し、CWRで動作モードを決定する。これらA,B,C,CWRの内部状態、それらに命令をするため、それぞれにI/Oポートが対応するようになっている。ここでMEX関数で作ったinp関数、outp関数が役に立つわけである。PIO-120D(PC)では、5つの8255Aが内蔵されていることから全部で5×420ポートがマッピングされる。これらポートをinp関数により参照したり、outp関数により命令、実行し、実際にハードウェアに命令実行することになる。

8255ACWRレジスタ

CWRレジスタでは、それぞれのビットに対し意味がありそれらビットを操作することで入出力モードなどが変更できる。

D7                                      D0

x7

x6

x5

x4

x3

x2

x1

x0

 

x7: CWRレジスタの動作モード選択

  1の時:制御信号を送る

  0の時:ポートCをビット制御する

x6,x5: ポートAグループのモード指定 00:モード0

                  01:モード1

                  1x:モード2  注:xはどちらでもよい。

x4:ポートA(PA0-7)の入出力設定 0:出力、1:入力

x3:ポートC(PC4-7)の上位入出力設定0:出力、1:入力

x2:ポートBのグループモード設定 0: モード0, 1:モード1

x1:ポートB(PB0-7)の入出力設定0:出力、1:入力

x0:ポートC(PC0-3)の下位入出力設定0:出力、1:入力

 

例えば、モード0でポートABが入力、ポートC(上位、下位)が出力であるとすると

x7

x6

x5

x4

x3

x2

x1

x0

1

0

0

1

0

0

1

0

となるMATLABで、16進数に変換するには、

dec2hex(sum([1 0 0 1 0 0 1 0].*[128 64 32 16 8 4 2 1]))

ans =

92

0x92と変換できる。

実際の手順

8255を使用するには、まず、対応する8255CWRポートに各ポートの使用法を設定する。ポートからデータを参照するには、inp関数、書き込むには、outp関数などを用い書き込みを行えばよい。ビット単位で命令を出す場合、ポートCは、ビット単位でセットリセットできるがポートA,Bでは、ビット単位での設定はできないので自分で他のビットを保護する必要がある。ここで示すサンプル応用サンプルスクリプトは、6軸のステッピングモータをPIOCW,CCWを直接接続コントロールする例である。配線等の詳細に関しては省略するが、ポートAがステッピングモータの正方向周りD0,1,2,3,4,5、ポートBが逆周りD0,1,2,3,4,5、ポートCには原点で出力符号が変わるセンサが接続されている。(原点を中心に符号が変化するような機構になっている。)ここでのPIOボードの設定アドレスは、0x300である。該当する8255を使用するためにまずCWRポートを初期化する。ここでは、モード0ABポートが出力、Cポートが入力に設定している。つまり、モード0でポートABが出力、ポートC(上位、下位)が入力であるので

x7

x6

x5

x4

x3

x2

x1

x0

1

0

0

0

1

0

0

1

となるMATLABで、16進数に変換するには、

dec2hex(sum([1 0 0 0 1 0 0 1].*[128 64 32 16 8 4 2 1]))

ans =

89

としている。なお実際のスクリプト例を挙げておく。このスクリプトは、PIO-120D(PC)があり、かつベースアドレスが0x300に設定されていないと実行できないので注意すること。無い場合、最悪、暴走するので試す際には十分注意すること。


baseadr=hex2dec('300');

porta=baseadr+0;

portb=baseadr+1;

portc=baseadr+2;

cwr=baseadr+3;

% initialize for 8255A-0

outp(cwr,'0x89');% mode 0 port AB out C in

for k = 0:8000

  gg2= inp(portc);% read port

  p=rem(floor(gg2*pow2(-7:0)),2);

  pow2dat=pow2(7:-1:0);

  po=p([1 3 5 7]);pe=~p([2 4 6 8]);

  pp=[po(1) pe(1) po(2) pe(2) po(3) pe(3) po(4) pe(4)];

  gg=sum(pow2dat.*pp);

  outp(porta,gg);

  ngg=sum(pow2dat.*~pp);

  outp(portb,ngg);

  outp(porta,00);

  outp(portb,00);

end


 

AD/DAインターフェース

 このinp,outp関数を用いると一般的なA/DDAカードなどのサンプル設定なども可能である。(専用に取り込みを使った方が効率よいが各ボード専用にカスタマイズし易い。)ここでは、以下に示すのは、Contec社製のPCMCIA AD/DAカードAD12-8(PM)MATLAB用のドライバである。プログラムは、フルにC言語で書かれたバージョンadpcmcia.cinp,outpを用いスクリプトで書かれたadpcm.mである。

C言語による例 adpcmcia.c


/*

  ADPCMCIA.C

  The calling syntax is:

  [yp] = ADPCMCIA(interval,data_num,channel,'ioadr')

  Kazuyuki Kobayashi   Feb 2, 1998

  Copyright (c) 1998 Gerox(c)

  All Rights Reserved

  for Visual C++ Version 4.0

  March 4, 1998 for Watcom C++ Version 10.5J

*/

 

#include <windows.h>

#ifdef __WATCOMC__

#include <conio.h>

#endif

#include <math.h>

#include "mex.h"

/* Input Arguments */

 

/* Output Arguments */

#define  YP_OUT  plhs[0]

 

#include <stdio.h>

#include <stdlib.h>

#include <conio.h>

#ifndef __WATCOMC__

#define outp _outp

#define inpw _inpw

#define inp _inp

#endif

union _tag {

  long l;

  char c[4];

};

#define ADR     0x240  /* I/O address */

#define CHLS    8      /* number of channels */

static void adpcmcia(

    double  yp[],

    double   *interval,

    double   *data_num,

    double   *channel,

    double   *ioaddr

    )

{

  int ModeData  = 1;/* mode data */

  union _tag  ClockData = { 799 };/* sampling clock */

  union _tag  CountData = { 9 };/* sampling count */

  int AiData, i, Count = 0;

  int j;

  unsigned short ioadr = (unsigned short)*ioaddr;

  CountData.l = (long) *data_num;

  ClockData.l = (long)(*interval *1e7) - 1L;

  if((int)*channel == 1) ModeData = 0;

  outp(ioadr+6, 0 );/* initialize */

  outp(ioadr+6, 1 );/* set sampling mode */

  outp(ioadr+7, ModeData );

  outp(ioadr+6, 2 );/* set sampling clock */

  outp(ioadr+7, ClockData.c[0] );

  outp(ioadr+7, ClockData.c[1] );

  outp(ioadr+7, ClockData.c[2] );

  outp(ioadr+6, 3 );/* set sampling count */

  outp(ioadr+7, CountData.c[0] );

  outp(ioadr+7, CountData.c[1] );

  if((int)*channel == 1) outp(ioadr+2,0);

  else outp(ioadr+2, (int)*channel-1 );/* sampling start */

  for(j = 0;j < *data_num;j++) {

    while (!(inp(ioadr+2) & 2));

    for ( i = 0; i < *channel; i++ ) {

      AiData = inpw(ioadr );               /* input data */

      yp[(int)*data_num * i + j] = (double)AiData * 20. / 4096. - 10.;

    }

    if(!inp(ioadr+2) &3) break;

  }

  return;

}

void mexFunction(

                 int nlhs,       mxArray *plhs[],

                 int nrhs, const mxArray *prhs[]

     )

{

  double  *yp;

  double  *t,*y;

  unsigned int  m,n;

  double    interval;

  double    data_num;

  double    channel;

  double    ioadr;

  char buf[256];

  int buflen = 255;

  int status;

  int i;

  /* Check for proper number of arguments */

  if (nrhs == 3);

  else

  if(nrhs == 5);

  else

  if (nrhs != 4) {

    mexPrintf("   This ADPCMCIA function is for CONTEC AD12-8(PM)    \n");

    mexPrintf("   AD/DA PCMCIA card for MATLAB Version 5.            \n");

    mexPrintf("   In order to use this fucntion,please check         \n");

    mexPrintf("   the AD/DA PCMCIA card I/O address under Windows 95 \n");

    mexPrintf("                         default I/O setting is 0x240\n\n");

    mexPrintf("   Usage: adpcmcia(interval,data_num,channel,'ioadr')\n\n");

    mexPrintf("   Example:  ADPCMCIA(0.01,1000,2,'0x240')            \n");

    mexPrintf("          :  ADPCMCIA(0.01,100,2)                     \n\n");

    mexPrintf("   interval range   :%f - %f Sec\n",1048576.0/1e7,100./1e7);

    mexPrintf("   data_num*channel :1-16384\n");

    mexPrintf("   channel          :1-8\n\n");

    mexPrintf("                       Gerox(c) 1997 All Rights Reserved\n");

    mexErrMsgTxt("");

  } else if (nlhs > 1) {

    mexErrMsgTxt("ADPCMCIA requires one output argument.");

  }

  /* Check the dimensions of Input parameters. 

    The parameter 0 to 2 should be Numeriacal and double. */

  for(i = 0;i < 3;i++) {

    if (!mxIsNumeric(prhs[i]) || mxIsComplex(prhs[i]) ||

      mxIsSparse(prhs[i])  || !mxIsDouble(prhs[i])){

      mexErrMsgTxt("ADPCMCIA requires that Input be numeric number.");

    }

  }

/* The 4-th paramter is optional. If function found 4-th parameter,

  check string or not and convert hex value from the string, otherwise

  set default hex value (0x240) */

  if(nrhs > 3) {

    if (!mxIsChar(prhs[3])) {

      mexErrMsgTxt("\nADPCMCIA requires that Input parameter be Char. such as '0x240'");

    }

    status = mxGetString(prhs[3],buf,buflen);

    if(status == 0);

    else mexErrMsgTxt("\nADPCMCIA requires that input be Char.");

  }

  interval = mxGetScalar(prhs[0]);

  data_num = mxGetScalar(prhs[1]);

  channel  = mxGetScalar(prhs[2]);

  if(nrhs > 3) {

    sscanf(buf,"%x",&i);

    ioadr = (double)i;

  }

  else ioadr = ADR;

  /* Create a matrix for the return argument */

  YP_OUT = mxCreateDoubleMatrix((int)data_num,(int) channel, mxREAL);

  /* Assign pointers to the various parameters */

  yp = mxGetPr(YP_OUT);

  if(interval > 1048576.0/1e7) {

    mexPrintf("Min sampling = %f [Sec]\n",1048576./1e7);

    interval = 1048576.0/1e7;

  }

  if(interval < 100.0/1e7) {

    mexPrintf("Max sampling = %f [Sec]\n",100./1e7);

    interval = 100.0/1e7;

  }

  if(nrhs != 5) {

    mexPrintf("   Gerox(c) 1997 All Rights Reserved\n");

    mexPrintf("   interval = %g,datanum = %g,channel = %g,ioadr = 0x%x\n   Please wait %g Sec  \a\n",

           interval,data_num,channel,(int)ioadr,interval*data_num);

  }

  adpcmcia(yp,&interval,&data_num,&channel,&ioadr);

  if(nrhs != 5) mexPrintf("\a");

  return;

}


スクリプトによる例 adpcm.m

 


% ADPCM Script function is for CONTEC AD12-8(PM)

%             Gerox(c) 1997,1998,1999 All Rights Reserved

% AD/DA PCMCIA card for MATLAB Version 5.3

% by using inp/outp MEX function

% if cannot work, please check assigned io address for PCcard

% AD/DA PCMCIA card I/O default default I/O setting is 0x240

% only works under WIN95/98

% Usage: adpcm(interval,data_num,channel,'ioadr')

%   Example:  data = ADPCM(0.01,1000,2,'0x240')

%          :  data = ADPCM(0.01,100,2)

%  data matrix format

%        channel

%  ad0(0) ad1 ad2 ... adn     d

%  ad0(1) ad1 ad2 ... adn     a

%  ad0(2) ad1 ad2 ... adn     t

%  ad0(3) ad1 ad2 ... adn     a

%  ad0(4) ad1 ad2 ... adn     n

%  ad0(5) ad1 ad2 ... adn     u

%  ad0(6) ad1 ad2 ... adn     m

% 

%   interval range :0.1049 - 1.0000e-005 Sec

%   channel max data_num  min interval

%     1      16384          0.000001

%     2       8192          0.000002

%     3       5461          0.000003

%     4       4096          0.000004

%     5       3276          0.000005

%     6       2730          0.000006

%     7       2340          0.000007

%     8       2048          0.000008

function data = adpcm(interval,data_num,channel,ioadrstr)

%interval = 0.001;

%data_num=1024;

%channel=1

%ioadr=hex2dec('240');

if nargin == 4

  if ischar(ioadrstr)

    ioadr = hex2dec(ioadrstr(3:end));

  else

    ioadr = ioadrstr;% BE CAREFUL!! THIS SHOULD DECIMAL number!!

  end

else

  ioadr = hex2dec('240');

end

if(interval > 1048576.0/1e7)

   interval = 1048576.0/1e7;

end

if(interval < 100.0/1e7)

   fprintf('Max sampling = %f [Sec]\n',100./1e7);

   interval = 100.0/1e7;

end

data = zeros(data_num,channel);

ModeData = 1;

if(channel == 1) ModeData = 0;end

ClockData = interval *1e7 - 1;

tmpClock = rem(floor(ClockData./[1 256 256*256]),256);

CountData = data_num;

tmpCount=rem(floor(CountData./[1 256]),256);

outp(ioadr+6, 0 );% initialize

outp(ioadr+6, 1 );%set sampling mode

outp(ioadr+7, ModeData );

outp(ioadr+6, 2 );% set sampling clock

outp(ioadr+7, tmpClock(1));

outp(ioadr+7, tmpClock(2));

outp(ioadr+7, tmpClock(3));

outp(ioadr+6, 3 );% set sampling count

outp(ioadr+7, tmpCount(1));

outp(ioadr+7, tmpCount(2));

if(channel == 1) outp(ioadr+2,0);

else outp(ioadr+2, channel-1);% sampling start

end

for j = 1:data_num;

while (~(bitand(inp(ioadr+2),2)));end

for i = 1:channel

   AiDatal = inp(ioadr );% input data

   AiDatah = inp(ioadr+1);% input data

   data(j,i)= sum([AiDatah AiDatal].*[256 1]) * 20. / 4096. - 10.;

end

if(~bitand(inp(ioadr+2),3)) break;end

end


スクリプト版もMEX関数版も使用法はほぼ等価である。4番目のパラメータのみ若干異なる。スクリプト版ではhex2dec関数の仕様により文字列の場合には、16進数、そうでない場合には、10進数として扱っている。MEX関数では、いろいろな引数の数や型に対応して処理しなければならないことから若干プログラムが長くなっている。その点では、スクリプト版の方が見易い。スクリプト版の方が処理速度が遅いかもしれないが、データをサンプルする時は、カードの内部タイマを使用しているので精度は変わりない。両方とも実質的な処理は、20行程度のinp,outpの部分だけである。ここではADカードの初期化、モード変更、チャンネル数の指定、サンプリングタイムの設定、実際のサンプルデータの取り込みなどを行っている。これら詳しい設定については、個々のボードによるため詳しくは述べない。特に注意してもらいたいのは、C言語版からスクリプト版に変更する際の違いである。

よくC言語では、例えば、16ビットでかかれたdata8ビットに分割するとき

lowdata = (data & 0x00ff); highdata = ((data >> 8 )& 0x00ff);

として処理するがunionを使うとその計算が省略できる。

union _tag {

long l;

char c[4];

};

union _tag  ClockData = { 799 };/* sampling clock */

を定義し、

ClockData.l = (long)(*interval *1e7) - 1L;

outp(ioadr+7, ClockData.c[0] );

outp(ioadr+7, ClockData.c[1] );

outp(ioadr+7, ClockData.c[2] );

として扱っている。MATLABでは、union演算子が無いので、8ビット単位に分解しポートに数値を代入する必要がある。(2進数に分解するときと同じ要領でできる。8ビットなので2の代わりに28乗つまり256がくる。)

ClockData = interval *1e7 - 1;

tmpClock = rem(floor(ClockData./[1 256 256*256]),256);

outp(ioadr+7, tmpClock(1));

outp(ioadr+7, tmpClock(2));

outp(ioadr+7, tmpClock(3));

必要最低限の所のみをMEX関数で記述し計算などの部分は、スクリプトで記述してあげるとデバックがし易い。

 

 

Tips Figureの保存

MATLAB5.3から、データの保存だけでなくfigureデータの保存も出来るようになった。

close all;figure(1);%plot(sin(0.1*(1:100)));

mesh(peaks);

saveas(1,'filename','m');

close all;clear

 

とする。図を保存するには、saveas関数、または、figureのメニューバーの中のファイルの保存を選択すると保存できる。なおsaveas関数の最初の引数は、ハンドルグラフィックスのハンドルを示す。例えば、figure(2)の場合には、2と指定する。現在の図の場合にはgcf(Get Current Figure)とする。2番目の引数は、保存するファイル名である。3番目には、保存するファイルの拡張子がくる。この場合figと言う拡張子を付けている。これ以外にも拡張子'm'または、'mfig'と付けてもよい。この場合には、拡張子figのファイルとそれを実行するmファイルの2つが保存されるようにされる。

この例では、filename.m,filename.figと言う名前で保存した例である。

この場合、MATLABコマンドプロンプト上でfilenameとタイプすると自動的にfigureが起動する。作られたfilename.mの中を見てみればわかるが、filename.mの中はopen('filename.fig');

となっている。つまり拡張子figのファイルから図を再現するには、open関数を用いれば出来る。

close all

clear all

open('filename.fig');

このようにいつでも再計算せずにグラフのみを保存、再現することが出来る。

図からのデータ抽出

またHandleGraphics を使えば逆にfigureだけ保存しておいてそれから数値データも取り出すことも可能である。グラフの種類によりデータの格納の仕方が異なるので一概には言えないがその一例を上の例題を元に示そう。まずfindobj関数を用いすべてのハンドルグラフィックスの中からtypesurfaceのものを探す。plotで書かれた図の場合には、typelineのものを探す。この場合、1つのデータしかないため、get関数を直接使えたがもし複数の線データがある場合には、11つ処理する必要がある。

get(findobj('type','surface'))


CData = [ (49 by 49) double array]

CDataMapping = scaled

EdgeColor = flat

EraseMode = normal

FaceColor = [1 1 1]

LineStyle = -

LineWidth = [0.5]

Marker = none

MarkerEdgeColor = auto

MarkerFaceColor = none

MarkerSize = [6]

MeshStyle = both

XData = [ (1 by 49) double array]

YData = [ (49 by 1) double array]

ZData = [ (49 by 49) double array]

FaceLighting = none

EdgeLighting = flat

BackFaceLighting = reverselit

AmbientStrength = [0.3]

DiffuseStrength = [0.6]

SpecularStrength = [0.9]

SpecularExponent = [10]

SpecularColorReflectance = [1]

VertexNormals = [ (49 by 49 by 3) double array]

NormalMode = auto

 

ButtonDownFcn =

Children = []

Clipping = on

CreateFcn =

DeleteFcn =

BusyAction = queue

HandleVisibility = on

HitTest = on

Interruptible = on

Parent = [72.0016]

Selected = off

SelectionHighlight = on

Tag =

Type = surface

UIContextMenu = []

UserData = []

Visible = on


この場合CDataZDataの数値がデータである。これらのデータをMATLABワークスペースに取り出すには、

peakval= get(findobj('type','surface'),'CData');%または、

peakval= get(findobj('type','surface'),'ZData');

とすればfigureからデータを取り出すことが出来る。

演習課題

4.1 MATLABで用意されているbitand,bitor,bitxorと同等の関数をrem,floor,&,|,等を用いスクリプトで作れ。

4.2 4.1と同等の関数をMEX関数で作れ。

4.3 ここで作られたscriptbeep関数の入力に0,1の数字の入ったベクトルが入力されても動作するように改造せよ。

4.4 ここで作られたmexbeep関数の入力に0,1の数字の入ったベクトルが入力されても動作するように改造せよ。


 

[]
Last modified[]