解封后的环京地区还要办理通行出入证才能自由,据说在中国领土内需要出入证的有:香港、台湾、澳门以及河北三河~
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取代了。
主要参考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 send
WT, //wait
OVFLW, //overflow
}FS_Type;
typedef enum{
SF , //single frame
FF , //first frame
CF , //consecutive frame
FC , //flow control
}N_PCIType;
typedef enum{
N_OK, //sender and receiver
N_TIMEOUT_A, //sender and receiver
N_TIMEOUT_Bs, //sender only
N_TIMEOUT_Cr, //receiver only
N_WRONG_SN, //receiver only
N_INVALID_FS, //sneder only
N_UNEXP_PDU, //receiver only
N_WFT_OVRN,
N_BUFFER_OVFLW, //sender only
N_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项,另外每次接收数据都要重启定时参数,做超时判断。
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 reject
SNS = 0x11,//service not supported 企标
SFNS = 0x12,//sub-function not supported 企标
IMLOIF = 0x13,//incorrect message length or invalid format 企标
RTL = 0x14,//response too long
BRR = 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: //22
break;
case SECURITY_ACCESS: //27
if(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: //28
break;
case WRITE_DATA_BY_ID: //2E
Service2EHandle(length, tMsg);
break;
case ROUTINE_CONTROL: //31
Service31Handle(length, tMsg);
break;
case REQUEST_DOWNLOAD: //34
Service34Handle(length, tMsg);
break;
case REQUEST_UPLOAD: //35
break;
case TRANSMIT_DATA: //36
Service36Handle(length, tMsg);
break;
case REQUEST_TRANSFER_EXIT: //37
Service37Handle(length, tMsg);
break;
case TESTER_PRESENT: //3e
break;
case CONTROL_DTC_SETTING: //85
break;
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:关
学而时习之,不易忘。