《電子技術應用》
您所在的位置:首頁 > 嵌入式技术 > 业界动态 > 在MATLAB环境下实现对硬件资源的访问

在MATLAB环境下实现对硬件资源的访问

2008-12-08
作者:李传日 齐 华 袁宏杰

  摘 要: 在MATLAB環(huán)境下對硬件資源如I/O端口或存儲單元" title="存儲單元">存儲單元進行訪問的方法進行討論,通過MEX程序的設計,MATLAB可以訪問硬件資源,與硬件進行數據交換,也可以在外部程序中調用MATLAB的函數。在MEX程序中需要將MATLAB下的數據格式進行轉換為C語言可以處理的數據類型。最后,結合應用實例說明MEX程序的設計。
  關鍵詞: 硬件資源 訪問 MATLAB MEX程序

?

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


本站內容除特別聲明的原創(chuàng)文章之外,轉載內容只為傳遞更多信息,并不代表本網站贊同其觀點。轉載的所有的文章、圖片、音/視頻文件等資料的版權歸版權所有權人所有。本站采用的非本站原創(chuàng)文章及圖片等內容無法一一聯系確認版權者。如涉及作品內容、版權和其它問題,請及時通過電子郵件或電話通知我們,以便迅速采取適當措施,避免給雙方造成不必要的經濟損失。聯系電話:010-82306118;郵箱:aet@chinaaet.com。