本文通过一个例程来简述如何使用Legacy Code Tool将外部C函数集成到Simulink模型中。

1 问题引入

假设一个公司在旧的项目中,没有通过Simulink建模并生成代码,而是纯手写的C代码。在新的项目中应用了Matlab的这一套工具链进行MBD开发,想要在模型中直接调用以前写的C函数可以吗?

答案是肯定的。很多工程师会首先想到通过S-function去完成。博主想介绍一种更加简便的方法——Legacy Code Tool,把已经写好的C函数集成到Simulink模型中。现有的C代码中的函数将会作为一个Simulink模块进行仿真以及生成代码。

其实Legacy Code Tool背后的机制也是通过S-Function实现,但是LCT可以直接生成.mexw64文件和S-Function代码,而不需要工程师手动创建。

2 编译器安装

由于集成C函数的过程中涉及到了C代码的编译,所以必须在Matlab中安装一个MingW64编译器,方法如下。

1)首先进入安装页面https://www.mathworks.com/matlabcentral/fileexchange/52848-matlab-support-for-mingw-w64-c-c-compiler?s_tid=mwa_osa_a

2)点击右侧的Download下载安装文件mingw.mlpkginstall。
在这里插入图片描述
3)在Matlab中打开这个安装文件,就会自动安装。
在这里插入图片描述
4)安装完成后,在Matlab命令行窗口输入mex -setup,就会显示MinGW64编译器信息如下:
在这里插入图片描述
这样就算是安装完成了。如果安装不成功就会报错。

3 Legacy Code Tool简单示例

本章节通过一个简单的例程来演示通过LCT集成C函数的方法。

3.1 创建C文件和头文件

既然是集成C函数,那么必须得要有个C文件和头文件才行。

1)首先在Matlab当前文件夹中新建一个gougu_lct.cgougu_lct.h文件。
在这里插入图片描述
2)打开C文件gougu_lct.c,在其中写入如下代码,包含一个计算勾股定理的函数。

#include <math.h>
#include "gougu_lct.h"

float gougu_lct(float* a, float* b)
{
    float c;
    c = sqrt((pow(*a,2) + pow(*b,2)));
    return c;    
}

3)打开头文件gougu_lct.h,写入函数的声明。

float gougu_lct(float* a, float* b);

3.2 通过LCT生成S-function模块

1)在Matlab命令行运行legacy_code函数来初始化一个LCT结构体def,该结构体中包含LCT的一些属性信息。

>> def = legacy_code('initialize')

def = 

  包含以下字段的 struct:

                  SFunctionName: ''
    InitializeConditionsFcnSpec: ''
                  OutputFcnSpec: ''
                   StartFcnSpec: ''
               TerminateFcnSpec: ''
                    HeaderFiles: {}
                    SourceFiles: {}
                   HostLibFiles: {}
                 TargetLibFiles: {}
                       IncPaths: {}
                       SrcPaths: {}
                       LibPaths: {}
                     SampleTime: 'inherited'
                        Options: [1×1 struct]
                        

2)接下来为这个结构体指定一些具体的属性值

>> def.SFunctionName = 'gougu_lct_sfcn';
>> def.HeaderFiles = {'gougu_lct.h'};
>> def.SourceFiles = {'gougu_lct.c'};
>> def.IncPaths = {'E:\学习\博客\Simulink代码生成\LCF'};
>> def.SrcPaths = def.IncPaths;

其中,各个属性的意思如下:

属性名称含义
def.SFunctionNameS-function的名字,也就是对应的模块名
def.HeaderFiles头文件名称,要用单元数组
def.SourceFiles源文件名称,要用单元数组
def.IncPaths头文件路径,这个根据自己放的路径来写,不要照搬博主的
def.SrcPaths源文件路径,这里和头文件路径相同

3)指定OutputFcnSpec,该属性是一个字符串,通过一定的书写规律表现出C函数和对应的S-Function(也就是最后的Simulink模块)之间的输入、输出映射关系。

>> def.OutputFcnSpec = 'single y1 = gougu_lct(single u1[1],single u2[1])';

这里做一些简单解释:

字符含义
single y1表示模块的输出y1是single类型,对应C函数返回的变量c
gougu_lctC代码中的函数名
single u1[1]u1表示第1个输入参数,对应C函数的第1个参数,类型为single
single u2[1]u2表示第2个输入参数,对应C函数的第2个参数,类型为single

需要注意一点,参数个数不同、返回个数不同、类型不同都会影响OutputFcnSpec字符串的写法,具体写法可以参照帮助文档Integrate C Functions Using Legacy Code Tool

4)通过legacy_code函数生成S-function源文件,在Matlab命令行运行如下指令。

>> legacy_code('sfcn_cmex_generate',def);

运行完成后会在Matlba当前路径下生成gougu_lct_sfcn.c这个S-function源文件。
在这里插入图片描述
5)通过legacy_code函数编译这个S-function源文件,在Matlab命令行运行如下指令。

>> legacy_code('compile',def);

然后,Matlab命令窗口会显示一些编译信息。
在这里插入图片描述
同时,会在当前路径生成gougu_lct_sfcn.mexw64文件。
在这里插入图片描述
6)通过legacy_code函数在一个新的Simulink窗口中生成S-function模块,该模块拥有C函数的功能。在Matlab命令行运行如下指令。

>> legacy_code('slblock_generate',def);

弹出的Simulink模型如图所示。
在这里插入图片描述

3.3 模型仿真

1)给模型加上输入常量,和输出display模块,并将常量设置为3和4.
在这里插入图片描述
2)由于C函数中输入输出是浮点型,并且OutputFcnSpec属性中也指定了single类型,所以要把输入的Constant常量的类型都改为single。
在这里插入图片描述
3)Ctrl + D更新模型,就会看到输出显示的数值是5,符合勾股定理的结果。
在这里插入图片描述
同时注意到,输入输出的类型都是single。

3.4 代码生成

1)通过legacy_code函数生成tlc文件,为后面的代码生成做准备。在Matlab命令行运行如下指令。

>> legacy_code('sfcn_tlc_generate',def);

运行后,Matlab当前路径会生成gougu_lct_sfcn.tlc文件。

2)回到模型,将输入输出替换成Inport和Outport,并配置好Embedded Coder代码生成。
在这里插入图片描述
3)Ctrl + B生成代码。生成代码成功后,可以在step函数中看到对勾股定理函数的调用。
在这里插入图片描述
在demo.h头文件中也包含了gougu_lct.h头文件。
在这里插入图片描述
在后面编译软件的时候,不要忘记把gougu_lct.c和gougu_lct.h加进去一起编译。

4 总结

本文只是演示了一个使用LCT的基本方法,展示了一个完整的流程。实际项目中可能会遇上更多的问题需要想办法解决。

>>返回个人博客总目录

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐