CAN 仿真
CAN仿真工程概述
随着汽车电子的发展,各品牌和车型中 ECU 功能的互联日益复杂,单个 ECU 需要处理的 CAN 数据量不断增加,这对 ECU 的数据处理能力提出了更高要求。为保障 ECU 的可靠开发与验证,可使用 ETStudio 搭建仿真模型来评估 ECU 的功能与性能。同时,基于 ETStudio 的仿真环境可以模拟其他功能 ECU,从而提前发现开发过程中的问题,加速功能验证并提升产品开发质量。
总线仿真工程在 ECU 开发流程中具有重要作用,通常需要需求分析、软件开发、软件测试、环境测试、硬件验证、生产检验、故障分析与客户支持等多方协作。对于功能复杂的 ECU,测试环境的要求很高,仿真环境可以显著加速开发与测试流程。
仿真工程的开发流程与策略
在 ECU 模块开发的早期阶段,应结合需求分析、软件开发与软件测试对产品功能进行拆分与分类,以确认仿真中需要关注的功能点与测试细节。软件开发或测试团队可负责仿真工程的初期开发与后续维护。
由于不同整车厂与产品的差异,读者在开发仿真项目时应针对性地分析需求并制定开发策略。下面列出在仿真项目过程中应注意的一些要点,有助于构建高质量的仿真工程:
- 尽量使用 OEM 提供的 DBC 文件,确保数据库准确,避免自建数据库导致的数据异常排查;
- 完善需求/开发文档记录:在仿真准备阶段确认网络节点、报文与功能节点要求,利于合理的项目初步设计;
- 积极参与需求评审/开发评审/测试评审:通过头脑风暴避免遗漏需求、功能异常或测试点不完整;
- 及时同步关键节点变更:便于跟踪项目进度并获取最新数据源以增删仿真模块;
- 跟进样车样机:将仿真节点替换为真实产品后可获得更准确的仿真结果;
- 合理构建仿真项目结构:
a. 将关键节点独立,使其可在项目中随时启闭,提升灵活性;
b. 对与关键节点无直接关联的可复用模块集中管理,避免重复工作;
c. 增加可扩展接口,以便扩展节点功能,提高代码可塑性; - 备份项目配置:在 ETStudio 升级前后制定兼容性方案,避免项目因版本变化无法打开;
- 代码备份与变更记录:发布前提交修改记录以防后续争议;
- 发布时配套版本控制与发布文档。
仿真工程示例
功能实现
示例主要实现交互功能:
- 控制操作:提供控制平台(操作仪表盘)用于模拟电源开关、暂停与恢复等操作;
- 数据展示:仪表盘接收总线消息并进行处理,显示倒计时与交通灯效果;
- 数据交互:通过代码实现对应的数据处理逻辑;
- 数据分析:添加 Trace 与 Diagram 窗口用于数据分析。
项目实现
创建仿真项目
在 ETStudio 中选择 File → New,从模板列表中选取 CAN_playback 等合适模板,在弹窗中选择工程保存路径(路径与文件夹名尽量避免中文),可创建包含 CAN 基础结构的空工程,并在工程目录下按需创建 DBC、dashboard、Code 与 Header 等子目录以便管理。
导入 DBC 文件 在开发流程中通常由整车厂提供可靠的 DBC 文件,仅需将其导入到仿真工程中。
导入后,可在 Simulation Setup 窗口中查看拓扑图:

系统变量
通过菜单 Setup → Global Variables 添加系统变量,并根据需要创建不同的命名空间以便管理。

仪表盘设计 示例使用两个仪表盘:一个用于模拟电源控制操作,另一个用于接收处理信号并展示交通灯效果。
Power_Control:

Traffic_Light:
C 小程序代码示例 示例代码展示了如何在小程序中响应系统变量变更并发送 CAN 报文(以下代码保持原样):
# PowerControl.cpp
#include "LightControl.h"
void On_SysVar_PowerOn()
{
show_console_message("On_SysVar_PowerOn 111");
if(PowerControl::PowerOnButton)
{
CanNetwork::PowerControl msg;
msg.TrafficPowerState = 1;
msg.TrafficLightRunningState =1;
msg.Transmit();
start_timer(100,true,On_Light_Count_Down_Timer);
}
}
void On_SysVar_PowerOff()
{
show_console_message("On_SysVar_PowerOff 111");
if(PowerControl::PowerOffButton)
{
CanNetwork::PowerControl msg;
msg.TrafficPowerState =0;
msg.TrafficLightRunningState =4;
msg.Transmit();
}
}
void On_SysVar_PauseControlButton()
{
if(PowerControl::PauseControlButton == 1 and powerState == 1)
{
CanNetwork::PowerControl msg;
msg.TrafficPowerState =0;
msg.TrafficLightRunningState =2;
msg.Transmit();
}
}
void On_SysVar_ResumeControlButton()
{
if(PowerControl::ResumeControlButton == 1 and powerState == 1)
{
CanNetwork::PowerControl msg;
msg.TrafficPowerState =0;
msg.TrafficLightRunningState =3;
msg.Transmit();
start_timer(100,true,On_Light_Count_Down_Timer);
}
}
void On_Message_PowerControl(CanNetwork::PowerControl *msg)
{
show_console_message("recevice");
powerState = msg->TrafficPowerState;
runningmode = msg->TrafficLightRunningState;
}
void recived(EM_ReceiveFrame_t const *msg)
{
show_console_message("read");
}
void PreviewApplicationInitialize()
{
}
void OnStart()
{
PowerControl::PowerOnButton.SetOnChange(On_SysVar_PowerOn);
PowerControl::PowerOffButton.SetOnChange(On_SysVar_PowerOff);
PowerControl::PauseControlButton.SetOnChange(On_SysVar_PauseControlButton);
PowerControl::ResumeControlButton.SetOnChange(On_SysVar_ResumeControlButton);
CanNetwork::TrafficLight::OnMessage = On_Message_TrafficLightBrtState;
CanNetwork::TrafficTimer::OnMessage = On_Message_TrafficTimerTimeout;
CanNetwork::PowerControl::OnMessage = On_Message_PowerControl;
}
void OnExit()
{
}