DSP概述
- Diagnostic Service Processing - DSP,诊断服务处理
- 当接收到来自 DSD 子模块的函数调用,DSP 子模块将处理诊断服务请求,DSP 始终执行以下基本处理步骤:
- 分析收到的请求消息
- 检查格式和是否支持子服务
- 在 DEM、SW-Cs 或其他 BSW 模块上获取数据或执行所需的功能调用
- 响应处理
检查格式和子服务支持
- DSP 子模块将在执行请求的命令之前检查合适的消息长度和结构
- 当请求消息解析格式或长度错误时,DSP 子模块将触发一个 NRC 0x13(错误的消息长度或无效的格式)的否定响应
响应处理
- DSP 子模块组织响应消息,但不包括响应服务标识符和确定消息长度
- 如果使用分页缓冲机制,那么 DSP 子模块应在数据传递给 DSD 子模块或 DSL子模块之前就应当确定响应消息总长度
- 使用分页缓冲机制原因是 CAN 物理层上一帧报文数固定,比如标准 CAN 一帧报文最多只有 8 个字节,这需要在数据传输的第一帧中提供完整的数据流长度
否定响应码处理
- 除非另有规定,当执行服务的 API 调用不返回 OK 时,DSP 子模块触将发一个 NRC 0x10 (generalReject) 的否定响应
- 若果 Dcm 调用了具有输出参数 Dcm_NegativeResponseCodeType ErrorCode 的任何 API 应用程序,如果返回值为 E_NOT_OK,则 Dcm 应只接受0x01-0xFF 范围内的 ErrorCode 值
- 如果 Dcm 调用带有输出参数 Dcm_NegativeResponseCodeType ErrorCode 的 API,并且应用程序将该参数设置为 DCM_POS_RESP 并且返回 E_NOT_OK,则 Dcm 应报告运行时错误 DCM_E_INVALID_VALUE
- 当对请求消息的分析产生其他不受支持的消息参数时,DSP 子模块应触发 NRC 0x31(请求超出范围)的否定响应
异步调用行为
- 如果 Dem 函数返回 DEM_PENDING,Dcm 将在稍后的时间点再次调用该函数
- 如果对请求的诊断任务的否定响应的数量达到配置参数 DcmDslDiagRespMaxNumRespPend 中定义的值,Dcm 模块将停止处理活动诊断请求,通过将活动端口接口的 OpStatus 参数设置为 DCM_CANCEL 通知应用程序或 BSW(如果此诊断任务包含对 SW-C 接口或 BSW 接口的调用),报告运行时错误DCM_E_INTERFACE_TIMEOUT,并发送 NRC 0x10(一般拒绝)的否定响应
- 如果之前的返回状态为 E_PENDING, Dcm_SetProgConditions API 将在下一个 Dcm 主函数循环中再次被调用
- 调用 OpStatus 等于 DCM_CANCEL 的接口的返回值将被忽略
使用 DEM 接口的一般行为
- 默认情况下,与故障内存相关的诊断服务直接在 Dcm 中处理,Dcm 通过 Dem 模块实现诊断故障码、状态及相关数据的清除、查询、读取等功能
- 诊断处理不全由 DSP 子模块负责,也有在 DSD 中处理的特例,例如:0x19 服务的 DcmDsdSubServiceFnc
- UDS 定义的 DTC 状态掩码由 Dem 模块管理,不在 Dcm 中配置,当需要这个掩码时,Dcm 将使用 Dem_GetDTCStatusAvailabilityMask API 来读取这个信息
- 为了确保读取序列中事件相关数据的一致性,Dcm 模块将在读取操作期间锁定事件相关数据的更新,Dem 提供了Dem_DisableDTCRecordUpdate 和 Dem_EnableDTCRecordUpdate 来关闭/开启事件内存的更新,特别是,在使用以下 Dem API 期间会发生这种情况
- Dem_SelectExtendedDataRecord()
- Dem_GetSizeOfExtendedDataRecordSelection()
- Dem_GetNextExtendedDataRecord()
- Dem_SelectFreezeFrameData()
- Dem_GetSizeOfFreezeFrameSelection()
- Dem_GetNextFreezeFrameData()
- Dem 中可能耗时的 API 调用被设计为异步的,因此调用可以返回,调用者可以在稍后的时间再次调用以检索所请求的数据,这些 API 将 DEM_PENDING 作为可能的返回值,如果检测到此返回值,Dcm 将使用相同的参数再次调用,下一次调用的时间是特定实现的,但大多数情况下,这将在下一个 Dcm_MainFunction 中发生,重复出现 DEM_PENDING 返回值时可能导致 Dcm 发送 NRC 0x78(ResponsePending)的否定响应
跳转到 Bootloader
- 由于 ECUReset 请求,Dcm 应该能够管理跳转到 Bootloader
- 由于实现这种跳转的可能性的多样性,这将使用 callout 调用来完成
- 如果所有先决条件都满足,并假设 'suppressPosRspMsgIndicationBit'标志设置为 'false',已经确定了 4 种不同的用例,用于跳转到 Bootloader
- 应用程序立即发送一个最终的积极响应,然后跳转到 Bootloader
- 应用程序首先发送一个 NRC 0x78 响应,然后发送最终的积极响应,然后跳转到 Bootloader
- 应用程序立即跳转到 Bootloader,Bootloader 发送最终的积极响应
- 应用程序首先发送一个 NRC 0x78响应,然后跳转到 Bootloader,Bootloader 发送最终的积极响应
- 注意:如果 suppressPosRspMsgIndicationBit 标志设置为 true,则用例 1 和用例 3 将不会发出积极响应
- 在接收到 DiagnosticSessionControl 服务时,如果如果所提供的会话被用来跳转到 OEM Bootloader,参数 DcmDspSessionForBoot 设置为 DCM_OEM_BOOT 或 DCM_OEM_BOOT_RESPAPP,Dcm 应该准备跳转到 OEM Bootloader(触发 ModeDeclarationGroupPrototype DcmEcuReset 的模式切换为 JUMPTOBOOTLOADER)
- 在接收到 DiagnosticSessionControl 服务时,如果所提供的会话用于跳转到系统供应商 Bootloader,参数 DcmDspSessionForBoot 设置为 DCM_SYS_BOOT 或 DCM_SYS_BOOT_RESPAPP,Dcm 应该准备跳转到系统供应商 Bootloader(触发 ModeDeclarationGroupPrototype DcmEcuReset 的模式切换为 JUMPTOSYSSUPPLIERBOOTLOADER)
- 注意:以上的模式切换,是由 Dcm 通知 BswM 准备跳转到 Bootloader
- DiagnosticSessionControl 服务意味着 ECU 复位,Dcm 在处理复位时将忽略所有进一步的请求
- 如果 ModeDeclarationGroupPrototype DcmEcuReset 切换到 JUMPTOBOOTLOADER 或 JUMPTOSYSSUPPLIERBOOTLOADER 模式,并且配置参数DcmSendRespPendOnRestart 设置为 TRUE,则 Dcm 应触发 NRC 0x78 响应
- 注意:在切换到 Bootloader 之前,NRC 0x78 的最后传输将在客户端重新加载 P2* 超时
- 如果 ModeDeclarationGroupPrototype DcmEcuReset 不能切换 JUMPTOBOOTLOADER 或 JUMPTOSYSSUPPLIERBOOTLOADER,则 Dcm 应以 NRC 0x22(条件不正确)否定回答请求
- 如果请求跳转到 Bootloader,配置参数 DcmSendRespPendOnRestart 设置为 TRUE,配置参数 DcmDspSessionForBoot 设置为 DCM_OEM_BOOT 或 DCM_SYS_BOOT,则 Dcm 将在成功传输 NRC 0x78(响应等待)后调用 Dcm_SetProgConditions,这将允许在跳转到 Bootloader 之前存储所有相关信息
- 注意:由软件集成商决定在哪里存储该数据,通常它将存储在非易失性存储器中,例如 data flash,将此数据“存储”在 RAM 部分中也是可以接受的,但该部分在重置时不能初始化
- 在请求跳转到 Bootloader 的上下文中,在 Dcm_SetProgConditions 返回 E_OK 之后,Dcm 将触发 ModeDeclarationGroupPrototype DcmEcuReset 的模式切换为 EXECUTE
- 如果请求跳转到 Bootloader,配置参数 DcmSendRespPendOnRestart 设置为 TRUE,配置参数 DcmDspSessionForBoot 设置为 DCM_OEM_BOOT_RESPAPP 或 DCM_SYS_BOOT_RESPAPP,则 Dcm 将在 NRC 0x78(响应等待)传输成功后发起最终响应,但如果 NRC 0x78 响应不成功,那么 Dcm 将取消当前请求,并且取消执行跳转动作,Dcm 将留在应用程序中,等待来自客户端的下一个请求
- 如果 ModeDeclarationGroupPrototype DcmEcuReset 切换到 JUMPTOBOOTLOADER 或 JUMPTOSYSSUPPLIERBOOTLOADER 模式,配置参数 DcmSendRespPendOnRestart 设置为 FALSE,配置参数 DcmDspSessionForBoot 设置为 DCM_OEM_BOOT_RESPAPP 或 DCM_SYS_BOOT_RESPAPP,那么 Dcm 应发起最终回应,如果最终响应已成功发送,Dcm 将调用 Dcm_SetProgConditions
- 如果 Dcm_SetProgConditions 返回 E_OK, Dcm 将触发 ModeDeclarationGroupPrototype DcmEcuReset 的模式切换为 EXECUTE
- 如果 Dcm_SetProgConditions 返回 E_NOT_OK, 则 Dcm 不会请求重置,不会跳转到 Bootloader,也不会将 ModeDeclarationGroupPrototype DcmEcuReset 切换为 EXECUTE
- 如果 ModeDeclarationGroupPrototype DcmEcuReset 切换到 JUMPTOBOOTLOADER 或 JUMPTOSYSSUPPLIERBOOTLOADER 模式,配置参数 DcmSendRespPendOnRestart 设置为 FALSE,配置参数 DcmDspSessionForBoot 设置为 DCM_OEM_BOOT 或 DCM_SYS_BOOT,那么 Dcm 应立即调用 Dcm_SetProgConditions。如果此时 Dcm_SetProgConditions 返回 E_OK,Dcm 将触发 ModeDeclarationGroupPrototype DcmEcuReset 的模式切换为 EXECUTE 且不发送 NRC 0x78(响应等待)
- 如果 Dcm_SetProgConditions 返回 E_NOT_OK,Dcm 不应采取进一步行动,并返回负响应 NRC 0x22(条件不正确)
由于 ECUReset 而跳转
- 在接收到 0x11 服务 ECUReset 请求时,如果配置参数 DcmResponseToEcuReset 被设置为 AFTER_RESET, Dcm 将通过调用 Dcm_SetProgConditions 来设置 ResponseRequired 标志,并且 Dcm 复位后应答 EcuReset 服务
- 在接收到 0x11 服务 ECUReset 请求时,如果配置参数 DcmResponseToEcuReset 被设置为 BEFORE_RESET, Dcm 将在复位前应答 EcuReset 服务
- 如果 Dcm 启动复位并且 DcmSendRespPendOnRestart 被设置为 TRUE,那么 Dcm 在复位前将触发 NRC 0x78(响应等待) 的响应
从 Bootloader/ECUReset 跳转
- 在 Dcm 初始化时,Dcm 应该调用 Dcm_GetProgConditions 来知道初始化是否是从 Bootloader/ECUReset 跳转的结果
- 注意:当调用 Dcm_Init 时,确保 Dcm_ProgConditionsType 中包含的数据有效性是软件集成商的责任,在设计 ECU 的启动过程时,必须考虑到这一点
- 如果 Dcm 的初始化是从 Bootloader/ECUReset 跳转的结果,那么 Dcm 应调用 ComM_DCM_ActiveDiagnostic(NetworkId) 请求 ComManager 全通信模式(full communication mode)
- 当 ComM 向 Dcm 报告全通信( full communication)时,Dcm 应通过 Dcm_ProgConditionsType 中的 Service Id 发送响应
- 如果 Dcm 的初始化是从 Bootloader 跳转的结果,并且应用程序已通过 FLASH 下载更新(Dcm_ProgConditionsType->ApplUpdated == True), 那么 Dcm 应调用 BswM_Dcm_ApplicationUpdated() 来通知 BswM 应用程序已更新
Flags 管理
- 接收到子功能 0x02 的 UDS 0x10 服务(Start Programming Session),Dcm 应该通过调用 Dcm_SetProgConditions 设置 ReprogramingRequest 标志和 responserrequirequired 标志(如果为此服务指定了该标志)
- 根据配置参数 DcmDspSessionForBoot,应用程序应该发送积极的响应(如果suppressPosRspMsgIndicationBit = FALSE),或者在 ECU 复位后,当 Bootloader 启动时,它应该发送响应并清除 ReprogramingRequest 标志
- 如果在跳转到 Bootloader Dcm_SetProgConditions API 返回 E_NOT_OK,则应报告 DET 错误 DCM_E_SET_PROG_CONDITIONS_FAIL,并恢复正常功能
- 成功重新编程应用软件后,Bootloader 将更新 ApplUpdated 标志和 ResponseRequired 标志
- 在 ECU 复位后,当新编程的应用程序第一次启动时,Dcm 将通过调用 Dcm_GetProgConditions 读取 ApplUpdated 标志和 ResponseRequired 标志,在这个函数调用期间,ApplUpdated 和 ResponseRequired 标志被集成代码清除
错误通知
- 默认错误跟踪模块只是帮助 BSW 开发和集成,它不能包含在生产代码中,定义了 API,但是可以根据开发需要选择和实现功能(例如,错误计数,通过串行接口向外部记录器发送错误信息,等等)
同步和异步实现
- Dcm 可以使用 R-Port 访问数据,需要同步或异步 ClientServertInterface DataServices_{data},在 Dcm SWS 中,参数 DcmDspDataUsePort 设置为 USE_DATA_SYNCH_CLIENT_SERVER 或 USE_DATA_ASYNCH_CLIENT_SERVER 或 USE_DATA_ASYNCH_CLIENT_SERVER_ERROR
- 对于 USE_DATA_SYNCH_CLIENT_SERVER,接口应兼容 Dem 接口 “DataServices_<Data>”(没有参数 OpStatus),参数 OpStatus 和返回参数DCM_E_PENDING 仅在 USE_DATA_ASYNCH_CLIENT_SERVER 或 USE_DATA_ASYNCH_CLIENT_SERVER_ERROR 的情况下可用
- 注意:当调用服务处理时,使用 AsynchronousServerCallPoint 或 SynchronousServerCallPoint 的 Dcm 实现完全是一个实现决策
- 如果使用异步接口,Dcm 将认为输出数据(OUT)只有在最后一次调用返回 E_OK 的接口后才有效
- 如果使用异步接口,Dcm 将认为输出参数 ErrorCode 只有在最后一次调用返回 E_NOT_OK 的接口后才有效
- 注:INOUT 参数是上述要求的组合,即每次调用接口时,参数必须具有有效的 in 值,Dcm 仅在最后一次调用接口后才认为 out 值有效
- 如果使用异步接口,则 Dcm 应在每次调用该接口时提供来自输入数据(IN)请求的值
DID 配置
- Dcm 的配置包含一个支持的 DID 列表,可以通过两种方式配置:
- 单独的 DID 配置,每个配置的 DID 数据元素需要一个连接(通过端口或c函数)来访问数据(读、写和控制)。在这种情况下,每个 DID 都应该使用一个 DataServices_Xxx 接口
- DID 范围配置,用于处理一组在一个 SW-C 中只有一个端口连接的 DID,这些 DID 共享相同的行为,在这种情况下,应该使用接口DataServices_DIDRange_{Range},使用这种配置可以实现接口优化,为了使用 DIDRange 优化,需要配置以下参数:
- DcmDspDidRangeIdentifierLowerLimit
- DcmDspDidRangeIdentifierUpperLimit
- DcmDspDidRangeMaxDataLength
- DcmDspDidRangeHasGaps
- 如果配置了 DcmVinRef,那么在启动期间,Dcm 将通过调用 Dcm_GetVin 来获取一次 VIN
- 单个 DID 可以在 DcmDspDid 中配置,在 DcmDspDidIdentifier 中配置一个唯一的 DID 标识符,长度为 2 字节
- 如果 DID 引用了其他 DID,则可以在 DcmDspDidRef2 中配置它们之间的链接
- 每个 DID 允许访问信号数据值(通过读和/或写)
- 信号引用(to DcmDspData)和数据在诊断应答(用于读取)或请求(用于写入)中的位置可以在 DcmDspDidSignal 中配置
- 属于 DID 的数据的配置可以在容器 DcmDspData 中提供,该容器收集以下信息:
- 属于 DID 的数据的数据端序(DcmDspDataEndianness)
- 数据的长度和类型(DcmDspDataByteSize, DcmDspDataType)
- 用于访问数据的接口(DcmDspDataUsePort)
- NRAM 块访问数据(DcmDspDataBlockIdRef)
- 应用程序的接口
- 检查 DID 在当前条件下是否可以访问,这可以通过检查每个 DataElement 是否满足读取数据的条件来实现(DcmDspDataConditionCheckReadFnc and DcmDspDataConditionCheckReadFncUsed)
- 请求冻结 IOControl 的当前状态(DcmDspDataFreezeCurrentStateFnc)
- 获取 DID 的缩放信息,这可以通过获取每个 DataElement 的缩放信息来实现(DcmDspDataGetScalingInfoFnc)
- 请求 DataElement 的数据长度(DcmDspDataReadDataLengthFnc)
- 读取 ECU 信号(DcmDspDataReadEcuSignal)
- 读/写数据的访问(DcmDspDataReadFnc/ DcmDspDataWriteFnc)
- 请求将 IOControl 重置为默认值(DcmDspDataResetToDefaultFnc)
- 请求将控制权返回到 IOControl 的 ECU(DcmDspDataReturnControlToEcuFnc)
- 请求调整 IO 信号(DcmDspDataShortTermAdjustmentFnc)
- 也可以通过 DcmDspDiagnosisScaling 配置另一种诊断表示
- 下面的示例显示了如何为单个 DID 0xF080 配置容器 DcmDspDid 和 DcmDspData
- 该配置允许通过同步 C API WriteDID_F080(写)和 ReadDID_F080(读)访问一个字节的数据
- DcmDspDidIdentifier=0xF080
- DcmDspDidByteOffset=0
- DcmDspDidDataRef=DcmDspData_F080
- DcmDspDataByteSize=8
- DcmDspDataType=UINT8_N
- DcmDspDataUsePort=USE_DATA_SYNCH_FNC
- DcmDspDataWriteFnc=WriteDID_F080
- DcmDspDataReadFnc=ReadDID_F080
- 只有当 DcmDspDataUsePort 设置为 USE_BLOCK_ID 时,DcmDspDataBlockIdRef 才会出现
DID 范围
- DID 范围通常与'正常' DID 读写函数相同,只是 DID 也作为参数传递,这允许在读/写函数中处理选择 case 的 DID 范围
- 该 DID 范围适用于读(ReadDataByIdentifier 0x22)和写(WriteDataByIdentifier 0x2E)
- 该范围可以在 DcmDspDidRange 中配置,默认情况下,每个配置的范围都可以通过 0x22 和 0x2E 服务访问
- 如果范围仅限于读或写,则应相应地定义引用的 DcmDspDidInfo 容器
- 也可以在范围内定义间隙(DcmDspDidRangeHasGaps),通过激活此特性,Dcm 在每次在配置范围内请求 DID 时都会调用操作 IsDidAvailable 检查当前的可用性
- 由于指定范围的 DID 可以具有不同的长度,因此必须配置最长 DID 的长度(DcmDspDidRangeMaxDataLength),以便保留足够的缓冲区传递给各自的函数
- 一般来说,如果您特别想将 DID 作为参数传递,则范围功能也可以用于单个 DID,然后较低的 DID 和较高的 DID 应该是相同的
- ReadDidRangeDataLength 操作允许请求应用程序返回 DID 范围的数据长度
- 任何定义的范围只能通过 DcmDspDidRangeInfoRef 引用,不能使用子容器 DcmDspDidControl 和 DcmDspDidDefineinDcmDspDidInfo
- DID 范围不能映射到 DDDID上,因为 0x2C DDDID 服务不支持范围特性
- 实际上,DcmDspDidRangeIdentifierLowerLimit 和 DcmDspDidRangeIdentifierUpperLimit 不应该包含从 0xF200 到 0xF3FF 的 DID
- 任何定义的范围只能通过 DcmDspDidRangeInfoRef 引用 DcmDspDidInfo,并设置 DcmDspDidDynamicallyDefined == False