摘 要: 在MATLAB環(huán)境下對(duì)硬件資源如I/O端口或存儲(chǔ)單元" title="存儲(chǔ)單元">存儲(chǔ)單元進(jìn)行訪問的方法進(jìn)行討論,通過MEX程序的設(shè)計(jì),MATLAB可以訪問硬件資源,與硬件進(jìn)行數(shù)據(jù)交換,也可以在外部程序中調(diào)用MATLAB的函數(shù)。在MEX程序中需要將MATLAB下的數(shù)據(jù)格式進(jìn)行轉(zhuǎn)換為C語言可以處理的數(shù)據(jù)類型。最后,結(jié)合應(yīng)用實(shí)例說明MEX程序的設(shè)計(jì)。
關(guān)鍵詞: 硬件資源 訪問 MATLAB MEX程序
?
MATLAB語言是一種高性能的數(shù)值計(jì)算和可視化軟件,它集數(shù)值分析、矩陣運(yùn)算、信號(hào)處理和圖形顯示于一體,構(gòu)成了一個(gè)方便的、界面友好的用戶環(huán)境。盡管MATLAB本身的編程和數(shù)據(jù)處理的環(huán)境是完整的和自成體系的,可經(jīng)常在這種環(huán)境下,仍有必要與外部的程序和數(shù)據(jù)進(jìn)行通訊和數(shù)據(jù)交換,如需要控制數(shù)據(jù)采集板的硬件,讀取采集后存于數(shù)據(jù)緩存區(qū)的數(shù)據(jù)等;為此它提供了應(yīng)用程序" title="應(yīng)用程序">應(yīng)用程序接口(API)函數(shù)來支持這樣的操作,這樣可以利用該函數(shù)來訪問硬件資源。MATLAB環(huán)境提供了MEX-文件,利用該文件可以調(diào)用用戶自己的C語言或FORTRAN語言程序,就像調(diào)用內(nèi)部函數(shù)一樣方便,這些程序是MATLAB編譯器自身可以加載和運(yùn)行的動(dòng)態(tài)連接" title="動(dòng)態(tài)連接">動(dòng)態(tài)連接子程序庫。本文主要就如何利用MEX文件實(shí)現(xiàn)在Windows環(huán)境下對(duì)數(shù)據(jù)采集硬件資源的控制和訪問。
1 Windows環(huán)境下對(duì)硬件資源的訪問
我們有時(shí)可能需要在MATLAB下直接操作I/O端口,或者自己設(shè)計(jì)了專用的數(shù)據(jù)采集硬件設(shè)備并在MATLAB下使用,希望能夠訪問這些硬件資源。由于MATLAB是在Windows環(huán)境下運(yùn)行,要在它的環(huán)境下實(shí)現(xiàn)對(duì)硬件資源(如I/O端口或存儲(chǔ)單元)的訪問,就有必要了解Windows下對(duì)硬件進(jìn)行操作的原理。
在Windows中,操作系統(tǒng)對(duì)I/O端口進(jìn)行保護(hù),它將檢查是否允許當(dāng)前程序?qū)@個(gè)端口進(jìn)行操作,如果允許,操作系統(tǒng)就代為執(zhí)行I/O指令;否則,操作系統(tǒng)就會(huì)采取相應(yīng)處理步驟,要么中止該程序,要么向用戶報(bào)警。
在Windows中,真正的核心是VMM(虛擬機(jī)管理器)和VxD(虛擬設(shè)備驅(qū)動(dòng)程序" title="設(shè)備驅(qū)動(dòng)程序">設(shè)備驅(qū)動(dòng)程序),它們工作在特權(quán)級(jí)0上,控制著整個(gè)系統(tǒng)的運(yùn)轉(zhuǎn)。正是VMM和VxD一起負(fù)責(zé)管理I/O端口操作。系統(tǒng)正常運(yùn)轉(zhuǎn)后,如果應(yīng)用程序執(zhí)行了1條I/O指令, VMM接收到這個(gè)消息后,它將調(diào)用曾申請(qǐng)截獲該端口的VxD 提供的處理函數(shù)。此時(shí)VxD可能會(huì)根據(jù)程序的需要選擇采取以下四種動(dòng)作之一:忽略這條I/O指令;仿真執(zhí)行I/O指令;局部解除對(duì)該端口截獲;代替應(yīng)用程序執(zhí)行I/O指令。如果I/O端口被保護(hù),則應(yīng)用程序需要利用VxD程序進(jìn)行訪問,否則應(yīng)用程序可以直接進(jìn)行訪問。系統(tǒng)初始化完畢后,沒有VxDs申請(qǐng)要截獲的I/O端口對(duì)應(yīng)用程序來說就是可直接使用Input/Output指令進(jìn)行訪問。
對(duì)內(nèi)存單元的訪問要復(fù)雜一些,一般情況下硬件使用的是物理地址如D800:0。而在Windows中,內(nèi)存采用平板模式,利用分頁式的內(nèi)寸管理方案,即內(nèi)存段起始地址為0,而偏移地址是線性地址,這樣要訪問實(shí)際的物理地址,就要先將物理地址變換為線性地址,而后利用指針對(duì)線性地址進(jìn)行操作,就如同對(duì)其它內(nèi)存單元進(jìn)行操作一樣。在Windows中,可以調(diào)用SDK中的MapPhysToLinear服務(wù)函數(shù)將物理地址轉(zhuǎn)換為線性地址,也可以利用現(xiàn)有的VxD程序進(jìn)行轉(zhuǎn)換,如使用VtoolsD公司的MAPDEV.VXD。
2 MATLAB環(huán)境下MEX程序的設(shè)計(jì)
MEX程序提供了MATLAB和外部應(yīng)用程序(如C語言程序)的接口,它自身包含兩部分代碼:(1)執(zhí)行外部程序中的計(jì)算和輸入/輸出命令的程序代碼;(2)通過入口函數(shù)mexFunction及其參數(shù)prhs、nrhs、 plhs nlhs將MATLAB環(huán)境下的變量和數(shù)據(jù)與應(yīng)用程序進(jìn)行接口,這部分程序稱為關(guān)口程序。
當(dāng)MATLAB要執(zhí)行子程序調(diào)用時(shí),常用以下命令格式:
[a、 b、 c……]=func (d、 e、 f……)
其中,a,b,c為左端變量,表示函數(shù)調(diào)用后要返回的參數(shù)值,而d,e,f等為右邊變量,表示調(diào)用函數(shù)時(shí)要送往函數(shù)的參數(shù)值。
在MEX程序中關(guān)口函數(shù)總是為mexFunction,其變量和格式為:
void mexFunction (int nlhs、 mxArray *plhs[]、 Int nrhs、 Const mxArray *plhs[])
其中nrhs、 nlhs分別表示輸入/輸出(右端/左端)參數(shù)數(shù)目; *plhs[]、 *prhs[]分別表示指向左端輸出/右端輸入變量的指針,這兩個(gè)變量具有MATLAB特有的數(shù)據(jù)結(jié)構(gòu)" title="數(shù)據(jù)結(jié)構(gòu)">數(shù)據(jù)結(jié)構(gòu)mxArray形式。
在MEX程序中,也可以調(diào)用MATLAB函數(shù)或用戶自定義的函數(shù)。調(diào)用的指令為mxCallMATLAB(plhs、 *plhs[])、 nrhs、 *prhs[]、 char *command-name) ,其中plhs、 *plhs、 nrhs、 *prhs等參數(shù)意義和前述參數(shù)意義相同,而*command-name為指令字符串的指針,該函數(shù)在調(diào)用成功以后、就返回0值。反之,則返回非零值。
在Windwows平臺(tái)下,要生成MEX文件,就必須先為編譯器安置選項(xiàng)文件mexopt.bat,通過setup開關(guān)可以進(jìn)入選項(xiàng)配置程序,只要按照程序提示內(nèi)容進(jìn)行,就可完成對(duì)編譯器的選項(xiàng)配置。在此之后要把外部MEX的C語言程序的路徑加入到MATLAB目錄路徑,這樣只要鍵入MEX [應(yīng)用程序名C]就可以編譯生成帶有DLL擴(kuò)展名的MEX文件。要調(diào)用MEX程序就和調(diào)用一般的MATLAB內(nèi)部命令一樣。由于MATLAB程序解釋器當(dāng)在同一目錄下遇到具有相同名字的M文件和MEX文件時(shí),首先執(zhí)行MEX文件;而使用HELP命令時(shí),MATLAB首先查找M文件,這樣就可以用M文件對(duì)MEX文件進(jìn)行注釋。
3 MATLAB環(huán)境下和MEX程序中的數(shù)據(jù)格式處理
MEX程序采用的數(shù)據(jù)格式與C語言的格式是相同的,具有不同的整數(shù)和浮點(diǎn)數(shù)類型。而在MATLAB語言中,僅有一種對(duì)象類型,MATLAB陣列mxArray。所有的變量包括標(biāo)量、向量、矩陣、字符串,單元陣列和結(jié)構(gòu)等都以該陣列方式存儲(chǔ),mxArray數(shù)據(jù)結(jié)構(gòu)包含有以下幾部分:
類型:如果是數(shù)值,標(biāo)明是實(shí)數(shù)或復(fù)數(shù)。
維數(shù):如是稀疏矩陣,包含索引和非零元素。
和該陣列相關(guān)的數(shù)據(jù):如是結(jié)構(gòu)或?qū)ο?,包含域的?shù)量和名字。
對(duì)于mxArray的數(shù)據(jù)結(jié)構(gòu)形式,MATLAB的API程序提供了一系列函數(shù),利用這些函數(shù)用戶可以生成具有mxArray格式的各種標(biāo)量、向量等,也可以將mxArray中的向量或標(biāo)量的維數(shù)和數(shù)據(jù)轉(zhuǎn)換為C語言可以直接處理的數(shù)據(jù)類型。
用戶在進(jìn)入MEX應(yīng)用程序以后,首先要確定輸入變量的參數(shù)個(gè)數(shù)和返回變量的參數(shù)個(gè)數(shù),再確定各變量的維數(shù)和類型是否和預(yù)先設(shè)定的一致,對(duì)于返回變量需要調(diào)用創(chuàng)建語句構(gòu)造數(shù)組。當(dāng)輸入和返回變量不止一個(gè)時(shí),plhs[]和prhs[]數(shù)組中分別包括了指向變量的指針,如plhs[0]表示指向第1個(gè)返回參數(shù)的指針,prhs[1]表示指向第2個(gè)返回參數(shù)的指針。在創(chuàng)建數(shù)組時(shí),返回的是指向mxArray類型數(shù)組的指針,要訪問具體的數(shù)據(jù)需要調(diào)用mxGetPr來獲取指向?qū)嶋H數(shù)據(jù)的指針。
結(jié)構(gòu)和單元陣列是MATLAB 5.0下的新的數(shù)據(jù)格式,將它們傳遞到MEX文件中就和傳遞其它類型的數(shù)據(jù)一樣簡(jiǎn)單,只不過應(yīng)注意調(diào)用函數(shù)mxGetField和mxGetCell返回的是指向mxArray類型的指針,而后可以調(diào)用mxGetData來獲取真正的數(shù)據(jù)。對(duì)復(fù)數(shù)而言,可以分別調(diào)用mxGetPr和mxGetPi來獲取指向真正的實(shí)部和虛部數(shù)據(jù)的指針。
在MEX函數(shù)中,要處理8位、16位和32位數(shù)據(jù),則可以先用mxCreatNumericArray來建立數(shù)組,在mxClassID中定義要?jiǎng)?chuàng)建數(shù)據(jù)的類型,一旦建立了數(shù)組,可以用mxGetData和mxGetImagData函數(shù)分別獲取實(shí)部和虛部的指針,而后進(jìn)行操作。這樣就可以在MEX函數(shù)中處理MATLAB中不易處理的8位、16位和32位整數(shù)數(shù)據(jù),當(dāng)數(shù)據(jù)傳送回MATLAB后,要將其變?yōu)殡p精度數(shù)據(jù)。對(duì)于多維數(shù)組,也可以按照相同的方式處理,只不過注意數(shù)據(jù)是按列存儲(chǔ),計(jì)算下標(biāo)時(shí)要注意。
4 應(yīng)用實(shí)例分析
#include <conio.h>
#include ″mex.h″ /* MEX 文件的頭文件 */
#include <memory.h>
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
?。? 調(diào)用VxD程序需要的頭文件 */
define NotVxD
#include ″mapdev.h″
#include ″winioctl.h″
HANDLE hDevice;
int dims[2]={4096、1};
unsigned short ADData1[8192]; /*申請(qǐng)緩沖區(qū)*/
unsigned short *buff1、 *buff2;
int points;
/* 關(guān)口函數(shù) */
void mexFunction( int nlhs、 mxArray *plhs[]、
int nrhs、 const mxArray *prhs[])
{
double *x、 *y;
unsigned short *temp;
double z;
int nx、 ny、 nz、 ad_mode、 status、mrows、ncols、channelnum、i、k;
float ADFre、 FilterFre;
unsigned short int adstopl、 adstoph;
?。? 調(diào)用VxD程序需要的變量聲明 */
PVOID inBuf[1]; //buffer for struct pointer to VxD
DWORD RetInfo[2]; //buffer to receive data
from VxD
DWORD cbBytesReturned; //count of bytes re-turned from VxD
MAPDEVREQUEST req; //map device request structure
const PCHAR VxDName=″\\\\.\\MAPDEV.VXD″;
const PCHAR VxDNameAlreadyLoaded=″\\\\.\\MAPDEV″;
?。?檢查參數(shù)數(shù)量 */
if(nrhs?選=3)
mexErrMsgTxt(″需要三個(gè)輸入?yún)?shù).″);
if(nlhs?選=2)
mexErrMsgTxt(″需要兩個(gè)輸出參數(shù).″);
?。? 檢查參數(shù)類型 */
if( ?選mxIsNumeric(prhs[2]) || ?選mxIsDouble(prhs[2]) ||
mxIsEmpty(prhs[2]) || mxIsComplex(prhs[2]) ||
mxGetN(prhs[2])*mxGetM(prhs[2])?選=1 )
{
mexErrMsgTxt(″第三輸入?yún)?shù)應(yīng)為標(biāo)量.″);
}
/* 獲取各輸入?yún)?shù)的長(zhǎng)度. */
nx = mxGetN(prhs[0]);
ny = mxGetN(prhs[1]);
nz = mxGetN(prhs[2]);
?。? 將雙精度型數(shù)據(jù)轉(zhuǎn)換為整數(shù) */
points = (int) mxGetScalar(prhs[2]);
?。? 創(chuàng)建16位無符號(hào)整數(shù)數(shù)組(4096X1)供輸出使用 */
plhs[0]=mxCreateNumericArray(2、dims、mxUINT16_
CLASS、mxREAL);
?。? 創(chuàng)建1X1雙精度型返回變量 */
plhs[1] = mxCreateDoubleMatrix(1、1、mxREAL);
temp=(unsigned short *)mxGetPr(plhs[0]);
?。? 獲取輸出變量指針 */
x = mxGetPr(prhs[0]);
y = mxGetPr(prhs[1]);
?。? 動(dòng)態(tài)調(diào)用VxD程序 */
hDevice = CreateFile(VxDName、 0、0、0、
CREATE_NEW、FILE_FLAG_DELETE_ON_CLOSE、 0);
if (hDevice == INVALID_HANDLE_VALUE)
hDevice = CreateFile(VxDNameAlreadyLoaded、 0、0、0、
CREATE_NEW、FILE_FLAG_DELETE_ON_CLOSE、 0);
if (hDevice == INVALID_HANDLE_VALUE)
{
mexPrintf( ″Cannot open driver、 error=%08lx\n″、
GetLastError());
}
req.mdr_ServiceID = MDR_SERVICE_M(jìn)AP;
req.mdr_PhysicalAddress = 0xd8000; /*物理地址為d800:0 */
req.mdr_SizeInBytes=0x8000; /*長(zhǎng)度為0x8000*/
inBuf[0] = &req;
if (?選DeviceIoControl(hDevice、 MDR_SERVICE_M(jìn)AP、
inBuf、 sizeof(PVOID)、 NULL、 0、 &cbBytesReturned、 NULL)
)
mexPrintf( ″Failed to map device\n″);
buff1=req.mdr_LinearAddress; /* 獲取線性地址指針 */
?。? 直接進(jìn)行I/O操作啟動(dòng)A/D */
itemp = inp(0x300);
itemp|=0x2;
outp(0x300、 itemp); /* start AD */
itemp |= 0x4;
outp(0x300、 itemp); /* A/D initial clear */
?。? 對(duì)線性地址直接進(jìn)行指針操作 */
memcpy((unsigned short *)temp、 (unsigned short *)buff1、 8192*sizeof(unsigned short));
?。? 設(shè)置返回標(biāo)量值為點(diǎn)數(shù) */
temp=(unsigned short *)mxGetPr(plhs[1]);
*temp=(unsigned short)points;
}
綜上所述,在MATLAB環(huán)境下對(duì)硬件資源的訪問可以通過MEX程序進(jìn)行,由MEX程序產(chǎn)生的DLL程序可以作為一個(gè)動(dòng)態(tài)連接庫被MATLAB代碼調(diào)用,而MEX程序也可以調(diào)用MATLAB的內(nèi)部函數(shù)或外部函數(shù)。在MEX程序中利用MATLAB提供的API函數(shù)可以將MATLAB的內(nèi)部數(shù)據(jù)類型轉(zhuǎn)換為C語言可以處理的數(shù)據(jù)類型格式。另一方面,MEX程序傳送回的整數(shù)數(shù)據(jù)也要變?yōu)殡p精度型數(shù)據(jù),才能為其它函數(shù)所處理。
參考資料
1 The MathWorks、 Inc. Application Program Interface Guide. January 1998 Revised for 5.2
2 徐志海,郭武,徐守時(shí).Win95 下利用VXD訪問物理地址.微計(jì)算機(jī)應(yīng)用,1998;19(3)
3 楊 強(qiáng)、李堂球編著.Win 9X虛擬設(shè)備驅(qū)動(dòng)程序編程指南.北京:清華大學(xué)出版社,1999.3
