新闻资讯

代码生成 | 概述从模型到代码到集成
2019-09-19 13:52:15来源:100唯尔

基于模型的设计 (MBD) 是未来嵌入式控制开发的主流方法,Simulink 也成为主流算法开发工具。部分从 C 转 Simulink 的资深工程师以及刚毕业的新晋工程师,对 Simulink 这个工具不是很熟悉,尤其是自动代码生成功能。在进行 Simulink 模型代码自动生成的时候,也会习惯性的去想生成的代码如何匹配脑子里所想的 C 代码。后续,我会用一个个的小例子来介绍如何让 Simulink 生成所希望的代码。在这里并不打算介绍系统代码生成应该怎么做,不过其实这些内容也会在接下来的小例子中一点一点的体现出来。那么,Let's Go。

Simulink 模型自动生成代码?

在我们的 “大” 项目中,需要实现很多算法,其中比如滤波器算法 myfilter。

现在可以不手写 C 了,而是搭建 Simulink 框图来实现这个滤波器。对那些算法比 C 语法熟悉的人来说,这是个福音。使用 Simulink / Stateflow 来实现算法,结构比较直观,测试也更方便。这里重点不在介绍 Simulink,所以此处省略 Simulink 优势一万字。

建模

总之,使用 Simulink 实现了该滤波器的算法,如下:它有一个输入 u,一个输出 y。

02模型测试验证

Simulink 模型的测试验证非常重要,然而这里不讨论。总之,算法没问题之后,

03代码生成

通过代码生成工具 Embedded Coder 把上述的框图翻译成 C 代码,跟手写的一样.c.h。如下图,myfilterModel.c 以及 myfilterModel.h 就是生成的主文件。另外,还可以控制函数原型、数据定义、文件结构、甚至注释......

总之,生成代码后,

04代码集成

把自动生成的算法文件 myfilterModel.c 以及 myfilterModel.h 引用到工程下。这里其实还有一个 rtwtypes.h 也需要引入进来,它里面有一些typedef 的定义。到此为止,已经完全脱离 Simulink 环境了。生成的函数 myfilter 就可以正常调用了,如自己手写 C 代码一样。

由各种编译器,把整个工程编译成不同的可执行文件,最后运行在不同的环境。比如这里就是通过 VS 编译成 exe。很简单吧。

所以,对于代码生成的任务来说,

重点在如何生成自己想要的 C 代码【但实际上到后期,

你会发现这里重要的是模型架构、

数据管理】

Coder 支持哪些板子?

经常有人会问,Coder 支持哪些板子。看下面这张嵌入式软件的伪代码的简单示意图。Simulink 算法模型通过 Coder 生成的是蓝色部分,标准 C;跟硬件打交道的底层驱动黑色部分,是手写代码。既然叫 Coder,所以本质上,它的本职工作就是完成将 Simulink 模型“翻译”为 C 代码这个步骤,就结束了。

至于 C 要通过什么编译器跑到什么硬件上,跟 Coder 没多大关系。看你用的什么编译器。如果非要有什么关系的话,那就是不同的目标硬件数据位数有点不同。

那么,为什么会有人问这个问题呢?

关于 Build Process

Coder 除了把模型翻译成代码的本职工作以外,还额外张罗了一些 Build 的任务,把 C 代码变成可执行文件。为什么说张罗,而不说完成呢,因为Coder 本身并没有编译的功能,它会后台调用你安装的第三方编译器来实现 ,看下图。默认下【Generate code only】不勾选,此时 Coder 会使用内置的 main 模板文件生成一个 main 文件,调用 VC 然后编译。这就是为什么每次生成代码,在当前目录下就会 “莫名其妙” 的生成一个 exe 程序。如果你不喜欢,那也可以 勾选【 Generate code only】。

如果你的编译器上面没列出来,自己也可以做些工作把它集成进来。比如需要构造个 Template makefile,描述怎么编译链接变成可执行文件。

好像还是没解释所谓硬件支持的问题?

I/O Drive blocks

在很久很久以前,代码生成工具为一些板卡提供了 I/O 驱动模块库,TIC2000/ 5000/ 6000,等等。然后,驱动模块可以做什么?看下面这张图,Simulink 模型两端的 In/Out 换成图中的硬件驱动模块。

(当时写这篇帖子的时候,TI 板卡的驱动库我没装,暂时拿 Arduino 的库示意一下,下图是 TI200 的驱动库模块示意图。)

那么,中间用 Simulink/Stateflow 搭好算法,两头的输入输出端口替换为这些硬件驱动模块,后台又做好了编译工具链的支持,就可以实现传说中的支持某款硬件:一键下载到硬件上运行了。如果使用这种方式来开发产品,这样看上去感觉真的很美好...但是,目测,要做的额外的工作也不少。所有的要用到的驱动都需要包裹成 Simulink Block 的形式、模型端口替换为底层驱动模块之后不能直接仿真测试、要自动生成所需要的 main 文件,自动调用后台编译器等等。做产品开发,使用哪种 “集成” 方式,仁者见仁。现在就 Coder 工具来说,这部分 “对硬件的支持”从Coder 里剥离了,变成了免费的 Hardware Support package 下载。


另一个经常会问的问题

代码生成工具的效率怎么样

虽然 Coder 习惯叫代码生成工具,但它实际上是个代码翻译工具,把 Simulink 模型翻译成 C 代码。其实就跟金山词霸,谷歌翻译一样,基于你写的中文翻译成英文。“今日天朗气清,看似极好的日子,私心想若能约上三五好友,结伴出游,陶冶性情,便是再好不过了。”再厉害的谷歌翻译,也翻译不出这句人话:“今天不想上班。”所以对于 Coder 来说,也是一样,它的目标是 C 代码如实的反映出你搭建的 Simulink 模型结构所表达的信息。在考虑自动生成 C 代码的效率如何时,所以:

更重要的是 Simulink 模型搭建的效率如何

下图是自动生成代码相关的工具箱。

Simulink Coder 生成的 C 代码主要用于加速桌面仿真、快速原型、以及HIL测试(被控对象模型也要生成 C 代码)。Embedded Coder 提供了更多的自定义的途径,生成高效率的嵌入式 C 代码。

但其实,对于我们用户来说,这些 Coder 都是 “看不见” 的,没有所谓打开 Embedded Coder 工具箱的说法。我们只需要关心选择哪一个系统目标文件 (System tareget file) 罢了,下图。

为什么这些后缀名是 .tlc呢?“无责任瞎猜一句”,因为 Coder 里面干翻译这个活的叫TargetLanguageCompiler。红框外的 .tlc 只是针对各自不同的目标稍作了些修改,完成一些额外的事。比如 ert_shrlib.tlc ,多做了一个工作,把生成的 .c.h变成.这一个小节说人话就是:

选择 ert.tlc

如果对于一些小算法你有什么高效率的实现,也可以通过

【Code replacement library】

【Code replacement library】添加进来。

比如说查表、针对某个特殊器件的四则运算。

到时候 Coder 就会把相关的计算过程替换为你的实现方式

当然,一些通用的提高代码效率的方法,还是可以试试。

Code generation objective

让 Coder 基于它的 “经验”,给你一些通用的建议:


所以,对于代码生成的任务来说,除了选择 ert.tlc 点个按钮之外,还需要再考虑数据管理、代码结构、文件结构......

说人话,就是:

如何生成自己想要的 C 代码

本文转载自:微信公众号熊猫硬件(ID:xiongmaoyingjian)