simulink可对生成的代码做深度定制化,主要从以下几个方面:
- 控制函数原型(function prototypes)
- 函数接口复用(reuse function interface)
- 数据存储管理(storage class & memory section)
- 函数模板(function templates)
下面一一说明:
控制函数原型(function prototypes)
默认设置下,3个入口函数和为以下形式
且输入输出和非内联变量会以全局结构体的形式被函数访问。
函数名管理
但我们可以对此进行管理:在模型的code mapping - c的界面中(序号1)点击function(序号2),我们可以在function name(序号3)处对3个入口函数的名字进行修改(图中已经做了修改),并且可以点击4修改model_step的函数原型(初始化函数和终止函数的原型不可修改,只能改函数名)。
我们点击上图4修改step函数的原型,在下图勾选3然后点击4会出现详细设置,如下图
1是step函数原型的预览
2是函数名
5是设置step函数的返回值,可以返回指针也可以返回变量
6是step函数的返回值和形参设置
7是初步校验
函数返回值管理
3处如果选择void,函数声明里的形参有一个指针,指向输出值。此时该step函数为无返回值(return),而生成了一个指针,指向本该输出的数据:静态变量FQF_arg_ETC_output_scaled
3处如果不选择void而是选择响应的信号标签(ETC_output_scaled),意思是:以数值方式返回计算结果(此时2出会改变)
代码如下,可着重对比一下”C return arguement“选项的影响,step函数的原型有变化:形参及返回值均变化。
extern void FQF_Step(real_T FQF_arg_pedal_scaled, real_T FQF_arg_angle_scaled, real_T *FQF_arg_ETC_output_scaled); // 选择void的代码
extern real_T FQF_Step(real_T FQF_arg_pedal_scaled, real_T FQF_arg_angle_scaled); // 选择信号标签(ETC_output_scaled)的代码
用return返回计算结果的值
函数形参管理
函数的形参也可以用指针传递,比如当输入信号为一个结构体时(vector),此时需要在形参的设置处选择为pointer即可。
总结一下:
- simulink仅允许用户在step函数上进行函数原型控制,但是可以对初始化函数和终止函数崇明ing
- 可以控制函数的形参和返回值(值和指针)
函数接口复用
一般来讲,函数的形参和返回值(输入和输出)都是以全局变量结构体的形式存在:
当需要对model_step函数进行多测调用时,全局变量结构体就会导致冲突:model_step.c中直接操作全局变量。
具体如下,下拉菜单选择“reusable function”和“individual arguments”。代码体现为:把全局变量改成独立的函数形参传进函数内部。(选择individual arguments时(code interface packaging会和控制函数原型里的configure arguments for steps function prototype冲突,需要取消configure arguments for steps function prototype的选择对勾))
代码如下,model_step形参里多了1个状态"RT_MODEL_piCtrl_prototype_sta_T *const piCtrl_prototype_start_M",2个输入“real_T piCtrl_prototype_start_U_pedal_scaled”、“real_T piCtrl_prototype_start_U_angle_scaled”,1个输出“real_T *piCtrl_prototype_start_Y_ETC_output_scaled”,此时该step函数就不是直接操作全局变量,而是操作函数内部变量(形参),不会出现数据冲突,此时函数便可复用。
model_step函数形参里的状态量的定义如下:
选择structure reference时
代码如下,1个状态“RT_MODEL_piCtrl_prototype_sta_T *const piCtrl_prototype_start_M”(同上),一个输入“ExtU_piCtrl_prototype_start_T *piCtrl_prototype_start_U”,1个输出“ExtY_piCtrl_prototype_start_T *piCtrl_prototype_start_Y”。
为何2个输入变成了1个输入?原因是把输入输入变成了结构体,2个输入信号被封装在一个结构体里。但本质是一样的,都是step函数对形参操作而不对全局变量操作。
选择为part of model data structure时,
代码如下,
model_step函数的形参超级简单,只有一个“RT_MODEL_piCtrl_prototype_sta_T *const piCtrl_prototype_start_M”。但是我们看下这个“piCtrl_prototype_start_M”里有什么。和model.c的赋值对比一下,其实本质也一样,把step函数对形参操作而不对全局变量操作,只不过函数接口的封装形式不同。
数据存储管理
下面这东西叫embedded coder dictionary,听起来有点像数据字典,用处也比较相似,用来管理data access formats,function templates,memory sections等。在1中点击2会弹出3,embedded coder dictionary中有3个主要界面:storage classes(很重要,数据字典必用)、function customization templates和memory section。其中storage classes和memory section配合使用,memory section用于精确管理变量的声明和定义,storage classes相当于对memory section进行了一次扩充和更多特性管理的封装。
storage classes可通过变量声明&定义对内存进行精确管理,此处也可定义关键字#pragma:
函数模板
上面说的embedded coder dictionary定义函数模板function templates,和C++里的函数模板不是一个意思,此处的函数模板有点类似于函数命名规则,见下图3,$R和N是命名规则缩写,鼠标放在上面就会弹出所代表的意思,比如代表模型名字等。