基于模型开发之代码集成——LegacyCodeTool

来源:互联网 发布:活动网络正在识别 编辑:程序博客网 时间:2024/06/12 20:44

有两种比较简单的方法可以将代码集成到模型中。

1、使用Legacy Code Tool来集成C代码到Simulink模型

2、在StateFlow建模过程中使用CustomCode 集成或调用已有C代码

下面主要介绍第一种方法。

LegacyCodeTool简要说明

 

Legacy Code Tool是Matlab中的一个命令行工具,它可以很方便的将已有的C代码或者C++代码(注:不支持C++对象)与模型结合起来。

可以将C或C++代码编译生成用于仿真的S-fuction;也可以生成一个封装了外部C代码参数化的S-fuction模块。使用过程非常的简便,但是也有一些限制,所以当你已有的代码不是C或者系统比较复杂,比如包含了控制器和运行环境的混合系统,关于代码的集成可能还是要求助于S-Fuction builder或者手写S-fuction来解决。

好了别的不多讲,下面先简单介绍一下Legacy Code Tool的工作步骤:

1 、首先生成一个特定的Legacy Code Tool的参数集(其实也就是一个结构体变量),这个参数集指定了你要包含的C代码,头文件,以及生成s-fuction的名称等等诸多信息。

2、通过Legacy Code Tool的命令行语句调用第一步配置好的参数集变量,生成相应的S-fuction源文件;

3、通过Legacy Code Tool的命令行语句编译生成的S-fuction源文件,使其变为可动态加载的执行文件,在32位windows系统中也就是mexw32文件;

4、如果需要还可以使用Legacy Code Tool的命令行语句将上面生成s-fuction执行文件封装成simulink模块。

基本上用Legacy Code Tool来集成C代码的应用也就是上面这么四步,大致有个概念后我们实际先来看一个简单的例子,之后我们在对每一个步骤进行详细的介绍,然后再做一些复杂点的应用。

 

这里说的例子是matlab的help中的一个简单例子,用C语言写的将输入放大两倍的函数:

1、准备好你的C文件和相应的头文件,如下:

 doubleIt.c doubleIt.h

 #include "doubleIt.h"

double doubleIt(double inVal)

{

  return(2*inVal);

}

#ifndef _DOUBLEIT_H_

#define _DOUBLEIT_H_

 

double doubleIt(double inVal);

 

#endif


2、定义一个Legacy Code Tool参数集变量,变量名为def,在matlab命令行中输入如下命令

 def = legacy_code('initialize')

legacy_code()就是matlab中Legacy Code Tool的功能函数,'initialize'是该函数其中的一个功能参数,即初始化一个参数集变量,如果上述语句在输入回车时没有打分号,可以看到matlab命令行中反馈的如下内容:

 def =

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

 

3、针对我们这个小例子,只需要指定其中的四项:源文件、头文件、S-fuction的名字、S-fuction的输出函数,同样使用命令行的方式进行参数的配置,如果源文件和头文件在当前目录下,可以在matlab命令行中输入下面语句:

def.SourceFiles = {'doubleIt.c'};
def.HeaderFiles = {'doubleIt.h'};

def.SFunctionName = 'ex_sfun_doubleit';
def.OutputFcnSpec = 'double y1 = doubleIt(double u1)';

 

4、完成上述配置后,在matlab命令行输入下面的命令,生成相应的S-fuction源文件

 legacy_code('sfcn_cmex_generate', def);

可以看到在matlab当前工作目录下生成了相应的S-fuction源文件:ex_sfun_doubleit.c

 

5、然后,继续在matlab命令行输入下面的命令,对生成相的S-fuction源文件进行编译

 legacy_code('compile', def);

这里要注意,必须事先配置matlab所要使用的C语言编译器,否则这一步会无法执行,一般32位matlab安装的时候会附带有一个LCC的编译器,也可以安装任何matlab可识别的编译器,然后使用 mex -setup命令,按照matlab中的提示设定好默认的编译器。

在编译的过程中,Matlab的命令行中会输出相应的文字提示,例如:

###Start Compiling……

……

### Finish Compiling ex_sfun_doubleit
### Exit

 

6、最后,在matlab命令行输入下面的命令,可以利用前面编译的可执行文件生成一个S-fuction的simulink模块。

 legacy_code('slblock_generate', def);

生成的模块如下图所示:



大家可以分别给模块加上输入信号和示波器,看一下模块运行的结果应该和C语言的意图一致。

这一部分的内容基本上来自于Matlab的Help,第二部分讲会详细介绍下legacy_code命令的使用方法和参数集中参数的含义。


LegacyCodeTool命令及参数详解

经过前一篇文章的介绍,基本上Matlab的LegacyCodeTool能干什么有个大概的了解。下面对LegacyCodeTool的命令和参数集做一些介绍。

LegacyCodeTool的命令集如下:

legacy_code('help')
specs = legacy_code('initialize')
legacy_code('sfcn_cmex_generate',
 specs)
legacy_code('compile',
 specs, compilerOptions)
legacy_code('slblock_generate', specs, modelname)
legacy_code('sfcn_tlc_generate',
 specs)

legacy_code('generate_for_sim', specs, modelname)
legacy_code('rtwmakecfg_generate',
 specs)
legacy_code('backward_compatibility')

含义分别如下:

1、legacy_code('help')顾名思义打开legacy的Help文档。

2、specs = legacy_code('initialize'),生成并初始化一个specs参数集变量,后面会详细介绍该参数集。

3、legacy_code('sfcn_cmex_generate', specs)根据specs配置参数生成相应的s-fuction源文件。

4、legacy_code('compile', specs, compilerOptions),根据specs配置参数对s-fuction源文件进行编译和关联,形成可执行文件,compilerOptions指的是编译器的选项,如果编译器没有提供附加选项可以为空,也可以根据不同的编译器进行相应的参数指定格式为'-DCOMPILE_VALUE1=1',或者有多个参数时格式为{'-DCOMPILE_VALUE1=1', '-DCOMPILE_VALUE2=2', '-DCOMPILE_VALUE3=3',...}

5、legacy_code('slblock_generate', specs, modelname),根据specs配置参数将编译后的s-fuction执行文件封装成simulink模块,并添加到当前名称为modelname指定的模型中,如果没有将名称为modelname的模型将生成一个名称为modelname的模型文件,里面包含相应封装好的s-fuction模块。当然如果这里不指定modelname,也将会新建一个默认的模型文件。

6、legacy_code('sfcn_tlc_generate', specs)可以根据specs配置参数生成相应的tlc文件,通过对生成的tlc文件应用,可以订制代码集成到s-fuction中的模式进而模型的加速仿真或者自动代码生成时控制生成的代码形式、分布等。

7、legacy_code('generate_for_sim', specs, modelname),根据specs配置参数,一步完成s-fuction的代码生成、编译、模块封装,基本上等同于3、4、5条命令一起执行,当specs配置参数中的Options.useTlcWithAccel配置为1(真)时,也一并执行了第6条命令,生成了用于加速仿真的tlc文件。

命令中的第三个参数modelname的用法和第5条命令相同。

8、legacy_code('rtwmakecfg_generate', specs)本命令仅和自动代码生成simulink coder相关,会生成一个 rtwmakecfg.m文件,可以根据其中的一些接口函数对代码生成的过程进行控制(这里就涉及到另外的话题了)。

9、legacy_code('backward_compatibility')该命令自动更新legacy_code的相关语法使得LegacyCodeTool可以向后兼容。

在这些命令中,如果仅是将代码集成到模型中,那么常用的命令也就是2、3、4、5、7。使用的顺序为2-(配置参数集)-3-4-5或者2-(配置参数集)-7;

下面说一下参数应该怎么配置,根据前面讲的使用第2条命令可以生成一个参数集变量,名称是可以自己定义的,只要在matlab输入相应格式的命令,既可以获得如下结构的参数集变量。

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

其中,源文件和头文件是必须要定义的——HeaderFiles: {}; SourceFiles: {};在指定文件的时候可以使用绝对地址也可以使用相对地址;

此外SFuction的名称也一定要定义 SFunctionName: '';

初始条件函数、输出函数、启动函数、停止函数四个中至少定义其中的一个:InitializeConditionsFcnSpec: ''; OutputFcnSpec: ''; StartFcnSpec: ''; TerminateFcnSpec: '';

稍后详细解释函数具体的定义格式。

再看剩下几个可以选择进行配置的参数项,IncPaths: {};SrcPaths: {};LibPaths: {};分别可以指定和程序相关联的头文件地址,源文件地址,库文件地址; Legacy Code Tool 在处理相应的代码时除了会在当前工作文件夹,以及Matlab已定义的搜索文件夹内进行相关文件的搜索,也会在IncPaths: {};SrcPaths: {};LibPaths: {};定义的文件夹内进行搜索。(地址都可以使用相对地址或者绝对地址,同样采用命令行直接赋值的方式进行设置。)

( HostLibFiles: {};和 TargetLibFiles: {}应该也是类似的功能,但目前我还没用过,根据Help文件说的是一个是主编译器的相关库文件,一个是目标编译器的相关库文件,等后续有时间我再弄明白,会再写出来)。

接下来是 SampleTime:,其默认值为'inherited',即后续生成的s-fuction及其模块都将继承调用它的模型或函数的仿真采样时间,当然也还有其他两个选项,一个是'parameterized',可以将采样时间参数化,通过C-MEX的API函数进行设定;另外一个是指定固定的采样时间,采样时间的表述格式需要参看Help中Specify Sample Time一章。

这里需要注意的是,SampleTime这个参数的设置,必须其他的参数项赋值完之后,最后进行,否则可能会出现异常。

另外,还有一个参数设置项,Options,如果已经通过第2条命令定义了参数集变量,例如lctspc,则可以直接在命令行中输入:

 lctspc.Options

按下回车键后,可以看到

 ans =

                          isMacro: 0
                       isVolatile: 1
         canBeCalledConditionally: 1
                  useTlcWithAccel: 1
                         language: 'C'
                 singleCPPMexFile: 0
    supportsMultipleExecInstances: 0

 

以上分别为选项参数的默认值,如果集成的代码时C++,则需要将language定义为'C++';

如果需要集成的函数为C语言的宏定义函数则 isMacro需要定义为真(1),默认时为(0);

 

其他几个选项和s-fuction处理函数的方式有关,一般情况下保持默认即可,此处不展开解释(需要了解可以看考Help文件),下一次将详细介绍初始条件函数、输出函数、启动函数、停止函数具体的定义格式。


LegacyCodeTool之函数声明

 

根据前面讲过的使用命令 specs = legacy_code('initialize') 生成的结构体变量中specs要定义一系列函数:初始条件函数、输出函数、启动函数、停止函数( InitializeConditionsFcnSpec: '',OutputFcnSpec: '',StartFcnSpec: '',TerminateFcnSpec: '')。

在定义这些函数时要注意:

1、输入参数在函数中不能被改变

2、函数体的返回值不能为指针(可以为指针指向的值)

3、初始条件函数、启动函数、停止函数不能通过输入输出参数来定义。

 

这些函数的定义方式都是通过带关键字的字符串来定义的,关键字如下:

y1,y2,……,yn用来表示输出变量

u1,u2,……,un用来表示输入变量

p1,p2,……,pn用来表示参数变量

work1,work2,……,workn用来表示工作向量参数

 

(个人理解:C语言中一般没有p,work的概念,之所以会有这两种形式的函数变量,是因为matlab本身是处理矩阵运算的,一般来说函数都可以理解或翻译为矩阵运算的形式,都可以转换为状态方程,所以有了参数变量和工作向量的概念。个人理解欢迎指正。)

 

下面列出,C语言中常见的函数形式,对应LegacyCodeTool应该使用的表达方法

C语言                                                       对应字符串

void fuction(void)                                     'void fuction(void)'

void fuction(TYPE x1,TYPE x2,...)          'void fuction(TYPE u1,TYPE u2,...)'

TYPE fuction(void)                                  'TYPE y1 = fuction(void)'

TYPE fuction(TYPE x1,TYPE x2,...)       'TYPE y1 = fuction(TYPE u1,TYPE u2,...)'

TYPE fuction(TYPE *x1,TYPE x2[],...)    'TYPE y1 = fuction(TYPE u1[1],TYPE u2[],...)'

void fuction(TYPE *y1,TYPE y2[],...)       'void fuction(TYPE y1[n],TYPE y2[n],...)' %n为实际应返回的值的大小,个数

TYPE fuction(TYPE *y2,TYPE y3[],...)    'TYPE y1 = fuction(TYPE y2[n],TYPE y3[n],...)'

......

 

其他可以依次类推,其中TYPE为数据类型,一般常见的数据类型,LegacyCodeTool的y,u,p,work都支持,空指针只有work可以支持。

 

所以使用,LegacyCodeTool的基本过程就是下面三步:

1、初始化一个参数化的结构体变量

2、对结构体变量中的参数及参数表达式进行赋值

3、使用相应的命令集生成sfun或者编译等等


LegacyCodeTool之例2

下面根据之前讲的LegacyCodeTool再举一个例子:

底层在进行通信时常会遇到一些数据解析或分解情况,可能的函数如下:

test.c

#include "test.h"

void testcan(unsigned char * phval, unsigned char *sg1,unsigned int *sg2,unsigned int *sg3,unsigned int *sg4,unsigned char *sg5)
{
   *sg1=phval[0];
   *sg2=(((unsigned short int)phval[2])<<8)+(unsigned short int)phval[1];
   *sg3=(unsigned short int)phval[3]+(unsigned short int)phval[4];
   *sg4=(unsigned short int)phval[5]*2;
   *sg5=phval[6]|phval[7];
};

 

test.h

 #ifndef ____testcan_H
#define ____testcan_H

extern void testcan(unsigned char *phval, unsigned char *sg1,unsigned int *sg2,unsigned int *sg3,unsigned int *sg4,unsigned char *sg5);

#endif

 

可以编辑如下m脚本或依次在Matlab中输入下列命令:

 %% Define Legacy Code Tool options
testdecode = legacy_code('initialize');                          %Create specifications structure
testdecode.OutputFcnSpec = 'void testcan(uint8 u1[8], uint8 y1[1],uint16 y2[1],...

           uint16 y3[1],uint16 y4[1],uint8 y5[1])';     %Function to be called during simulation
testdecode.HeaderFiles = {'test.h'};                       %Necessary external header files
testdecode.SourceFiles = {'test.c'};                       %Necessary external source files
testdecode.SFunctionName = 'testmycode';                 %S-function name

%% Create S-function, TLC file, etc.
legacy_code('sfcn_cmex_generate', testdecode);     %Generate S-function code
legacy_code('compile', testdecode)                            %Compile S-function
legacy_code('slblock_generate',testdecode)              %Create block for S-function
%legacy_code('sfcn_tlc_generate', testdecode)           %Inline S-function by creating TLC file
%legacy_code('rtwmakecfg_generate', testd
ecode)     �d path info to RTW make file

 

然后会自动生成相应的slx模型如下:



然后,该模块可以正常用于simulink仿真,可以联合模型和底层代码一起进行仿真验证。

 

另外输入输出可以可以使用结构体的形式,但是需要事先在h头文件中定义好相应的结构体声明,大家可以自己试验一下,纸上得来终觉浅,绝知此事要躬行。


0 0