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