uint16 DTC_GetDtcCountByStatusMask(uint8 status_mask) /*按照状态掩码统计ECU中与之匹配的DTC数*/
{
uint16 Dtc_count = 0;
uint8 Record_count;
for(Record_count = 0; Record_count < DTC_CODE_MAX_NUM; Record_count++)/*检索所有的DTC*/
{
if((Dtc_dtc_status_record[Record_count].dtc_status.status_byte & status_mask) != 0)
{
Dtc_count++;
}
}
return Dtc_count;
}
FUNC(void,DCM_CODE) App_Fault_Memory_Read_Number(P2VAR(Dcm_MsgContextType,AUTOMATIC,DCM_APPL_DATA) pMsgContext)
{
uint16 Counter = 0U;
uint8 DtcStatus_Temp;
DtcStatus_Temp = pMsgContext->reqData[DCM_INDEX_2]; /*通过19 01服务第3个字节发送状态掩码;*/
Counter = DTC_GetDtcCountByStatusMask(DtcStatus_Temp); /*按照状态掩码统计ECU中与之匹配的DTC数(代码如上)*/
pMsgContext->resData[DCM_INDEX_2] = DTCStatusAvailabilityMask; /*返回ECU支持的状态位*/
/* Change below data if necessary */
/* 0x00 ISO15031-6Format,0x01 ISO14229-1Format,0x02 J1939 Format */
pMsgContext->resData[DCM_INDEX_3] = 0x00U; /*返回ECU使用的DTC格式标识符,
00:15031-6;01:14229-1;02:J1939*/
pMsgContext->resData[DCM_INDEX_4] = (uint8)(Counter >> 8U); /*返回按照状态掩码统计出来的DTC数*/
pMsgContext->resData[DCM_INDEX_5] = (uint8)(Counter);
/* Always equals 6, don't change it */
pMsgContext->resDataLen = 6U;
DsdInternal_ProcessingDone(pMsgContext);
}
Ps:DTC状态掩码参数包含8个DTC状态位,其位定义如下:
2、02子服务
按照定义的状态掩码的形式去查找匹配的故障,将匹配的DTC标识符(3个字节)、DTC状态(1个字节)信息返回。上一小节的01子服务只统计与状态掩码相匹配的DTC个数,02子服务则会将这些匹配的DTC信息返回。请求格式如下:
收到请求后,ECU的响应报文格式如下:
关于19 02服务代码部分示例如下(帮助理解,仅供参考):
uint16 DTC_GetDtcByStatusMask(uint8 *p_dtc, uint8 status_mask) /*按照状态掩码统计ECU中与之匹配的DTC,返回该DTC信息*/
{
uint16 dtc_count = 0;
uint8 record_count;
for(record_count = 0; record_count < DTC_CODE_MAX_NUM; record_count++)
{
if((Dtc_dtc_status_record[record_count].dtc_status.status_byte & status_mask) != 0)
{
*p_dtc++ = Dtc_dtc_code_data[record_count].dtc_high_byte;
*p_dtc++ = Dtc_dtc_code_data[record_count].dtc_middle_byte;
*p_dtc++ = Dtc_dtc_code_data[record_count].dtc_low_byte;
*p_dtc++ = Dtc_dtc_status_record[record_count].dtc_status.status_byte;
dtc_count++;
}
}
return dtc_count;
}
FUNC(void,DCM_CODE) App_Fault_Memory_Read_identified_errors(P2VAR(Dcm_MsgContextType,AUTOMATIC,DCM_APPL_DATA) pMsgContext)
{
uint16 counter = 0U;
uint8 DtcStatus_Temp;
DtcStatus_Temp = pMsgContext->reqData[DCM_INDEX_2]; /*通过19 02服务第3个字节发送状态掩码;*/
/*按照状态掩码统计ECU中与之匹配的DTC,返回其DTC信息(代码如上)*/
counter = DTC_GetDtcByStatusMask(&(pMsgContext->resData[DCM_INDEX_3]),DtcStatus_Temp);
pMsgContext->resData[DCM_INDEX_2] = DTCStatusAvailabilityMask; /*返回ECU支持的状态位*/
pMsgContext->resDataLen = DCM_INDEX_3 + (counter * 4U); /*更新响应报文的长度;*/
DsdInternal_ProcessingDone(pMsgContext);
}
3、04子服务
为了方便找到故障的原因,车厂一般会在诊断调查表中定义一些信息作为快照信息,例如故障的发生时间、电压、行驶里程数、车速等。在对应故障发生时,ECU端要记录发生故障时的快照信息;而04服务就是用于请求指定故障码(DTC)的快照信息,通过查找故障发生时刻的这些数据,来分析故障原因。请求格式如下:
其中,DTCSnapshotRecordNumber表示DTC快照记录码,占一个字节,表示特定的 DTC快照数据记录编号。例如当我们需要记录某个DTC第一次发生(假设用1表示)和最近一次发生的快照数据时(假设用2表示);那么当DTCSnapshotRecordNumber为1时,则表示请求该DTC第一次发生时的快照信息。
如果ECU支持多个DTC快照数据记录,那么该纪录码应使用0x01~0xFE范围内的数值。当该参数值为FF hex时,要求ECU一次性报告所有存储的DTC快照数据记录。
收到请求后,ECU的响应报文格式如下:
如上,响应报文中DTCSnapshotRecordNumber表示返回的是该DTC的哪一个快照记录;DTCSnapshotRecordNumberOfIdentifiers表示快照信息中定义的成员量;如定义的快照数据有车速、电压、转速、里程这四项信息;则该值为4。
如下,假设有两个快照记录信息,快照记录的成员数有4个;则对应的19 04服务的诊断代码设计如下(帮助理解,仅供参考):
FUNC(void,DCM_CODE) App_Fault_Memory_Read_snapshot(P2VAR(Dcm_MsgContextType,AUTOMATIC,DCM_APPL_DATA) pMsgContext)
{
uint8 error = 0U;
uint32 Dtc;
uint32 i;
uint8 DTCSnapshotRecordNumber;
uint8 DTCSnapshotRecordLength = 0U;
uint8 status = 0U;
uint8 snap_data_len = 0U;
DTCSnapshotRecordNumber = pMsgContext->reqData[DCM_INDEX_5];
Dtc = Make32Bit(pMsgContext->reqData[DCM_INDEX_2], pMsgContext->reqData[DCM_INDEX_3], pMsgContext->reqData[DCM_INDEX_4]);
/* Check DTC */
error = DTC_GetStatusByDtcNumber(Dtc, &status); /*获取DTC的状态位,存放到status*/
if(error == 0U)
{
pMsgContext->resData[DCM_INDEX_5] = status; /*返回该DTC的状态位*/
if(Dtc_Fault_IsConfirmed(status))
{
switch(DTCSnapshotRecordNumber)
{
/* Add your code here. Below codes should be changed as Spec */
case 0x01: /*返回对应编号的快照记录信息*/
case 0x02:
{
pMsgContext->resData[DCM_INDEX_6] = DTCSnapshotRecordNumber; /* 快照记录码数*/
pMsgContext->resData[DCM_INDEX_7] = 4U; /* 定义的快照成员数*/
/*获取快照成员的ID信息、数值信息;从pMsgContext->resData[DCM_INDEX_8]这个地址开始存储*/
DTC_GetDtcSnapData(DTCSnapshotRecordNumber, &pMsgContext->resData[DCM_INDEX_8], Dtc, &snap_data_len);
DTCSnapshotRecordLength = DCM_INDEX_8 + snap_data_len;/*更新响应报文的长度*/
break;
}
case 0xFF: /*返回所有快照记录信息*/
{
pMsgContext->resData[DCM_INDEX_6] = 0x01; /* 第一个快照记录*/
pMsgContext->resData[DCM_INDEX_7] = 4U; /* 定义的快照成员数*/
DTCSnapshotRecordLength = DCM_INDEX_8;
/*获取第一个快照成员的ID信息、数值信息;*/
DTC_GetDtcSnapData(0x01, &pMsgContext->resData[DTCSnapshotRecordLength], Dtc, &snap_data_len);
DTCSnapshotRecordLength += snap_data_len;
pMsgContext->resData[DTCSnapshotRecordLength++] = 0x02; /* 第二个快照记录*/
pMsgContext->resData[DTCSnapshotRecordLength++] = 4U;
/*获取第二个快照成员的ID信息、数值信息;*/
DTC_GetDtcSnapData(0x02, &pMsgContext->resData[DTCSnapshotRecordLength], Dtc, &snap_data_len);
DTCSnapshotRecordLength += snap_data_len;/*更新响应报文的长度*/
break;
}
default:
{
DsdInternal_SetNegResponse(pMsgContext,DCM_E_REQUESTOUTOFRANGE);
error = 1U;
break;
}
}
}
else
{
DTCSnapshotRecordLength = 6u;
}
}
else
{
DsdInternal_SetNegResponse(pMsgContext,DCM_E_REQUESTOUTOFRANGE);
}
if(error == 0U)
{
pMsgContext->resDataLen = DTCSnapshotRecordLength;
DsdInternal_ProcessingDone(pMsgContext);
}
else
{
DsdInternal_ProcessingDone(pMsgContext);
}
}
4、06子服务
除了前面04服务中介绍到的快照信息;一般还会再定义一个扩展信息,用于记录故障的一些其他信息,比如故障发生的次数、老化次数、已老化次数等。而将下来介绍的06服务就是用于请求指定故障码(DTC)的扩展信息。请求格式如下:
其中,DTCExtendedDataRecordNumber表示扩展数据记录码,占一个字节,表示诊断仪请求的指定故障码扩展数据记录的编号。(即要请求的故障码中指定的第几个的扩展数据)
收到请求后,ECU的响应报文格式如下:
以返回第一个扩展数据记录为例,关于19 06服务代码部分示例如下(帮助理解,仅供参考):
FUNC(void,DCM_CODE) App_Fault_Memory_Read_DTC_Extended_Data_Records_By_DTC_Number(P2VAR(Dcm_MsgContextType,AUTOMATIC,DCM_APPL_DATA) pMsgContext)
{
uint8 error = 0U;
uint32 Dtc;
uint8 status = 0;
uint8 extern_data_len = 0;
/* change below length according to App*/
if(pMsgContext->reqData[DCM_INDEX_5] >= 1U)
{
Dtc = Make32Bit(pMsgContext->reqData[DCM_INDEX_2], pMsgContext->reqData[DCM_INDEX_3], pMsgContext->reqData[DCM_INDEX_4]);
/* Check DTC */
error = DTC_GetStatusByDtcNumber(Dtc, &status);/*获取DTC的状态位,存放到status*/
if(error == 0U)
{
pMsgContext->resData[DCM_INDEX_5] = status;/*返回该DTC的状态位*/
}
else
{
DsdInternal_SetNegResponse(pMsgContext,DCM_E_REQUESTOUTOFRANGE);
}
}
else
{
error = 1U;
DsdInternal_SetNegResponse(pMsgContext,DCM_E_REQUESTOUTOFRANGE);
}
if(error == 0U)
{
pMsgContext->resData[DCM_INDEX_6] = 0x01; /* 扩展数据记录码,DTCExtendedDataRecordNumber */
DTC_GetDtcExternData(&(pMsgContext->resData[DCM_INDEX_7]), Dtc, &extern_data_len);/*该函数里去获取扩展数据信息,自己定义*/
pMsgContext->resDataLen = DCM_INDEX_7 + extern_data_len; /* 更新返回报文的长度*/
DsdInternal_ProcessingDone(pMsgContext);
}
else
{
DsdInternal_ProcessingDone(pMsgContext);
}
}
5、0A子服务
该服务用于请求所有支持的DTC信息(3字节的DTC标识符+1字节的DTC状态位),其响应报文与02服务一致;但要区分,该服务返回的是所有DTC的信息;而02服务是返回与请求时状态掩码相与不为0 的DTC信息。请求格式如下:
收到请求后,ECU的响应报文格式如下:
关于19 0A服务代码部分示例如下(帮助理解,仅供参考):
void DTC_GetSupportedDtc(uint8 *p_dtc, uint16 *pCount) /*返回所有支持的DTC信息*/
{
uint8 record_count;
uint8 *pDtc = NULL;
if((p_dtc == NULL) || (pCount == NULL))
{
return;
}
pDtc = p_dtc;
*pCount = 0;
for(record_count = 0; record_count < DTC_CODE_MAX_NUM; record_count++)
{
*pDtc++ = DTC_dtc_code_data[record_count].dtc_high_byte;
*pDtc++ = DTC_dtc_code_data[record_count].dtc_middle_byte;
*pDtc++ = DTC_dtc_code_data[record_count].dtc_low_byte;
*pDtc++ = DTC_dtc_status_record[record_count].dtc_status.status_byte;
(*pCount)++;
}
}
FUNC(void,DCM_CODE) App_Fault_Memory_Read_supported_errors(P2VAR(Dcm_MsgContextType,AUTOMATIC,DCM_APPL_DATA) pMsgContext)
{
uint16 count = 0;
pMsgContext->resData[DCM_INDEX_2] = DTCStatusAvailabilityMask; /*返回ECU支持的状态位*/
DTC_GetSupportedDtc(&pMsgContext->resData[DCM_INDEX_3], &count);/*返回所有支持的DTC信息*/
pMsgContext->resDataLen = 3U + count * 4U; /*更新响应报文的长度*/
DsdInternal_ProcessingDone(pMsgContext);
}
2、14服务-——清除故障信息 (ClearDiagnosticInformation)
14服务用于清除存储的故障诊断信息,该服务内容很简单。其请求格式如下:
groupOfDTC表示要清除的某一个类别的诊断故障码(例如动力P、车身B以及底盘C等类别),或者是要清除的某一个特定的故障码;由3个字节组成。
收到请求后,ECU的响应报文格式如下:
总结
19服务用于故障码信息的读取;14服务则用于对故障码信息的清除。14服务的操作简单,但关于19服务,包含了比较多的子服务功能,其中,比较常用的子服务如下:
————————————————
推荐阅读
特斯拉的电池管理系统 (BMS) 相比其他电动车有哪些优势?
原文始发于微信公众号(汽车ECU开发):关于DTC诊断故障码的获取与清除