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

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

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

  摘 要: 在MATLAB环境下对硬件资源如I/O端口或存储单元" title="存储单元">存储单元进行访问的方法进行讨论,通过MEX程序的设计,MATLAB可以访问硬件资源,与硬件进行数据交换,也可以在外部程序中调用MATLAB的函数。在MEX程序中需要将MATLAB下的数据格式进行转换为C语言可以处理的数据类型。最后,结合应用实例说明MEX程序的设计。
  关键词: 硬件资源 访问 MATLAB MEX程序

 

  MATLAB语言是一种高性能的数值计算和可视化软件,它集数值分析、矩阵运算、信号处理和图形显示于一体,构成了一个方便的、界面友好的用户环境。尽管MATLAB本身的编程和数据处理的环境是完整的和自成体系的,可经常在这种环境下,仍有必要与外部的程序和数据进行通讯和数据交换,如需要控制数据采集板的硬件,读取采集后存于数据缓存区的数据等;为此它提供了应用程序" title="应用程序">应用程序接口(API)函数来支持这样的操作,这样可以利用该函数来访问硬件资源。MATLAB环境提供了MEX-文件,利用该文件可以调用用户自己的C语言或FORTRAN语言程序,就像调用内部函数一样方便,这些程序是MATLAB编译器自身可以加载和运行的动态连接" title="动态连接">动态连接子程序库。本文主要就如何利用MEX文件实现在Windows环境下对数据采集硬件资源的控制和访问。
1 Windows环境下对硬件资源的访问
  我们有时可能需要在MATLAB下直接操作I/O端口,或者自己设计了专用的数据采集硬件设备并在MATLAB下使用,希望能够访问这些硬件资源。由于MATLAB是在Windows环境下运行,要在它的环境下实现对硬件资源(如I/O端口或存储单元)的访问,就有必要了解Windows下对硬件进行操作的原理。
  在Windows中,操作系统对I/O端口进行保护,它将检查是否允许当前程序对这个端口进行操作,如果允许,操作系统就代为执行I/O指令;否则,操作系统就会采取相应处理步骤,要么中止该程序,要么向用户报警。
  在Windows中,真正的核心是VMM(虚拟机管理器)和VxD(虚拟设备驱动程序" title="设备驱动程序">设备驱动程序),它们工作在特权级0上,控制着整个系统的运转。正是VMM和VxD一起负责管理I/O端口操作。系统正常运转后,如果应用程序执行了1条I/O指令, VMM接收到这个消息后,它将调用曾申请截获该端口的VxD 提供的处理函数。此时VxD可能会根据程序的需要选择采取以下四种动作之一:忽略这条I/O指令;仿真执行I/O指令;局部解除对该端口截获;代替应用程序执行I/O指令。如果I/O端口被保护,则应用程序需要利用VxD程序进行访问,否则应用程序可以直接进行访问。系统初始化完毕后,没有VxDs申请要截获的I/O端口对应用程序来说就是可直接使用Input/Output指令进行访问。
  对内存单元的访问要复杂一些,一般情况下硬件使用的是物理地址如D800:0。而在Windows中,内存采用平板模式,利用分页式的内寸管理方案,即内存段起始地址为0,而偏移地址是线性地址,这样要访问实际的物理地址,就要先将物理地址变换为线性地址,而后利用指针对线性地址进行操作,就如同对其它内存单元进行操作一样。在Windows中,可以调用SDK中的MapPhysToLinear服务函数将物理地址转换为线性地址,也可以利用现有的VxD程序进行转换,如使用VtoolsD公司的MAPDEV.VXD。
2 MATLAB环境下MEX程序的设计
  MEX程序提供了MATLAB和外部应用程序(如C语言程序)的接口,它自身包含两部分代码:(1)执行外部程序中的计算和输入/输出命令的程序代码;(2)通过入口函数mexFunction及其参数prhs、nrhs、 plhs nlhs将MATLAB环境下的变量和数据与应用程序进行接口,这部分程序称为关口程序。
  当MATLAB要执行子程序调用时,常用以下命令格式:
  [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文件时,首先执行MEX文件;而使用HELP命令时,MATLAB首先查找M文件,这样就可以用M文件对MEX文件进行注释。
3 MATLAB环境下和MEX程序中的数据格式处理
  MEX程序采用的数据格式与C语言的格式是相同的,具有不同的整数和浮点数类型。而在MATLAB语言中,仅有一种对象类型,MATLAB阵列mxArray。所有的变量包括标量、向量、矩阵、字符串,单元阵列和结构等都以该阵列方式存储,mxArray数据结构包含有以下几部分:
  类型:如果是数值,标明是实数或复数。
  维数:如是稀疏矩阵,包含索引和非零元素。
  和该阵列相关的数据:如是结构或对象,包含域的数量和名字。
  对于mxArray的数据结构形式,MATLAB的API程序提供了一系列函数,利用这些函数用户可以生成具有mxArray格式的各种标量、向量等,也可以将mxArray中的向量或标量的维数和数据转换为C语言可以直接处理的数据类型。
  用户在进入MEX应用程序以后,首先要确定输入变量的参数个数和返回变量的参数个数,再确定各变量的维数和类型是否和预先设定的一致,对于返回变量需要调用创建语句构造数组。当输入和返回变量不止一个时,plhs[]和prhs[]数组中分别包括了指向变量的指针,如plhs[0]表示指向第1个返回参数的指针,prhs[1]表示指向第2个返回参数的指针。在创建数组时,返回的是指向mxArray类型数组的指针,要访问具体的数据需要调用mxGetPr来获取指向实际数据的指针。
  结构和单元阵列是MATLAB 5.0下的新的数据格式,将它们传递到MEX文件中就和传递其它类型的数据一样简单,只不过应注意调用函数mxGetField和mxGetCell返回的是指向mxArray类型的指针,而后可以调用mxGetData来获取真正的数据。对复数而言,可以分别调用mxGetPr和mxGetPi来获取指向真正的实部和虚部数据的指针。
  在MEX函数中,要处理8位、16位和32位数据,则可以先用mxCreatNumericArray来建立数组,在mxClassID中定义要创建数据的类型,一旦建立了数组,可以用mxGetData和mxGetImagData函数分别获取实部和虚部的指针,而后进行操作。这样就可以在MEX函数中处理MATLAB中不易处理的8位、16位和32位整数数据,当数据传送回MATLAB后,要将其变为双精度数据。对于多维数组,也可以按照相同的方式处理,只不过注意数据是按列存储,计算下标时要注意。
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]; /*申请缓冲区*/
  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]);
  /* 创建16位无符号整数数组(4096X1)供输出使用 */
  plhs[0]=mxCreateNumericArray(2、dims、mxUINT16_
  CLASS、mxREAL);
  /* 创建1X1双精度型返回变量 */
  plhs[1] = mxCreateDoubleMatrix(1、1、mxREAL);
  temp=(unsigned short *)mxGetPr(plhs[0]);
  /* 获取输出变量指针 */
  x = mxGetPr(prhs[0]);
  y = mxGetPr(prhs[1]);
  /* 动态调用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环境下对硬件资源的访问可以通过MEX程序进行,由MEX程序产生的DLL程序可以作为一个动态连接库被MATLAB代码调用,而MEX程序也可以调用MATLAB的内部函数或外部函数。在MEX程序中利用MATLAB提供的API函数可以将MATLAB的内部数据类型转换为C语言可以处理的数据类型格式。另一方面,MEX程序传送回的整数数据也要变为双精度型数据,才能为其它函数所处理。
参考资料
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


本站内容除特别声明的原创文章之外,转载内容只为传递更多信息,并不代表本网站赞同其观点。转载的所有的文章、图片、音/视频文件等资料的版权归版权所有权人所有。本站采用的非本站原创文章及图片等内容无法一一联系确认版权者。如涉及作品内容、版权和其它问题,请及时通过电子邮件或电话通知我们,以便迅速采取适当措施,避免给双方造成不必要的经济损失。联系电话:010-82306118;邮箱:aet@chinaaet.com。