UDS诊断服务开发

解封后的环京地区还要办理通行出入证才能自由,据说在中国领土内需要出入证的有:香港、台湾、澳门以及河北三河~


一、标准文件

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 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项,另外每次接收数据都要重启定时参数,做超时判断。

三、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 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执行完全初始化。

从默认会话到非默认会话(状态2):基本不动。
从非默认会话到非默认会话(状态3):安全状态切换到锁定状态,通讯控制不受影响。
从非默认会话到默认会话(状态4):ECU执行完全初始化,复位所有在非默认模式下做的事。

ECU复位-11服务

就是对ECU进行复位操作,不过需要注意的是复位之前一定要先发送正响应。

清除诊断信息-14服务

正响应应在诊断信息清除请求完全处理发出,即使ECU没有存储的DTC,同样需要发出正响应
UDS规定FFFFFF清除所有DTC。

读取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:关


学而时习之,不易忘。

  • 7
    点赞
  • 114
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

记性不好的JJ

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值