目录
1.代码生成的准备
将Matlab根安装目录下 “R2015b\toolbox\shared\can\src\scanutil\can_message.h” can_message.h头文件放入此次建立的simulink模型文件夹下。同时将matlab工作目录切换至simulink模型所在目录,即can_message.h所在的文件夹。
因为can_message.h里声明了CAN报文类:
2.Simulink CAN unpack模块建模
在Simulink中添加Vehicle Network Toolbox——CAN Communication——CAN Unpack模块,要解析几条报文就添加几个unpack模块。
3.加载DBC选择报文及信号
双击can unpack模块,设置为CANdb specified signals 即解析DBC指定报文的信号,下面的CANdb file选择为需要的dbc文件,然后在下面的Message list可以选择需要的报文,下面的表格里会显示报文里的信号的信息。
4.设置模型的输入与输出
设置输入输出
source——in
sinks——out
同时更改in,out的信号名称与DBC一致,这样在生成的代码可读性也会比较好。
勾选Display——Signal & ports——Port Data Types,即可显示信号线传递的变量类型
有时候不能及时显示信号的变量类型,关掉port data types再重新勾选即可,报文的类型为can_message.
5.模型的参数设置
配置参数配置 simulation——Configuration Parameter
选择为定步长fixed-step
设定步长,我这里设置为0.01s
6.开始代码生成
先执行菜单栏Code——C/C++ Code——Build Model
再选择菜单栏的Code——C/C++ Code——Embedded Coder Quicker Start
基本上一路Next,到这里时注意选择产生的代码类型为C++ code。
这里我是准备移植到Ubuntu系统上,因此这里选择"Device Type"为Linux。
同时,菜单栏里的code generation options里tool chain也要设置;
tool chain和上面保持一致,也选择Linux的,这里选择的是cadence incisive (Linux)
这里不设置运行系统保持一致可能会生成代码失败。
7.Simulink代码生成报告
生成好以后,弹出以下界面,点击第一项可以看到代码生成报告。
8.生成代码的解读
代码生成报告的左端有3个重要的程序文件,可以通过点击左侧进入程序看详细的代码:
ert_main.cpp 是主程序示例,我们模型功能被封装到一个类里,这个main函数里将演示如何使用这个类实现我们原有Simulink模型的功能。
msg_unpack.cpp 主要就是我这个生成代码模型的主要实现部分,是通过一个类来实现的;
msg_unpack.h是这个类相关的声明;
8.1 生成的模型头文件 msg_unpack.h
因为我这个Simulink模型命名为msg_unpack,所以生成的.cpp文件和.h文件都是模型名'msg_unpack"。
这个头文件里主要是模型的输入,输出的结构体定义,然后还有模型的实现部分的类的定义。
结构体ExtU为输入报文的定义:
typedef CAN_MESSAGE CAN_DATATYPE; //CAN_DATATYPE类型就是CAN_MESSAGE类型,参见第一小节
结构体ExtY为输出的结构体定义:
封装模型CAN报文解析的类的定义,模型的输入结构体实例 rtU,输出结构体实例rtY,初始化函数 initialize(), 最重要的部分功能实现函数step()。
8.2 模型功能实现函数 msg_unpack.cpp
这里主要是CAN报文各信号的解析实现,主要是通过类msg_unpackModelClass的成员函数step()实现.
8.3 模型代码调用示例ert_main.cpp
模型功能实现类 msg_unpackModelClass 实例化为rtObj对象。
static msg_unpackModelClass rtObj; // Instance of model class
模型代码的使用示例:
首先设置模型的输入,通过对象rtObj.rtU.报文名 来设定输入的报文长度,id,数据等;
然后通过对象访问成员函数rtObj.step()实现报文解析;
最后通过对象访问成员函数rtObj.rtY. 信号名 访问报文解析出的物理信号值;
8.4 Simulink生成的代码使用依赖项
因为最后代码可能移植到嵌入式设备或者没有Matlab的其他计算机上,需要确定依赖项。
ert_main.cpp只是一个使用的示例代码,我们不需要用,不管。
我们主要把生成的模型实现的.cpp和.h文件是我们需要的,这两个文件才是需要复制到我们实际的C++工程下的。这里是msg_unpack.cpp和msg_unpack.h。
我们看这两个文件的代码确定依赖项:
msg_unpack.cpp依赖于"msg_unpack.h"就是这个模型实现程序本身的头文件:
msg_unpack.h这个头文件又依赖于 string.h (c++标准库,可忽略);rtwtypes.h(matlab安装目录下的一个头文件(也在代码生成的文件夹中产生了,可以直接复制到自己的c++工程中),还有"can_message.h"第一节中已复制到该模型文件夹下,可直接复制到自己的C++工程目录。
rtwtypes.h也在代码生成的文件夹中产生了,可以直接复制到自己的c++工程中
再看这个头文件是不是又有其他依赖项:
rtwtypes.h里不包含头文件
再看can_message.h头文件的依赖项:
依赖rtwtypes.h,和tmwtypes.h(在matlab安装目录下)
最后再看tmwtypes.h的依赖项:
这3个头文件都是c++标准库可以不用管了,依赖项追溯就到此为止。
因此总共的依赖项有:
msg_unpack.h(模型名.h),can_message.h(matlab安装目录),rtwtypes.h(matlab安装目录),tmwtypes.h(matlab安装目录),总共4个头文件加1个源文件msg_unpack.cpp(模型名.cpp)复制到自己的c++工程目录。
9. Simulink生成代码的使用
按照8.4节复制4个头文件,1个源文件到自己的c++工程后即可按照8.3节仿照ert_main.cpp里调用类来实现报文解析了。
如下图是单个报文解析的c++代码运行效果:
每10ms解析一次CAN报文并打印。
可见调用模型代码的核心步骤:
static msg_unpackModelClass rtObj; //定义模型类对象rtObj
rtObj.initialize(); //对象初始化
rtObj.rtU.In1.Length; //设定输入
rtObj.rtU.In1.ID;
rtObj.rtU.In1.Data[];
rtObj.step(); //调用模型CAN报文解析功能实现函数
rtObj.rtY.Out1; //访问输出的解析到的信号物理值
可以看到生成的代码成功的根据DBC的定义对模拟的CAN报文进行了解析得到了物理值。
Simulink生成CAN报文解析代码的好处就是便于维护,减少人为编码产生的Bug。而且速度也非常快。便于扩展。