如下图所示。
NL_OBD_SendCANFrame函数有4个参数:
第1个参数 pro, 当前要处理的CAN协议类型,协议类型可以是SAEJ1939,ISO15765_4STD_500K,ISO15765_4EXT_500K。当前我们讨论的是后两种协议。
第2个参数TxMessage,待发送的请求数据,如上图中的EntCmd15765,DTCCmd15765,VinCmd15765,DSCmd15765。
第3个参数TimeOut,等待响应超时时间,单位为毫秒。如果在该设置时间内没有得到汽车或者模拟器响应请求数据,则超时。超时标志由第4个参数表示。
第4个参数 *err, 该参数用以表示函数请求响应是否超时,参数必须为NLStatus定义变量的地址。超时则未得到汽车或者模拟器响应 *err = NL_NOK;没有超时则说明在第3个参数规定的时间内获得了汽车或者模拟器针对当前请求信息的响应 *err=NL_OK。
看函数体本身代码,51行将第1个参数值赋值给全局变量OBDFlag.ProType,它将在中断中起作用。52行到56行用于SAEJ1939协议,前面已经解释过,这里不再讨论。57行到64行通过判断pro的值确定当前请求数据是以11bit标准CAN请求还是以29bit扩展CAN请求,并设置发送数据的IDE类型。65行OBDFlag.RxFlag用来表示当前协议请求数据发送后,中断是否获得完整响应数据,如果获得完整响应数据OBDFlag.RxFlag=NL_SUCCESS,否则OBDFlag.RxFlag=NL_FAILURE,此处让OBDFlag.RxFlag=NL_FAILURE就是为了等待中断赋值NL_SUCCESS给OBDFlag.RxFlag,如果中断始终没有获得请求数据的完整汽车响应数据OBDFlag.RxFlag的值一直是NL_FAILURE。66行*err 首先赋值NL_NOK,如果在第3个参数规定时间内获得请求数据的汽车完整响应数据,OBDFlag.RxFlag=NL_SUCCESS,我们就让*err=NL_OK,下面的68行到77行代码就是实现这个内容的。67行代码通过_NL_OBD_CAN_Transmit(TxMessage)函数把第2个参数待发送的请求数据发送出去。68行到77行代码刚刚提到过,通过for循环延时等待,等待中断获得请求数据的汽车完整响应数据,通过OBDFlag.RxFlag=NL_SUCCESS判断中断的处理结果,如果规定时间内OBDFlag.RxFlag=NL_SUCCESS就让*err=NL_OK, 否则保持不变*err=NL_NOK。这里每次循环通过系统延时函数_NL_Delay(1)限定每次循环时间为1毫秒。循环次数由函数第3个参数TimeOut决定,所以赋值多少就是多少毫秒的等待,除非OBDFlag.RxFlag=NL_SUCCESS则立即退出循环等待。78行返回响应数据的存储地址。
从NL_OBD_SendCANFrame函数源码,我们只看到了请求数据的发送和等待,并没有看到响应数据的的处理和存储,这部分的实现主要是通过CAN中断完成。CAN中断和NL_OBD_SendCANFrame函数体代码是通过全局变量OBDFlag.RxFlag保持的同步。下面我们具体看CAN中断源码如何处理响应数据的。下图所示。
因为我们的C300车联网开发板采用HAL库编写,在HAL库里面,中断函数都被处理成回调函数的形式,类似于事件。CAN中断的回调函数 HAL_CAN_RxFifo0MsgPendingCallback,所以我们的处理代码都在这个函数内进行。如图所示,419行我定义了一个流控帧结构体变量,其实它只对数据域的第一字节进行赋值0x30,标识符并没有赋值,从前面的协议解读中我们知道,流控帧的标识符是根据响应数据的标识符来得到它的物理性请求地址的标识符。421行是HAL库里面的函数,用于接收CAN中断的CAN数据,对于我们有用的数据是最后两个参数,RxHeader存储的是当前接收CAN数据的标识符,IDE,RTR,DLC等信息,RxData存储的是数据域。所以这跟固件函数库有点区别,它把标识符和数据域分开存储在不同的变量里,这些变量需要我们提前定义好。
由于C300开发板编写的HAL_CAN_RxFifo0MsgPendingCallback函数代码较长,不能在一个截图中完整呈现,上图的代码基本是用于SAEJ1939协议的,下面我们部分截取关于ISO15765-4协议的响应数据处理代码进行讲解。
1.SingleFrame响应数据处理。
532行判断全局变量OBDFlag.ProType只要是ISO15765_4STD_500K或者ISO15765_4EXT_500K,并且MUXFrameFlagBool 是RESET的话,说明当前接收的是SingleFrame的数据。MUXFrameFlagBool是个全局变量,用在CAN中断内部使用。用来指示当前接收的数据是SingleFrame还是MultipleFrame。MUXFrameFlagBool值是RESET表示SingleFrame,MUXFrameFlagBool值是SET表示MultipleFrame。534行到537行通过RxHeader的DLC信息利用for循环,把RxData接收到的数据存储在CAN_RxRAM数组中。NL_OBD_SendCANFrame函数的返回值是个指针,这个指针指向的地址正是这个CAN_RxRAM数组的地址。存储完毕后,为了告知NL_OBD_SendCANFrame函数退出等待,让OBDFlag.RxFlag赋值NL_SUCCESS。
1. MultipleFrame响应数据处理
488行判断全局变量OBDFlag.ProType只要是ISO15765_4STD_500K或者ISO15765_4EXT_500K,并且数据域的第1字节是0x10,则说明当前接收的是FirstFrame。FirstFrame有很多MultipleFrame特征信息,所以490行到508行对FirstFrame的处理很重要。490行到493行把FirstFrame存储在数组CAN_RxRAM里。494行到501行通过FirstFrame第2个字节计算ConsecutiveFrame有多少帧,计算结果存储在变量MUXFrameMaxValue中。502行设置MUXFrameFlagBool=SET,标志要处理MultipleFrame。503行设置ConsecutiveFrame首字节的比较值,从0x21开始计数。504行根据当前响应数据设置流控帧的IDE类型。505行和506行分别根据响应数据计算流控帧的物理性请求标识符。507行发送流控帧。接下来的509行到521行用来接收并存储ConsecutiveFrame中的应用层原始数据。509行和510行判断全局变量OBDFlag.ProType只要是ISO15765_4STD_500K或者ISO15765_4EXT_500K,并且MUXFrameFlagBool值是SET,还有接收的CAN数据域第1字节RxData[0] 和MUXFrameCount值一致,就确认为ConsecutiveFrame。512行到515行提取ConsecutiveFrame后7个字节进行存储在CAN_RxRAM。516行通过判断MUXFrameMaxValue和MUXFrameCount-0x20计数值是否一致判断接收ConsecutiveFrame数据是否完毕。完毕则恢复MUXFrameFlagBool值是RESET,并且设置OBDFlag.RxFlag = NL_SUCCESS说明MultipleFrame响应数据处理完毕。