0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

CAPL脚本使用介绍

汽车ECU开发 来源:汽车ECU开发 2024-04-01 11:23 次阅读

之前断断续续分享过一些Vector工具的使用总结,如下所示,包括CANape、CANoe、CANalyzer。

然而里面还有一项最重要的,也是平时使用过程中,提效很明显的CAPL脚本的使用,覆盖整车各节点的模拟、软件刷写、诊断测试等等,今天就来理一理CAPL脚本。 首先理一理基本语法和常用语句。在打开CAPl编辑界面时,下默认组成部分有:Include、Variable、System、Value Objects,其中Include为需要包含的已存在的头文件,一般不配置;Variable为申明与定义全局变量,需要定义的变量包括需要发送的信号以及其数据类型。CAPL中的数据类型有: 无符号整型:byte(1字节),word(2字节),dword(4字节)

有符号整型:int(2字节),long(4字节)

浮点数:float(8字节),double(16字节)

CAN消息类型:Message;定时器类型:timer(单位为s),msTimer(单位为ms);

单个字符:char(1字节)。

除了界面基础的信息外,在CAPL脚本中,我们大量使用官方定的的一些接口,这些接口通常需要查看help文档或者是CAPL的手册,下面是梳理的一些常用接口。

1、定时器

CAPL中的定时器的使用相当频繁,比如测试时需要向定时发送某条CAN报文时就需要用定时器;定时器的声明:

msTimer myTimer1;//声明了一个ms定时器,定时单位是毫秒
timermyTimer2;//声明了一个以秒为单位的定时器;

设置定时器:

setTimer(myTimer1,400);//设置定时器myTimer1为一个400ms定时器;
setTimerCyclic(myTimer2,2);//设置定时器myTimer2为一个2s为周期的循环定时器;

设置定时器定时事件,即当定时器计时时间到时将要执行的操作:

on timer myTimer1
{
.......
}

2、信息的操作和发送

//CAN消息发送:
message0x7ffMsg;//声明一个message,ID=0x7ff
Msg.dlc=8;//设置其DLC=8;
Msg.id=0x100;//更改其ID=0x100;
Msg.byte(0)=55;//设置数据场的第一个字节为55
output(Msg);//发送Msg


//CANFD消息发送:
  msg1.FDF=1;
  msg1.BRS=1;
  msg1.dlc=8;
Msg.id=0x100;//更改其ID=0x100;
  msg1.byte(0)=0x44;
  msg1.byte(10)=0x10;
  msg1.byte(11)=0x11;
  output(Msg);//发送Msg

3、节点上下线操作

节点是在dbc中定义的,如VCU,BMS,MCU等,有时需要将它们离线,离线后不再向总线上发送报文,在线时可以向总线上发送报文。

节点上线:

voidtestSetEcuOnline(dbNodeaNode);
void testSetEcuOnline(char aNodeName[]);

节点下线:

voidtestSetEcuOffline(dbNodeaNode);
void testSetEcuOffline(char aNodeName[]);

4、检查错误帧

进行CAN通讯的测试时,检查错误帧是很常见的,要用CAPL脚本实现自动检测错误帧也不困难,它的核心就是调用错误检查函数ChkStart_ErrorFrameOccured(),该函数一旦被调用,CANoe就会从此函数被调用时开始持续检测总线上有没有出现错误帧。

下面是一个小的例子

dword chechId;
dword numCheckEvents;
checkId=ChkStart_ErrorFrameOccured();//开始检测错误帧
TestAddCondition(checkId);//添加检测条件,如果出现了错误帧,则输出报告中会记录下来
TestWaitForTimeout(5000);//持续检测5s
checkControl_Stop(checkId);//停止检测错误帧
numCheckEvents=ChkQuery_NumEvents(checkId);//对5s内的检测情况进行获取,若函数返回0则没有出现错误帧
if(numCheckEvents>0)
     TestStepFail("Error Frames Occured");

5、添加事件信号

这种事件信号相当于信号量机制,一般使用在需要等待某个或者是多个条件满足时进行下一步操作。

具体做法是:在一个位置添加需要等待的事件,程序中的其他地方,如果某个事件发生了(如周期超界等),提供该事件的供应,则等待的程序段获得了该事件,继续执行下面的操作。主要使用的函数有以下几个:

//供应text事件
long TestSupplyTextEvent( char aText[] );
//添加text事件
long TestJoinTextEvent(char[]aText);
//等待text事件,有一个出现则程序执行下一步
long TestWaitForAnyJoinedEvent(dword aTimeout);
//等待text事件,所有等待事件都出现则程序执行下一步
long TestWaitForAllJoinedEvents(dword aTimeout);

以下是一个例子:

TestJoinTextEvent("Test finished");
TestJoinTextEvent("Error Frame Occured");
TestWaitForAnyJoinedEvents(20000);
或者:
TestWaitForAllJoinedEvents(20000);
在系统事件on errorFrame中:
on errorFrame
{
   TestSupplyTextEvent("Error Frame occured");
}
在系统的on message 中:
on message 0x400
{
   TestSupplyTextEvent("Test Finished")
}

6、回调函数

CAPL中也有类似于C语言中的回调函数的机制,如检测报文周期和错误帧的函数中就可以使用,当周期超界或者总线出现错误帧就会自动调用回调函数执行一些操作;如:

ErrChkId=ChkStart_ErrorFramesOccured("Callback_ErrorFrameOccured");//检查错误帧,如果发现错误帧就调用回调函数
回调函数设计如下:
void Callback_errorFrameOccured(dword chk_id)
{
  float t;
  t=timeNow()/100000.0;//记录出现错误帧的时间
  testStep("ErrorFrameTimeStamp","%.6f s",t);//打印该事件戳
  TestSupplyTextEvent("ErrorFrameOccured");//供应Text事件
}

7、监视总线的情况,这一般会用在查看一段时间内,总线上有没有出现通讯异常的情况。需要使用函数ChkStart_NodeBabbling( ). 如,检测一段时间内总线有没有出现停止通讯的情况:

CheckId=ChkStart_NodeBabbling(CAN::PT_MCU,0);//立即开始检查总线状态
testWaitForTimeout(2000);//延时2s
ChkControl_Stop(CheckId);//停止检测
QueryNumberEvents=ChkQuery_NumEvents(CheckId);//如果在2s内总线停止通讯,则QueryNumberEvents!=0

8、关于获取关键时间点

(1)CANoe中获取定时器当前计时值的函数为:timerToElapse();该函数原型如下:

long timerToElapse(timer);
long timerToElpase(msTimer);                

(2)获取等待某个事件的时间,需要使用函数TestGetLastWaitElapsedTimeNS(),其原型如下:

float TestGetLastWaitElapsedTimeNS();

(3)获取当前的仿真时间点:

float timeNowFloat();

(4)等待指定报文:

long TestWaitForMessage(dbMessage aMessage,dword aTimeout);
long TestWaitForMessage(dword aMessageId,dword aTimeout);

若在aTimeout时间内等到了指定ID的报文,函数返回1,否则返回0;

(5)获取报文的数据,等到了报文之后,如果想知道报文的具体内容可以使用函数:

message msg;
long result;
result=TestGetWaitEventMsgData(msg);
.....处理msg.....

9、多总线测试

设置总线背景,一般都总线测试都会有两路及以上的CAN,这时若要通过CAPL脚本获取某个CAN通道上的报文时,就需要先设置好总线背景,即将总线设置为值监听某一路的CAN通道。下面是一个例子:

void BusContextConfiguration(char yBus[])
{
   yBusContext=GetBusNameContext(yBus);//这里的yBusContext为全局变量
   SetBusContext(yBusContext);
}
//使用:
BusContextConfiguration("CAN1");//将总线监听设为CAN1

此时等待某一路的CAN报文可是这样实现:

res=testWaitForMessage(CAN1::NM_IPU,600);//等待CAN1上的名称为NM_IPU的报文,等待事件为600ms

10、诊断报文的发送和接收

request_A.SendRequest();//诊断请求
TestWaitForDiagResponse(request_A,5000);//诊断接收

11、

将诊断请求 / 响应写入报告

TestReportWriteDiagObject (diagRequest req);


TestReportWriteDiagObject (diagResponse resp);


TestReportWriteDiagResponse (diagRequest req);

12、获取诊断请求 / 响应的原始数据

long diagGetPrimitiveByte( diagRequest request, DWORD bytePos);


long diagGetPrimitiveByte( diagResponse response, DWORD bytePos);

13、获取诊断请求 / 响应的参数

long diagGetParameter (diagResponse obj, char parameterName[], double output[1])


long diagGetParameter (diagRequest obj, char parameterName[], double output [1])


double diagGetParameter (diagResponse obj, char parameterName[])


double DiagGetParameter (diagRequest obj, char parameterName[])


long diagGetParameter (diagResponse obj, long mode, char parameterName[], double output[1])


long DiagGetParameter (diagRequest obj, long mode, char parameterName[], double output [1])


double diagGetParameter (diagResponse obj, long mode, char parameterName[])


double diagGetParameter (diagRequest obj, long mode, char parameterName[])

最后分享最近刚使用CAPL脚本的一些注意点以及一个示例

第一CAPL 的局部变量是静态局部变量。经过使用发现,在 variables{ }之外,事件或者函数内部定义的局部变量是静态局部变量,其值不会因为退出本事件或者函数,而变为初始值。所以如果真的需要一个局部变量,在每次退出之前,重新使用赋值语句赋为初始值。

第二建议使用系统变量或者环境变量,这样可以跨不同的capl脚本操作,比如检测某个环境变量或者系统变量的变化,来执行一些动作。

on sysvar sysvar::EngineStateSwitch
{
  $EngineState::OnOff = @this;
if(@this)
  $EngineState::EngineSpeed = @sysvar::Engine::EngineSpeedEntry;
else
  $EngineState::EngineSpeed = 0;
}

第三个就是以太网转CAN的capl脚本示例:

/*@!Encoding:1252*/


variables
{
  //
  // Constants
  //
  
  const WORD kPort         = 23; // UDP port number for instance
  const WORD kRxBufferSize = 1500;
  const WORD kTxBufferSize = 1500;
  
  //
  // Structure of UDP payload
  //
  
  _align(1) struct CANData
  {
    BYTE  dlc;
    BYTE  flags; // Bit 7 - Frame type (0 = standard, 1 = extended)
                 // Bit 6 - RTR bit ('1' = RTR bit is set)
    DWORD canId;
    BYTE  canData[8];
  };
  
  //
  // Global variables
  //
  
  UdpSocket gSocket;
  CHAR      gRxBuffer[kRxBufferSize];
  CHAR      gTxBuffer[kTxBufferSize];
  DWORD     gOwnAddress;
  DWORD     gModuleAddress= 0xFFFFFFFF; // default is the broadcast address 255.255.255.255  and the TCP/IP stack will build the Network broadcast address
}


//
// Measurement start handler
//


on start
{
  DWORD addresses[1];
  
  // get own IP address of the Windows TCP/IP stack
  IpGetAdapterAddress( 1, addresses, elcount(addresses) );
  gOwnAddress = addresses[0];
  
  // open UDP socket
  gSocket = UdpSocket::Open( 0, kPort ); 
  
  if (gSocket.GetLastSocketError() != 0)
  {
    write( "<%BASE_FILE_NAME%> Open UDP socket failed, result %d. Measurement stopped!", gSocket.GetLastSocketError() );
    stop();
    return;
  }


  if (gSocket.ReceiveFrom( gRxBuffer, elcount(gRxBuffer) ) != 0)
  {
    if (gSocket.GetLastSocketError() != 997) // ignore pending IO operation
    {
      write( "<%BASE_FILE_NAME%> UDPReceive failed, result %d. Measurement stopped!", gSocket.GetLastSocketError() );
      stop();
      return;
    }
  }


}
//
// On receive UDP data handler using CAPL Callback 
//
void OnUdpReceiveFrom( dword socket, long result, dword address, dword port, char buffer[], dword size)
{
  DWORD          dataOffset;
  struct CANData canData;
  message *      canMsg;
  
if(address==gOwnAddress)return;//ignoreownbroadcasts
  //
  // Store IP address of module to reach
  //
  
  if (gModuleAddress == 0)
  {
    gModuleAddress = address;
  }
  
  //
  // Handle received data
  //
  
  dataOffset = 0;
  while (dataOffset + __size_of(struct CANData) <= size)
  {
    memcpy( canData, buffer, dataOffset );
    
    canMsg.id      = (canData.canId & 0x1FFFFFFF) | ((canData.flags & 0x80) ? 0x80000000 : 0);
    canMsg.dlc     = canData.dlc & 0x0f;
    canMsg.rtr     = ((canData.flags & 0x40) ? 1 : 0);
    canMsg.byte(0) = canData.canData[0];
    canMsg.byte(1) = canData.canData[1];
    canMsg.byte(2) = canData.canData[2];
    canMsg.byte(3) = canData.canData[3];
    canMsg.byte(4) = canData.canData[4];
    canMsg.byte(5) = canData.canData[5];
    canMsg.byte(6) = canData.canData[6];
    canMsg.byte(7) = canData.canData[7];
    
    output( canMsg );
   
    dataOffset += __size_of(struct CANData);
  }
  


  //
  // Receive more data
  //
  if (gSocket.ReceiveFrom( gRxBuffer, elcount(gRxBuffer) ) != 0)
  {
    if (gSocket.GetLastSocketError() != 997) // ignore pending IO operation
    {
      write( "<%BASE_FILE_NAME%> UDPReceive failed, result %d. Measurement stopped!", gSocket.GetLastSocketError() );
      stop();
      return;
    }
  }
}


//
// Handler for CAN messages
//


on message *
{
  int i;
  struct CANData canData;
  
  if ((this.dir == RX) && (gModuleAddress != 0))
  {
    canData.canId = this.id & 0x1FFFFFFF;
    canData.flags = ((this.id & 0x80000000) ? 0x80 : 0x00) | ((this.rtr == 1) ? 0x40 : 0x00);
    canData.dlc   = this.dlc;
    
    for( i = 0; i < 8; i++ )
    {
      canData.canData[i] = (i < this.dlc) ? this.byte(i) : 0;
    }
    
    memcpy( gTxBuffer, canData );
    
    gSocket.SendTo( gModuleAddress, kPort, gTxBuffer, __size_of(struct CANData) );
  }
  else if (gModuleAddress == 0)
  {
    write( "<%BASE_FILE_NAME%> Tx not possible. Module to reach must send packets first." ); //Server simulation
  }
}
审核编辑:黄飞

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • C语言
    +关注

    关注

    180

    文章

    7596

    浏览量

    135959
  • 函数
    +关注

    关注

    3

    文章

    4290

    浏览量

    62346
  • 脚本
    +关注

    关注

    1

    文章

    387

    浏览量

    14813

原文标题:CAPL脚本使用介绍

文章出处:【微信号:eng2mot,微信公众号:汽车ECU开发】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    ProF脚本指令介绍及解决方案

    INCA是车辆控制器标定的首选软件之一,主要包含功能模型标定、测量数据分析(MDA)、标定数据管理(CDM)、控制器刷新(ProF)功能等。而本文将介绍常用却又陌生的ProF脚本的扩展用法,通过编写
    的头像 发表于 10-08 11:13 4542次阅读

    CANoe编写CAPL测试脚本的几点思考

    测试脚本的开发人员,需要考虑到测试执行者测试不同控制器时的参数配置。比如不同的网络唤醒条件、不同的网络管理消息、不同的时间参数等等。
    的头像 发表于 01-02 10:42 1895次阅读
    CANoe编写<b class='flag-5'>CAPL</b>测试<b class='flag-5'>脚本</b>的几点思考

    CAPL介绍-脚本编辑和常用基本事件#CANoe#CAPL#脚本

    编程语言
    北汇信息POLELINK
    发布于 :2023年01月06日 09:06:50

    CAPL编程语言 官方英文原版

    之前看过的一份CAPL编程语言资料,英文原版,分享给需要的小伙伴!!!
    发表于 01-14 18:19

    Lua脚本简单介绍

    Lua简单介绍Lua[1]是一个小巧的脚本语言。作者是巴西人。该语言的设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能。Lua脚本能够非常easy的被C/C++ 代码调用,也能够
    发表于 08-20 06:37

    Lua脚本简单介绍

    Lua简单介绍Lua[1]是一个小巧的脚本语言。作者是巴西人。该语言的设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能。Lua脚本能够非常easy的被C/C++ 代码调用,也能够
    发表于 08-20 08:06

    如何用CAPL通过RS232远程控制ALR3220

    CAPL通过RS232远程控制ALR3220程控电源
    发表于 12-28 06:37

    什么是脚本

    什么是脚本脚本是什么意思,脚本错误是什么意思电子发烧友深入为大家讲解了脚本相关知识
    发表于 12-07 10:36 2799次阅读

    什么是脚本脚本程序学习

    脚本中编写VB脚本代码。可以象使用系统函数一样使用项目中完成的脚本。创建脚本时,确定其型号并定义传送参数。“Function”类型的脚本
    的头像 发表于 05-11 10:39 6688次阅读
    什么是<b class='flag-5'>脚本</b>?<b class='flag-5'>脚本</b>程序学习

    详细介绍下如何解析ODX数据库

    针对涉及诊断功能类(如DTC等)测试的项目,实现过程大致为两步:先通过CANoe-CAPL完成通用的诊断功能测试脚本的开发;
    的头像 发表于 02-17 10:27 1114次阅读

    什么是CAPL编程?

    与Vspy的"C Code Interface"一样;在CANoe的使用中,一样提供了我们进行二次编程开发的工具——”CAPL Browser”。
    的头像 发表于 06-18 10:13 2662次阅读
    什么是<b class='flag-5'>CAPL</b>编程?

    CAPL编程语言快速入门

    CAPL是由Vector公司开发的类似于C语言的面向过程编程语言,是CANoe和CANalyzer中可用的编程语言。CAPL中程序块的执行由事件控制,在专用的编译器中开发和编译,这样可以访问数据库中
    的头像 发表于 09-17 16:11 2593次阅读
    <b class='flag-5'>CAPL</b>编程语言快速入门

    Linux主机排查脚本介绍

    介绍 HScan,本脚本旨在为安全应急响应人员对Linux主机排查,日志分析等提供便利,定制化在主机中执行命令 获取脚本 git clone https://github.com/HZzz2
    的头像 发表于 06-28 09:44 529次阅读
    Linux主机排查<b class='flag-5'>脚本</b><b class='flag-5'>介绍</b>

    CAPL在诊断中的应用,你值得了解!

    的过程中相信每位工程师都或多或少的要和“CAPL”打交道。学好CAPL的用法可以让我们更加高效、便捷地使用CANoe。本文就CANoe中关于诊断的CAPL函数进行介绍
    的头像 发表于 09-07 08:27 953次阅读
    ​<b class='flag-5'>CAPL</b>在诊断中的应用,你值得了解!

    Shell脚本检查工具ShellCheck介绍

    ShellCheck是一个用于bash/sh shell脚本的静态分析工具,可以辅助检查脚本语法错误,给出建议增强脚本健壮性。
    的头像 发表于 12-27 13:43 1971次阅读
    Shell<b class='flag-5'>脚本</b>检查工具ShellCheck<b class='flag-5'>介绍</b>