注:大部分内容来自全网可见渠道!!

CAPL

 capl简介

CAPL(Communication Access Programming Laguage)语言它是Vector公司专门为CANoe开发环境设计的编程语言,在语法和概念上与C语言类似。借助CAPL,用户可以编写程序并应用到网络的各个节点上。

CAPL是类C语言,语法其实与C语言很相似,但同时又包含了一些C++的特性,CAPL语言针对的主要是一些复杂状态情况下的一些输入输出的编写,如果简单的话,我们直接绑定信号就可以了。但是如果我们信号需要转变,或者说进行一些运算,然后再输出的话,我们的CAPL就会派上用场。

应用于Vector CAN工具节点的编程,是基于事件建模的语言;可以使用write()函数进行调试,用于将调试信息输出到CANoe的write窗口上;通过output()函数进行指定报文的发送;通常是通过环境变量事件与CANoe面板进行关联,实现交互;

例如,模拟远程启动空调系统时,需要检查引擎状态等,仅使用数据库和仿真面板还远远不够。这时CANoe就需要使用自带的CAPL通过编程来实现对应的功能。CAPL既能够通过编程实现节点的仿真,也可以在数据分析、测试等方面发挥很大的作用,通过与外界系统的交互,实现仿真、诊断和测试的自动化。

 CAPL用途

CAPL是CANoe的灵魂,因为毕竟是任何的硬件软件才是控制硬件的核心,然后使用CANoe可以满足仿真分析测试诊断的各种复杂要求。CAPL主要用途有以下几点:

(1)使用易于理解的编程语言来仿真节点或模块;

(2)仿真事件报文、周期报文或者附加条件的重复报文;

(3)使用PC键盘模拟操作按钮等人工操作事件;

(4)仿真节点的定时或网络事件;

(5)仿真多个时间事件,每个事件有自己的特定行为;

(6)仿真普通操作、诊断或生产操作;

(7)仿真物理参数或报文的变化;

(8)生成错误帧,评估模块和网络软件处理机制;

(9)仿真模块或网络错误来评估相关的防错机制;

(10)提供网络测试、诊断等功能测试库函数。

第一个就是用便于理解的语言来仿真节点或模块,这就是我们的CAPL语言的就是简单之处。

第二个就是仿真事件报文周期或者附加条件的重复报文。这个也比较好理解,就是我们本身CAPL就为了总线通讯编程语言,总线里面肯定有一些报文或者周期报文或者说报文相关的一些属性,这时候我们就可以很方便地来添加。

第三个就是我们可以通过 Pc按键来模拟人工操作事件,这样的话就大大减少我们一些外设对采集方面的要求,我们直接通过按键来触发我们ECU里面的一些动作就可以了,就不用外设直接接到ECU里面,然后通过外设来操作我们的ECU仿真ECU。

第四个就是我们仿真节点的定时换网络事件,这也它是针对总线的总线,肯定是有网络事件的,所以我们开发里面有一些针对网络事件的函数,可以轻松做出来。

第五个就是仿真多个时间事件,每个事件都有自己的特定行为,这就是我们CAPL就是基于事件来处理一些或者说产生一些事件。

第六个就是仿真普通操作诊断和生产操作,操作就比较好仿真,按键也可以仿真,或者说我们通过报文输入也可以仿真,诊断我们也就CAPL里面有很多诊断函数,通过自动的输入诊断指令,也可以达到我们仿真的目的。

第七个就是仿真物理参数或者报文的变化,物理参数主要是一些电压电流什么的,因为它一般上都会转移到电压或电流,我们 CAPL也会通过采集我们硬件里面的电压信号,来输出我们的报文,或者说来使报文产生变化。

第八个就是生成错误帧评估模块和网络软件处理机制。这个也比较好理解,就是我们开发里面有很多针对总线的一些故障处理,出故障产生的函数,通过这些函数来模拟故障,来评价我们的 ECU软件的网络管理软件。

第九个就是仿真模块或网络错误来评估相关的防错机制,其实跟第八相似原理都是通过故障注入的模式来仿真,或者说来测试我们的ECU对这些故障产生了一些防错和处理。

第十个就是提供网络测试诊断等测试款函数,这就是我们CAPL最强大的地方,它有完备的库函数,它的库函数针对总线的各个方面,所以我们才能很方便地上手来测试或者仿真我们的ECU等等,

 CAPL特点

CAPL类似C语言,与C语言在语法和结构上有很多相同之处,但

也有一些其特殊的地方(主要表现在函数声明和调用)。

(1)未定义返回类型,默认为void类型;

(2)像C++一样允许空的参数列表;

(3)像C++一样允许函数重载;

(4)参数检测与C++中一样;

(5)CAPL提供一些自带的库函数;

(6)CAPL编译时不对自带的关键字和自带的函数名做区分。

CAPL页面简介

File菜单主要用于对代码文件的操作,具体内容如下表

Home功能区: 提供CAPL编译常用功能,包括代码编译以及编译等操作

Filter功能区: 管理CAPL函数库,可在函数浏览器中屏蔽掉不需要的函数库

Debug功能区: 提供CAPL编程常用功能,包括代码编译以及变异操作等

Layout功能区:设置各子窗口的显示模式

编辑区的左侧部分是一个导航区,用户可以便捷地查看各种变量、事件、函数的声明和定义,单击这些对象,可以快速跳转到编辑区对应的代码段。编辑区为一个文本编辑器,是用户编写代码的区域。

CAPL注释,变量,常量,运算符

注释规则:

CAPL语言的语法与C语言基本相同

注释

//(单行注释)放置在需要注释的语句之前,注释单行

/*(多行注释)注释起始符,其后的内容被注释

*/注释结束符,结束由/*开始的注释

事件过程

分号  语句结束符

大括号

变量:

  1. 变量名、函数名和数组名可以由字母和数字和下划线组成,但是首字母不能是数字。
  2. 使用CAPL编写时,一定要记住,对于用户定义的变量,小写字母和大写字母是不同的变量。
  3. 但是CAPL支持的关键字是不区分大小写的,比如 INT a; 和int a;是一样的
  4. 关键字不能用来命名变量或函数。CAPL使用C编程中保留的关键字语言。但是,CAPL不支持一些常见的C关键字。
  5. 不能将CAPL的函数名和对象名用作变量
  6. 变量名的字符长度最好不要超过32个编译系统只能识别前32个字符)

变量名最好具有易读性,见名知意

简单来看变量的声明定义以及规则也不多,类似与c语言里的规则。虽然函数名和对象名不属于关键字,CAPL也不允许将它们用作变量名,因为使用同一个名称用作函数名和变量名会把编译器搞糊涂。

比如abs,它是CAPL的一个库函数,用来返回参数的绝对值,如果在程序中同时定义了一个abs变量,这时编译器无法有效辨别,编译将无法通过。

对于有效性,主要是指变量名的字符长度最好不要超过32个,编

译系统只能识别前32个字符,也就是说,前32个字符相同的两个不同变量将被编译系统认为是同一个变量。对于易读性,主要是指在CAPL编程中提倡使用有一定含义的变量名,能做到“见名知意”。如果变量表示ECU的供电电压,可以将其命名为voltage_of_power或VoltageOfPower,而不要将其命名为i或者po。

必须强调的是,与C语言不同,CAPL语言中为了用户使用的方便灵活,编译器不区分CAPL自带的关键字和函数名的大小写,例如DWORD、dword和Dword在代码中使用效果一样,所以用户定义变量时也需要避免此类情况。

这里需要指出的是,以上命名规则同样适用于函数名、数组名、结构体等。

全局变量与局部变量

在CAPL中,全局变量需要被声明在variables部分,同时可以使用直接赋值方法进行初始化。如果没有初始化,编译器会执行自动初始化,默认值为0。全局变量的作用域包括整个CAPL文件以及与此文件有链接的其他CAPL文件。

与C语言不同,局部变量在CAPL中总是被静态的创建。这意味着初始化值在程序体启动时执行,当再次进入程序时,局部变量被假定是上一次跳出程序时的值。局部变量的作用域,仅限于当前函数体范围内,即该函数的大括号范围内。

简单变量

简单的三种变量类型——整型、字符和浮点数。

整型根据数值大小不同有CAPL提供了以下变量

字符区别于C语言,CAPL未将char类型(长度1B)归类至整型中,这是因为在CANoe中提供了byte类型。如果数据是具体数值则应使用byte,而对于字符,则应用char(字符串使用char数组)。char类型和byte类型之间可以直接转换。

浮点数与C语言相同,分单精度float和双精度double

复合变量

CAPL 同时也支持复合类型变量,如数组、结构和枚举。它们的声明方法与C语言基本一致。

特殊变量

CAPL还预定义了一些特有的、针对CANoe仿真的类。

Message

报文(CAN/LIN messages)是车载网络最基本的构成部分,CAPL提供了各种网络相对应的报文类。这里主要介绍CAN报文和LIN报文。

使用关键字message来声明一个报文变量,当使用message声明报文变量时,默认变量为CAN报文变量。当有数据库支撑的时候,一个 完 整 的 声 明 应 该 包 括 message ID 或 者 message name 。 结 合database的例子,使用ID 0xA或者报文名来声明一条数据库中的EngineData报文。

以标识符“x”结尾的ID表示这是一个扩展帧ID,例如,100x。

而“*”则表明这条报文在声明时还不含有CAN ID。例如:

切记,使用这种方式声明报文时,一定要指定ID后才能将msg发送出去。

CAPL提供了一系列的选择器(Selector)以保证用户能够按照自己的意图去修改CAN message的属性。

2.系统变量

系统变量是一种特殊的变量,用来描述某种特殊状态(例如某种事件触发)或者记录测量数据。一般有系统定义和用户自定义两种,它们的作用域都是在各自的命令空间内。之前已经介绍了如何创建系统变量,这里只介绍系统变量的数据类型及描述。

3.定时器

CAPL 提 供 了 两 种 定 时 器 变 量 : timer 基 于 秒 的 时 间 单 位 ;msTimer基于毫秒的时间单位,

常量:

初始化一个变量意味着给它赋一个初始值或开始值。在CAPL中,这可以在同一行中完成变量声明。当值在声明期间赋值时,它们被视为常量

整形

整形可以是十进制和十六进制

int value = 20;

int value2 = 0x14;

浮点数

浮点数可以是十进制数,或者科学记数法,以下都是合法的

float value = 0.23;

float value2 = 23E-2;

2.1

2.1e0

3.1415

0.00034

22e+3

1E-6

字符

字符常量 是用单引号,括起来的一个字符

char value = ‘B’;

char value2 = ‘8’;

char value3 = ‘?’;

CAPL支持使用ASCII字符集。上面的三个字符赋值可以用十六进制分别

char value = 0x42;

char value2 = 0x38;

char value3 = 0x3F;

字符串

字符串常量由一系列由双引号括起来的一个或多个连续字符组成。

字符串存储在char类型的数据元素数组中。最后一个元素包含空字符\0,即用于指示字符串的结束。

要确保字符串数组定义时的大小总是字符串长度 + 1,因为结束符\0也占用一个字符

char value[30] = “Here’s a string in C and CAPL”;

char value2[19] = “spaces are allowed”;

char value3[31] = “with a tab escape sequence \t”;

如果已经定义过了一个字符串数组,不可以直接给它赋值的,下面代码是不允许的

char value3[31] ;

value3 = “with a tab escape sequence ”;

可行的一种方法,是通过CAPL自带的字符串操作函数实现

char value3[31] ;

strncpy(value3,  “with a tab escape sequence ”, elcount(value3));

运算符:

CAPL也提供了算术、逻辑和位运算的运算符,其用法也与C语言保持一致。

CAPL程序结构

一个完整的CAPL程序由4部分组成:头文件include files、全局变量声明、事件处理和自定义函数。当然不是每个因素都要有,视具体程序功能确定。

 CAPL头文件

头文件Includes {}

{}中一般放.cin文件,该文件- 一般是一些可复用的代码段构成

详解

includes {

#include "CommonFunction .cin"

#include "BaseServices .cin" }

用户可以通过.cin文件搭建自定义的测试框架

cin文件中可包含其他的.cin文件, 可形成多层的引用结构

cin文件中一般存放可复用的代码

 全局变量声明

全局变量的声明Variables {}

在此部分声明的全局变量生命周期从仿真开始持续到仿真结束,作用域为

整个CAPL文件

详解

variables

inti=0;

message 100 msg;

msTimer myTimer,

 事件处理

CANoe主要是用来对CAN通信网络进行建模、仿真、测试和开发的一种工具,因此对实时性和逻辑关系尤为关注。作为CANoe的辅助语言,CAPL采用了面向事件的机制来满足这样的需求。通俗来说就是,在什么条件下,在什么时间节点,发生了什么样的报文传递,得到了什么样的报文反馈。而这种面向事件的机制是通过event handler来实现的。

事件起始关键字on *

on后面加某种条件,一旦条件满足则执行下面函数体内的语句。函数体内的语句是实现接下来需要完成的操作。

关键字this

在CAN报文事件或变量事件中,可以使用关键字this访问数据内容。

  系统事件

在CAPL的系统事件中,有preStart、start、preStop、stopMeasurement这4种。我们可以根据需要在相应的系统事件函数接口中定义想要进行的操作;当工程运行时,下述系统事件的发生顺序依次是

preStart–>start–>preStop–>stopMeasurement

on preStart             /*系统事件,初始化时执行*/

{

   resetCan();            /*CAPL接口函数,用于复位CAN控制器*/

}

on start              /*系统事件,工程开始时执行*/

{

   write(“Just A Try”);      /*write()函数将字符串信息在”write”窗口输出*/

}

on preStop            /*系统事件,工程预备停止时执行;发生在stopMeasurement事件前面*/

{

    write("The Project Will Stop!”);

}

on stopMeasurement      /*系统事件,工程停止时执行*/

{

    write("The End!\n");

}

 控制器事件

CAN控制器事件

当硬件对CAN控制器检测到相应动作发生时执行;以BusOff事件为例,格式如下:

on busOff       /*CAN控制器事件:硬件检测到BusOff时执行*/

  {

      write("BusOff Error!");

  }

  消息/报文事件

CAN消息事件

通过”on message”定义消息事件,该事件会在指定的报文消息被接收时被调用。关于消息事件的定义格式示例如下:

on message 123             /*接收到123(10进制)这个ID的报文时执行*/

on message 0x441           /*接收到0x441(16进制)这个ID的报文时执行*/

on message BCM              /*接收到BCM(工程dbc文件中的报文名)这个报文时执行*/

on message*              /*接收到任意报文时都执行(注意*与message之间没有空格)*/

on message 0x300-0x444     /*接收到这个范围内的ID报文时执行*/

{

    write(“Received %x”,this.id);   /*打印接收到的报文id*/

    write(“Received Message %d in total!”,count);

}

以上是关于消息事件的定义格式,关于消息的索引及发送操作我们通过下例介绍:

假设VoiceStatus是我们工程dbc文件中定义的一个报文,该报文包括了VoiceType和VoiceOperation这两个信号;其中,VoiceType这个变量占据第1个字节;VoiceOperation占据第2、3个字节;则关于消息的索引,通过报文的信号(msg.VoiceType这样)去操作如下:

void TxMsg_VoiceStatus(void)

{

    message VoiceStatus msg;         /*将工程中dbc中定义的VoiceStatus这条报文取名为msg*/

    msg.VoiceType = @VoiceType;      /*对应赋值给到报文的信号,通过报文别名"msg."调出*/

    msg.VoiceOperation = @VoiceOperation;

    output(msg);                     /*通过output指令发送该报文*/

}

也可以直接通过后接数据类型(msg.byte(0)这样)去操作,此时操作如下:

void TxMsg_VoiceStatus(void)

{

    message VoiceStatus msg;         /*将工程中dbc中定义的VoiceStatus这条报文取名为msg*/

    msg.byte(0) = @VoiceType;        /*报文第1个数据字节*/

    msg.word(1) = @VoiceOperation; ; /*报文从第1个字节开始的一个字(2个字节)*/

    output(msg);                     /*通过output指令发送该报文*/

}

  信号事件

CAN信号事件是在CAN总线上出现指定的信号时被调用(需要配合DBC文件使用)。关键字为:on signal xxx或on signal_updatexxx。需要注意的是,前者只在指定信号的值发生变化时被调用,后者在每次接收到指定信号时均被调用。

on signal Li ghtSwitch: :OnOff

{

STAT1=this;

}

on signal_ update Li ghtSwi tch: :0nOff

{

STAT2=this;

}

  键盘事件

通过”on key”定义键盘事件,该事件会在我们按下指定按键时执行;需要由用户来触发某些事件来模拟实际测试环境的人工操作,关于键盘事件的定义格式示例如下:

on key ‘a’        /*在小写输入法下,按下键盘的’A’键时执行*/

on key ‘A’        /*在大写输入法下,按下键盘的’A’键时执行*/

on key ‘ ’        /*按下键盘的空格键时执行,注意单引号中间是有空格的*/

on key 0x20       /*按下键盘的空格键时执行*/

on key F2            /*按下键盘的’F2’键时执行*/

on key CtrlF3        /*同时按下键盘的’Ctrl’键和’F3’键时执行*/

on key*          /*按下键盘的任意键时都会执行(注意*与key之间没有空格) */

{

    write(“The Key Is Press”);

}

例题1//定义了ID为0x555长度为1的消息值为0xAA,按键b触发发送

variables

{

message 0x555 msg1 = {dlc=1};

}

on key 'b'

{

msg1.byte(0)=0xAA;

output(msg1);

}

  时间/定时器事件

时间事件

通过”on timer”定义时间事件;该事件会在设定的时间到达时执行。关于时间事件的定义格式及使用示例如下:

variables

{

  msTimer Timer1;        /*在variables中声明一个以ms为单位的定时器变量Timer1*/

}

on start

{

  setTimer(Timer1,100);     /*将Timer1的定时时间设定为100ms,并启动它*/

}

on timer Timer1         /*定义的Timer1时间事件,每100ms执行一次*/

{

  setTimer(Timer1,100);     /*启动下一个周期循环*/

}

on key ‘a‘           /*键盘事件,按下键盘’A’键时执行*/

{

  cancelTimer(Timer1);     /*停止Timer1这个100ms执行一次的定时器*/

}

例题2//定义了ID为0x555长度为1的消息,按周期100ms,值++发送

variables

{

message 0x555 msg1 = {dlc=1};

mstimer timer1; //define timer1

}

on start

{

setTimer(timer1,100);//initialize timer1 to 100ms

}

on timer timer1

{

setTimer(timer1,100);//reset timer

msg1.byte(0)=msg1.byte(0)+1;//change the data

output(msg1);//output message

}

  错误帧事件

通过”on errorFrame ”定义错误帧事件;该事件会在硬件检测到错误帧时执行。关于错误帧事件的定义格式示例如下:

on errorFrame       /*错误帧事件:硬件检测到错误帧时执行*/

{

  write("The error has occur");

}

  环境变量事件

通过”on envVar”定义环境变量事件;该事件会在指定的环境变量值有新的输入时执行(环境变量常常用于关联上一个面板控件,当我们对控件进行操作时,对应改变关联上的环境变量值;而此时我们在CAPL中关于该环境变量的事件就会被调用;以此完成交互操作)。关于环境变量事件的定义格式示例如下:

on envVar BCM_HightBeamAlarm    /*环境变量事件:指定的环境变量值有输入时执行*/

{

    byte num=0;

    num = getValue(this);     /*可以使用getValue(环境变量名/this关键字)获取指定的环境变量的值*/

if(num == 1)

    {

      write("The envVar is %d",@BCM_HightBeamAlarm);  

    }

else

    {

      putValue(this,1);/*使用putValue(环境变量名/this关键字,设定的值)改变指定的环境变量的值;直接赋值的话,格式是@BCM_HightBeamAlarm = 1; */

      write("Change envVar to %d",@BCM_HightBeamAlarm);

    }

}

关于在CAPL中对环境变量的操作中,getValue()与putValue()是常用的接口函数。

关于环境变量的定义是在dbc文件中完成的;CANoe工程导入该dbc文件即可使用其定义的环境变量了。(在数据库new:Environment variables,新建,定义环境变量相关设置)

  系统变量事件

通过”on sysvar”定义系统变量事件;该事件会在指定的系统变量值有新的输入时执行,其格式及使用方法与前一小节的环境变量基本一致;差别只在于环境变量是在dbc文件中定义的;而系统变量的定义如下:

点击工具栏的”Environment”下的”System Variables”;此时界面如下,右键空白处,选择”New”进行新建;在弹出的窗口对新建的系统变量进行参数设置。

关于系统变量事件的定义格式示例如下:

on sysvar SysVar1   /*系统变量事件:指定的系统变量值有新的输入时执行*/

  {

      write("The SysVar1 is %d",@SysVar1);

  }

 自定义函数

CAPL函数致力于定义接口,形成模块化的代码以提高代码的重用性。它的语法跟C语言很类似,但也包含一些C语言所不具备的功能。

(1)当返回值类型省略时,被默认解释为void类型。

(2)正如C++一样,允许函数包含一个空的形参列表。

(3)允许重载函数(即同一个函数名,但每个函数的形参列表必须不同,例如,不同的形参类型或者在形参列表中的不同次序)。读者可以研究一下CANoe函数库所带函数toUpper,其功能是将字符或字符串转换为大写字母。函数原型如下。

char toUpper(char c);

void toUpper(char dest[], char source[], dword bufferSize);

  1. 函数会对实参进行类型检查,如果类型不同则检查是否能够通过隐式类型转换,如不能,则无法通过编译,例如:

void Functionl (long lpar){}

void Function1 (dword lpar){}

void Function2 (dword lpar){}

void Function3 {}

{

byte bvar;

long lvar;

Functionl((long)bvar);// 显示类型转换

Function1((dword)bvar);// 显示类型转换

Functionl(lvar);// 类型匹配;没有转换

Function2(bvar);// 隐式类型转换

}

  1. 任意维度或大小的数组都可被作为函数参数传递。例如,以下函数参数为数组,将在Write窗口输出一个矩阵。

void printMatrix(int m[] [] )

{

int i,j;

for(i=0; i<elCount(m) ;i++) {

for (j=0;j <elCount (m[0]) ;j++) {

write("%ld", m[i] [j]) ;

}

}

}

(6)大部分CAPL支持的数据类型都可以直接声明为函数参数,例如,整型、浮点型、枚举、结构、定时器以及它们的引用。但有一些类型不能被直接声明,而需要加上*号(注意该符号并不是C语言中指针的意思)。

例 如 , signal * s 、 envvarInt * ev 、 sysvarFloat * sv 、diagRequest * dr、diagResponse * dr、int matrix[][],以及所有来自database的变量,均需要加上*号声明。

需要注意的是message类型比较特殊,如果该变量是用户自定义的,那么在函数参数声明时,message和message*均可以,但如果该变量来自database,那么只有message *可用。

流程控制语句

CAPL支持的控制语句和C语言中一样,包括 if else ,do ,while ,for 等

if语句

If (表达式)语句;

If(表达式)语句; else语句

switch语句

Switch (表达式):

Case常量表达式1: 语句1;

Case 常量表达式2:语句2;

Case 常量表达式n:语句n;

Default: 语句n+1;

while语句

While(表达式) 语句; Do while语句:

Do循环语句;

While(表达式)

for语句For (〈初始化〉; 〈条件表达式〉; 〈增量〉)语句;

初始化:为-赋值语句,给循环变量赋初始值

条件表达式:关系表达式,决定退出循环的条件

增量:定义循环控制变量变化/单次循环

break语句

break语句用于跳出循环体

在多层循环中,一个break语句只能向外跳出一层

returen语句

格式: Return语句;

一般放在函数体的最后

一个函数中可以有多个returm语句, 但实际运行时只能有一个returm语句起作用

利用以上内容编写一个复合条件的CAPL文件。

第三题//定义了ID为0x400长度为1的消息,按键a有效触发发送,按照周期200ms,值++发送

variables

{

message 0x400 msgA = {dlc=1};

mstimer timerA;

int conditionA = 0;//initialize conditionA =off

}

on key 'a'

{

conditionA = !conditionA;//toggle conditionA

if(conditionA == 1)//if conditionA is active

{

setTimer(timerA,200);//then start timer

}

}

on timer timerA

{

if(conditionA == 1)//if conditionA is still true

{

{

setTimer(timerA,200);//then continue timer

}

msgA.byte(0)=msgA.byte(0)-1;//change the data

output(msgA);//output the message

}

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐