Matlab - 经VS将.m文件转换为.dll文件
引言
在使用 LabVIEW 进行软件开发的过程中,有时需要使用 Matlab 强大的数据处理功能,以提高开发效率,此时可以通过 Matlab Coder 将 Matlab 中的函数转换为 C/C++ 源文件,然后通过 Visual Studio 将这些源文件封装为 .dll 文件,最后通过 LabVIEW 调用库函数实现对 Matlab 函数的调用。
本文将通过一个实例讲述以上功能的实现过程,大体步骤如下:
- Matlab 编写代码
1.1 编写 .m 函数
1.2 使用 Coder 生成 C/C++ 文件 - Visual Studio 生成 .dll 文件
- LabVIEW 调用 .dll文件
软件版本说明:
- Matlab 2016a (64bit)
- LabVIEW 2015 (64bit)
- Visual Studio 2015 (64bit)
通过 Matlab 生成 C/C++ 源文件
.m 函数编码
在 Matlab 文件目录中新建文件夹"CreateDll",在该文件夹下新建两个函数文件,分别实现简单加法运算函数,以及一个用自相关求解周期信号频率的函数。
add(a, b)
function [ answer ] = add( a,b )
% 加法运算
% a,b 均为 double 型数据
answer = a + b;
end
getFreq( signal, fs )
function [ frequency ] = getFreq( signal,fs )
%GETFREQ 自相关法求周期信号频率
% [input]
% signal : double 1*n
% fs : double 1*1
% [output]
% frequency : double 1*1
y_xcorr = xcorr(signal,'coeff'); % 信号自相关
[~,locs] = findpeaks(y_xcorr); % 获取峰值位置
middlePosition = fix(length(locs)/2); % 计算处于中部的峰值位置
frequency = fs/(locs(middlePosition)-locs(middlePosition-1)); % 计算信号频率
end
使用 Coder 生成 C/C++ 文件
- 1.在命令行中输入 coder 打开 Matlab Coder 工具。
>> coder
- 2.添加 add 函数及 getFreq 函数
- 3.定义函数输入参数的数据类型(两种定义方式)
- 手动修改输入参数的数据类型
- 输入函数调用示例,通过 Coder 工具的自定义输入功能自动匹配输入参数类型
调用示例:
>> add(3,4);
手动可选择的数据类型如下图所示:
- 4.设定数据长度
- 对于大部分变量,数据长度为 1×1
- 对于一维向量、数组
- 1 固定长度的变量:直接设定具体值(如:一个星期每天的工作时间,可设为 1×7)
- 2 不定长度但有上限的变量:设定 1×:100 (假定上限值为 100)
- 3 不定长度上限不定的变量:设定 1×:inf (inf代表无限)
- 二维数组设定规则与一维数组类似
- 5.Check for Run-Time Issues
这一步可以跳过,这是用来生成试用代码以及用于 Matlab 的 MEX 文件。
- 6.Generate Code
选择 C++ 语言,通过 " More Settings " 修改代码生成相关的配置选项,最后点击 " Generate " 按钮生成源代码。
选择语言<
配置选项<
生成代码<
在代码生成界面可以看到以下内容:
- Source Code : Matlab 中创建的函数源代码
- Output Files : Coder 工具生成得到的 .h 头文件和 .cpp 源文件
- Build Log : 用于查看代码生成报告
- Variables : 选中 Matlab 文件时显示函数的输入输出数据类型和大小
- 7.Finsh Workflow
完成工作流程<
查看代码<
可以看出,最终生成的文件较多,代码量较大,这可以归结为以下几个原因:
- 重新实现fft等复杂算法
- 考虑各种异常情况的处理
- 考虑不定长数组或向量的内存分配问题
- 考虑无限值 inf 和 无效值 NaN
- 不同编程语言间数据类型的转换
当然,我们可以在代码生成前根据需求进行相关设置,以避免不必要文件的产生,此处不予详述。
最后将生成的所有 .h/.cpp 文件复制到剪切板中以备后用。至此,Matlab 部分的工作已经全部完成。
通过 Visual Studio 生成 .dll 文件
在 VS 中要做的就是将 Matlab Coder 生成的 C++ 文件封装为 dll 文件,以供 LabVIEW 程序调用。
创建 win32 项目
在win32应用程序向导中选择 DLL,并勾选 空项目 ,然后点击完成。
添加文件
将从 Matlab 中复制的 C++ 文件粘贴到 CreateDll 项目的本地文件中,然后在项目中添加这些文件。
新建模块定义文件
在 VS 中新建 .def 模块定义文件,输入以下代码
LIBRARY CreateDll
EXPORTS
add
getFreq
LIBRARY 后面添加库名称,EXPORTS 后面添加需要导出的函数名称。
编辑完成后,可以在 "项目属性>>链接器>>输入>>模块定义文件" 中看到,项目已将该选项自动添加为了当前 .def 文件,对于低版本的 VS 可能需要手动添加。
生成 .dll 文件
最后一步,选中项目名称,点击右键选择 "生成" ,从本地项目目录的 Debug 文件夹中可以找到已生成的 .dll 文件。
值得注意的是:
- 选择 Debug 模式为 x64 时生成 64 位 dll 文件,文件存于 .\x64\Debug\Create.dll
- 选择 Debug 模式为 x86 时生成 32 位 dll 文件,文件存于 .\Debug\Create.dll
对于不同位数的 LabVIEW 程序,我们可以选择对应的 Debug 模式,这个功能真的是非常棒!
函数接口
经 VS 生成的 API 函数接口说明如下:
add
double add(double a, double b);
getFreq
double getFreq(const double b_signal[1000], double fs);
因为例程所用数据为定长数组(1×1000),所以输入参数的数据类型为基本的 double 型。倘若定义的 b_signal 为不定长数组 (1×:inf) ,其格式将变为:
double getFreq(const emxArray_real_T *b_signal, double fs);
这是因为程序需要为不确定长度的数组动态分配内存,所以有效数据与其描述信息将被封装到结构体中,该结构体为 "emxArray_real_T"
struct emxArray_real_T
{
double *data;
int *size;
int allocatedSize;
int numDimensions;
boolean_T canFreeData;
};
结构体中的 "boolean_T" 定义如下
typedef unsigned char boolean_T;
由于不定长数据会导致大量冗余代码,并且在 LabVIEW 中调用时非常麻烦,除非必须,否则不建议使用,本文后续所述也是定长数组。
通过 LabVIEW 调用 .dll文件
通过 VS,我们已经得到了想要的 .dll 文件,那么在 LabVIEW 中该如何调用呢?这里提供两种方法。
- 通过调用库函数节点(Call Library Function Node)
- 通过 LabVIEW 导入工具(Tools->Import->Shared Library(.dll)...)
两种方法的原理一致,以第一种方法为例进行讲解。
Call Library Function Node
在 LabVIEW 函数面板搜索 "Call Library" ,中文版可直接搜索 dll
。将调用库函数节点放入程序框图中。
调用 add 函数
- 选择 CreateDll.dll 文件
- 配置 add 函数接口
- 函数调用示例
调用 getFreq 函数
- 配置 getFreq 函数接口
- 函数调用示例
先产生一个数据长度为 1000 ,频率可调的正弦信号,采样频率 1kHz;然后调用 .dll文件中的 getFreq 函数,通过自相关方法求取信号频率,并输出到变量 getFreq 中。
不定长数组问题
这里对不定长数组问题进行简单介绍:.dll 文件中的函数将不再是简单的 double 指针,而是一个结构体指针;在 LabVIEW 中调用时,可以创建自定义控件,用簇代替该结构体,簇的引用作为结构体指针;簇的内容包括以下几部分。
double *pData : 一维数组引用,用于传递信号数据
int *pSize : 一维数组引用,用于传递数据长度
int allocatedSize : 分配内存大小,调用时可设为 0
int numDimensions : 数组维数,设为 1 或 2
unsigned char canFreeData : 布尔变量,确定能否释放数据内存
小结
本文对 "Matlab Coder 生成 C++ 代码","VS 生成 .dll 文件","LabVIEW 调用外部库函数" 这三部分内容进行讲述。通过Matlab , VS , LabVIEW 的结合使用,我们便可实现 LabVIEW 程序调用 Matlab 函数的目的。
此外,在 64 位的 VS 中可以通过修改 Debug 模式生成 32 位或 64 位的动态链接库,以适配不同版本的 LabVIEW。当然最后不要忘了,不同编译模式下生成的 .dll 文件存放在不同的目录下。
版权声明:本博客所有文章除特殊声明外,均采用 CC BY-NC 4.0 许可协议。转载请注明出处 litreily的博客!