目录

0. 概要

1. Matlab /Simulink/Embedded Coder关系与区别

2. 搭建Simulink模型及仿真

2.1 搭建模型

 2.2 仿真

3. 生成代码

 3.1 求解器设置为定步长

3.2 安装 MinGW-w64 编译器

 3.3 调出Simulink Coder

 4. 工具都生成了啥呢?


0. 概要

        Matlab网站提供了很多关于如何使用Coder自动生成代码的教程,也有很多相关的博客或者视频,但是“纸上得来终觉浅,绝知此事要躬行”,光看不练终究是如隔靴搔痒,不知所云。

        本文从搭建一个simulink模型开始,到生成代码并进行生成的C模型与原simulink模型的一致性验证,走完一个最小完整流程。希望对基于simulink模型自动生成C代码感兴趣的小伙伴们能有所帮助。

        以下实验用的Matlab-2021b版本。

1. Matlab /Simulink/Embedded Coder关系与区别

ref1: Difference between Simulink Coder and Embedded Coder - (mathworks.cn)

ref2: Difference between Simulink Coder, Embedded Coder and Matlab Coder - (mathworks.cn) 

ref3: Welcome to the Coders! » Guy on Simulink - MATLAB & Simulink (mathworks.com) 

MATLAB Coder is used to generate ANSI C/C++ code from MATLAB and is a pre-requisite for Simulink Coder and Embedded Coder. Simulink Coder does the same thing, but from Simulink. Embedded Coder allows to optimize/customize the code generated by MATLAB Coder and Simulink Coder for production use.

首先,MATLAB Coder是Simulink Coder和Embedded Coder的基础。

Simulink Coder以前称为Real-Time Workshop,所以生成的代码存放目录等名称中可以看到的_rtw后缀名就源于此。

Both Simulink Coder generate C code from Simulink models, and both of them can put that C code in a C++ wrapper to be effectively integrated with other C++ code.

The main difference between these products is:

  • Simulink Coder generates code with the purpose of the model being able to run standalone without a MATLAB license, or to verify the behavior of the generated code on a host computer.
  • Embedded Coder generates readable code that can be much more finely configured, customized, and optimized through various settings and constructs. Embedded Coder also provides support for automatically building the generated code on embedded systems (hence its name).

        Simulink Coder和Embedded Coder都是用于将simulink模型自动转换成C代码。Simulink Coder生成的代码可以独立于matlab进行运行,这样可以在主机上运行以验证所生成的代码。而Embedded Coder生成的代码,一言以蔽之,就是更加优化、简洁,并且支持面向目标嵌入式平台生成代码,并因此得名。从实际实验生成的代码量也可以看出,同样的Simulink模型,Embedded Coder生成的代码量要比Simulink Coder生成的代码量更小一些。而且,根据以上描述,Embedded Coder生成的代码的可读性更好一些(?这个有待亲测确认)

f489828c5ab118e5a474c1b634d0591d.png

2. 搭建Simulink模型及仿真

2.1 搭建模型

        本实验用的模型如下图所示,实现的功能是两个正弦波信号相加后灌入一个低通滤波器。

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA56yo54mb5oWi6ICV,size_20,color_FFFFFF,t_70,g_se,x_16

       两个正弦波信号的频率分别为1kHz,和5kHz,本实验中采样率统一为44.1kHz。其中第一个正弦波信号模块的参数设置如下:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA56yo54mb5oWi6ICV,size_10,color_FFFFFF,t_70,g_se,x_16

        滤波器系数是通过BL1和AL1设置,这两个参数分别表示IIR滤波器传输函数的分子和分母,我们在matlab命令行输入命令进行设置,如下以下代码所示。

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA56yo54mb5oWi6ICV,size_10,color_FFFFFF,t_70,g_se,x_16

fL=3e3; fs=44.1e3; [BL1 AL1] = butter(4, fL/fs);

        这条语句设计了一个采样率为44.1kHz、通带频率为3kHz的4阶巴特沃斯低通滤波器。这样的话可以将5kHz的正弦波率除掉,保留1kHz的正弦波信号。这个变化可以通过观测各节点频谱很容易确认。

        除了输入正弦波信号、低通滤波器模块,其它还有存储各滤波器输入输出数据的数据缓存模块,TimeScope用于观测时域波形,以及几个频谱分析仪(Spectrum Analyzer)模块。

 2.2 仿真

        设置仿真时长1秒钟(由于是44.1kHz采样率,1kHz和5kHz的正弦波,1秒钟的仿真时间足够了),仿真结果如下所示。

        四个节点的信号频谱,从左到右分别是1kHz的输入信号,5kHz的输入信号,滤波器输入信号(即两者相加的结果),滤波器输出结果。结果符合预期,从滤波器输出端频谱可以看出5kHz的信号分量收到了抑制。

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA56yo54mb5oWi6ICV,size_20,color_FFFFFF,t_70,g_se,x_16

        TimeScope输出的时域波形如下(中间两个顺序颠倒了,各波形的title请忽视),同样可以看出最后滤波器输出信号只剩下了1kHz的低频成分。

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA56yo54mb5oWi6ICV,size_20,color_FFFFFF,t_70,g_se,x_16

        由于有两个out(To WorkSpace)模块,所以仿真还生成了一个存储这些数据的结构体out,其中所包含的信息如下:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA56yo54mb5oWi6ICV,size_15,color_FFFFFF,t_70,g_se,x_16

        执行命令“save('ex_lpf_ref.mat','out')”将这个数据存储下来用于后与生成的代码运行结果做对比。用plot语句同样可以画出以上TimeScope相同的波形,如下所示。

figure;
plot(out.tout, out.flt_dout(:,:));
hold on
plot(out.tout, out.flt_din(:,:));

        其中out.tout代表的是采样时间序列。 

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA56yo54mb5oWi6ICV,size_17,color_FFFFFF,t_70,g_se,x_16

3. 生成代码

 3.1 求解器设置为定步长

        在Simulink模型窗口的最上面的菜单Tab中选择“建模”-->"模型设置"后弹出“配置参数”对话窗,将“求解器选择”下面的“类型”改为“定步长”,其它不用管。只有定步长设置才能正确地生成C代码。

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA56yo54mb5oWi6ICV,size_20,color_FFFFFF,t_70,g_se,x_16

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA56yo54mb5oWi6ICV,size_20,color_FFFFFF,t_70,g_se,x_16

3.2 安装 MinGW-w64 编译器

        执行mex -setup检查是否为matlab配置了C编译器。如果没有的话,可以使用“附加功能”菜单安装,比如说我安装的是MinGW-w64:

  • 在 MATLAB 主页选项卡的环境部分,点击附加功能 > 获取附加功能

  • 0b3aed9a5a0c4a18aa19da8f56d19002.png

  • 在附加资源管理器中右上搜索框中输入“MinGW”回车会出现MinGW-64安装包下载链接,点进去再点击安装即可。

        然后遵照指令一步一步往前走即可。

        注意,MinGW 安装文件夹名称不能包含空格。例如,以下这样的的缺省安装路径是不行。

        C:\Program Files\mingw-64

        工具不能正确识别这种路径。

 3.3 调出Simulink Coder

        在Simulink模型窗口的“APP”,缺省地有“Embedded Coder”,但是没有“Simulink Coder”。本实验要生成可以独立运行的C程序,所以要用Simulink Coder。

点击最右端的下拉标志:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA56yo54mb5oWi6ICV,size_20,color_FFFFFF,t_70,g_se,x_16

        在弹出的窗口中下拉到“代码生成”部分,选中“Simulink Coder”即可。

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA56yo54mb5oWi6ICV,size_16,color_FFFFFF,t_70,g_se,x_16

        这样会在原来APP旁边出现一个“C CODE”的TAB。

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA56yo54mb5oWi6ICV,size_20,color_FFFFFF,t_70,g_se,x_16

         可以从“Quick Start”出发去生成代码,Matlab内置例子通常是走这个流程,不过我走了一遍后没有感受到什么好处,所以以下实验不走这个流程。此处的Settings以下的内容,其实和上一层的“建模”-->"模型设置"相同,点进去再确认一下代码生成的设置:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA56yo54mb5oWi6ICV,size_20,color_FFFFFF,t_70,g_se,x_16

        如果只想生成代码,可以勾选“仅生成代码”,但是本实验想让matlab走完编译的全过程。其它选项暂时就缺省就好了。万事俱备,点击“Build”按钮即可。顺利的话,可以看到在matlab的当前工作目录下生成了:ex_lpf.exe,ex_lpf.slxc以及存放所生成的代码的目录ex_lpf_grt_rtw。

        接下来我们先不管它生成了啥,先看看所生成的exe文件是否正确。在matlab命令行执行命令"! ex_lpf.exe"(用“!”后接exe文件名可以运行exe可执行文件),可以看到执行结果生成了一个ex_lpf.mat。

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA56yo54mb5oWi6ICV,size_10,color_FFFFFF,t_70,g_se,x_16

        将ex_lpf.mat加载进来(load ex_lpf.mat),里面包含rt_flt_din,rt_flt_dout,rt_tout,分别上面提到的out中的三个变量对应,只不过多了个rt_的前缀名(表示real-time?)。 对比这些数据与上面simulink模型运行所得到的数据结果可以确认它们的确是一致的。这就表明所生成的C代码模型与原simulink模型的确是等价的。

 4. 工具都生成了啥呢?

        是时候看看Coder都给我们生成了啥了。在以上代码生成完后会弹出一个“Code Generation Report”对话窗口,如下所示:

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA56yo54mb5oWi6ICV,size_20,color_FFFFFF,t_70,g_se,x_16

        点击左边各文件可以查看各代码文件的内容。 第一印象一定是,哇,这么简单的一个模型,居然生成了这么多东西。需要花一些时间来看看:

  • 到底生成了啥
  • 为什么要生成这么多的东西,
  • 如何在C的开发平台中集成使用这些所生成的代码呢
  • 这些生成的代码的运行效率如何呢?

等等等等。不过,现在我写累了,等我歇一歇再回来。。。

        

相关博文:

Matlab/Simulink Embedded Coder一个非常迷幻的问题

Simulink模型参数初始化方法种种

Matlab/Simulink Coder: 将子系统生成为独立的函数和文件

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐