第13章 仿真工程开发进阶I——CAN+LIN仿真
13.1 工程实例简介
由于LIN总线的低成本、易实现等优点,在安全性和实时性要求不高的模块中得到了广泛应用,如车窗控制、座椅调节和雨刮控制等。
13.1.1网络拓扑图
在网络拓扑图中,网关与座椅记忆控制模块(Seat ControlMemory Module, SCMM)之间通过LIN总线通信,其中,网关作为Master节点,而座椅记忆控制模块则作为Slave节点。
LIN总线的仿真采用LIN 2.2协议,总线波特率为19.200kb/s。
因为节点HU发出的座椅调节报文通过节点Gateway直接转发,节点SCMM反馈过来的当前座椅位置也是节点Gateway发送给节点HU,所以对于节点SCMM来说,只需要关心节点Gateway发过来的数据。
实例中将把与节点HU座椅相关的部分功能当成节点Gateway的一部分来仿真,影音CAN不再单独需要,节点HU用虚线表示,这样拓扑图简化如图
13.1.2 实现功能
(1)座椅位置的手动改变:通过控制面板改变LDF文件里面定义的座椅操作Signal的值(模拟在节点HU屏幕上设置座椅前后、左右、上下以及头部靠垫的位置)。
(2)根据Driver信息改变座椅设置:节点Gateway将当前Driver信息的CAN报文转为LIN报文,节点SCMM在接收到该报文后,自动调用预存储的Driver座椅位置信息。
(3)显示座椅位置:通过显示面板指示座椅的位置信息(模拟节点HU屏幕上座椅位置的动态变化)。
13.2 工程实现
新建工程名
另存工程名
13.2.1添加LIN总线支持
之前的仿真实例只支持Can总线,需要通过修改相关配置来支持LIN。在CANoe主界面通过Hardware→Channel Usage,将LIN通道由0改为1
添加完毕后,将在Simulation Setup窗口右边的系统视图中的Networks下面出现LIN Networks。
添加LIN网络
单击SimulationSetup窗口左下角的网络切换书签LIN,切换到LIN网络视图,可以看到一个没有任何节点的LIN网络
13.2.2 数据库LDF文件设计与导入
与CAN总线类似,LIN总线也需要一个数据库文件来管理相关的报文和信号。CANoe利用这个数据库,可以清晰地解析出LIN总线上的数据信息。
在CANoe主界面的Tools功能区单击LDF Explorer,可以打开LDF编辑器Vector LDF Explorer Pro。在编辑器的工具栏中选择New File新建一个LDF 2.2版本的文件
更改LIN_Network名称为Seatdb , 波 特 率 保 持 19.200kb/s 不 变 。
将Node/Frame选项卡中的LIN_Master更名为Gateway,将Slave_1更名为SCMM。
更改Slave节点的属性
1.创建一条LIN报文,并命名为Seat_Setting。
Length为1B, ID为0,在Publisher中选择节点Gateway。
在报文中添加8个信号。
可以查看报文中Signal的排列状况。在bit排列视图中,将光标停留在不同的bit位置,可以查看出该bit所对应的信号。
2.创建一条LIN报文,并命名为Driver_Info
再创建一个名为Driver_Info的报文,Length为1B, ID为1,在Publisher中选择节点Gateway。该报文包含一条Signal Name为DriverID的信号.
3.创建一条LIN报文,并命名为Seat_Position
再创建一个名为Seat_Position的报文,Length为2B, ID为2,在Publisher中选择节点SCMM。
4.创建ScheduleTable 来 规 定 每 条 报 文 的 发 送 周 期
新建table
之 前 创 建 的 Seat_Setting 、Driver_Info和Seat_Position三条报文拖曳到Table_0下面
在Table_0下面设置当前报文的发送周期,在右侧属性窗口中将每条报文的Delay time都设置成30ms。
5.仿真工程中添加LDF
LDF文件创建完毕后,读者可以将LDF文件导入到仿真工程中。与CAN总线数据库导入相似,在Simulation Setup窗口的系统视图中,鼠标右键单击LIN总线下的Database选择Import Wizard,选择Seatdb.ldf文件并添加SCMM和Gateway两个节点。
可以看到,在LDF文件中创建的两个节点Seat和Gateway已经被添加进LIN总线中。这里需要指出的是,对于Gateway这个节点,由于在CAN总线中也存在节点Gateway,因此该节点有个堆叠效果,表示该节点同时挂在两条不同的总线上。
13.2.3 添加环境变量
使用环境变量(Environment Variable)来显示当前座椅位置以及指示调节座椅按键是否有效。利用EmptyTemplate.dbc模板来创建一个只含有EnvironmentVariable的database。
在Simulation Setup窗口的系 统 视 图 中 , 将 Env_LINGateway.dbc 添 加 到 LINNetwork→LIN→Database下面。
13.2.4 面板设计
添加两个面板,一个用于实现可视化的座椅位置显示,另一个用于控制座椅位置的调节。这里将这两个面板分别命名为Seat Display和Seat Control。
1.Panel:Seat Display
此面板模拟HU显示屏端座椅位置的动态变化,同时显示与之对应的LIN总线上报文Seat_Setting中信号的状态。
2.Panel:Seat Control
此面板模拟节点HU显示屏端控制座椅位置的操作,以及节点BCM端信号driver ID的变化和切换操作。在Seat Control Elements Group里面添加8个Switch/Indicator并关联到创建的LINSignal信号,分别用Static Text控件来添加文字说明。
13.2.5 CAPL实现
本实例中,使用8个环境变量显示收到的LIN报文中8个信号的状态,同时又使用4个环境变量对应一个37帧图片seat.jpg中不同的帧来表征不同的座椅位置。
1.Seat.Can
`/*@!Encoding:936*/
includes
{
}
variables
{
// driver1 座椅初始位置
int horipos1 = 5,vertpos1 = 10,backpos1 = 17,headpos1 = 28;
// driver2 座椅初始位置
int horipos2 = 2,vertpos2 = 13,backpos2 = 21,headpos2 = 26;
}
//处理信号DriverID的变化
on signal DriverID
{
if($Driver_Info::DriverID==1)
{
driver2_pos();
}
else
{
driver1_pos();
}
}
// 切换座椅位置到Driver2,更新LIN报文Seat_Position中的信号
void driver2_pos(void)
{
$Vertical_Position=vertpos2-8;
$Horizontal_Position=horipos2;
$SeatBack_Position=backpos2-16;
$Head_Position=headpos2-26;
}
// 切换座椅位置到Driver1,更新LIN报文Seat_Position中的信号
void driver1_pos(void)
{
$Vertical_Position=vertpos1-8;
$Horizontal_Position=horipos1;
$SeatBack_Position=backpos1-16;
$Head_Position=headpos1-26;
}
// 处理接收LIN报文Seat_Setting
on linFrame Seat_Setting
{
// 赋值给环境变量
putValue(EnvSeat_back_Dsp,$Seat_back);
putValue(EnvSeat_forward_Dsp,$Seat_forward);
putValue(EnvSeat_up_Dsp,$Seat_up);
putValue(EnvSeat_down_Dsp,$Seat_down);
putValue(EnvSeatback_back_Dsp,$Seatback_back);
putValue(EnvSeatback_forward_Dsp,$Seatback_forward);
putValue(EnvSeathead_up_Dsp,$Head_up);
putValue(EnvSeathead_down_Dsp,$Head_down);
// 更新返回LIN报文Seat_Postion
if($DriverID==1)
{
Driver2_Adjust();
}
else
{
Driver1_Adjust();
}
}
// Driver1:更新座椅位置并更新LIN报文Seat_Position
void Driver1_Adjust(void)
{
//更新水平位置
if (horipos1 < 7 && $Seat_back)
{
horipos1 = horipos1 + 1;
}
if (horipos1 > 0 && $Seat_forward)
{
horipos1 = horipos1 - 1;
}
//更新垂直位置
if (vertpos1 < 15 && $Seat_up)
{
vertpos1 = vertpos1 + 1;
}
if (vertpos1 > 8 && $Seat_down)
{
vertpos1 = vertpos1 - 1;
}
//更新座椅靠背位置
if (backpos1 < 25 && $Seatback_back)
{
backpos1 = backpos1 + 1;
}
if (backpos1 > 16 && $Seatback_forward)
{
backpos1 = backpos1 - 1;
}
//更新座椅靠枕位置
if(headpos1 < 29 && $Head_up)
{
headpos1 = headpos1 + 1;
}
if(headpos1 > 26 && $Head_down)
{
headpos1 = headpos1 - 1;
}
// 更新LIN报文Seat_Position
$Vertical_Position=vertpos1-8;
$Horizontal_Position=horipos1;
$SeatBack_Position=backpos1-16;
$Head_Position=headpos1-26;
}
// Driver1:更新座椅位置并更新LIN报文Seat_Position
void Driver2_Adjust(void)
{
//更新水平位置
if (horipos2 < 7 && $Seat_back)
{
horipos2 = horipos2 + 1;
}
if (horipos2 > 0 && $Seat_forward)
{
horipos2 = horipos2 - 1;
}
//更新垂直位置
if (vertpos2 < 15 && $Seat_up)
{
vertpos2 = vertpos2 + 1;
}
if (vertpos2 > 8 && $Seat_down)
{
vertpos2 = vertpos2 - 1;
}
//更新座椅靠背位置
if (backpos2 < 25 && $Seatback_back)
{
backpos2 = backpos2 + 1;
}
if (backpos2 > 16 && $Seatback_forward)
{
backpos2 = backpos2 - 1;
}
//更新座椅靠枕位置
if(headpos2 < 29 && $Head_up)
{
headpos2 = headpos2 + 1;
}
if(headpos2 > 26 && $Head_down)
{
headpos2 = headpos2 - 1;
}
// 更新信号到报文Seat Position
$Vertical_Position=vertpos2-8;
$Horizontal_Position=horipos2;
$SeatBack_Position=backpos2-16;
$Head_Position=headpos2-26;
}`
2.更新Gateway代码
更新LIN总线发送的控制,以及Gateway上CAN信号触发LIN信号的变化,以及LIN信号更新环境变量的函数。
/*@!Encoding:1252*/
includes
{
//To add the head files
}
variables
{
msTimer msTVehSpeedDown; //define the timer(ms) for Vehicle Speed down
msTimer msTEngSpeedDown; //define the timer(ms) for Engine Speed down
dword WritePage;
int busflag=0; //current bus status: 0 - Deactivate CANoe IL ; 1 - Activate CANoe IL
}
on preStart
{
ILControlInit(); //Initialize CANoe IL
ILControlStop(); //Deactivate CANoe IL
LINStopScheduler();// Stop LIN scheduler before measurement starts
//Write simulation information in write window
writeLineEx(WritePage,1,"--------This demo demonstrated the CAN bus simulation!!--------");
writeLineEx(0,1,"Press <1> to start/stop CAN_logging");
}
on key '1'
{
int logflag;
if(logflag==0)
{
logflag=1;
write("CAN logging starts");
//start logging Logging CAN with a pretrigger with 500ms
startlogging("CAN_Logging",500);
}
else
{
logflag=0;
write("CAN logging ends");
//stop logging Logging CAN with a posttriggre with 1000ms
stoplogging("CAN_Logging",1000);
}
}
on signal_update LockStatus
{
if(this!=busflag)
{
if(this==1)
{
ILControlStart();
LINStartScheduler();
}
else if(this==0)
{
ILControlStop();
LINStopScheduler();
}
busflag=this;
}
}
on sysvar Vehicle_Control::Gear
{
$Gear=@this; //Change Gear status based on user operation
}
void EngineData_Init(void)
{
//Initialize the data of Engine
$VehicleSpeed=0;
$EngSpeed=0;
$EngTemp=0;
$PetrolLevel=0;
}
on signal_update KeyState
{
if(this==0)
{
EngineData_Init(); //Init engine data based on KeyState
}
if(this>0)
{
$PetrolLevel=255; //Init PetrolLevel based on KeyState
}
}
on sysvar_update Vehicle_Control::Eng_Speed
{
//Engine speed only available when Key is ON
if(@Vehicle_Key::Key_State==2)
{
$EngineData::EngSpeed=@this;
}
else
{
$EngineData::EngSpeed=0;
}
}
on sysvar_update Vehicle_Control::Veh_Speed
{
//Vehicle speed only available when Gear is Drive and Key is ON
if((@Vehicle_Control::Gear==3)&&(@Vehicle_Key::Key_State==2))
{
$VehicleData::VehicleSpeed=@this;
}
else
{
$VehicleData::VehicleSpeed=0;
}
}
//only for simulation
on sysvar_update Vehicle_Control::Speed_Up //Speed up when sysvar speed_up enabled
{
if($EngTemp<90)
{
$EngTemp=@this*1.5;
}
else
{
$EngTemp=90;
}
if($PetrolLevel<255)
{
$PetrolLevel=@this*8.5;
}
else
{
$PetrolLevel=255;
}
@Vehicle_Control::Veh_Speed=@this;
@Vehicle_Control::Eng_Speed=@this*40;
if(@this>120)
{
@this=60;
}
}
on sysvar Vehicle_Control::Brake //Speed down when Brake is enable
{
int i;
if(@this==1)
{
$GearLock=0;
setTimer(msTVehSpeedDown,50);
setTimer(msTEngSpeedDown,50);
}
else
{
$GearLock=1;
cancelTimer(msTVehSpeedDown);
cancelTimer(msTEngSpeedDown);
}
}
on timer msTVehSpeedDown //Implement speed down function
{
@Vehicle_Control::Veh_Speed=@Vehicle_Control::Veh_Speed-1;
setTimer(this,50);
if(@Vehicle_Control::Veh_Speed<=0)
{
cancelTimer(msTVehSpeedDown);
@Vehicle_Control::Veh_Speed=0;
}
}
on timer msTEngSpeedDown //Implement engine speed down function
{
@Vehicle_Control::Eng_Speed=@Vehicle_Control::Eng_Speed-40;
setTimer(this,50);
if(@Vehicle_Control::Eng_Speed<=0)
{
cancelTimer(msTEngSpeedDown);
@Vehicle_Control::Eng_Speed=0;
}
}
// to process the CAN Signal Driver_Info::Driver Update
on signal_update Driver
{
$Driver_Info::DriverID = $Driver;
}
//To process the receiving of LIN Frame of Seat_Position
on linFrame Seat_Position
{
@EnvSeatvert_Dsp=$Vertical_Position+8;
@EnvSeathori_Dsp=$Horizontal_Position;
@EnvSeatback_Dsp=$SeatBack_Position+16;
@EnvSeathead_Dsp=$Head_Position+26;
}
13.2.6 Trace窗口与Logging
13.2.7 设置Desktop布局
1.添加LIN
添加LIN的Desktop以后,可以将Seat Control和Seat Display两个面板添加到该Desktop中。为了便于观察LIN总线上的活动,可以同时添加Write窗口和LIN_Trace窗口
2.修改Trace
在Trace布局中可以将Trace窗口移除,将LIN_Trace窗口添加到布局中,这样既可以在CAN_Trace窗口中观察CAN上的总线信息,也可以在LIN_Trace窗口查看LIN的报文和变量等信息。
13.3 工程运行测试
操作LIN的面板前需要在CAN的Desktop布局中操作Control面板,钥匙解锁并将引擎钥匙置于RUN状态。节点SCMM会根据车钥匙上驾驶员(Car_Driver)信息的不同,调整座椅至相对应的位置。在SeatControl面板中,读者可以单击不同的箭头按钮来调节座椅位置,同时在Seat Display窗口中显示出座椅的位置,并用LED指示当前调节的方向。