MATLAB利用共享内存与应用程序传递数据

前言

MATLAB/Simulink将数值计算或者仿真结果传递给其他应用软件或者其他硬件平台最简单的方法是采用UDP通信,该通信协议使用简单方面,而且MATLAB中也有支持UDP通讯的封装函数,Simulink模块库中也有UDP通讯的封装模块。然而该种方法的安全性不足,而且没有同步机制,且自定义不同应用之间的信号传递机制和逻辑较为困难,还存在数据的解包过程,利用UDP在不同硬件平台上传递信息比较方便。但是在一个平台下传递数据的效率、速度上不太理想。
本篇文件主要介绍在一个硬件平台上(例如一台电脑上)利用共享内存的方式,将MATLAB/Simulink中运算的数据传递给其他应用软件。或者将其他应用软件(例如vs,或者用.Net等编写的用户交互界面软件)输入信息控制MATLAB/Simulink执行。但是共享内存的缺点是互斥锁、同步操作需要使用者自定义规则,才能使不同应用之间有序、正确、高效的传递。本篇文章主要讲述MATLAB通过调用mex函数操作共享内存,Simulink操作共享内存的方法请转博文:
本文中所有的代码均可在文末链接获取。

MATLAB共享内存

共享内存是windows提供的系统函数,可以编写C和C++,甚至是C#来新建共享内存并操作共享内存。基于此,我们可以利用MATLAB软件提供的调用外部C函数方式,利用C语言和MATLAB联合编程,调用编译后的C,达到操作共享内存的目的。
具体MATLAB实现共享内存的方式如下:
1.配置MATLAB mex编译环境;首先需要安装visual studio或者C语言编译器 ,注意,需要安装vs软件并与MATLAB的版本适配,具体可参考可在MATLAB中输入命令:mex -setup 选择编译器)。
2.将操作共享内存的C函数转换为MEX文件,编译C函数的MATLAB命令如下:

mex write_register_double.c
在MATLAB命令窗中,将共享内存操作的MATLAB C混合编程的C文件编译成MEX文件
语法为 : mex 函数名称.c,上面编译的函数名为 write_register_double.c

3.当MATLAB正确的编译C函数后,会将该C函数文件转换为MEX函数文件,然后用MATLAB像调用M函数一样的方式直接调用MEX文件,通过调用MEX函数即可向共享内存中写入MATLAB环境下的数据和信息。

MATLAB共享内存函数

一、MATLAB与C联合交互函数

该函数为MATLAB调用C函数的入口函数,可理解为main函数。当MATLAB调用C编译后的MEX文件时,MATLAB将把mexFunction作为调用MEX的入口函数调用整个C文件中的所有函数,该函数可以从MATLAB帮助文档中查到。在代码中注释可得到函数的输入参数说明。
其中:nlhs为输出参数的个数,从英文名称可以看出,等号左边的参数个数(Number of left-side arguments),简写为nlhs
plhs为输出参数矩阵指针,指向mex函数所有的输出参数。从英文名称可以看出,等号左边的参数(Array of left-side output arguments),简写为plhs。
nrhs和prhs分别为输入参数的个数和输入参数的指针。
下述的函数首先通过MATLAB提供的mxGetPr获取输入参数的指针,这两个指针分别指向待写入共享内存的参数值和共享内存偏移地址,然后将这两个参数作为调用共享内存写函数write_register,完成写共享内存的操作,写共享内存的函数请看本节的第二部分。

/*******************************************************************************
* Function mexFunction()
* Goal  : The gateway routine called mexFunction is the entry point to the MEX
*         file. It is through this routine that MATLAB accesses the rest of the
*         routines in the MEX files. It takes the place of the main function in
*         the source code.
* IN    : - nlhs: Number of left-side arguments, or the size of the plhs array
*         - plhs: Array of left-side output arguments
*         - nrhs: Number of right-side arguments, or the size of the prhs array
*         - prhs: Array of right-side input arguments
* IN/OUT: -
* OUT   : -
*******************************************************************************/
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{
    
    
  /* Declaration of local variables */
  unsigned short *register_address;
  unsigned char  *register_value;
  mwSignedIndex dims[2];

  /* Read input data */
  register_address = mxGetPr(prhs[0]);
  register_value   = mxGetPr(prhs[1]);

  /* Call of the C computational routine */
  write_register(*register_address, *register_value);
  return;
}

二、写共享内存函数

写共享内存的函数write_register如下所示,根据输入的共享内存偏移地址address和共享内存写入值value,将value写入偏移地址为address的内存中。具体函数的内部操作非常简单,即将将值写入内存。其中sharedMemoryAddress 为共享内存的基地址,也是调用开辟共享内存函数时动态获得的地址,该变量由于在共享内存写函数、开辟共享内存函数、读共享内存函数中均有使用,因此将该变量声明为了全局变量。开辟共享内存函数请看本节的第三节。

static volatile char * sharedMemoryAddress = NULL;
/*******************************************************************************
* Function write_register()
* Goal  : Write the current value to the desire HW register address
* IN    : - value to store in the register as an unsigned 8 bit
* IN/OUT: - address of the register in unsigned 16 bit
* OUT   : -
*******************************************************************************/
void write_register(unsigned short address, unsigned char value)
{
    
    
  /* Access the shared memory area */
  if (open_shared_memory() == 0)
  {
    
    
    /* Set the new value in the desired register */
    *(sharedMemoryAddress + address) = value;
  }
  else
  {
    
    
    getchar();
    exit(EXIT_FAILURE);
  }
}

三、开辟共享内存空间函数

利用C函数在系统中申请共享内存,代码如下所示:
开辟共享内存时最关键的输入量为共享内存的标签tag,即函数中的 SHARED_MEMORY_NAME,该标签作为系统共享内存的唯一标示;只要通过该标示的所有应用程序都可访问和操作这一块共享内存。

/*******************************************************************************
* Function open_shared_memory()
* Goal  : Creation of a memory-mapped file to share data between 2 processes
* IN    : -
* IN/OUT: -
* OUT   : - Error status in integer coded as following:
*          > 0 => No error occurred and the shared memory has been created
*          > 1 => The process could not create the memory-mapped file object
*          > 2 => The process could not return a pointer to the file view
*******************************************************************************/
int open_shared_memory()
{
    
    
  /* Definition of the handle to the memory-mapped file object */
  HANDLE hMappingFile;

  /* Create a new shared memory area if it does not exist yet */
  if (sharedMemoryAddress == NULL)
  {
    
    
    /* Creation of the file mapping object */
    hMappingFile = CreateFileMapping(
      INVALID_HANDLE_VALUE, // use paging file
      NULL,                 // default security
      PAGE_READWRITE,       // read/write access
      0,                    // maximum object size (high-order DWORD)
      BUFFER_SIZE,          // maximum object size (low-order DWORD)
      SHARED_MEMORY_NAME);  // name of the mapping object
    /* Exception handling */
    if (hMappingFile == NULL)
    {
    
    
      /* The file mapping object could not have been created */
      _tprintf(TEXT("Could not create the shared memory called: %s\n"), SHARED_MEMORY_NAME);
      return 1;
    }
    /* Create a view of the file in the process address space */
    sharedMemoryAddress = MapViewOfFile(hMappingFile, FILE_MAP_ALL_ACCESS, 0, 0, BUFFER_SIZE);
    /* Exception handling */
    if (sharedMemoryAddress == NULL)
    {
    
    
      /* No pointer to the file view could have been returned */
      _tprintf(TEXT("Could not map the shared memory called: %s\n"), SHARED_MEMORY_NAME);
      /* The process no longer needs access to the file mapping object */
      CloseHandle(hMappingFile);
      return 2;
    }
  }
  /* No error occurred */
  return 0;
}

四、读取内存空间函数

既然已经介绍了共享内存写入函数,那自然就可以利用读取共享内存函数来读取共享内存。该函数需要输入共享内存的偏移地址即可。这个偏移地址与写共享内存的输入偏移地址保持一致时,即可利用读取共享内存函数来获取写入共享内存函数写入的数值了。具体代码如下所示:

/*******************************************************************************
* Function read_register()
* Goal  : Get the current value of the desire HW register address that is mapped
*         on top of the offset address of the shared memory location
* IN    : - address of the register in unsigned 16 bit
* IN/OUT: -
* OUT   : - value stored in the register as an unsigned 8 bit
*******************************************************************************/
unsigned char read_register(unsigned short address)
{
    
    
  /* Access the shared memory area */
  if (open_shared_memory() == 0)
  {
    
    
    /* Output the value of the desired register */
    return *(sharedMemoryAddress + address);
  }
  else
  {
    
    
    getchar();
    exit(EXIT_FAILURE);
  }
}

五、头文件 .h 与一些附件信息

由于以上几个函数使用了mex获取指针的函数和windows提供的开辟共享内存的函数,因此需要加入一些头文件。并声明一些全局变量辅助。

六、实验测试

具体的函数 . c文件和函数声明头文件 .h 可在文末下载源代码。
压缩包中提供了MATLAB/Simulink的共享内存操作函数,其中read_register.c和 write_register.c 分别负责读取和写入共享内存。用户可直接利用MEX命令编译这两个函数从而生成read_register.mex 和 write_register.mex 函数。编译成功且生成mex文件后,可同时打开两个MATLAB窗口进行测试:将一个MATLAB调用write_register写入共享内存,同时将另一个MATLAB调用read_register读取共享内存。当共享内存的偏移地址,即这两个函数的输入地址保持一致时,可输入的共享内存偏移地址为整数即可(例如0xA010),当写入值和读取值相同时,即可实验了这两个MATLAB共同操作了共享的内存地址。(注意这两个函数接收的读取和写入的数值为uint8类型,当传入该函数的数值必须转换为uint8类型,例如uint8(255));
另外,压缩包中还提供了对double量操作的读取和写入共享内存函数,分别为read_register_double.c 和 write_register_double.c ,在压缩包中已经编译完成了 MEX 文件。由于MATLAB中默认的数值类型均为double,因此用户可直接输入MATLAB中的变量进行实验。
以上内容可以将多个MATLAB之间传递参数,与MATLAB和 .Net,C,C#应用程序交互类似,利用C#调用共享内存函数即可读取MATLAB写入的数值,(可参考附件2,github共享内存函数应用的参考例子与附件3,C#共享内存操作源码),从而实现了MATLAB与其他应用程序之间的数值交互。

总结

利用MATLAB和C混合编程,采用开辟共享内存函数、读取共享内存函数、写入共享内存函数、以及MATLAB与C混合编程的入口mexfunction函数,即可完成MATLAB对共享内存的操作,从而实现MATLAB与其他应用程序之间的数据传递。

欢迎评价与留言。

源代码下载

猜你喜欢

转载自blog.csdn.net/qq_36320710/article/details/112004756