《UDS协议从入门到精通》系列——图解0x86:事件响应
- 一、简介
- 1.1 什么是事件响应?跟其他服务有何不同?
- 1.2 到底如何理解事件响应机制?
- 1.3 使用事件响应机制有哪些注意点?
- 二、数据包格式
- 三、通信示例
Tip📌:本文描述中但凡涉及到其他UDS服务的,均提供专栏内文章链接跳转方式以便快速了解他们。
>>>>>>>>> 返回专栏总目录 《UDS协议从入门到精通(UDS速查手册)》<<<<<<<<<
一、简介
1.1 什么是事件响应?跟其他服务有何不同?
该服务与其他服务不同:其他服务是一条请求对应一条响应,诊断仪发送请求,目标ECU给出响应。而这个服务叫做事件响应,从字面意思理解,就是当(目标ECU)发生了某个事件,那就产生相应的响应。
事实上也大致如此,首先诊断仪发送的请求指定该事件(包括可选的事件参数)以及该事件发生时需要执行的服务(包括服务参数),可以理解为这一步设置了事件及其对应的响应。随后,诊断仪会再次发送指令控制这个事件的启动,指令中会携带事件的有效持续时间,在这个有效时间内,一旦发生了上一步设定的事件,目标ECU就会返回一条响应。因此,叫做基于事件触发的响应。
1.2 到底如何理解事件响应机制?
对于该服务定义的事件响应机制,14229-1标准中给出如下图片描述了客户端(诊断仪)和服务器(目标ECU)的行为逻辑(上半个虚线框内是诊断仪的行为,下半个是目标ECU的行为):
简单解释下上图中的流程:
- 首先诊断仪发送事件响应请求(携带一些可选数据参数);
- 目标ECU收到后根据请求中的信息设置event逻辑并发送初步响应;
- 随后诊断仪发送启动事件监控的请求;
- 目标ECU收到该请求后会激活事件逻辑并启动一个timer,再次回复响应情况;
- 在timer超时时间内,一旦server端检测到event发生,就会触发相应的服务逻辑,简单来说就是触发一个操作,具体做些什么由协议应用者决定。
- timer超时,发送最终响应。
Tip📌:看到这里后,建议先跳到请求及响应格式简单看一下该服务的协议格式(不然接下来的几段文字可能看起来有点懵)。该服务参数及含义最为复杂,可先大致浏览。
第2步中,目标ECU在收到请求后,需要对请求中携带的数据(子功能和数据内容)进行检查,该环节检查如下三项,如果事件响应服务(ResponseOnEvent)请求报文中的数据无效,则发送否定应答码0x31(表示参数无效):
- eventType:事件类型
- eventWindowTime:事件窗口时间
- eventTypeRecord:事件类型携带的参数
检查不包括第四个参数——serviceToRespondToRecord。当指定事件发生时,才会检查serviceToRespondToRecord参数,这将触发serviceToRespondToRecord中所包含服务的执行。在事件发生时,应执行serviceToRespondToRecord(诊断服务请求报文)。如果条件不正确,则应发送带有合理的否定应答码的否定应答报文。多个事件应按其发生的顺序发出信号。
1.3 使用事件响应机制有哪些注意点?
⚠️ 除了以上提到的一些内容外,标准中针对该服务还罗列了如下一些执行规则:
- 该服务可以在任意会话模式下执行,并且无需0x3E服务保持当前通信状态;
- 如果目标ECU在诊断服务运行过程中发生了该服务请求中设置的指定事件,那应当保持原来的处理过程优先执行,0x86服务的响应或者处理应该等当前诊断响应结束后再执行;(因此,0x86事件响应sevice的延迟有可能导致,service携带的数据参数跟触发事件的数据值不对应)
- 多事件发生时,具体的处理顺序/逻辑由整车厂商(协议的具体实现者)指定;
- 客户端请求目标ECU启动事件逻辑后,目标ECU在eventWindowTime时间内,当事件逻辑满足时,目标ECU将执行serviceToRespondToRecord参数中包含的诊断服务,并发送响应。
- 只要收到进入非默认回话的请求,不管当前是什么会话状态,所有已激活的事件应当被暂停;当重新切换到默认回话时,之前在默认回话中激活的所有事件响应服务都将被重新激活。
- SPR位只能被用于子功能stopResponseOnEvent、startResponseOnEvent和clearResponseOnEvent,当事件发生时,目标ECU总是要给出响应。
- 当eventWindowTime有限时,目标ECU应在eventWindowTime结束时发送final response。但当事件在eventWindowTime前被终止,比如使用stopResponseOnEvent或者切换非默认回话导致事件终止时,不应发送final response。
- 为防止干扰正常的诊断通讯,基于事件的响应最好应用于瞬时事件,事件发生时返回一个响应。对于持续发生的事件,可以在时间结束时返回一个响应。此外,对于高频发生的事件,需要制定合理的方案避免高频发送响应,比如可以在eventTypeRecord定义一个最小时间间隔。
- 标准中推荐了以下几个可以使用事件响应的诊断服务,其他的不推荐使用事件响应机制。
- 0x22 (ReadDataByIdentifier)
- 0x19 (ReadDTCInformation)
- 0x31 (RoutineControl)
- 0x2F (InoutOutputControlByIdentifier)
二、数据包格式
该服务的请求响应的数据包格式解释起来颇为复杂,因此本文不再采用本专栏普遍使用的图示形式,下面将请求和响应格式放在一起集中解释,看起来会更加紧凑,便于查找上下文。
请求格式 0x86 + eventType (1Byte) + eventWindowTime(1Byte) + eventTypeRecord + serviceToRespondToRecord
eventType
- Bit7:跟其他请求一样,SPR,正响应抑制位
- Bit6:指示将要设置的这个事件要不要存储在目标ECU的非易失性存储器中,并在目标ECU下次上电时重新激活,或者在目标ECU下电时终止,简单来说这是一个存储状态标识位(storage state):
- 值为0:表示不存储,即目标ECU掉电后事件将终止,并且目标ECU复位或重新上电后不会存在原来设置的这个事件逻辑;
- 值为1:表示设置的事件逻辑会被永久存储,直到事件逻辑被显式清除或者设置了新的事件逻辑。
- Bit5~0:取值情况对应含义如下
- 0x00 - stopResponseOnEvent:使目标ECU停止发送事件的响应,但设置的事件逻辑不会被清除,之后可以通过下面的startResponseOnEvent再使能目标ECU对事件的响应。该取值情况下不需要使用eventTypeRecord携带更多信息,为0Byte
- 0x01 - onDTCStatusChange:(要了解这个参数的作用,需要先对AutoSAR中的Dem模块以及DTC有一些了解,这里假设读者有这方面的基础,如果没接触过相关内容可以跳过这个,对于把这个服务用起来没什么影响。)该值表示将事件定义为检测一个新DTC,相匹配的DTCStatusMask要在eventTypeRecord中指定(因此需要1Byte的 eventTypeRecord),收到这个请求后,目标ECU中应当有一个DTC计数算法,计算某个周期内与请求中定义的DTCStatusMask匹配的DTC数量,当发现计数值与上一次不同时,就需要触发service的执行。
- 0x02 - onTimerInterrupt:(⚠️:在2020版本的14229-1中,这个值的含义未定义,是保留的,这里解释的是2013版本的)该值表示将事件定义为计时器中断,但计时器的及其值不属于事件响应服务的一部分,也就是说这个计时器本身的操作是独立的,跟这里设置的事件响应没有直接关系,标准中指出该eventType在请求报文中需要更多的细节,也就是需要eventTypeRecord,标准中定义该取值情况下eventTypeRecord为1Byte。
- 0x03 - onChangeOfDataIdentifier:该值表示将事件定义为产生了由数据标识符指定的一条新的内部数据记录,同样需要eventTypeRecord指定更多信息,标准中定义该取值情况下eventTypeRecord为2Bytes,通常情况下是DID的高低字节。
- 0x04 - reportActivatedEvents:该值表示将事件定义为通过肯定响应中触发的service汇报目标ECU中处于激活状态的事件有多少个,包括本身这个事件-reportActivatedEvents。该取值情况下不需要使用eventTypeRecord携带更多信息,为0Byte。
- 0x05 - startResponseOnEvent:该值表示使能/激活目标ECU对已设置的事件逻辑的响应(包括启动事件窗口计时器),开始发送对事件的响应。该取值情况下不需要使用eventTypeRecord携带更多信息,为0Byte。
- 0x06 - clearResponseOnEvent:该值表示清除目标ECU中已设置的事件逻辑,这将停止目标ECU发送对事件的响应。该取值情况下不需要使用eventTypeRecord携带更多信息,为0Byte。
- 0x07 - onComparisonOfValues:修改由数据标识符(DID)标识的数据值。首先有一个已定义的测量值,用收集到的结果与之比较,当相等时,就可以触发用户定义的事件服务。因此需要携带指定测量值并指定要绑定的数据标识符DID,目标ECU中拿指定测量值和收集的值做比较,因此还要指定二者比较的类型,如果比较结果为正,则发生该事件。该取值情况下需要使用eventTypeRecord携带更多信息,标准中设定为10Bytes。(⚠️:接下来的08 09两种取值情况都是在2020版本的14229-1标准中定义的,2013版本中均是保留值)
- 0x08 - reportMostRecentDtcOnStatusChange:该值表示将事件定义为检测一个新DTC,这个DTC应当是testFailed或者是confirmedDTC类型,即相匹配的DTCStatusMask中Bit0和Bit3位应该是1,要在eventTypeRecord中指定(因此需要1Byte的 eventTypeRecord)
- 0x09 - reportDTCRecordInformationOnDtcStatusChange:该值表示将事件定义为DTC状态发生变化,这个事件逻辑在目标ECU中是通过DTC status与客户端提供的DTCStatusMask进行逻辑与操作判定的,使用0x14服务和aging导致的DTC状态变化不会触发事件响应。
- 0x0A - 0x3F:保留
eventWindowTime
- 简单理解就是设置的事件逻辑的有效时间,超时之后事件监测就会失效。
- Tips ⚠️:
- 如果该字节值设为0x02,那请求中的事件逻辑将设置为永久事件,不会自动停止,只能使用stop子功能来停止或者被新定义的同一事件覆盖;
- 如果事件要存储在非易失性存储区,那这个eventWindowTime只能是永久,设置为0x02。
eventTypeRecord
不同子功能所需要的参数,每种情况是否需要这个,需要多少字节,均已经在eventType中说明。至于每个字节用来表示什么,可以在实际用到的时候去标准文件的 chapter-10.9.2.2(2020版本14229-1)以及 chapter-9.10.2(2013版本14229-1)中查阅,此处不再赘述。serviceToRespondToRecord
当指定的事件发生时,目标ECU应该执行的诊断请求数据,可以简单理解为某个诊断请求。比如10 02进入编程会话状态。肯定响应 0xC6 + eventType (1Byte) + numberOfIdentifiedEvents + eventWindowTime + eventTypeRecord + serviceToRespondToRecord
eventType的取值与请求中的eventType值保持一致即可;
numberOfIdentifiedEvents
此参数包含在激活的事件窗口中已识别的事件数量,并且仅适用于在事件窗口结束时发送的响应消息(如果是有限事件窗口)。对请求报文的初始化响应,在此参数中应该包含一个零(0)。
eventWindowTime
请求报文中eventWindowTime参数的回显,当请求是上报激活的事件数的时候,这个参数就是剩余时间。
eventTypeRecord
请求报文中eventTypeRecord参数的回显。
serviceToRespondToRecord
请求报文中serviceToRespondToRecord参数的回显。
当请求中的subFunction = reportActivatedEvents时,肯定应答报文格式是比较特殊的,如下所示:
0xC6 + 0x04 + numberOfActivatedEvents +
eventTypeOfActiveEvent #1 + eventWindowTime #1 +
eventTypeRecord1 + serviceToRespondToRecord1
eventTypeRecord2 + serviceToRespondToRecord2
… …
eventTypeOfActiveEvent #2 + eventWindowTime #2 +
eventTypeRecord1 + serviceToRespondToRecord1
eventTypeRecord2 + serviceToRespondToRecord2
… …否定响应 0x7F + 0x85 + NRC
可能出现的NRC及其含义如下:
0x12:子功能参数不受支持
0x13:消息长度错误
0x22:不满足请求标准/条件(比如请求复位操作时,ECU判断当前车速不满足复位条件)
0x31:请求中携带的数据是无效的,无效参数
三、通信示例
标准文件中提供了三个具体的例子,我们选第一个梳理一下,其他的一通百通。在这个例子中,我们将诊断服务0x19 (ReadDTCInformation)设置为使用这种事件响应的机制。设置的事件窗口时间是有限的,即在ECU下电之前事件窗口时间将会超时,因此最终目标ECU将有一个final response。
Tip⚠️:目标ECU中的eventWindowTime = 请求信息中的eventWindowTime * 10s,比如下面的例子中请求信息中的eventWindowTime参数值为0x08,但目标ECU中设置的这个超时时间值为80s。(该服务通信流程较为繁琐,携带参数较多,直接通过标准中的表格的形式看每一条指令会相对清晰些)
- 诊断仪发送一个事件响应请求,目标ECU根据该请求中携带的参数设置事件逻辑,在下面这个请求中,请求初始化一个类型为0x01的事件(DTC状态变化事件)。不存储在非易失性存储区,不抑制正响应。设置事件窗口有效时间为80s,事件类型参数(该子功能下这个参数为1个Byte,表示DTC状态掩码)设置为0x01,最低位有效表示testFaild有效(这个需要了解过Dem/DTC相关的知识)。建议先阅读《到底什么是DTC?》以及《0x19-读取故障码信息》。
目标ECU端应根据该请求设置如下处理逻辑:
存储当前testFaild为1的DTC个数,周期性重新计算testFaild为1的DTC个数,如果跟上次计算结果不同就要触发这个事件,并更新DTC的个数。当事件触发时,应当检查serviceToRespondToRecord中的参数,并执行其中的诊断请求(19 01 01:返回testFaild为1的DTC个数)。
- 目标ECU中完成事件初始化,返回肯定响应:
- 诊断仪发送请求,启动事件监控,对应子功能0x05 (startResponseOnEvent):
- 目标ECU返回肯定响应:
-
触发事件,目标ECU执行serviceToRespondToRecord中携带的诊断请求并返回响应消息:
-
诊断仪请求读取当前激活的事件:
- 目标ECU返回当前已激活的事件:
- 事件窗口计时器超时(这里就是超过80s),目标ECU发出最终肯定响应:
以上面这个相对简单的例子为基础,标准中提供了目标ECU端可能发生的两种情况,一是整个事件窗口时间内没有任何事件响应,此时目标ECU端的处理流程如下所示:
二是在有限的事件窗口时间内多次触发事件,针对每一次事件执行的诊断服务的响应都会跟每个事件关联起来(event #1 ~ #n),最后事件窗口时间结束前,目标ECU应当发送一个正响应指明numberOfIdentifiedEvents及其相关信息。
>>>>>>>>> 返回专栏总目录 《UDS协议从入门到精通(UDS速查手册)》<<<<<<<<<