作者:王御 吉林大学 车辆工程工学博士
我们团队负责的控制器(ECU)有很多选项是在中控屏(IVI)中设置的,交互逻辑修改过多次,也踩过非常多的坑,简单做个阶段性总结分析,给以后的开发工作做个参考。
交互的信号虽多但道理类似,以下以模式记忆开关(ModeMemorySwitch)为例进行说明。
对IVI:
当用户没有对该选项进行设置时,周期性发送CAN信号IVI_ModeMemorySwitchSet==Inactive,按ECU发来的CAN信号ECU_ModeMemorySwitchStatus进行显示,当ECU_ModeMemorySwitchStatus为OFF或者ON时,显示相应状态,用户可对该选项进行设置;当收到其它值或报文丢失或LiveCounter/CheckSum出错时,界面变灰,用户无法对该选项进行设置;
当用户在界面触碰该虚拟开关时,触发式发送一次IVI_ModeMemorySwitchSet为设定值,再下一个定时发送周期继续发送IVI_ModeMemorySwitchSet==Inactive;按用户的选择显示2s,之后按ECU_ModeMemorySwitchStatus显示
对ECU:
应记忆ModeMemorySwitchStatus,确保唤醒后的IVI显示与上次下电时相同
当接收到IVI_ModeMemorySwitchSet!=Inactive时,使ECU_ModeMemorySwitchStatus=IVI_ModeMemorySwitchSet。
上面这个功能逻辑非常简单,但是在一直以来的开发过程中,也出了不少问题,总结如下:
用户设置该选项时,IVI的显示无变化:原因是IVI_ModeMemorySwitchSet的最短周期为20ms,但ECU的Step函数放在100ms,导致无法读到IVI_ModeMemorySwitchSet的变化
出厂时正常,偶发选项变灰后下电不可恢复:怀疑IVI_ModeMemorySwitchSet给出ON/OFF以外的值(曾定义过多个选项,目前其余选项reserve),ECU未做范围有效性判断(因为ECU想做平台化策略),在存NVM过程中也未做有效性判断。导致发出了无效的ECU_ModeMemorySwitchStatus,由于IVI的逻辑无法再选择,逻辑死锁无法恢复
出厂时异常,选项变灰不可恢复:ECU的NVM区初始化的值在linker script file中被设置为0xFF,同时底软对NVM区的有效性没有做校验,导致控制器第一次上电读到的ECU_ModeMemorySwitchStatus值错误,发出了无效的ECU_ModeMemorySwitchStatus,由于IVI的逻辑无法再选择,逻辑死锁无法恢复
此外,之前还有过逻辑,IVI_ModeMemorySwitchSet为常值信号,IVI_ModeMemorySwitchSet发出的值在用户输入2s后为ECU_ModeMemorySwitchStatus的值,这就导致在用户以某个频率连续设置该选项时,用户松手后界面在ON/OFF间来回跳动的现象。
首先分析这个交互逻辑中ECU应该有哪些函数
初始化函数:在初始化过程中调用,通过NVM_ModeMemorySwitchStatus信号更新ECU_ModeMemorySwitchStatus,需要对NVM_ModeMemorySwitchStatus的有效性进行校验(我一般不会将0设置为枚举信号的有效范围,因为全局变量和NVM的默认初始值就是0,很容易出现一些奇奇怪怪的错误)。如果有效性校验失败,需要给出一个默认值。此外,还需要将底软传过来的首值存起来,方便解决问题(和底软撕逼)时使用。
报文接收触发函数:当接收到IVI_ModeMemorySwitchSet信号时进行调用,通过IVI_ModeMemorySwitchSet更新ECU_ModeMemorySwitchStatus。在底软集成时需要将这个函数放到CAN报文接收的callback函数中,这样就能确保每个用户的输入都能被响应。同样需要做有效性校验。
周期函数:该函数中会放置一些用于调试的bypass功能,方便在HIL或者车上调试。
再分析输入输出信号、参数,用于设计数据字典
输入:
NVM_ModeMemorySwitchStatus:底软NVM模块给的
IVI_ModeMemorySwitchSet:来自IVI,底软CAN模块处理
输出:
ECU_ModeMemorySwitchStatus:发给底软CAN模块,再给IVI用于显示;同时该信号发送给底软NVM模块,用于下电时写入NVM区
参数:
ECU_p_ModeMemorySwitchStatusDefault:这个参数有两个作用,一是NVM区是出厂状态的无效值,那这个值作为初始值使用;二是NVM区虽然不是出厂状态,但是被错误写入了,那这个值作为替代值使用;两个情况虽然不同,但处理方式相同,可以使用一套逻辑。如果车型有特殊需求,可以修改逻辑。另外,一般会和底软沟通,让他们在NVM的校验逻辑中(比如CRC)将故障状态的初始值设计为ECU_p_ModeMemorySwitchStatusDefault,注意,一定是设置为ECU_p_ModeMemorySwitchStatusDefault这个变量,这样当不同的车型要求的初始值不同时,底软不用修改代码,应用层改一下参数就可以了。
数据类型:
目前说的这四个全局变量可以使用一个枚举类型。用一个数据类型的好处是在标定工程中多个信号画在一个图上是可以共Y轴,很容易分析和对比。
光说不练假把式,使用Simulink把这个逻辑做出来。
需要注意,最好使用Reference ConfigSet,可以参考
枚举类型的使用可以参考
这种模型的单元测试需要建一个上层模型通过modereference的方式测试被测模型,还需要依据条件产生functioncall调用。测试用例需要保障模型覆盖度的要求。今天先不整了。
照理说应该做完静态测试和动态测试以后再生成代码,做demo就不那么严格了,简单跑了一下MAAB、MISRA、26262三个ModelAdvisor,然后生成代码看看,感觉基本上是对的,动态测试改天再补充。
#include "U_ECU_ModeMemorySwitchProcess.h"
#include "rtwtypes.h"
#include "ECU_commonEnum_def.h"
#include "ECU_Par.h"
#include "ECU_Sig.h"
#include
DW_U_ECU_ModeMemorySwitchProc_T U_ECU_ModeMemorySwitchProces_DW;
void ECU_FC_100ms(void)
{
if (ECU_p_ModeMemorySwitchStatus_Switch)
{
ECU_ModeMemorySwitchStatus = ECU_p_ModeMemorySwitchStatus_Bypass;
}
else
{
ECU_ModeMemorySwitchStatus = ECU_ModeMemorySwitchStatus_beforebypass;
}
}
void ECU_FC_Initial(void)
{
switch (NVM_ModeMemorySwitchStatus)
{
case ECU_ET_ModeMemorySwitch_Inactive:
U_ECU_ModeMemorySwitchProces_DW.UnitDelay_DSTATE =
ECU_p_ModeMemorySwitchStatusDefault;
break;
case ECU_ET_ModeMemorySwitch_OFF:
U_ECU_ModeMemorySwitchProces_DW.UnitDelay_DSTATE =
ECU_ET_ModeMemorySwitch_OFF;
break;
case ECU_ET_ModeMemorySwitch_ON:
U_ECU_ModeMemorySwitchProces_DW.UnitDelay_DSTATE =
ECU_ET_ModeMemorySwitch_ON;
break;
default:
U_ECU_ModeMemorySwitchProces_DW.UnitDelay_DSTATE =
ECU_p_ModeMemorySwitchStatusDefault;
break;
}
ECU_ModeMemorySwitchStatus_beforebypass =
U_ECU_ModeMemorySwitchProces_DW.UnitDelay_DSTATE;
if (U_ECU_ModeMemorySwitchProces_DW.DLY_blk_DSTATE)
{
NVM_ModeMemorySwitchStatus_FirstRun = NVM_ModeMemorySwitchStatus;
}
else
{
NVM_ModeMemorySwitchStatus_FirstRun =
U_ECU_ModeMemorySwitchProces_DW.DLY_blk1_DSTATE;
}
U_ECU_ModeMemorySwitchProces_DW.DLY_blk_DSTATE = false;
U_ECU_ModeMemorySwitchProces_DW.DLY_blk1_DSTATE =
NVM_ModeMemorySwitchStatus_FirstRun;
}
void ECU_FC_OnSigModeMemorySwitchSet(void)
{
ECU_ET_ModeMemorySwitch rtb_MultiportSwitch1;
switch (IVI_ModeMemorySwitchSet)
{
case ECU_ET_ModeMemorySwitch_Inactive:
rtb_MultiportSwitch1 = ECU_ET_ModeMemorySwitch_Inactive;
break;
case ECU_ET_ModeMemorySwitch_OFF:
rtb_MultiportSwitch1 = ECU_ET_ModeMemorySwitch_OFF;
break;
case ECU_ET_ModeMemorySwitch_ON:
rtb_MultiportSwitch1 = ECU_ET_ModeMemorySwitch_ON;
break;
default:
rtb_MultiportSwitch1 = ECU_ET_ModeMemorySwitch_Inactive;
break;
}
if (rtb_MultiportSwitch1 != ECU_ET_ModeMemorySwitch_Inactive)
{
U_ECU_ModeMemorySwitchProces_DW.UnitDelay_DSTATE = rtb_MultiportSwitch1;
}
ECU_ModeMemorySwitchStatus_beforebypass =
U_ECU_ModeMemorySwitchProces_DW.UnitDelay_DSTATE;
}
void U_ECU_ModeMemorySwitchProcess_initialize(void)
{
ECU_ModeMemorySwitchStatus_beforebypass = ECU_ET_ModeMemorySwitch_Inactive;
NVM_ModeMemorySwitchStatus_FirstRun = ECU_ET_ModeMemorySwitch_Inactive;
ECU_ModeMemorySwitchStatus = ECU_ET_ModeMemorySwitch_Inactive;
(void)memset((void *)&U_ECU_ModeMemorySwitchProces_DW, 0,
sizeof(DW_U_ECU_ModeMemorySwitchProc_T));
NVM_ModeMemorySwitchStatus = ECU_ET_ModeMemorySwitch_Inactive;
IVI_ModeMemorySwitchSet = ECU_ET_ModeMemorySwitch_Inactive;
U_ECU_ModeMemorySwitchProces_DW.DLY_blk_DSTATE = true;
U_ECU_ModeMemorySwitchProces_DW.UnitDelay_DSTATE =
ECU_p_ModeMemorySwitchStatusDefault;
ECU_ModeMemorySwitchStatus_beforebypass = ECU_ET_ModeMemorySwitch_Inactive;
}
可以看到C里面有四个函数,U_ECU_ModeMemorySwitchProcess_initialize和ECU_FC_Initial应该放到底软初始化NVM完成之后,ECU_FC_OnSigModeMemorySwitchSet放到底软的CAN接收Callback,ECU_FC_100ms放到底软100ms。对应的变量赋值需要底软连起来。等有机会集成再放例子吧。
(留空,等有机会补几张CANape的图)
链接:https://pan.baidu.com/s/1m8V-W5KZoTBQEerhdFtS2g?pwd=wswa
提取码:wswa