CANanlystII 基于linux的二次开发实践

1. USBCAN分析仪国内现状

这是目前国内市场上的USBCAN分析仪现状。

2. 创芯科技产品

        创芯科技的这个红色盒子是我比较下来综合性价比最高的。同时支持windows和linux的设备只要320元左右。你既可以用可视化界面发送/接收报文,也可以二次开发,设置复杂的收发以及数据处理的逻辑。可视化界面操作很简单,我就不赘述了,主要谈谈怎么基于linux二次开发。

2.1 设备自测

设备拿到时最好进行自测,判断设备是否可用。

自测时设备线路连接如下:

PC上virtualbox安装ubuntu虚拟机的USB接口选择

执行自测用例

root@zyy-VirtualBox:/home/zyy/controlcan# ls
controlcan.h  hello_cpp  libcontrolcan.so  main_bak.cpp  main.cpp  Makefile
root@zyy-VirtualBox:/home/zyy/controlcan# rm -r hello_cpp 
root@zyy-VirtualBox:/home/zyy/controlcan# make clean
rm -f *.o hello
root@zyy-VirtualBox:/home/zyy/controlcan# make
g++ -o hello_cpp main.cpp /home/zyy/controlcan/libcontrolcan.so  -lpthread 
root@zyy-VirtualBox:/home/zyy/controlcan1# ./hello_cpp 
>>this is hello !
>>USBCAN DEVICE NUM:1 PCS
Device:0
>>Get VCI_ReadBoardInfo success!
>>Serial_Num:31F100016D6
>>hw_Type:CAN-Linux
>>Firmware Version:V3.40
>>
>>
>>
>>open deivce success!
>>Get VCI_ReadBoardInfo success!
>>Serial_Num:31F100016D6
>>hw_Type:CAN-Linux
>>Firmware Version:V3.40
Index:0000  CAN1 TX ID:0x00000000 Extend    Data   DLC:0x08 data:0x 00 01 02 03 04 05 06 07
Index:0001  CAN2 TX ID:0x00000001 Extend    Data   DLC:0x08 data:0x 00 01 02 03 04 05 06 07
Index:0002  CAN2 RX ID:0x00000000 Extend    Data   DLC:0x08 data:0x 00 01 02 03 04 05 06 07 TimeStamp:0x0009D81E
Index:0003  CAN1 TX ID:0x00000002 Extend    Data   DLC:0x08 data:0x 00 01 02 03 04 05 06 07
Index:0004  CAN1 RX ID:0x00000001 Extend    Data   DLC:0x08 data:0x 00 01 02 03 04 05 06 07 TimeStamp:0x0009D85C
Index:0005  CAN2 TX ID:0x00000003 Extend    Data   DLC:0x08 data:0x 00 01 02 03 04 05 06 07
Index:0006  CAN2 RX ID:0x00000002 Extend    Data   DLC:0x08 data:0x 00 01 02 03 04 05 06 07 TimeStamp:0x0009D890
Index:0007  CAN1 TX ID:0x00000004 Extend    Data   DLC:0x08 data:0x 00 01 02 03 04 05 06 07
Index:0008  CAN1 RX ID:0x00000003 Extend    Data   DLC:0x08 data:0x 00 01 02 03 04 05 06 07 TimeStamp:0x0009D8B6
Index:0009  CAN2 TX ID:0x00000005 Extend    Data   DLC:0x08 data:0x 00 01 02 03 04 05 06 07
Index:0010  CAN2 RX ID:0x00000004 Extend    Data   DLC:0x08 data:0x 00 01 02 03 04 05 06 07 TimeStamp:0x0009D8E8
Index:0011  CAN1 TX ID:0x00000006 Extend    Data   DLC:0x08 data:0x 00 01 02 03 04 05 06 07
Index:0012  CAN1 RX ID:0x00000005 Extend    Data   DLC:0x08 data:0x 00 01 02 03 04 05 06 07 TimeStamp:0x0009D912
Index:0013  CAN2 TX ID:0x00000007 Extend    Data   DLC:0x08 data:0x 00 01 02 03 04 05 06 07
Index:0014  CAN2 RX ID:0x00000006 Extend    Data   DLC:0x08 data:0x 00 01 02 03 04 05 06 07 TimeStamp:0x0009D93D
Index:0015  CAN1 TX ID:0x00000008 Extend    Data   DLC:0x08 data:0x 00 01 02 03 04 05 06 07
Index:0016  CAN1 RX ID:0x00000007 Extend    Data   DLC:0x08 data:0x 00 01 02 03 04 05 06 07 TimeStamp:0x0009D96F
Index:0017  CAN2 TX ID:0x00000009 Extend    Data   DLC:0x08 data:0x 00 01 02 03 04 05 06 07
Index:0018  CAN2 RX ID:0x00000008 Extend    Data   DLC:0x08 data:0x 00 01 02 03 04 05 06 07 TimeStamp:0x0009D99F
Index:0019  CAN1 RX ID:0x00000009 Extend    Data   DLC:0x08 data:0x 00 01 02 03 04 05 06 07 TimeStamp:0x0009D9DC
run thread exit

2.2 基于实际场景的二次开发

2.2.1 一种测试场景

将目标ECU的CAN引脚和USBCAN分析仪相连, 目标ECU的电源引脚接到相应电压的供电电源上。

关于ECU刷写,这篇博客写得比较简洁又清晰

can例程 ecu_基于CAN的ECU刷写流程_司马各的博客-CSDN博客

我得测试场景分六步,后续步骤大同小异,可以仿照前几步格式添加。

1. 进入扩展会话模式,并获得正面响应。

2. tester向ECU请求seed,并获得seed。

3. tester根据seed计算key, tester向ECU发送key,并获得正面响应。

4. 进入编程会话,并获得正面响应

5.tester向ECU请求seed,并获得seed。

6.tester根据seed计算key, tester向ECU发送key,并获得正面响应。

针对ECU刷写的OTA升级的步骤资料可以查阅

新能源汽车OTA升级中的UDS通信分析_晓翔仔的博客-CSDN博客

2.2.2 编写代码以及调试

代码里组装的报文符合UDS协议,UDS描述见:

统一诊断服务(UDS)_晓翔仔的博客-CSDN博客

编写代码如下:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
#include "controlcan.h"
 
#include <ctime>
#include <cstdlib>
#include "unistd.h"
 
#define  NO_RES_COUNT_MAX  10   //等待响应的时间(周期)上限
 
VCI_BOARD_INFO pInfo;//用来获取设备信息。
int count=0;//数据列表中,用来存储列表序号。
VCI_BOARD_INFO pInfo1 [50];
int num=0;
 
 
//重要全局变量
//int i;
int STEP=0X01;  //CAN数据发送和接收的步骤,用于控制步骤顺序
int CURRENT_STEP=0x00; //当前所处的CAN数据发送和接收的步骤,“接收线程“会根据”当前步骤”和“CAN数据内容”判断是否是正确响应
unsigned int Ecu_DialogId= 0x000; // ECU的CANID,每个ECU不一样
int Fun_Id = 0x7df; //功能寻址的CANID,固定值
DWORD seed0 =0; //UDS 0X27,ECU返回过来的seed第1位
DWORD seed1 =0; //UDS 0X27,ECU返回过来的seed第2位
DWORD seed2 =0; //UDS 0X27,ECU返回过来的seed第3位(若有)
DWORD seed3 =0; //UDS 0X27,ECU返回过来的seed第4位(若有)
DWORD seed0_tmp;
DWORD seed1_tmp;
DWORD seed2_tmp;
DWORD seed3_tmp;
DWORD seed;// UDS 0X27,ECU返回过来的seed
int Seedsize = 0; // UDS 0X27,ECU返回过来的seed位数,一般是2或者4
DWORD key_tmp;
DWORD key;   //UDS 0X27, 由seed计算出的key,两位或者四位,用于ECU安全解锁
int NoResCount=0; // 未接收响应的等待次数,超过一定数目应当认为测试失败,进入shutdown流程
int LogAllRecv =1; //接收数据是否全打印
int TESTMODE =1; //是否是测试模式,测试模式时,CAN1 CAN2直连。非测试模式时CAN2不使用。
 
 
void *receive_func(void* param)  //接收线程。
{
	int reclen=0;
	VCI_CAN_OBJ rec[3000];//接收缓存,设为3000为佳。
	int i,j;
	
	int *run=(int*)param;//线程启动,退出控制。
    int ind_recv=0; //0: CAN1  1:CAN2
	
	while((*run)&0x0f)
	{
		usleep(300000);//延时300ms。
		if((reclen=VCI_Receive(VCI_USBCAN2,0,ind_recv,rec,3000,100))>0)//调用接收函数,如果有数据,进行数据处理显示。
		{
			for(j=0;j<reclen;j++)
			{
				if(LogAllRecv)
				{
					printf("[RECV]Index:%04d  ",count);count++;//序号递增
					printf("CAN%d RX ID:0x%08X", ind_recv+1, rec[j].ID);//ID
					if(rec[j].ExternFlag==0) printf(" Standard ");//帧格式:标准帧
					if(rec[j].ExternFlag==1) printf(" Extend   ");//帧格式:扩展帧
					if(rec[j].RemoteFlag==0) printf(" Data   ");//帧类型:数据帧
					if(rec[j].RemoteFlag==1) printf(" Remote ");//帧类型:远程帧
					printf("DLC:0x%02X",rec[j].DataLen);//帧长度
					printf(" data:0x");	//数据		
					for(i = 0; i < rec[j].DataLen; i++)
					{
						printf(" %02X", rec[j].Data[i]);
					}
					printf(" TimeStamp:0x%08X",rec[j].TimeStamp);//时间标识。
					printf("\n");
				}
				if(0X01 == CURRENT_STEP &&0 == rec[j].ExternFlag && 0x50 == rec[j].Data[1] && 0x03 == rec[j].Data[2])
				{
					printf("STEP[0X01] 03 diagConversation resp received!\n");
					STEP =0x07; //设置全局变量STEP指示下一步tester应该发送什么CAN消息
				}
				else if(0X07 == CURRENT_STEP && 0 == rec[j].ExternFlag && 0x06 == rec[j].Data[0]&& 0x67 == rec[j].Data[1] && 0x01 == rec[j].Data[2])
				{
					printf("STEP[0X07] ask seed resp 4 seeds received!\n");
					seed0 =  rec[j].Data[3];
					seed1 =  rec[j].Data[4];
					seed2 =  rec[j].Data[5];
					seed3 =  rec[j].Data[6];
					Seedsize = 4; //全局变量Seedsize指示,下一步发送CAN消息前应该组装多少位的key
					STEP = 0X71;	//设置全局变量STEP指示下一步tester应该发送什么CAN消息		
				}
				else if(0X07 ==CURRENT_STEP && 0 == rec[j].ExternFlag && 0x04 == rec[j].Data[0]&& 0x67 == rec[j].Data[1] && 0x01 == rec[j].Data[2])
				{
					printf("STEP[0X07] ask seed resp 2 seeds received!\n");
					seed0 =  rec[j].Data[3];
					seed1 =  rec[j].Data[4];
					Seedsize = 2;//全局变量Seedsize指示,下一步发送CAN消息前应该组装多少位的key
					STEP = 0X71;		//设置全局变量STEP指示下一步tester应该发送什么CAN消息	
				}
				else if(0X71 == CURRENT_STEP && 0 == rec[j].ExternFlag && 0x67 == rec[j].Data[1] && 0x02 == rec[j].Data[2])
				{
					printf("STEP[0X71] send key resp received!\n");	
					STEP = 0x08;	//设置全局变量STEP指示下一步tester应该发送什么CAN消息		
				}
				else if(0X08 == CURRENT_STEP && 0 == rec[j].ExternFlag && 0x50 == rec[j].Data[1] && 0x02 == rec[j].Data[2])
				{
					printf("STEP[0X08] enter codeConversation resp received!\n");
					STEP =0x09;//设置全局变量STEP指示下一步tester应该发送什么CAN消息
				}
				else if(0X09 == CURRENT_STEP &&0 == rec[j].ExternFlag && 0x06 == rec[j].Data[0]&& 0x67 == rec[j].Data[1] && 0x01 == rec[j].Data[2])
				{
					printf("STEP[0X09] ask seed resp 4 seeds received!\n");
					seed0 =  rec[j].Data[3];
					seed1 =  rec[j].Data[4];
					seed2 =  rec[j].Data[5];
					seed3 =  rec[j].Data[6];
					Seedsize = 4;	//全局变量Seedsize指示,下一步发送CAN消息前应该组装多少位的key	
					STEP = 0x91;		//设置全局变量STEP指示下一步tester应该发送什么CAN消息	
				}
				else if(0X09 == CURRENT_STEP &&0 == rec[j].ExternFlag && 0x04 == rec[j].Data[0]&& 0x67 == rec[j].Data[1] && 0x01 == rec[j].Data[2])
				{
					printf("STEP[0X07] ask seed resp 2 seeds received!\n");
					seed0 =  rec[j].Data[3];
					seed1 =  rec[j].Data[4];
					Seedsize = 2;//全局变量Seedsize指示,下一步发送CAN消息前应该组装多少位的key
					STEP = 0X91;	//设置全局变量STEP指示下一步tester应该发送什么CAN消息		
				}
				else if(0X91 == CURRENT_STEP &&0 == rec[j].ExternFlag && 0x67 == rec[j].Data[1] && 0x02 == rec[j].Data[2])
				{
					printf("STEP[0X91] send key resp resp received!\n");			
					STEP = 0xFF;	//进入0xFF步骤表示测试成功,即将是进入结束流程		
				}
			}
		}		
	}
	printf("run thread exit\n");//退出接收线程	
	pthread_exit(0);
}
 
int main(int argc, char *argv[])
{
    int i;
    for (i = 0; i < argc; i++)
    {
        printf("argc = %d | argv[%d] %s\n", i, i, argv[i]);
    }
//解析输入参数 	Ecu_DialogId ,并打印
    Ecu_DialogId = atoi(argv[1]);
	printf("Ecu_DialogId = 0x%04x\n",Ecu_DialogId);
 
 
//设备信息打印	
	printf(">>this is hello !\r\n");//指示程序已运行
	num=VCI_FindUsbDevice2(pInfo1);
	printf(">>USBCAN DEVICE NUM:");printf("%d", num);printf(" PCS");printf("\n");
	for(int i=0;i<num;i++)
	{
		printf("Device:");printf("%d", i);printf("\n");
		printf(">>Get VCI_ReadBoardInfo success!\n");	
 
	}
	if(VCI_OpenDevice(VCI_USBCAN2,0,0)==1)//打开设备
	{
		printf(">>open deivce success!\n");//打开设备成功
	}
	else
	{
		printf(">>open deivce error!\n");
		exit(1);
	}
	if(VCI_ReadBoardInfo(VCI_USBCAN2,0,&pInfo)==1)//读取设备序列号、版本等信息。
	{
		printf(">>Get VCI_ReadBoardInfo success!\n");	
	}
	else
	{
		printf(">>Get VCI_ReadBoardInfo error!\n");
		exit(1);
	}
 
	//初始化参数,严格参数二次开发函数库说明书。
	VCI_INIT_CONFIG config;
	config.AccCode= (Ecu_DialogId+8) << 21;
	printf("config.AccCode:%08X\n",config.AccCode); 
	config.AccMask=0x00000000;//AccMask的值0x00000000表示所有位均为相关位,AccMask=0xFFFFFFFF表示都接收    
	
	config.Filter=1;//1:接收所有帧  2:只接收标准帧 3:只接收扩展帧 
	config.Timing0=0x00;/*波特率125 Kbps  0x03  0x1C ||| 波特率500 Kbps  0x00  0x1C*/
	config.Timing1=0x1C;
	config.Mode=0;//正常模式		
 
//CAN1通道的初始化
	if(VCI_InitCAN(VCI_USBCAN2,0,0,&config)!=1)
	{
		printf(">>Init CAN1 error\n");
		VCI_CloseDevice(VCI_USBCAN2,0);
	}
 
	if(VCI_StartCAN(VCI_USBCAN2,0,0)!=1)
	{
		printf(">>Start CAN1 error\n");
		VCI_CloseDevice(VCI_USBCAN2,0);
 
	}
//在自测模式下,需要使用CAN2通道,所以进行CAN2通道初始化
	if(TESTMODE)
	{
		if(VCI_InitCAN(VCI_USBCAN2,0,1,&config)!=1)
		{
			printf(">>Init can2 error\n");
			VCI_CloseDevice(VCI_USBCAN2,0);
		}
		if(VCI_StartCAN(VCI_USBCAN2,0,1)!=1)
		{
			printf(">>Start can2 error\n");
			VCI_CloseDevice(VCI_USBCAN2,0);
		}		
	}
 
	//创建接收线程,持续接收数据包
	int m_run0=1;
	pthread_t threadid;
	int ret;
	ret=pthread_create(&threadid,NULL,receive_func,&m_run0);

//ID默认设置为CANID, 所有报文都是标准帧,默认填充字符是0x55
	VCI_CAN_OBJ send[1];
	
	while(1)
	{
		//每次发送前报文初始化
		send[0].ID= Ecu_DialogId;
		send[0].SendType=0;
		send[0].RemoteFlag=0;
		send[0].ExternFlag=0;
		send[0].DataLen=8;
		for (i=0;i<8;i++)
		{
			send[0].Data[i] = 0x55;
		}
//STEP0x00:还没有收到ECU回复
//step0x01:enter 03 diagConversation
//step0x07:ask seed
//step0x71:send key
//step0x08:enter codeConversation
//step0x09:ask seed
//step0x91:send key
//step0xFF:所有流程结束
		switch(STEP) //本switch-case里所有的发送都是通过CAN1通道
		{
//step0x01:enter 03 diagConversation
		case 0x01:
			//需要发送的帧,结构体设置
			send[0].Data[0] = 0x02;
			send[0].Data[1] = 0x10;
			send[0].Data[2] = 0x03;			
			if(VCI_Transmit(VCI_USBCAN2, 0, 0, send, 1) == 1)
			{
				printf("STEP[0X%02x] enter 03 diagConversation SEND SUCCESS!Index:%04d   CAN1 TX ID:0x%08X data:0x",STEP,count,send[0].ID);
				count++;
				for(i=0;i<send[0].DataLen;i++)
				{
					printf(" %02X",send[0].Data[i]);
				}
				printf("\n");
			}
			else
			{
				printf("STEP[%02x] VCI_Transmit failed!",STEP);
				goto SHUTDOWN;
			}
			CURRENT_STEP =STEP;//全局变量CURRENT_STEP是为了记录当前所处步骤(接收线程和自测时的模拟ECU需要这个变量)
			STEP = 0x00; //tester的CAN消息发送成功后将STEP设置为0x00是为了防止重复发送,也方便计算等待时间,长时间等待不到响应则判定测试失败
			usleep(200000);//延时200ms
			break;
//step0x07:ask seed
		case 0x07:
			//需要发送的帧,结构体设置
			send[0].Data[0] = 0x02;
			send[0].Data[1] = 0x27;
			send[0].Data[2] = 0x01;
			if(VCI_Transmit(VCI_USBCAN2, 0, 0, send, 1) == 1)
			{
				printf("STEP[0X%02x] ask seed SEND SUCCESS!Index:%04d   CAN1 TX ID:0x%08X data:0x",STEP,count,send[0].ID);
				count++;
				for(i=0;i<send[0].DataLen;i++)
				{
					printf(" %02X",send[0].Data[i]);
				}
				printf("\n");
			}
			else
			{
				printf("STEP[%02x] VCI_Transmit failed!",STEP);
				goto SHUTDOWN;
			}
			CURRENT_STEP =STEP;//全局变量CURRENT_STEP是为了记录当前所处步骤(接收线程和自测时的模拟ECU需要这个变量)
			STEP = 0x00;//tester的CAN消息发送成功后将STEP设置为0x00是为了防止重复发送,也方便计算等待时间,长时间等待不到响应则判定测试失败
			usleep(300000);//延时300ms
			break;
//step0x71:send key
		case 0x71:
			//需要发送的帧,结构体设置
			//Seedsize有2和4两种可能,分别采取不同的组装CAN报文的形式
			if(4 == Seedsize)
			{ 
				seed0_tmp = seed0 << 3*8;
				seed1_tmp = seed1 << 2*8;
				seed2_tmp = seed2 << 8;
				seed3_tmp = seed3;
				seed = seed0_tmp + seed1_tmp + seed2_tmp + seed3_tmp;
				printf("seed is  0x%08x\n",seed);
				key = seed;//汽车厂自定义安全解锁的算法,这里直接key=seed。
				printf("key is  0x%08x\n",key);
				send[0].Data[0] = 0x06;
				send[0].Data[1] = 0x27;
				send[0].Data[2] = 0x02;
				send[0].Data[3] = (key & 0xff000000) >> 3*8;
				send[0].Data[4]  = (key & 0x00ff0000) >> 2*8;
				send[0].Data[5] = (key & 0x0000ff00) >> 8;
				send[0].Data[6] = (key & 0x000000ff);			
			}
			else if(2 == Seedsize)
			{
				seed0_tmp = seed0 << 1*8;
				seed1_tmp = seed1;
				seed = seed0_tmp + seed1_tmp;
				printf("seed is  0x%04x\n",seed);
				key_tmp = seed;//汽车厂自定义安全解锁的算法,这里直接key=seed。
				key = key_tmp & 0xffff;
				printf("key is  0x%04x\n",key );
				send[0].Data[0] = 0x04;
				send[0].Data[1] = 0x27;
				send[0].Data[2] = 0x02;
				send[0].Data[3] = (key & 0xff00) >> 1*8;
				send[0].Data[4]  = (key & 0x00ff);			
			}
			else
			{
				printf("Seedsize error!");
				goto SHUTDOWN;
			}
		
			if(VCI_Transmit(VCI_USBCAN2, 0, 0, send, 1) == 1)
			{
				printf("STEP[0X%02x] send key SEND SUCCESS!Index:%04d   CAN1 TX ID:0x%08X data:0x",STEP,count,send[0].ID);
				count++;
				for(i=0;i<send[0].DataLen;i++)
				{
					printf(" %02X",send[0].Data[i]);
				}
				printf("\n");
			}
			else
			{
				printf("STEP[%02x] VCI_Transmit failed!\n",STEP);
				goto SHUTDOWN;
			}
 
			CURRENT_STEP =STEP;//全局变量CURRENT_STEP是为了记录当前所处步骤(接收线程和自测时的模拟ECU需要这个变量)
			STEP = 0x00;//tester的CAN消息发送成功后将STEP设置为0x00是为了防止重复发送,也方便计算等待时间,长时间等待不到响应则判定测试失败
			usleep(500000);//延时500ms
			break;	
//step0x08:enter codeConversation			
		case 0x08:
			//需要发送的帧,结构体设置	
			send[0].Data[0] = 0x02;
			send[0].Data[1] = 0x10;
			send[0].Data[2] = 0x02;
			if(VCI_Transmit(VCI_USBCAN2, 0, 0, send, 1) == 1)
			{
				printf("STEP[0X%02x] enter codeConversationng SEND SUCCESS!Index:%04d   CAN1 TX ID:0x%08X data:0x",STEP,count,send[0].ID);
				count++;
				for(i=0;i<send[0].DataLen;i++)
				{
					printf(" %02X",send[0].Data[i]);
				}
				printf("\n");
			}
			else
			{
				printf("STEP[%02x] VCI_Transmit failed!",STEP);
				goto SHUTDOWN;
			}
			CURRENT_STEP =STEP;//全局变量CURRENT_STEP是为了记录当前所处步骤(接收线程和自测时的模拟ECU需要这个变量)
			STEP = 0x00;//tester的CAN消息发送成功后将STEP设置为0x00是为了防止重复发送,也方便计算等待时间,长时间等待不到响应则判定测试失败
			usleep(500000);//延时500ms
			break;
//step0x09:ask seed
		case 0x09:
			//需要发送的帧,结构体设置
			send[0].Data[0] = 0x02;
			send[0].Data[1] = 0x27;
			send[0].Data[2] = 0x01;
		
			if(VCI_Transmit(VCI_USBCAN2, 0, 0, send, 1) == 1)
			{
				printf("STEP[0X%02x] ask seed SEND SUCCESS!Index:%04d   CAN1 TX ID:0x%08X data:0x",STEP,count,send[0].ID);
				count++;
				for(i=0;i<send[0].DataLen;i++)
				{
					printf(" %02X",send[0].Data[i]);
				}
				printf("\n");
			}
			else
			{
				printf("STEP[%02x] VCI_Transmit failed!",STEP);
				goto SHUTDOWN;
			}
			CURRENT_STEP =STEP;//全局变量CURRENT_STEP是为了记录当前所处步骤(接收线程和自测时的模拟ECU需要这个变量)
			STEP = 0x00;//tester的CAN消息发送成功后将STEP设置为0x00是为了防止重复发送,也方便计算等待时间,长时间等待不到响应则判定测试失败
			usleep(500000);//延时500ms
			break;
//step0x91:send key
		case 0x91:
			//需要发送的帧,结构体设置
			//Seedsize有2和4两种可能,分别采取不同的组装CAN报文的形式
			if(4 == Seedsize)
			{ 
				seed0_tmp = seed0 << 3*8;
				seed1_tmp = seed1 << 2*8;
				seed2_tmp = seed2 << 8;
				seed3_tmp = seed3;
				seed = seed0_tmp + seed1_tmp + seed2_tmp + seed3_tmp;
				printf("seed is  0x%08x\n",seed);
				key = seed;//汽车厂自定义安全解锁的算法,这里直接key=seed。
				printf("key is  0x%08x\n",key);
				send[0].Data[0] = 0x06;
				send[0].Data[1] = 0x27;
				send[0].Data[2] = 0x02;
				send[0].Data[3] = (key & 0xff000000) >> 3*8;
				send[0].Data[4]  = (key & 0x00ff0000) >> 2*8;
				send[0].Data[5] = (key & 0x0000ff00) >> 8;
				send[0].Data[6] = (key & 0x000000ff);			
			}
			else if(2 == Seedsize)
			{
				seed0_tmp = seed0 << 1*8;
				seed1_tmp = seed1;
				seed = seed0_tmp + seed1_tmp;
				printf("seed is  0x%04x\n",seed);
				key_tmp = seed;//汽车厂自定义安全解锁的算法,这里直接key=seed。
				key = key_tmp & 0xffff;
				printf("key is  0x%04x\n",key );
				send[0].Data[0] = 0x04;
				send[0].Data[1] = 0x27;
				send[0].Data[2] = 0x02;
				send[0].Data[3] = (key & 0xff00) >> 1*8;
				send[0].Data[4]  = (key & 0x00ff);			
			}
			else
			{
				printf("Seedsize error!");
				goto SHUTDOWN;
			}
		
			if(VCI_Transmit(VCI_USBCAN2, 0, 0, send, 1) == 1)
			{
				printf("STEP[0X%02x] send key SEND SUCCESS!Index:%04d   CAN1 TX ID:0x%08X data:0x",STEP,count,send[0].ID);
				count++;
				for(i=0;i<send[0].DataLen;i++)
				{
					printf(" %02X",send[0].Data[i]);
				}
				printf("\n");
			}
			else
			{
				printf("STEP[%02x] VCI_Transmit failed!\n",STEP);
				goto SHUTDOWN;
			}
			CURRENT_STEP =STEP;//全局变量CURRENT_STEP是为了记录当前所处步骤(接收线程和自测时的模拟ECU需要这个变量)
			STEP = 0x00;//tester的CAN消息发送成功后将STEP设置为0x00是为了防止重复发送,也方便计算等待时间,长时间等待不到响应则判定测试失败
			usleep(500000);//延时500ms
			break;		
		case 0x00:
			//还没有收到ECU回复
			printf("[WAITING RESPONSE]VCI_Receive have not found corresponding ECU response!\n");
			if(NoResCount < NO_RES_COUNT_MAX)
			{
				NoResCount++;
			}
			else
			{
				printf("VCI_Receive have not found corresponding ECU response > %d times,test failed!!!",NO_RES_COUNT_MAX);
				goto SHUTDOWN;				
			}	
			usleep(1000000);//延时1000ms
			break;
//step0xFF:所有流程结束
		case 0xFF:
			//所有流程结束
			printf("VCI_Receive have found all corresponding ECU response!\n test SUCESS!!!\n");
			goto SHUTDOWN;
			break;
		default:
			printf("unknown step\n");
			goto SHUTDOWN;
		}
// 自测模式下,以下代码用于CAN2通道模仿ECU给tester回复
		if(TESTMODE)
		{	
			switch(CURRENT_STEP)//根据当前所处步骤决定ECU模拟器回复什么消息,本switch-case里所有的发送都是通过CAN2通道
			{
			case 0x01:
				//需要发送的帧,结构体设置
 
				send[0].ID= Ecu_DialogId +8; //ECU的响应报文的CANID时ECU_DialogId加8
				send[0].SendType=0;
				send[0].RemoteFlag=0;
				send[0].ExternFlag=0;
				send[0].DataLen=8;		
				send[0].Data[0] = 0x06;
				send[0].Data[1] = 0x50;
				send[0].Data[2] = 0x03;
				send[0].Data[3] = 0x00;
				send[0].Data[4] = 0x32;
				send[0].Data[5] = 0x01;
				send[0].Data[6] = 0xf4;
				send[0].Data[7] = 0xaa;	
			
				if(VCI_Transmit(VCI_USBCAN2, 0, 1, send, 1) == 1)
				{
				}
				else
				{
					printf("STEP[%02x] VCI_Transmit failed!",STEP);
					goto SHUTDOWN;
				}
				usleep(300000);//延时300ms
				break;
			case 0x07:
				//需要发送的帧,结构体设置
 
				send[0].ID= Ecu_DialogId + 8;
				send[0].SendType=0;
				send[0].RemoteFlag=0;
				send[0].ExternFlag=0;
				send[0].DataLen=8;
				if(0)//设为1,表示seedsize 4, 0表示seedsize2
				{
					send[0].Data[0] = 0x06;
					send[0].Data[1] = 0x67;
					send[0].Data[2] = 0x01;
					send[0].Data[3] = 0x88;
					send[0].Data[4] = 0x88;
					send[0].Data[5] = 0x88;
					send[0].Data[6] = 0x88;
					send[0].Data[7] = 0xaa;					
				}				
				else
				{
					send[0].Data[0] = 0x04;
					send[0].Data[1] = 0x67;
					send[0].Data[2] = 0x01;
					send[0].Data[3] = 0x88;
					send[0].Data[4] = 0x88;
					send[0].Data[5] = 0xaa;
					send[0].Data[6] = 0xaa;
					send[0].Data[7] = 0xaa;				
				}
				
			
				if(VCI_Transmit(VCI_USBCAN2, 0, 1, send, 1) == 1)
				{
				}
				else
				{
					printf("STEP[%02x] VCI_Transmit failed!",STEP);
					goto SHUTDOWN;
				}
				usleep(300000);//延时300ms
				break;
			case 0x71:
				//需要发送的帧,结构体设置
 
				send[0].ID= Ecu_DialogId + 8;
				send[0].SendType=0;
				send[0].RemoteFlag=0;
				send[0].ExternFlag=0;
				send[0].DataLen=8;			
				send[0].Data[0] = 0x02;
				send[0].Data[1] = 0x67;
				send[0].Data[2] = 0x02;
				send[0].Data[3] = 0xaa;
				send[0].Data[4] = 0xaa;
				send[0].Data[5] = 0xaa;
				send[0].Data[6] = 0xaa;
				send[0].Data[7] = 0xaa;	
			
				if(VCI_Transmit(VCI_USBCAN2, 0, 1, send, 1) == 1)
				{
				}
				else
				{
					printf("STEP[%02x] VCI_Transmit failed!",STEP);
					goto SHUTDOWN;
				}
				usleep(300000);//延时300ms
				break;
			case 0x08:
				//需要发送的帧,结构体设置
 
				send[0].ID= Ecu_DialogId + 8;
				send[0].SendType=0;
				send[0].RemoteFlag=0;
				send[0].ExternFlag=0;
				send[0].DataLen=8;	
				send[0].Data[0] = 0x06;
				send[0].Data[1] = 0x50;
				send[0].Data[2] = 0x02;
				send[0].Data[3] = 0x00;
				send[0].Data[4] = 0x32;
				send[0].Data[5] = 0x01;
				send[0].Data[6] = 0xf4;
				send[0].Data[7] = 0x00;	
			
				if(VCI_Transmit(VCI_USBCAN2, 0, 1, send, 1) == 1)
				{
				}
				else
				{
					printf("STEP[%02x] VCI_Transmit failed!",STEP);
					goto SHUTDOWN;
				}
				usleep(300000);//延时300ms
				break;
			case 0x09:
				//需要发送的帧,结构体设置
 
				send[0].ID= Ecu_DialogId + 8;
				send[0].SendType=0;
				send[0].RemoteFlag=0;
				send[0].ExternFlag=0;
				send[0].DataLen=8;
				if(1)//设为1,表示seedsize 4, 0表示seedsize2
				{
					send[0].Data[0] = 0x06;
					send[0].Data[1] = 0x67;
					send[0].Data[2] = 0x01;
					send[0].Data[3] = 0x88;
					send[0].Data[4] = 0x88;
					send[0].Data[5] = 0x88;
					send[0].Data[6] = 0x88;
					send[0].Data[7] = 0xaa;					
				}				
				else
				{
					send[0].Data[0] = 0x04;
					send[0].Data[1] = 0x67;
					send[0].Data[2] = 0x01;
					send[0].Data[3] = 0x88;
					send[0].Data[4] = 0x88;
					send[0].Data[5] = 0xaa;
					send[0].Data[6] = 0xaa;
					send[0].Data[7] = 0xaa;				
				}	
			
				if(VCI_Transmit(VCI_USBCAN2, 0, 1, send, 1) == 1)
				{
				}
				else
				{
					printf("STEP[%02x] VCI_Transmit failed!",STEP);
					goto SHUTDOWN;
				}
				usleep(300000);//延时300ms
				break;
//step0x91:send key
			case 0x91:
				//需要发送的帧,结构体设置
 
				send[0].ID= Ecu_DialogId + 8;
				send[0].SendType=0;
				send[0].RemoteFlag=0;
				send[0].ExternFlag=0;
				send[0].DataLen=8;	
				send[0].Data[0] = 0x02;
				send[0].Data[1] = 0x67;
				send[0].Data[2] = 0x02;
				send[0].Data[3] = 0xaa;
				send[0].Data[4] = 0xaa;
				send[0].Data[5] = 0xaa;
				send[0].Data[6] = 0xaa;
				send[0].Data[7] = 0xaa;	
			
				if(VCI_Transmit(VCI_USBCAN2, 0, 1, send, 1) == 1)
				{
				}
				else
				{
					printf("STEP[%02x] VCI_Transmit failed!",STEP);
					goto SHUTDOWN;
				}
				usleep(300000);//延时300ms
				break;
			default:
				printf("unknown CAN2 step:%02X\n",CURRENT_STEP);
				goto SHUTDOWN;
			}
		}
	}// while end
	
SHUTDOWN:
	m_run0=0;//线程关闭指令。
	pthread_join(threadid,NULL);//等待线程关闭。
	usleep(100000);//延时100ms。
	//VCI_ResetCAN(VCI_USBCAN2, 0, 0);//复位CAN1通道。
	usleep(100000);//延时100ms。
	//VCI_CloseDevice(VCI_USBCAN2,0);//关闭设备。
	//除收发函数外,其它的函数调用前后,最好加个毫秒级的延时,即不影响程序的运行,又可以让USBCAN设备有充分的时间处理指令。
	//goto ext;
}

代码执行结果:

└─# ./hello_cpp 1800
argc = 0 | argv[0] ./hello_cpp
argc = 1 | argv[1] 1800
Ecu_DialogId = 0x0708
>>this is hello !
>>USBCAN DEVICE NUM:1 PCS
Device:0
>>Get VCI_ReadBoardInfo success!
>>open deivce success!
>>Get VCI_ReadBoardInfo success!
config.AccCode:E2000000
STEP[0X01] enter 03 diagConversation SEND SUCCESS!Index:0000   CAN1 TX ID:0x00000708 data:0x 02 10 03 55 55 55 55 55
[RECV]Index:0001  CAN1 RX ID:0x00000710 Standard  Data   DLC:0x08 data:0x 06 50 03 00 32 01 F4 AA TimeStamp:0x0033A452
STEP[0X01] 03 diagConversation resp received!
STEP[0X07] ask seed SEND SUCCESS!Index:0002   CAN1 TX ID:0x00000708 data:0x 02 27 01 55 55 55 55 55
[RECV]Index:0003  CAN1 RX ID:0x00000710 Standard  Data   DLC:0x08 data:0x 04 67 01 88 88 AA AA AA TimeStamp:0x0033BC1C
STEP[0X07] ask seed resp 2 seeds received!
seed is  0x8888
key is  0x8888
STEP[0X71] send key SEND SUCCESS!Index:0004   CAN1 TX ID:0x00000708 data:0x 04 27 02 88 88 55 55 55
[RECV]Index:0005  CAN1 RX ID:0x00000710 Standard  Data   DLC:0x08 data:0x 02 67 02 AA AA AA AA AA TimeStamp:0x0033DC85
STEP[0X71] send key resp received!
STEP[0X08] enter codeConversationng SEND SUCCESS!Index:0006   CAN1 TX ID:0x00000708 data:0x 02 10 02 55 55 55 55 55
[RECV]Index:0007  CAN1 RX ID:0x00000710 Standard  Data   DLC:0x08 data:0x 06 50 02 00 32 01 F4 00 TimeStamp:0x0033FC21
STEP[0X08] enter codeConversation resp received!
STEP[0X09] ask seed SEND SUCCESS!Index:0008   CAN1 TX ID:0x00000708 data:0x 02 27 01 55 55 55 55 55
[RECV]Index:0009  CAN1 RX ID:0x00000710 Standard  Data   DLC:0x08 data:0x 06 67 01 88 88 88 88 AA TimeStamp:0x00341BC1
STEP[0X09] ask seed resp 4 seeds received!
seed is  0x88888888
key is  0x88888888
STEP[0X91] send key SEND SUCCESS!Index:0010   CAN1 TX ID:0x00000708 data:0x 06 27 02 88 88 88 88 55
[RECV]Index:0011  CAN1 RX ID:0x00000710 Standard  Data   DLC:0x08 data:0x 02 67 02 AA AA AA AA AA TimeStamp:0x00343B93
STEP[0X91] send key resp resp received!
VCI_Receive have found all corresponding ECU response!
 test SUCESS!!!
run thread exit

3. 总结

        这款CAN分析仪性价比不错,功能也够强大,如果你想使用,可以参考本博客试试。

  • 4
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值