网络知识 娱乐 怎么用Canoe CAPL发送诊断

怎么用Canoe CAPL发送诊断

怎么用Canoe CAPL发送诊断

  • 前言
  • 1、直接调用CDD里的诊断发送
  • 2、用报文的形式发送诊断
  • 3、怎么用CAPL发送多帧的诊断
    • 3.1监控流控帧后,把多帧数据自己分帧发送
    • 3.2调用CDD里的诊断发送
    • 3.3使用CanTp分包分帧发送


前言

以UDS诊断为例,GMLAN会有些许差异,大同小异。
一般Canoe工程加载CDD以后,可以直接通过canoe工程的诊断界面,点击需要的诊断命令就可以发送诊断了。
在这里插入图片描述
那为什么还需要通过CAPL来发送诊断呢,
1.诊断界面的数据长度和有的参数范围是有限制(最大值最小值)的,但是如果你需要验证发送错误的长度或者超出范围的参数数据时ECU的响应是否符合要求的时候,没有办法通过诊断面板发送。
2.时间,可能你需要两个连续的诊断在间隔多长时间内发送,手动点击太慢。
或者你的ECU修复了某个bug软件升级之后,仅仅测试你修复的那个bug是不够的,最好把所有的诊断都测一下,一个一个点太慢,脚本测试才够快够准确。


1、直接调用CDD里的诊断发送

在CAPL里,诊断你也可以理解为一种类似于int这种的特殊数据类型。
关键字是diagRequest,前提是你的Canoe工程里有加载CDD.
在CAPL里编辑diagRequest,然后按空格,编译器会自动匹配你的canoe工程里加载的CDD名称。
在这里插入图片描述
选中CDD名字后,按‘.’,编译器会自动匹配出这个CDD支持的诊断列表,然后选中你要发送的诊断。
在这里插入图片描述
以要发送进入扩展层的诊断请求 10 03 为例,给这个诊断请求取个名字Diag_Tx_10_03,这样一个诊断请求的数据类型就定义好了。
在这里插入图片描述
定义完成以后,发送诊断请求的函数是diagSendRequest(diagRequest obj),括号里的参数就是我们定义的诊断请求。
那就是:

on key 't'
{
  diagSendRequest(Diag_Tx_10_03);
}

保存,编译,运行工程,点击按键t,
在这里插入图片描述
其他服务也是一样的,比如0x22服务的读SN的诊断

diagRequest Door.SerialNumber_Read Diag_Tx_ReadSN;

on key 's'
{
  diagSendRequest(Diag_Tx_ReadSN);
}

在这里插入图片描述

细心的同学可能会问,为什么Trace两个都是Tx,没有Rx,因为我手边暂时没有真实的ECU模块,是用的虚拟ECU,响应的数据是用另外一个节点模拟的,不过这不影响我们的测试验证,没有真实ECU的时候,想用验证就需要设置为模拟总线。
在这里插入图片描述

2、用报文的形式发送诊断

调用CDD里的诊断进行发送,最终体现在数据流里,还是以报文的形式发送出来的,所以也可以用发送报文的形式来发送诊断,只不过定义报文的请求ID要是诊断的请求id,而且诊断里的前2个byte决定了是单帧还是多帧,
如果是单帧,诊断里的第一个byte就代表者后续的有效字节长度。
如果是多帧,诊断里的第一个byte的低4位和第二个byte的高8位代表有效字节长度

比如发送进入扩展层的请求,发送的报文数据是02 10 03,其中的02就代表后面只有2个byte是有效数据位(10 03)。

如果不知道怎么用Canoe发送报文的同学,可以参考这个
链接: CAPL发送报文.

图片:

message 0x700 Diag_Tx_Request;

on key 't'
{
  Diag_Tx_Request.dlc=8;
  Diag_Tx_Request.byte(0)=0x02;
  Diag_Tx_Request.byte(1)=0x10;
  Diag_Tx_Request.byte(2)=0x03;
  output(Diag_Tx_Request);
}

3、怎么用CAPL发送多帧的诊断

ECU的诊断每帧最多只能发8byte的数据(CAN FD好像可以64byte),但是如果有个诊断要发送的数据超过了8byte的怎么办,那需要发送多帧数据才能完成一次发送了,而且多帧数据的发送是需要ECU端响应的,就是说

我上位机先发第一帧数据(首帧)给ECU端,
需要ECU给个响应(流控帧),
上位机才能接着发送后续数据帧(连续帧)。
在这里插入图片描述
比如说我需要发1,2,3,4,5,6,7,8,9,A共10个数据,一帧只能发8个,发不完。
1.我需要先发首帧,首字节的高4位是1才代表首帧,人家ECU收到这个之后才知道你一帧发不完需要多帧,才会发流控帧给你,让你有机会接着往下发
首帧如下:

0x10 0x0A 0x01 0x02 0x03 0x04 0x05 0x06
其中0x10的1代表首帧,00A代表有效数据长度,1,2,3,4,5,6是要发送的数据。

2.ECU回复个流控帧,第一个字节是0x30,上位机端接收到这个流控帧以后,才会接着发刚才没有发完的数据。
0x30 0x00 0x00 0x00 0x00 x00 0x00 0x00

3.上位机接着发后续数据帧(连续帧),连续帧的首字节的高4位是2,ECU端接收到2以后,就知道这个数据是跟上一帧连起来的。
0x21 0x07 0x08 0x09 0x0A
在这里插入图片描述

3.1监控流控帧后,把多帧数据自己分帧发送

假设我们通过发送报文的方式发送诊断里的F189,需要写的数据有13byte,加上0x2E 0xF1 0x89,就是16位,一帧发送不完,需要多帧发送。
第一步:向ECU发首帧
第二步:等待接收ECU端响应的流控帧
第三步:向ECU发送连续帧

那首帧里表示长度的就占据2个byte了(byte1的高4位代表帧类型,byte1的低4位和byte2的8位一起定义有效数据长度)

在这里插入图片描述
首帧应该这样发送,第一个0x10,高四位是1代表是首帧,第二个byte的0x10是代表后续的有效数据长度是16位。
0x10 0x10 0x2E 0xF1 0x89 0x00 0x00 0x00

发送完之后,需要等待ECU响应(回复流控帧0x30),才能接着往下发送后续数据帧(连续帧)。

on key 't'
{
  Diag_Tx_Request.dlc=8;
  Diag_Tx_Request.byte(0)=0x10;
  Diag_Tx_Request.byte(1)=0x10;
  Diag_Tx_Request.byte(2)=0x2E;
  Diag_Tx_Request.byte(3)=0xF1;
  Diag_Tx_Request.byte(4)=0x89;
  Diag_Tx_Request.byte(5)=0x00;
  Diag_Tx_Request.byte(6)=0x00;
  Diag_Tx_Request.byte(7)=0x00;
  output(Diag_Tx_Request);//先发首帧
  Flag_Multi_F189=1;
}

//0x600是ECU的响应ID,就是说接收到ID为0x600的报文以后就会触发下面这个函数
on message 0x600
{
  if(this.byte(0)==0x30)//看看第一个byte如果是0x30代表是流控帧,代表可以接着往下发送数据
  {
    if(Flag_Multi_F189)//判断是不是F189的流控帧
    {
      Flag_Multi_F189=0;
      Diag_Tx_Request.byte(0)=0x21;
      Diag_Tx_Request.byte(1)=0x00;
      Diag_Tx_Request.byte(2)=0x00;
      Diag_Tx_Request.byte(3)=0x00;
      Diag_Tx_Request.byte(4)=0x00;
      Diag_Tx_Request.byte(5)=0x00;
      Diag_Tx_Request.byte(6)=0x00;
      Diag_Tx_Request.byte(7)=0x00;
      output(Diag_Tx_Request);//连续帧一次发不完可以一直连续发
      
      Diag_Tx_Request.byte(0)=0x22;
      Diag_Tx_Request.byte(1)=0x00;
      Diag_Tx_Request.byte(2)=0x00;
      Diag_Tx_Request.byte(3)=0x00;
      output(Diag_Tx_Request);//连续帧一次发不完可以一直连续发,两个连续帧之间是否需要时间间隔是看流控帧返回的数据决定的,这里不展开讲了,有需要的可以留言,可以专门写一篇各个时间参数的
    }
  }
}
  

在这里插入图片描述
这是发送16个数据的多帧传输可以这样写。
如果要发送1000个byte呢,也可以这么写,不过会比较麻烦。

3.2调用CDD里的诊断发送

还是以发送诊断F189为例,可以参考上面介绍的调用CDD里的诊断发送的,但是发送的数据是诊断界面默认的数据,如下全0。
在这里插入图片描述
如果想用修改写入的数据需要调用函数

long diagSetParameterRaw (diagRequest obj, char parameterName[], byte* buffer, DWORD buffersize)

![在这里插入图片描述](https://img-blog.csdnimg.cn/bea9acad12b9495f8b7780868d852622.png
每个参数的描述如下

在这里插入图片描述
第一个参数 obj就是前面用diagrequest定义的那个变量,就是说你要发哪条诊断请求
第二个参数是你想写入的参数的修饰词qualifier,这个修饰词去哪里找呢,我理解去CDD文件里找才是最准确的,比如F189的这个参数修饰词就是Part_Number。(一般安装完Canoe后默认附带安装candelastudio的,可以直接双击点开CDD文件,查看CDD,编辑CDD的话还需要硬件license,但是查看是不用license的)
在这里插入图片描述
第三个参数就是你要写入的数据放到一个buffer里
第四个参数是buffer的长度.

如下,先定义diagrequest变量和要写入的数据buffer,再给写入的的诊断参数赋值,再发送诊断

byte ID_Write[13];
diagRequest Door.EcuIdentification_Write Diag_Tx_WriteID;

on key 'm'
{
  ID_Write[0]=0xff;
  ID_Write[1]=0x55;
  ID_Write[2]=0xAA;
  diagSetParameterRaw(Diag_Tx_WriteID,"Part_Number",ID_Write,13);
  diagSendRequest(Diag_Tx_WriteID);
}

在这里插入图片描述

3.3使用CanTp分包分帧发送

当你使用第一种报文的方式发送多帧的诊断时,有没有想过要是有个函数,我直接告诉多发多长的数据,然后把数据给他,让它自己发送就好了,这样就不用我自己在把数据分包了。

有的,cantp,我理解它的作用就是把这些数据按照协议打包/分包这种。

不过不是CAPL自带的函数,需要额外加载库,但是这个库是安装canoe的时候就有了的,
3.1:把安装目录下的dll文件拷贝到你的工程目录下。
在这里插入图片描述
3.2:在节点的配置Configuration选项里,选择Components,再选择Add添加osek_dp.dll文件
在这里插入图片描述
3.3在CAPL里选择添加osek_tp.dll文件,保存,这里".osek_tp.dll"是相对路径,其中.就代表你的工程文件路径,我的dll文件是直接放在工程目录下的
在这里插入图片描述
在这里插入图片描述
3.4.点击保存CAPL文件以后你会发现在CAPL编辑器的右侧多了osek_tp.dll,点击+展开以后,会看到这个dll库里支持的函数,它给你开放了好多函数接口,前面includes了之后,你就可以用这些函数了,就像编辑器自带的函数一样。
在这里插入图片描述
3.5那怎么用这个库里面的函数呢,函数很多,大部分我也不懂什么意思,
不过了解几个能够发送消息的函数就够用了,其他的大家可以自己去尝试。
要用cantp发送函数,分几步
第一步:先连接
用的函数是CanTpCreateConnection(),我理解这一步的作用就是先确定是标准帧还是扩展帧。
在这里插入图片描述
第二步:设置ID
设置发送请求的ID和ECU响应的ID
在这里插入图片描述
第三步发送数据
在这里插入图片描述
这几步里都会用一个connhandle,就是在第一步建立连接的时候返回的一个数据吧,就是说无论干啥都要建立在连接ok的基础上才可以。。
第一个参数是句柄,建立连接后得到的,
第二个参数是你要发送的数据
第3个数据是你要发送的数据长度

我们还是以发送诊断F189为例
在这里插入图片描述

代码如下

includes
{
   #pragma library (".osek_tp.dll")
}

variables
{
    const dword Diag_Tx_id = 0x700;
    const dword Diag_Rx_id = 0x600;
    const byte  TpPadding =  0x55;

    byte ID_Write[16];
    long TpHandle;
}
on start
{
    TpHandle = CanTpCreateConnection(0); //连接,参数是0代表标准帧
    CanTpSetTxIdentifier(TpHandle, Diag_Tx_id); //设置ID
    CanTpSetRxIdentifier(TpHandle, Diag_Rx_id);  
    CanTpSetPadding(TpHandle, TpPadding);//设置填充位
}

on key 't'
{
 ID_Write[0]=0x2E;
 ID_Write[1]=0xF1; 
 ID_Write[2]=0x89;
 ID_Write[3]=0x33; //想用发送什么数据,给要发送的数组赋值就好了,我这里随便举例一个数据
 CanTpSendData(TpHandle, ID_Write, 16);//发送
}

建立连接,设置id这些只需要做一次就好了,所以放在on start里在工程运行的时候设置一次就好了。
还多了一个函数CanTpSetPadding
这个函数的意思是,
比如当你发送一帧数据时,02 10 03只有3个byte,但是一帧里需要发满8个byte的时候(有的车厂就有这样的要求),后面5个byte其实是没有意义的,可以随便填充,但是有的车厂会要求填充0xAA或0x55…
CanTpSetPadding这个函数就是设置填充数据的,车厂没有要求的话可以不用这个.

看下面的数据,填充0x55的那几位,就是最后一帧连续帧里有效数据为不够8位了,需要填充。在这里插入图片描述