之前一篇博客《Simulink代码生成: 信号线、参数配置》中,提及了一部分Storage Class(存储类型)的配置及其代码。本文更加详细地研究Storage Class中各个选项的含义以及生成的代码。
首先打开Simulink,建立一个简单的具有输入、输出和Gain模块的模型。将输入信号线命名为Input1,并Resolve to Simulink Signal object,也就是左边的蓝色小叉子。
然后在Matlab命令行输入:
>> Input1 = Simulink.Signal;
此时在Matlab工作区就会建立一个名为Input的信号对象。双击Input1,就可以编辑该对象,包括编辑Storage Class。
将Data Type配置为single,然后本文开始研究Storage Class中的各个选项,以及对应生成的代码。
Storage Class的第一个选项就是Auto,也就是自动的意思。字面上看不出来自动的存储类型究竟是什么样,所以Ctrl + B生成代码看一下。
观察生成的c文件可以看出,Input1是作为demo_U这个结构体变量的一个成员出现的。这个结构体的定义又是在demo.h中完成的,见下图。如果模型由多个输入,那么这个结构体中就会有多个一一对应的成员。
这种Auto的方式一般是不会使用的。如果需要了解更多Auto配置的生成代码的机制,可以参考Matlab帮助文档《Standard Data Structures in the Generated Code》。
将Storage Class选为Model Default后,会多出两个文本框可以填写。
其中,Alias表示该存储对象的别名。也就是说,该信号无论在Simulink里面叫什么名字,生成代码的时候都会换成这个名字。Alignment表示数据对齐边界,但博主也并不是很了解,可以参考帮助文档。一般设为-1的默认值,生成代码的时候会采用最佳的对齐方式。
如果Alias和Alignment这两项默认不改,生成的代码就会和Auto一致。假设将Alias改为Input1_Alias,并生成代码,代码中所有原来的Input1,就会被替换成Input1_Alias,如下图。
将Storage Class选为Exported Global。字面上意思是将其输出为全局变量。
生成代码如下:
这里可以看到,生成的代码直接把Input1这个变量定义出来了,而不象是之前的结构体。而且,定义Input1的时候是在模型对应的c文件内定义的。接着再打开demo.h头文件。
可以看出,在demo.h中用extern关键字对Input1这个变量进行外部声明。也就是说,只要别的C文件包含了这个demo.h,就可以给Input1这个全局变量赋值。
这一点非常有价值,可以通过这个特性,在一个项目中用很多模块分别生成代码,分别编译代码,最后再链接到一起,从而实现了一个团队的分布式开发。而不需要将所有人的模型先集中成为一个大模型,再生成代码。
博主认为,Exported Global比较适合用于Outport的信号线,因为Inport的输入量一般是别的模型产生的,Outport是自己的模型产生的。
将Storage Class选为Imported Extern。字面上意思是这个变量是外部引入的。
生成代码如下:
可以看出,step函数中用到了这个Input1,但是并没有在本文件中定义这个变量。
打开demo_private.h可以看到,在这个头文件中外部声明了Input1。也就是说,别的文件中定义了Input1。
这个选项还是比较适用于输入端口的代码生成的。
将Storage Class选为Imported Extern Pointer,字面上意思是引入外部指针。
观察生成的step函数和private头文件可以发现,除了Input1是个指针,其余的和Imported Extern选项一样,这里不做赘述。
这个确实不太懂,工作中也没遇到过。为了不误导大家,就不写了,只占个坑。如果有了解的朋友,还望不吝赐教哈。
将Storage Class选为Volatile,字面上意思是可变的。选完以后,会多出3个用户参数可以填。HeaderFile指的是头文件,这里写headerfile.h,变量的外部声明就在这;DefinitionFile指的是C文件,这里写definitionfile.c,变量的定义在这个c文件中;Owner写chhttty(也就是博主的网名),也可以不写。
生成代码如下:
可以看出改变了的定义和声明是在headerfile.h和definitionfile.c中的。这里要注意到,Input1这个变量是被volatile修饰的,也就是告诉编译器,这个变量的值要从内存中读取。
搞汽车软件开发的肯定会马上想到标定量的概念。但是标定量定义的时候,一般还需要加上一个const关键字,以免程序其他地方修改这个变量值。如果要生成const这个关键字,可以通过脚本替换,也可以通过自定义用户自己的storageclass类型来实现。
将Storage Class选为ExportToFile,同时设定HeaderFile和DefinitionFile。
这个选项和Exported Global相似,会定义这个变量。区别在于,定义的文件不是在demo.c本身,而是用户自己指定的文件HeaderFile和DefinitionFile中。DefinitionFile代码如下:
将Storage Class选为ImportFromFile,同时设定HeaderFile。
这个选项和ImportExtern类似,只是这里需要指定headerfile。生成代码如下:
注意观察private头文件,这里和ImportExtern选项不同,不再是extern修饰Input1变量,而是直接用#include包含了我们指定的头文件。
将Storage Class选为FileScope。这个选项会生成静态全局变量,只在本文件内调用。如果还是把之前的Input1变量设为FileScope,生成代码会报错,应该是和模块输入的性质有冲突。所以这里就用Gain模块的增益参数来演示。
将Gain模块的参数设为gain1,如下图。
然后再Matlab命令行执行以下命令:
>> gain1=Simulink.Parameter;
>> gain1.Value = 3;
此时在Matlab工作区会生成gain1参数对象。将其Value值改为3,StorageClass改为FileScope,保存。
然后生成代码如下:
可以观察到,在本代码中,用static关键字修饰gain1这个参数,并将其赋值为3。也就是说,gain1是个静态的全局变量。
这就表明,gain1这个参数只有在demo.c这个c文件中才能用,其他文件用到了就会报错。
这个选项和ExportedGlobal的结果一样,有可能是模型太简单没有反映出区别。待进一步研究。
将Storage Class选为Sturct。这个选项生成的代码和默认的Auto类似,都会有一个结构体来包含成员Input1,只是这个结构体是用户自定义的,比如下图中定义为struct1。
生成代码中可以看到自定义的结构体:
不过博主确实想不到需要这么搞的需求,毕竟结构体这个不够简洁直观。
将Storage Class选为GetSet。这里和之前都不同,代码会生成一个函数来获取输入值或者设置输出值。定义一个头文件headerfile.h,同时会默认GetFunction和SetFuction函数。
生成代码如下:
可以理解成从headerfile.h中调用get_Input1()这个函数。另外,也可以自定义get函数。
Simulink的StorageClass配置选项很多,可以按照项目需求自己定义。
>>返回个人博客总目录