欢迎大家学习我的《带你玩转车载测试——CAPL入门篇》系列课程,我是分享人M哥,目前从事车载控制器的软件开发及测试工作。
学习过程中如有任何疑问,可底下评论!
如果觉得文章内容在工作学习中有帮助到你,麻烦点赞收藏评论+关注走一波!感谢各位的支持!
每种编程语言都有一定的程序结构,这样不仅看起来思路清晰,而且还便于移植;CAPL作为一种类C的语言,其也有一定的结构,通常一个完整的CAPL程序包含以下几个部分:头文件、全局变量定义、事件处理和自定义函数,不同于C语言的是CAPL程序没有程序入口(main函数)。
CAPL中的includes{}一般存放.cin文件,在大型的程序开发过程中,采用这种方法可以提高程序的复用性,使用方法如下:
includes
{
#include "CANDisturbanceCaplLibs\CANdisturbance.cin"
#include "TriggerConfigurationPanel.cin"
#include "SequenceConfigurationPanel.cin"
}
CAPL中的全局变量定义在variables{}中,作用域为整个程序从开头到结尾,使用方法如下:
variables
{
// Global initial Parameter values for ECU simulation ("internal ECU memory")
int gVehicleSpeedToLockDoor=15; // vehicle speed for locking the doors in km/h
byte gEcuIdentification[13]={0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D};
int gVehicleType=2; // Sedan
// Access modes for reading and writing diagnostic parameters
const cAccessModeNumerical=0;
const cAccessModePhysical=1;
const cAccessModeCoded=2;
}
CAPL是面向事件的程序语言,即触发什么样的事件后会有什么样的动作,例如:当按下按键a时,向总线发送一条报文。在CAPL中使用关键字on来表示某一事件的触发,触发事件后则执行函数体内的语句,常用的事件有如下几类:
CAPL中的系统事件包括:on prestart,on prestop和on start等,具体使用需根据实际情况;
on prestart
{
resetCan();
}
on start
{
setTimer(tIGNcycle,100);
}
CAN控制器事件是对CAN硬件的响应,包括busoff,主被动错误事件等;例如:当发生busoff时执行重启CAN的程序;
on busOff
{
write("busoff故障发生");
resetCan();
}
报文事件会在总线上出现指定报文时被触发,如:
on message 0x123
{
write("报文接收");
time = timeNow()/100;
}
同报文事件类似,信号事件会在总线上出现指定信号时被触发,通常需要配合DBC使用,如:
on signal LightSwitch::OnOff
{
v1 = this.raw;
v2 = $LightSwitch::OnOff.raw;
}
在CAPL中可以定义时间事件。当此事件发生时,即当一段时间过去时,将调用相关的on timer过程,可以通过在on timer过程中重置相同的时间事件来编程循环程序序列。 同时可以在事件过程中使用关键字this访问计时器变量,使用setTimer函数启动之前定义的计时器。
在CAPL中存在以下定时器变量类型:
timer-基于秒的定时器
msTimer -基于毫秒的定时器
例如:
msTimer myTimer;
message 100 msg;
...
on key 'a' {
setTimer(myTimer,20);
}
...
on timer myTimer
{
output(msg);
}
使用按键事件时可以通过按下键来执行定义的操作。如:每次按下键'a',会在总线上发送报文0x100。
message 100 msg;
...
on key 'a' {
output(msg);
}
错误帧处理程序在接收到错误帧或过载帧后调用。下面的代码将错误代码和错误帧的方向作为格式化字符串输出到write窗口。
on errorFrame
{
const int bufferSize = 256;
char buffer[bufferSize];
char cdirection[2][3] = {"RX", "TX"};
int ndir;
word ecc;
word extInfo;
int isProtocolException;
ecc = (this.ErrorCode >> 6) & 0x3f;
extInfo = (this.ErrorCode >> 12) & 0x3;
isProtocolException = (this.ErrorCode & (1 << 15)) != 0;
ndir = extInfo == 0 || extInfo == 2 ? 0 : 1; //set ndir to 0 for RX and to 1 for TX
if(this.CtrlType == 1){
//SJA1000 specific
switch (ecc){
case 0: snprintf(buffer, bufferSize, "Bit error"); break;
case 1: snprintf(buffer, bufferSize, "Form error"); break;
case 2: snprintf(buffer, bufferSize, "Stuff error"); break;
case 3: snprintf(buffer, bufferSize, "Other error"); break;
default: snprintf(buffer, bufferSize, "Unknown error code");
}
}
else if(this.CtrlType == 2){
//CAN core specific
switch (ecc){
case 0: snprintf(buffer, bufferSize, "Bit error"); break;
case 1: snprintf(buffer, bufferSize, "Form error"); break;
case 2: snprintf(buffer, bufferSize, "Stuff error"); break;
case 3: snprintf(buffer, bufferSize, "Other error"); break;
case 4: snprintf(buffer, bufferSize, "CRC error"); break;
case 5: snprintf(buffer, bufferSize, "ACK Del. error"); break;
case 7:
{
switch (extInfo){
case 0: snprintf(buffer, bufferSize, "RX NACK error (recessive error flag)"); break;
case 1: snprintf(buffer, bufferSize, "TX NACK error (recessive error flag)"); break;
case 2: snprintf(buffer, bufferSize, "RX NACK error (dominant error flag)"); break;
case 3: snprintf(buffer, bufferSize, "TX NACK error (dominant error flag)"); break;
}
break;
}
case 8: snprintf(buffer, bufferSize, "Overload frame"); break;
case 9: snprintf(buffer, bufferSize, "FDF or res recessive"); break; //protocol exception specific
default: snprintf(buffer, bufferSize, "Unknown error code"); break;
}
}
else snprintf(buffer, bufferSize, "Unsupported CAN controller");
if(isProtocolException){
write("Protocol exception on CAN%d at %fs: %s", this.can, this.time/1e5, buffer);
}
else{
write("%s error frame on CAN%d at %fs: %s", cdirection[ndir], this.can, this.time/1e5, buffer);
}
}
On sysVar上的事件过程类型用于响应CANoe中系统变量的值变化。与消息相反,系统变量不会被测量设置中的CAPL节点阻塞。因此,当有两个CAPL节点串联时,它们都使用sysVar上的事件过程对同一个系统变量作出反应。
on sysvar IO::DI_0
{
$Gateway::IOValue = @this;
}
当有诊断请求或诊断响应发生时会触发诊断事件,如:
on diagRequest FaultMemory_ReadAllIdentified
{
diagResponse this resp;
// Set the number of bytes needed to transfer the response with 2 DTCs (in this example: overall 11 bytes)
diagResize( resp, 11); // 3 Bytes Header (SID, Subfunction, AvailabilityMask) + 2 * 4 Bytes for DTCs = 11 bytes
// Set the value of the DTCs
diagSetComplexParameter ( resp, "ListOfDTC", 0, "DTC", 0x000001 );
diagSetComplexParameter ( resp, "ListOfDTC", 0, "DtcStatusbyte", 0xF1 );
diagSetComplexParameter ( resp, "ListOfDTC", 1, "DTC", 0x000002 );
diagSetComplexParameter ( resp, "ListOfDTC", 1, "DtcStatusbyte", 0xF3 );
diagSendResponse ( resp );
}
在进行CAPL编程时,对于经常使用的一些模块可以写成函数,方便后期调用,其自定义函数跟C语言类似,但也有区别:
在声明函数参数时,除整型、浮点型、结构体及枚举等类型外,像类似singal、message及diagrequest等来自dbc中的变量,在声明时需要增加*号,如:singal * s;
void SetChannelName(long deviceId)
{
char channelName[32];
long channelNumber;
channelNumber = CommonCANDisturbanceLibrary_Get_ChannelNo(deviceId);
if(channelNumber <= 0)
{
sysSetVariableString(sysvar::CanDisturbanceMainPanel::ChannelName, "Invalid DeviceId");
cancelTimer(DeviceUpdateTimer);
}
else
{
snprintf(channelName, elCount(channelName), "CAN %d", channelNumber);
sysSetVariableString(sysvar::CanDisturbanceMainPanel::ChannelName, channelName);
setTimerCyclic(DeviceUpdateTimer,10);
}
}
感谢对本期内容不遗余力的学习,下期内容即将奉上,欢迎下次光临!