访问手机版页面
你的位置:老古开发网 > 其他 > 正文  
在MATLAB环境下实现对硬件资源的访问
内容导读:

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

    关键词:硬件资源  访问  MATLAB  MEX程序

    MATLAB语言是一种高性能的数值计算和可视化软件,它集数值分析、矩阵运算、信号处理和图形显示于一体,构成了一个方便的、界面友好的用户环境。尽管MATLAB本身的编程和数据处理的环境是完整的和自成体系的,可经常在这种环境下,仍有必要与外部的程序和数据进行通讯和数据交换,如需要控制数据采集板的硬件,读取采集后存于数据缓存区的数据等;为此它提供了应用程序接口(API)函数来支持这样的操作,这样可以利用该函数来访问硬件资源。MATLAB环境提供了MEX-文件,利用该文件可以调用用户自己的C语言或FORTRAN语言程序,就像调用内部函数一样方便,这些程序是MATLAB编译器自身可以加载和运行的动态连接子程序库。本文主要就如何利用MEX文件实现在Windows环境下对数据采集硬件资源的控制和访问。

1 Windows环境下对硬件资源的访问

    我们有时可能需要在MATLAB下直接操作I/O端口,或者自己设计了专用的数据采集硬件设备并在MATLAB下使用,希望能够访问这些硬件资源。由于MATLAB是在Windows环境下运行,要在它的环境下实现对硬件资源(如I/O端口或存储单元)的访问,就有必要了解Windows下对硬件进行操作的原理。

    在Windows中,操作系统对I/O端口进行保护,它将检查是否允许当前程序对这个端口进行操作,如果允许,操作系统就代为执行I/O指令;否则,操作系统就会采取相应处理步骤,要么中止该程序,要么向用户报警。

    在Windows中,真正的核心是VMM(虚拟机管理器)和VxD(虚拟设备驱动程序),它们工作在特权级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特有的数据结构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程序传送回的整数数据也要变为双精度型数据,才能为其它函数所处理。

标签:
来源:电子技术应用 作者:北京航空航天大学(100083) 李传日 齐 华 袁宏杰 时间:2006/5/7 0:00:00
相关阅读
推荐阅读
阅读排行
最近更新
商品推荐