本文研究如何在Matlab Function模块中调用外部的C函数,以及一些注意点。
文章目录
1 问题引入
博主以前写过一篇博客《Simulink代码生成:通过Legacy Code Tool集成C函数》,可以在Simulink模型中调用外部的C代码。在Matlab Funciton中,用m语言写着代码,突然想要调用一个现成的C文件中的函数是不是也可以呢。答案是肯定的,而且调用的方法比LCT还要更简单一些。本文会用一个简单的例子来演示在Matlab Function模块中调用外部的C函数的方法。
2 简单例程
2.1 创建C文件和头文件
首先先在当前路径下创建一个C文件和一个头文件,如下图所示:
C文件内容如下所示,实现了一个简单的勾股定理的计算:
//gougu.c
#include <math.h>
#include "gougu.h"
float gougu(float a, float b)
{
float c;
c = sqrt((pow(a,2) + pow(b,2)));
return c;
}
头文件内容如下所示,对函数进行外部声明:
// gougu.h
extern float gougu(float a, float b);
2.2 在Matlab Function中调用C函数
1)新建一个Simulink空白模型demo.slx,在其中加入两个输入port,一个输出port和Matlab Function:
这里博主把数据类型强制设为single,为了和C代码匹配,否则后面会报错。在实际使用的过程中如果调用的C代码传参类型和Matlab中不一致也会报错,这个要注意。
2)双击进入Matlab Function,改成如下m代码:
代码很精简,但是其中每一部分都很重要。
首先是用了coder.cinclude和coder.updateBuildInfo函数,添加了调用函数的C文件和头文件。
coder.cinclude('gougu.h');
coder.updateBuildInfo('addSourceFiles', 'gougu.c');
接下来先要对输出参数做一个初始化,这点是符合C语言的用法,最好要做,否则有可能Matlab Function无法编译通过。
y = single(0);
然后再用coder.ceval函数调用外部函数,其中的第一个参数是函数名,后面的参数依次为C函数传入的参数(将Matlab变量传参进入C函数),等号左边是C函数的返回值赋值给Matlab变量。
y = coder.ceval('gougu',u,u2);
这样就完成了这个简单的例子,可以在Simulink里仿真C函数了。
2.3 仿真及代码生成
1)将Matlab Function的输入改成常数,输出改成disp模块,可以先仿真看一下调用的效果。
注意到Matlab当前路径下生成了一个demo_sfun.mexw64文件,这是编译C代码的过程中产生的mex文件。因此,如果修改了C代码后,要先删掉这个mex64文件再重新运行Simulink仿真。
2)在初步验证仿真结果后,把输入输出改成Port,配置代码生成。生成的demo.c文件如下图所示。
图中可以看出,在生成的代码中调用了gougu()这个C函数。
在生成的demo.h文件中也会包含gougu.h这个头文件。
最后软件编译的时候不要忘记把外部的C文件和头文件一起加进去。
3 其他技巧和注意点
3.1 在Simuink中配置C文件和头文件
如果模型中有很多地方用了Matlab Function调用外部C文件,每个地方都写上coder.cinclude和coder.updateBuildInfo配置C文件和头文件会很繁琐。这时,可以考虑在Simuink中配置C文件和头文件,该配置会作用于所有Matlab Function。
首先将Maltab Function中的前两行注释掉:
function y = fcn(u , u2)
% coder.cinclude('gougu.h');
% coder.updateBuildInfo('addSourceFiles', 'gougu.c');
y = single(0);
y = coder.ceval('gougu',u,u2);
end
接着在Simulink配置中的Simulation Target中加上C文件和头文件:
然后删除当前路径下的mex64文件,再重新仿真Simulink即可。
3.2 C函数传入指针参数
在C语言中,可以传入指针的参数,利用这个指针获取计算的结果,而不是函数return的返回值。这一点也是可以用MatlabFunction做到的。
例如,将C文件和头文件修改一下:
//gougu.c
#include <math.h>
#include "gougu.h"
void gougu(float a, float b, float* c)
{
*c = sqrt((pow(a,2) + pow(b,2)));
}
// gougu.h
extern void gougu(float a, float b ,float* c);
这样的情况下,函数返回为void类型,而返回的结果用float* c这个指针来代替。这样的话,函数体内的计算结果会存放在该指针所指向的内存空间中。
在Matlab Function中要改成如下所示:
function y = fcn(u , u2)
coder.cinclude('gougu.h');
coder.updateBuildInfo('addSourceFiles', 'gougu.c');
y = single(0);
coder.ceval('gougu',u,u2,coder.wref(y));
end
区别就在于coder.ceval函数调用外部函数时,将等号左边去掉,而是加了一个coder.wref(y)这样的参数来对应C代码中的float* c。具体用法可以参照coder.wref的帮助文档。
生成代码后,如下所示:
在step函数中调用gougu()函数时,第三个参数传入了Out1的地址(利用&字符取地址),从而也实现了同样的效果。
4 总结
本文研究了通过Matlab Function集成C函数的方法,做了简单的示例。在实际使用时,会遇到比demo更加复杂的情况,例如数组、结构体怎么处理,再例如C代码没办法进去调试等。因此,使用这种方法需要慎重,要进行充分的验证才行。