从DBC和Simulink模型生成CAN报文C++解析代码

目录

1.代码生成的准备

2.Simulink CAN unpack模块建模

3.加载DBC选择报文及信号

4.设置模型的输入与输出

5.模型的参数设置

6.开始代码生成

7.Simulink代码生成报告

8.生成代码的解读

8.1 生成的模型头文件 msg_unpack.h

8.2 模型功能实现函数 msg_unpack.cpp

8.3 模型代码调用示例ert_main.cpp

8.4 Simulink生成的代码使用依赖项

9. Simulink生成代码的使用


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。而且速度也非常快。便于扩展。

  • 10
    点赞
  • 88
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 8
    评论
以下是基于QT C++的CAN DBC文件解析代码示例: 1. 首先需要引入相关头文件 ```C++ #include <QFile> #include <QTextStream> #include <QDebug> #include <QRegExp> ``` 2. 定义CAN信号结构体 ```C++ struct CanSignal { QString name; //信号名称 int startBit; //信号起始位 int bitLength; //信号长度 double factor; //信号因子 double offset; //信号偏移量 double minValue;//最小值 double maxValue;//最大值 QString unit; //信号单位 }; ``` 3. 定义CAN消息结构体 ```C++ struct CanMessage { int id; //消息ID QString name; //消息名称 QList<CanSignal> signals;//信号列表 }; ``` 4. 解析DBC文件 ```C++ void parseDBCFile(const QString& filePath, QList<CanMessage>& messages) { QFile file(filePath); if (!file.open(QIODevice::ReadOnly)) { qWarning() << "open DBC file failed."; return; } QTextStream in(&file); QRegExp messageRegExp("^BO_\\s+(\\d+)\\s+(\\w+):\\s+(\\d+)\\s+(\\w+)"); QRegExp signalRegExp("^ SG_\\s+(\\w+)\\s+:\\s+(\\d+)\\|(\\d+)@([+-]?\\d+\\.?\\d*[eE]?[+-]?\\d*)\\s+\\(([+-]?\\d+\\.?\\d*[eE]?[+-]?\\d*)\\s*,\\s*([+-]?\\d+\\.?\\d*[eE]?[+-]?\\d*)\\)\\s+\\[(\\w+)\\]"); CanMessage message; while (!in.atEnd()) { QString line = in.readLine().simplified(); if (line.startsWith("BO_")) { if (message.id > 0) { messages.append(message); } message.signals.clear(); messageRegExp.indexIn(line); message.id = messageRegExp.cap(1).toInt(); message.name = messageRegExp.cap(2); } else if (line.startsWith(" SG_")) { signalRegExp.indexIn(line); CanSignal signal; signal.name = signalRegExp.cap(1); signal.startBit = signalRegExp.cap(2).toInt(); signal.bitLength = signalRegExp.cap(3).toInt(); signal.factor = signalRegExp.cap(4).toDouble(); signal.offset = signalRegExp.cap(5).toDouble(); signal.minValue = signalRegExp.cap(6).toDouble(); signal.maxValue = signalRegExp.cap(7).toDouble(); signal.unit = signalRegExp.cap(8); message.signals.append(signal); } } if (message.id > 0) { messages.append(message); } } ``` 5. 使用示例 ```C++ QList<CanMessage> messages; parseDBCFile("test.dbc", messages); foreach (const CanMessage& message, messages) { qDebug() << "Message ID:" << message.id << "Name:" << message.name; foreach (const CanSignal& signal, message.signals) { qDebug() << "Signal Name:" << signal.name << "Start Bit:" << signal.startBit << "Bit Length:" << signal.bitLength << "Factor:" << signal.factor << "Offset:" << signal.offset << "Min Value:" << signal.minValue << "Max Value:" << signal.maxValue << "Unit:" << signal.unit; } } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

wujiangzhu_xjtu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值