CANoe 基于CDD文件进行UDS诊断 + Capl
·
文章目录
一、导入CDD文件




二、新建ECU节点




三、CAPL中与诊断相关函数及事件
函数:diagSetTarget
//在CAPL中要通过诊断库函数进行uds诊断,需要先设置对应的诊断目标ECU(通过匹配目标ECU,
//CAPL才能知道我们的诊断请求应该通过哪个ID来发送,哪个ID是诊断响应)
long diagSetTarget (char ecuName[])
函数:诊断请求
//通过diagSendRequest函数来发送物理寻址的诊断请求
long diagSendRequest (diagRequest obj)
//通过diagSendFunctional函数来发送功能寻址的诊断请求
long diagSendFunctional( diagRequest request)
变量:诊断请求
//方式1,定义一个通用的诊断请求,不限制诊断服务类型,不限制ECU
diagRequest * diagReq;
//方式2,定义一个名为"DemoEcu"的ECU的通用诊断服务,不限制诊断服务类型,限制ECU
diagRequest DemoEcu.* diagReq1;
//方式3,定义一个名为"DemoEcu"的ECU的通用诊断服务,限制诊断服务类型,限制ECU
diagRequest DemoEcu.Programming_Session_Start diagReq2;
诊断ECU名称就是诊断描述文件的名称,即下图红框处的名称

事件:诊断响应事件
| 写法 | 作用 |
|---|---|
| on diagResponse * | 匹配所有诊断响应 |
| on diagResponse DomeECU.Programming_Start | 只匹配 CDD 中名为 Programming_Start 的服务响应 |
on diagResponse DomeECU.Programming_Start //Programming_Start 表示仅切换到编程会话的诊断响应会触发该事件
{
byte diagRespData[4096]; //定义存储诊断响应的buff数组
long diagRespDataLen; //诊断响应长度
int i;
this.GetPrimitiveData(diagRespData,4096); //将诊断响应的原始数据存储在此数组中
diagRespDataLen=this.GetPrimitivesize(); //获取诊断响应的大小
write("诊断响应长度为%d", diagRespDataLen);
for (i=0; i<diagRespDataLen; i++)
{
write("收到的诊断响应数据为%.2LX",diagRespData[i]);//打印诊断响应
}
}
使用函数直接获取诊断响应(不借助事件回调)
CAPL除了事件回调之外,还提供了一个函数用于直接获取对应的诊断响应
long diagGetLastResponse (diagRequest req, diagResponse respOut);
long diagGetLastResponse (diagResponse respOut);
on key'B'
{
diagRequest DomeECU.Extended_Start diagReq;
diagSendRequest(diagReq);
}
on key'C'
{
diagResponse * resp;//定义一个诊断响应
byte diagRespData[4096];//定义存储诊断响应的buff数组
long diagRespDataLen;//诊断响应长度
int i;
diagGetLastResponse(resp);//将上一次的诊断响应赋值给刚刚定义的变量
resp.GetPrimitiveData(diagRespData,4096);//将诊断响应的原始数据存储在此数组中
diagRespDataLen=resp.GetPrimitiveSize(); // 获取诊断响应的大小
write("诊断响应长度为%d",diagRespDataLen);
for(i=0;i<diagRespDataLen;i++)
{
write("收到的诊断响应数据为%.2LX",diagRespData[i]);//打印诊断响应
}
}
四、示例
示例1:通用诊断请求
/*@!Encoding:936*/
includes
{
}
variables
{
}
on start
{
// 定义诊断ECU名称,此名称需要与CDD文件中 "ECU qualifier" 名称一致
char ECU_Name[32] = "DomeECU";
diagSetTarget(ECU_Name);
}
on key'A'
{
diagRequest* diagReq;//定义一个通用的诊断对象
byte diagReqData[2]={0x10, 0x02};//定义请求内容
diagReq.Resize(2);//重新设定诊断请求的长度
diagReq.SetPrimitiveData(diagReqData,2);//设置诊断请求的原始数据,设置为数组中的内容,并设置数据长度
diagsendRequest(diagReq);//发送诊断请求
write("使用cAPL发送诊断请求");
}
注:进入非默认会话后,ECU 会有一个会话超时时间(通常几秒到几十秒),如果超时未收到新的诊断请求,ECU 会自动切回默认会话,导致后续诊断失败。Vector 的诊断栈(基于 CDD/ODX 文件配置)为了维持会话,会在会话即将超时前,自动发送 3E 00(TesterPresent 服务) 来 “保活”,防止会话超时。

当报文数据超过DLC时,会自动拆分为多帧。
示例2:发送诊断数据库中的诊断请求
on start
{
// 定义诊断ECU名称,此名称需要与CDD文件中 "ECU qualifier" 名称一致
char ECU_Name[32] = "DomeECU";
diagSetTarget(ECU_Name);
}
on key'A'
{
// 定义2E写DID诊断对象
diagRequest DomeECU.SMKVersion_Write diagReq;
// 2E报文格式:SID(0x2E) + DID(2字节) + 自定义写入数据
// 示例:DID=0xF1E4,写入数据0x04 0x05 0x06 0x07
byte sendBuf[7] = {0x2E, 0xF1, 0xE4, 0x04, 0x05, 0x06, 0x07};
// 设置请求原始数据
diagReq.Resize(elcount(sendBuf));
diagReq.SetPrimitiveData(sendBuf, elcount(sendBuf));
// 发送请求
diagSendRequest(diagReq);
write("已发送 2E 写DID诊断请求");
}
示例3:27服务解锁
在CAPL中调用 CDD 文件中导入的dll文件获取27秘钥依靠函数diagGenerateKeyFromSeed
long diagGenerateKeyFromSeed ( byte seedArray[], dword seedArraySize, dword securityLevel, char variant[], char ipOption[], byte keyArray[], dword maxKeyArraySize, dword& keyActualSizeOut);
seedArray:种子数组
seedArraySize:种子的长度
securityLevel:安全访问等级,2701则等级为0x1,2703则等级为0x3,2709则等级为0x9...
variant:诊断描述字符串,通常写一个空白字符串或者"Variant1"
ipOption:诊断描述字符串,通常写一个空白字符串或者"option"
keyArray:存放秘钥的数组
maxKeyArraySize:秘钥数组最大可能/最大允许的长度
keyActualSizeOut:秘钥数组中实际使用的字节/秘钥实际的长度。此参数在c++中是一个变量的引用,在CAPL中直接传变量即可
variables
{
diagRequest * diagReqSeed;//定义一个通用的诊断请求,用于请求种子
diagRequest * diagReqKey;//定义通用的诊断请求,用于发送秘钥
byte gSeedArray[4];//存储秘钥的数组,这里我由于我的种子长度为4,所以给4个字节
dword gSeedArraySize = 4;//种子长度
dword gSecurityLevel = 0x01;//安全等级
char gVariant[200] = "Variant1";//诊断对象描述
char gOption[200] = "option";//诊断对象描述
byte gKeyArray[255];//存储种子的数组
dword gMaxKeyArraySize = 255;//种子可能的最大长度
dword gActualSizeOut;//种子的实际长度
}
on start
{
char ECU_Name[32] = "BasicDiagnosticsEcu";//定义诊断ECU名称,此名称需要与诊断描述文件完全相同
diagSetTarget(ECU_Name);//设置诊断对象名称
}
On key'B'
{
byte reqData[2] = {0x27,0x01};//定义一个种子请求数组内容
word reqDataLen;//种子请求的长度
word i;
reqDataLen = elcount(reqData);//计算种子请求的长度
diagReqSeed.Resize(reqDataLen);//重置用于请求种子的诊断请求的长度
diagReqSeed.SetPrimitiveData(reqData,reqDataLen);//将请求数组的数据设置给用于请求种子的诊断请求
diagSendRequest(diagReqSeed);//发送诊断请求
}
on diagResponse *
{
byte respData[4096];//定义存放诊断响应的数组
word respDataLen; //存放诊断响应的数组长度
respDataLen = elcount(respData);//计算长度
this.GetPrimitiveData(respData,respDataLen);//获取诊断响应到响应数组中
if(respData[0] == 0x67 && respData[1] == 0x01)//如果回复的前面两个字节是67 01,即回复的是种子
{
byte keyReqData[6];//秘钥请求数组
word keyReqLen;//秘钥请求数组长度
keyReqLen = elcount(keyReqData);//计算长度
memcpy_off(gSeedArray,0,respData,2,4);//将respdata的第2~6个字节拷贝放在gSeedArray中,即种子
diagGenerateKeyFromSeed(gSeedArray,gSeedArraySize,gSecurityLevel,gVariant,gOption,gKeyArray,gMaxKeyArraySize,gActualSizeOut);//调用诊断描述文件中dll计算秘钥的函数
//此时已经获得了秘钥,存在在keyArray中
keyReqData[0] = 0x27;keyReqData[1] = gSecurityLevel + 1;//设置对应的秘钥请求前两个字节的内容,第一个字节为0x27,第二个字节为种子请求+1
memcpy_off(keyReqData,2,gKeyArray,0,4);//将秘钥拷贝到秘钥请求数组中去
diagReqKey.Resize(keyReqLen);//调节诊断请求大小
diagReqKey.SetPrimitiveData(keyReqData,keyReqLen);//设置发送秘钥的诊断请求的数据
diagSendRequest(diagReqKey);//发送秘钥诊断请求
}
}
参考
更多推荐

所有评论(0)