解封后的环京地区还要办理通行出入证才能自由,据说在中国领土内需要出入证的有:香港、台湾、澳门以及河北三河~
一、标准文件
ISO 14229 定义的是诊断服务。
ISO 15765 定义的是诊断服务在总线上的传输方式。
ISO 11898 定义的CAN总线在物理层面传输的规范。
下图摘抄至ISO 14229 -1:

我们只考虑基于CAN总线的UDS,所以只需学习标准文件:ISO 15765-2,ISO14229-1、2、3即可,ISO 11898是硬件物理层传输面的,可不必理会。另外ISO 15765-3被ISO14229-3取代了。
二、UDS网络层
主要参考ISO 15765-2,定义了CAN总线传输的数据结构,以及多帧传输的方法,并对帧传输规约了时间参数。
1、协议数据单元
共有四种帧类型:

单帧:
就是传统的8字节CAN报文,此基础上增加了数据长度SF_DL,占一个字节的前4位。
第一帧:
第一帧用于数据长度大于7字节的诊断数据,它和一个或多个连续帧并用,其中FF_DL表示这套多帧的数据长度,12 位 FF_DL 理论上支持发送多达 4096 个数据字节。
连续帧:
如果是多帧数据,先发第一帧,然后是连续帧,其中SN是帧计数,初始为1(因为有第一帧),累加达到15后,置0.

流控:
第一帧或数据块的最后一个连续帧之后,需要更多的连续帧(CF)来完成数据流传输时,接收节点发送一个流控。一个流控帧包含三个参数:流控状态(FS),持续发送次数(BS),最小间隔时间(STmin).


多帧的交互机制:

2、网络层定时计数参数
ISO 15765-2 对网络层数据的传输时间进行约束,实际交互时间可以在超时范围内自定义,一些企业的企标会对此有详细要求。


3、代码实现
首先是UDS网络层的相关定义,帧类型、时间参数等。
typedef struct
{uint16 N_As;uint16 N_Ar;uint16 N_Bs;uint16 N_Br;uint16 N_Cs;uint16 N_Cr; uint16 STmin;uint16 BS;
}TimePeriodParam;/*--流控帧第一个字节的低4位为FS,有3种状态*/
typedef enum{CTS, //continue to sendWT, //waitOVFLW, //overflow
}FS_Type;typedef enum{SF , //single frameFF , //first frameCF , //consecutive frameFC , //flow control
}N_PCIType;typedef enum{N_OK, //sender and receiverN_TIMEOUT_A, //sender and receiverN_TIMEOUT_Bs, //sender onlyN_TIMEOUT_Cr, //receiver onlyN_WRONG_SN, //receiver onlyN_INVALID_FS, //sneder onlyN_UNEXP_PDU, //receiver onlyN_WFT_OVRN, N_BUFFER_OVFLW, //sender onlyN_ERROR, //sender and receiver
}N_Result;typedef enum
{NWL_IDLE,NWL_TRANSMITTING,NWL_RECIVING,NWL_WAIT,
}NWL_Status;/**--------------------------------------------*/
typedef enum{PHYSICAL,FUNCTIONAL,
}N_TAtype;typedef enum{CONFIRM, FF_INDICATION,INDICATION,
}NotificationType;typedef enum{RX_IDLE,RX_WAIT_FC_REQ,RX_WAIT_FC_CONF,RX_WAIT_CF,
}RecivingStep;typedef enum{TX_IDLE,TX_WAIT_FF_CONF,TX_WAIT_FC,TX_WAIT_CF_REQ,TX_WAIT_CF_CONF,
}TransmissionStep;
时间参数要有初始化,因为这个时间有可能根据企业要求自定义,可根据实际要求配置
static TimePeriodParam m_TimePeriod;
static NWL_Status m_NetworkStatus = NWL_IDLE;
static TransmissionStep m_TxStep;
static RecivingStep m_RxStep;void UDS_NetWork_Init(void)
{ m_TimePeriod.N_As = 70; m_TimePeriod.N_Ar = 70; m_TimePeriod.N_Br = 50; m_TimePeriod.N_Bs = 150; m_TimePeriod.N_Cs = 50; m_TimePeriod.N_Cr = 150; RxParam.STmin = 0; RxParam.BlockSize = 0; m_NetworkStatus = NWL_IDLE;m_TxStep = TX_IDLE;
}
网络层提供诊断层的发送接口,通过数据长度判断是单帧还是多帧,如果是单帧的话,发送成功后要恢复初始状态,如果是多帧,发送完首帧FF之后,在NetworkLayer_TxStart()里启动定时参数N_As和N_Bs,在规定时间内如果收到流控帧,则继续发送多帧CF。
void N_USData_request(uint8* MessageData, uint16 Length)
{if((Length == 0)||(Length > UDS_FF_DL_MAX)) {//长度错误}if(Length <= 7) //SF length must <= 7{NetworkLayer_SendSF((uint8)Length,MessageData);NetworkLayer_TxEnd();}else{NetworkLayer_SendFF(Length,MessageData);NetworkLayer_TxStart();}
}
网络层的数据接收接口,首先判断通信模式是功能还是物理,所谓物理是1对1通信,支持所有类型,功能地址是1对多通讯的,相当于广播,仅支持单帧,并且报文ID可能是特定的。
void NetworkLayer_RxProcess(void)
{N_PCIType PciType;if(UDS_NetWork_CanRxBuf[RxOutIndex].id ==FunctionID){m_N_TAtype = FUNCTIONAL;} else if(UDS_NetWork_CanRxBuf[RxOutIndex].id ==UDS_DownLoadID){m_N_TAtype = PHYSICAL;}if(!IsRxBuffEmpty()) {PciType = (UDS_NetWork_CanRxBuf[RxOutIndex].data[0] >> 4) & 0x0F;switch (PciType){case SF:NetworkLayer_RxSF(UDS_NetWork_CanRxBuf[RxOutIndex]);break;case FF:if(m_N_TAtype == PHYSICAL){ NetworkLayer_RxFF(UDS_NetWork_CanRxBuf[RxOutIndex]);} else{//ignore functional}break;case CF:NetworkLayer_RxCF(UDS_NetWork_CanRxBuf[RxOutIndex]);break; case FC:if(m_N_TAtype == PHYSICAL){ NetworkLayer_RxFC(UDS_NetWork_CanRxBuf[RxOutIndex]);}else{//ignore functional}break;default:m_N_Result = N_UNEXP_PDU;break; }(RxOutIndex == MAX_BUFF_NUMBER - 1) ? (RxOutIndex = 0) : (RxOutIndex++);}
}
接收完之后还要通知上层诊断层当前帧的服务项,通过NotificationType定义的,见上文的相关定义,因为没有涉及到发送,所以缺少了ISO15765里的request项,另外每次接收数据都要重启定时参数,做超时判断。
三、UDS诊断服务层
1、诊断时间参数
诊断服务层同样也有一些超时判断用的参数,这些在IOS14229-2中有定义:


当时我写的时候这部分定时并没有启动~~!
2、诊断报文格式
报文格式有请求和响应两种:
请求通常是从诊断仪到ECU,对于ECU来说,就是接收解析请求报文,即诊断服务。
有带子服务的请求和不带子服务请求两种,没有子服务项的报文第二字节就是数据。

响应则从ECU到诊断仪,对于ECU来说,就是发送,有正响应和负响应。
正响应Positive Response帧格式:
(SID+0x40) + Data Parameter
(SID+0x40) + Sub-Function + Data Parameter
负响应Negative Response帧格式:
0x7F + SID + NRC
其他空数据一般添0x00。
负响应的NRC码在ISO14229-1最后的附表里,不同的诊断服务对应若干不同的负响应NRC,即一个诊断服务对应的几种异常情况,实际应用不一定全部会用到。常用的NRC有:
typedef enum{PR = 0x00,//positive response GR = 0x10,//general rejectSNS = 0x11,//service not supported 企标SFNS = 0x12,//sub-function not supported 企标IMLOIF = 0x13,//incorrect message length or invalid format 企标RTL = 0x14,//response too longBRR = 0x21,//busy repeat request 企标CNC = 0x22,//condifitons not correct 企标RSE = 0x24,//request sequence error 企标NRFSC = 0x25,FPEORA = 0x26,ROOR = 0x31,//reqeust out of range 企标SAD = 0x33,//security access denied 企标IK = 0x35,//invalid key 企标ENOA = 0x36,//exceed number of attempts 企标RTDNE = 0x37,//required time delay not expired 企标UDNA = 0x70,//upload download not accepted 企标TDS = 0x71,//transfer data suspended 企标GPF = 0x72,//general programming failure 企标WBSC = 0x73,//wrong block sequence coutner 企标RCRRP = 0x78,//request correctly received-respone pending 企标SFNSIAS = 0x7e,//sub-function not supported in active session 企标SNSIAS = 0x7F,//service not supported in active session 企标VTH = 0x92,//voltage too high 企标VTL = 0x93,//voltage too low 企标
}NegativeResposeCode;
带子服务的请求和不带子服务请求均支持功能寻址和物理寻址,其中带子服务的请求中包含“抑制正响应报文指示位”,此位为真时,不回复正响应,负响应正常回复,但一些NRC下,不论抑制正响应报文指示位是真是假,负响应也不回复,例如:SNS、SFNS等。
3、诊断服务内容

除了这些SID外,UDS还通过Sub-function来补充SID的意图,而sub-function严格来说是7个bit,因为它的最高位bit被“抑制正响应报文指示位”占用。
常用的服务项有:
typedef enum{SESSION_CONTROL = 0x10,RESET_ECU = 0x11,SECURITY_ACCESS = 0x27,COMMUNICATION_CONTROL = 0x28,TESTER_PRESENT = 0x3E,GET_TIME_PARAM = 0x83,SECURITY_DATA_TRANSMISSION = 0x84,CONTROL_DTC_SETTING = 0x85,RESPONSE_ON_EVENT = 0x86,LINK_CONTROL = 0x87,READ_DATA_BY_ID = 0x22,READ_MEMORY_BY_ADDRESS = 0x23,READ_SCALING_DATA_BY_ID = 0x24,READ_DATA_PERIOD_ID = 0x2A,DYNAMICALLY_DEFINE_DATA_ID = 0x2C,WRITE_DATA_BY_ID = 0x2E,WRITE_MEMORY_BY_ADDRESS = 0x3D,CLEAR_DTC_INFO = 0x14,READ_DTC_INFO = 0X19,IO_CONTROL_BY_ID = 0x2F,ROUTINE_CONTROL = 0x31,REQUEST_DOWNLOAD = 0x34,REQUEST_UPLOAD = 0x35,TRANSMIT_DATA = 0x36,REQUEST_TRANSFER_EXIT = 0x37,
}ServiceName;
4、代码实现
网络层接收的数据经解析后,通知服务层,服务层解析出相应的SID,执行对应操作及回复正负响应。
void Diagnostic_RxProcess(void)
{NetworkNotification temp; NetworkLayer_RxProcess();temp = PullIndication();if(temp.NoticeType == INDICATION) {if((temp.n_Resut == N_OK)||(temp.n_Resut == N_UNEXP_PDU)){Diagnostic_ServiceHandle(temp.length,temp.MessageData);} }else if(temp.NoticeType == CONFIRM) {// temporary NO}else if(temp.NoticeType == FF_INDICATION){//NO}}
void Diagnostic_ServiceHandle(uint16 length,uint8 *tMsg)
{switch(tMsg[0]){case SESSION_CONTROL: //10服务Service10Handle(length, tMsg); break;case RESET_ECU: //11服务Service11Handle(length, tMsg);break;case CLEAR_DTC_INFO: //14服务if(m_CurrSessionType ==ECU_PAOGRAM_SESSION){UDS_req_negative_responses(CLEAR_DTC_INFO, SNSIAS);} //编程会话下不支持else{//Service14Handle(length, tMsg);} break;case READ_DATA_BY_ID: //22break;case SECURITY_ACCESS: //27if(AccessErrCount >= 3){UDS_req_negative_responses(SECURITY_ACCESS, ENOA);// AccessErrCount = 0;AccessTimerEn=1;}if((m_CurrSessionType ==ECU_DEFAULT_SESSION)||(m_N_TAtype == FUNCTIONAL)){UDS_req_negative_responses(SECURITY_ACCESS, SFNS);} //默认会话下不支持 功能寻址不支持else{Service27Handle(length, tMsg);} break;case COMMUNICATION_CONTROL: //28break;case WRITE_DATA_BY_ID: //2EService2EHandle(length, tMsg); break;case ROUTINE_CONTROL: //31 Service31Handle(length, tMsg);break;case REQUEST_DOWNLOAD: //34Service34Handle(length, tMsg);break;case REQUEST_UPLOAD: //35break;case TRANSMIT_DATA: //36Service36Handle(length, tMsg);break; case REQUEST_TRANSFER_EXIT: //37Service37Handle(length, tMsg);break;case TESTER_PRESENT: //3ebreak; case CONTROL_DTC_SETTING: //85break; default: break; }}
5、常用的诊断服务说明
不是所有的服务都支持两种寻址方式。
诊断会话控制-10服务
其他诊断服务实现时必定要经过10服务进行会话模式的切换,可能会用到的有4种:
typedef enum{
ECU_DEFAULT_SESSION = 1,
ECU_PAOGRAM_SESSION = 2,
ECU_EXTENED_SESSION = 3,
USER_DEFINED_SESSION = 0xXX,
}SessionType;
除默认会话外,其他会话都要有时间限制,计时到点后,非默认会话要退出。

从默认会话到默认会话(状态1):重新进入默认会话时,ECU执行完全初始化。
ECU复位-11服务
就是对ECU进行复位操作,不过需要注意的是复位之前一定要先发送正响应。
清除诊断信息-14服务
读取DTC故障码-19服务

通过SubFunction定义19服务不同的请求类型,服务请求第三个字节是DTC状态位,有8个状态:

ECU响应的每个DTC都会有一个对应的DTC状态字节。
关于DTC码在另一个标准文件ISO 15031-6里有定义,一般企业会给出定义好的DTC码。
读取数据-22服务
通过标识符读取数据,22服务与2E成对使用 前者是读,后者是写 。
UDS规定,诊断数据使用两个byte的标识符来标记,比如,0xF187用来标记ECU的零件号。
通过地址读取内存-23服务
通过地址读取内存,23服务与3D成对使用
如果这条命令的格式是 23 22 xx yy aa bb,则它的含义就是,读取xx yy地址的长度为aa bb的数据。
22的前4位表示地址长度,后4位表示数据长度。
安全访问-27服务
1、诊断仪向ECU请求“Seed”(通常是一个与时间相关的伪随机数),
2、ECU向诊断仪发送“Seed”,
3、诊断仪向ECU发送“Key” (根据请求得到的Seed和一个本地的密码进行计算得来)
4、ECU判断诊断仪发来的“Key”是否有效
一般企业会提供校验Key的算法,同时访问错误累积3次后,规定时间内不允许继续解锁访问。
访问有不同的安全级别,不同的安全级别下,访问范围也是有区别的。
通信控制-28服务
用于打开/关闭某些类别的报文的发送/接收,例如某些时刻不需要多余的通信则通过此服务关闭。
切换到默认会话时,通信控制恢复正常。
程序控制 -31服务
31服务中的SubFunction表示ECU要执行的动作,如某外设的启动,停止、FLASH的擦除等。boot loader用到此服务。
请求下载 -34服务
告知ECU准备接受数据,ECU应答自己的接收能力等信息,此服务用于BOOT下载。
请求下载报文带有帧计数,每次+1。
传输数据 36服务
boot loader用到此服务,接收到的数据写入FLASH。
请求传输退出-37服务
说明数据传输完毕,在本服务里,我置位了一个BOOTLOADER_SUCCEED的标志,表示APP程序成功下载。
诊断仪在线 -3E服务
用于使ECU保持在当前session,永远只有2个字节,3E 80(00) ,80时不需要应答。
控制DTC设置 85服务
用于开启或关闭ECU内部的诊断故障码更新功能。0x01/0x81:开 0x02/0x82:关

学而时习之,不易忘。


















