基于CAN总线的汽车诊断协议UDS--ECU 下位机设计(RT1062)

4 篇文章 4 订阅
3 篇文章 2 订阅

笔者在19年,有写过一篇《基于CAN总线的汽车诊断协议UDS,上位机下位机开发》文章,后面陆陆续续有读者询问相关问题,接下来分两章分别介绍上下位机工程基础搭建。本章内容,介绍ECU开发的基本流程。

1. 芯片选用
nxp MIMXRT1062芯片,为什么选用这款作为demo,有以下优点:

  • nxp mcu用于汽车电子上是特别的多
  • rt10xx是近几年比较火的芯片,cortex-m7性能更强大
  • fsl库兼容性高,用好该芯片像nxp其他系列芯片,基本都能轻松移植

2.bootloader 和 App 中断向量重映射
bootloadr 向量表操作方法,从Flash地址0x60002000拷贝到ITCM–0x00000000,向量表寄存器设置为:SCB->VTOR = (uint32_t )0x00000000;

void xBSP_VectorInit(void)
{
	int *pS = (int*)0x60002000;
	int *pT = (int*)0x00000000;

	__asm
	{
		CPSID   i
	}

	SCB->VTOR = (uint32_t )0x00000000;

	__asm
	{
		CPSIE   i
	}


	for(int i = 0 ; i < (0x400 / 4) ; i++)
	{
		pT[i] = pS[i];
	}
}

App 向量表操作方法,从Flash地址0x60010000拷贝到ITCM–0x00000000,向量表寄存器设置为:SCB->VTOR = (uint32_t )0x00000000;

void xBSP_VectorInit(void)
{
	int *pS = (int*)0x60010000;
	int *pT = (int*)0x00000000;

	__asm
	{
		CPSID   i
	}

	SCB->VTOR = (uint32_t )0x00000000;

	__asm
	{
		CPSIE   i
	}


	for(int i = 0 ; i < (0x400 / 4) ; i++)
	{
		pT[i] = pS[i];
	}
}

综上Bootloader和App,向量表映射都在0x00000000,原因是ITCM响应执行速度更快。另外需要注意修改链接脚本,明白bootloader和app是处于ROM的哪个位置。笔者设计的是 bootloader:0x6000_0000 app:0x6001_0000

3.FlexCan初始化

/*------------------------------------------------------------------------------------------------------------------
Macros
*/
#define EXAMPLE_CAN CAN2 

#define FLEXCAN_BAUDRATE	(500000U)

/* Select 60M clock divided by USB1 PLL (480 MHz) as master flexcan clock source */
#define FLEXCAN_CLOCK_SOURCE_SELECT  (0U)
/* Clock divider for master flexcan clock source */
#define FLEXCAN_CLOCK_SOURCE_DIVIDER (2U)
/* Get frequency of flexcan clock */
#define EXAMPLE_CAN_CLK_FREQ ((CLOCK_GetFreq(kCLOCK_Usb1PllClk) / 8) / (FLEXCAN_CLOCK_SOURCE_DIVIDER + 1U))

//Message Box 定义 0~63
#define RX_MESSAGE_BUFFER_A (8)
#define TX_MESSAGE_BUFFER_NUM (9)


/*------------------------------------------------------------------------------------------------------------------
Variables
*/
flexcan_handle_t flexcanHandle;

uint32_t txIdentifier = 0x721;
uint32_t rxIdentifier = 0x710;

flexcan_frame_t txFrame,rxFrame;
flexcan_mb_transfer_t txXfer, rxXfer;

/*
********************************************************************************************************************
@ Brief  : Can Init

@ Param  : None

@ Return : NONE

@ Author : LYC

@  Date  : 2019 - 04 - 03
********************************************************************************************************************
*/
void xBSP_CanInit(void)
{
    flexcan_config_t flexcanConfig;
    flexcan_rx_mb_config_t mbConfig;	

	IOMUXC_SetPinMux(IOMUXC_GPIO_AD_B0_02_FLEXCAN2_TX, 1U); 
	IOMUXC_SetPinMux(IOMUXC_GPIO_AD_B0_03_FLEXCAN2_RX, 1U); 
	IOMUXC_SetPinConfig(IOMUXC_GPIO_AD_B0_02_FLEXCAN2_TX, 0x10B0u); 
	IOMUXC_SetPinConfig(IOMUXC_GPIO_AD_B0_03_FLEXCAN2_RX, 0x10B0u); 	

	/*Clock setting for FLEXCAN*/
	CLOCK_SetMux(kCLOCK_CanMux, FLEXCAN_CLOCK_SOURCE_SELECT);
	CLOCK_SetDiv(kCLOCK_CanDiv, FLEXCAN_CLOCK_SOURCE_DIVIDER);	
	
    /* Get FlexCAN module default Configuration. */
    /*
     * flexcanConfig.clkSrc                 = kFLEXCAN_ClkSrc0;
     * flexcanConfig.baudRate               = 1000000U;
     * flexcanConfig.baudRateFD             = 2000000U;
     * flexcanConfig.maxMbNum               = 16;
     * flexcanConfig.enableLoopBack         = false;
     * flexcanConfig.enableSelfWakeup       = false;
     * flexcanConfig.enableIndividMask      = false;
     * flexcanConfig.disableSelfReception   = false;
     * flexcanConfig.enableListenOnlyMode   = false;
     * flexcanConfig.enableDoze             = false;
     */
	FLEXCAN_GetDefaultConfig(&flexcanConfig);	
	flexcanConfig.enableLoopBack = false;   
	flexcanConfig.baudRate = FLEXCAN_BAUDRATE;      
	FLEXCAN_Init(EXAMPLE_CAN, &flexcanConfig, EXAMPLE_CAN_CLK_FREQ);	
	
    /* Create FlexCAN handle structure and set call back function. */
    FLEXCAN_TransferCreateHandle(EXAMPLE_CAN, &flexcanHandle, flexcan_callback, NULL);	
	
    txFrame.id     = FLEXCAN_ID_STD(txIdentifier);
    txFrame.format = (uint8_t)kFLEXCAN_FrameFormatStandard;
    txFrame.type   = (uint8_t)kFLEXCAN_FrameTypeData;    
    /* Setup Tx Message Buffer. */
    FLEXCAN_SetTxMbConfig(EXAMPLE_CAN, TX_MESSAGE_BUFFER_NUM, true);    //设置发送邮箱 

    /* Set Rx Masking mechanism. */
    FLEXCAN_SetRxMbGlobalMask(EXAMPLE_CAN, FLEXCAN_RX_MB_STD_MASK(0x7FC, 0, 0));  //rxIdentifier 0x710 0x711 都有效
	
    /* Setup Rx Message Buffer. */
    mbConfig.format = kFLEXCAN_FrameFormatStandard;
    mbConfig.type = kFLEXCAN_FrameTypeData;
    FLEXCAN_SetRxIndividualMask(EXAMPLE_CAN, RX_MESSAGE_BUFFER_A, FLEXCAN_RX_MB_STD_MASK(0x7FC,0,0));  //rxIdentifier 0x710 0x711 都有效
	
    mbConfig.id = FLEXCAN_ID_STD(rxIdentifier);
    FLEXCAN_SetRxMbConfig(EXAMPLE_CAN, RX_MESSAGE_BUFFER_A, &mbConfig, true);
    rxFrame.id = FLEXCAN_ID_STD(rxIdentifier);
    rxXfer.mbIdx = RX_MESSAGE_BUFFER_A;
    rxXfer.frame = &rxFrame;
    FLEXCAN_TransferReceiveNonBlocking(EXAMPLE_CAN, &flexcanHandle, &rxXfer);
	
	NVIC_SetPriority(CAN2_IRQn, CAN_ISR_PRE);
}

重点需要注意的是帧过滤,如下图,MB0-63 都可用于发送接收的Buffer,在Rx中是需要Match的,也就是ID的过滤,过滤OK产生中断,执行相关操作。
请添加图片描述
笔者代码中使用了MB[8]作为接收,所以MASK需要写在对应的RXIMR[8]上,FLEXCAN_SetRxIndividualMask设置为0x7FC,即最后两bit都为0,这两个位是不会校验的,那此时MB[8]会接收ID范围为0x710-0x713的数据。更多过滤细节大家可翻阅手册实验,这里不展开讲解。

4.Can接收发送处理

extern TaskHandle_t Handle_CanMessageTask;
volatile bool txComplete = false,rxComplete = false;

static void flexcan_callback(CAN_Type *base, flexcan_handle_t *handle, status_t status, uint32_t result, void *userData)
{
    switch (status)
    {
        case kStatus_FLEXCAN_RxIdle:
           if (RX_MESSAGE_BUFFER_A == result)
           {
#if 0  			   
                rxComplete = true;
#else           /*任务通知*/
                BaseType_t xHigherPriorityTaskWoken = pdTRUE;
                vTaskNotifyGiveFromISR(Handle_CanMessageTask, &xHigherPriorityTaskWoken);  
                portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
#endif                
           }
            break;

        case kStatus_FLEXCAN_TxIdle:
        case kStatus_FLEXCAN_TxSwitchToRx:
           if (TX_MESSAGE_BUFFER_NUM == result)
           {
               txComplete = true;
           }
            break;

        case kStatus_FLEXCAN_WakeUp:
//            wakenUp = true;
            break;

        default:
            break;
    }
}

/*
********************************************************************************************************************
@ Brief  : Can send message

@ Param  : pBuffer:    len: 

@ Return : NONE

@ Author : LYC

@  Date  : 2019 - 01 - 22
********************************************************************************************************************
*/
void xBSP_CAN_TX(uint8_t* pBuffer,uint8_t len)
{
    txFrame.length = len;

    txFrame.dataByte0 = pBuffer[0];
    txFrame.dataByte1 = pBuffer[1];
    txFrame.dataByte2 = pBuffer[2];
    txFrame.dataByte3 = pBuffer[3];
    txFrame.dataByte4 = pBuffer[4];
    txFrame.dataByte5 = pBuffer[5];
    txFrame.dataByte6 = pBuffer[6];
    txFrame.dataByte7 = pBuffer[7];

    txXfer.mbIdx = (uint8_t)TX_MESSAGE_BUFFER_NUM;
    txXfer.frame = &txFrame;

    FLEXCAN_TransferSendNonBlocking(EXAMPLE_CAN, &flexcanHandle, &txXfer);
}

/*
********************************************************************************************************************
@ Brief  : Can 接收8Byte 处理

@ Param  : None

@ Return : NONE

@ Author : LYC

@  Date  : 2019 - 01 - 22
********************************************************************************************************************
*/
void xBSP_CanReceive(uint8_t* pRxCanBuf)
{
    FLEXCAN_TransferReceiveNonBlocking(EXAMPLE_CAN, &flexcanHandle, &rxXfer);

	pRxCanBuf[0]=rxFrame.dataByte0;
	pRxCanBuf[1]=rxFrame.dataByte1;
	pRxCanBuf[2]=rxFrame.dataByte2;
	pRxCanBuf[3]=rxFrame.dataByte3;
	pRxCanBuf[4]=rxFrame.dataByte4;
	pRxCanBuf[5]=rxFrame.dataByte5;
	pRxCanBuf[6]=rxFrame.dataByte6;
	pRxCanBuf[7]=rxFrame.dataByte7;
}

flexcan_callback 是can中断事件,这个callback实际是在中断模式,不建议接收处理放在中断中做,这里给了两种方案,一个是看标志位(裸机方式),另一种是中断中发起任务通知(操作系统)。如下函数为,接收处理线程,当然裸机可以放在while中处理一样。

/*
***************************************************************************************************************
@ Brief  : Can 接收解包任务

@ Param  : None

@ Return : None

@ Author : Lyc

@  Date  : 2019 - 04 - 03
***************************************************************************************************************
*/
void xAPP_DealCanMessageTask(void* argument)
{
	uint8_t rxCanBuf[8];
	uint8_t txCanBuf[8];

	while(1)
	{
		if(ulTaskNotifyTake(pdTRUE, portMAX_DELAY))
		{
			/*0.接收8Byte*/
			xBSP_CanReceive(rxCanBuf);
			/*1.解包分析*/
			if(xSYS_CANUnpackFrame(rxCanBuf) == true)
			{
				/*2.处理具体的帧数据*/
				switch(ServiceUnpackData.frameType)
				{
					/*服务接收完毕时*/
					case 0:   
					case 2:
					{
						xSYS_CANAppService(ServiceUnpackData.validData,ServiceUnpackData.validSize - 1);				
					}
					break;

					case 1:  //当前发送流控帧
					{
						memset(txCanBuf,0xff,8);
						txCanBuf[0] = 0x30;
						xBSP_CAN_TX(txCanBuf,8);				
					}	
					break;

					default:
					break;							
				} 
			}
		}
		//vTaskDelay(1);
	}
}

5.基于UDS解包思路
UDS是一套完整的标准协议,其实只需要熟透ISO 15765-2ISO 14229-1 用代码形式表达出来就可以了。因为有保密协议,笔者就不展示核心代码了,

//** Service running state **
#define SrvStat_Run_Req        0x00 
#define SrvStat_Run_Resp       0x01 
#define SrvStat_ALLPhase_Over  0x02 

//** SubFunc of Diag Ser $10 Session Define ** 
#define  Srv10_DefaultMod      0x01 
#define  Srv10_ProgramMod      0x02 
#define  Srv10_ExternMod       0x03 

//** SubFunc of Diag Ser $11 ECUReset** 
#define  Srv11_HardReset       0x01 
#define  Srv11_KeyOffOnReset   0x02 
#define  Srv11_SoftReset       0x03 

//** SubFunc of Diag Srv 19  ReadDTCInfo **
#define  Srv19_REPORT_DTC_NUM_BY_MASK      0x01
#define  Srv19_REPORT_DTC_BY_MASK          0x02
#define  Srv19_REPORT_DTC_EXT_BY_DTCNUM    0x06
#define  Srv19_REPORT_SUPPORTED_DTC        0x0A

//** Service $22: phase define ** 
#define  Srv22_Phase_ReadProductNum   0x00 
#define  Srv22_Phase_ReadProductor    0x01 
#define  Srv22_Phase_ReadSerialNum    0x02 

//** Service $2E: phase define ** 
#define  Srv2E_Phase_WriteProductNum  0x00 
#define  Srv2E_Phase_writeProductor   0x01 
#define  Srv2E_Phase_writeSerialNum   0x02 
 
//** Service $27: Security Level define ** 
#define  Srv27_SecLevel_AskSeed       0x03 
#define  Srv27_SecLevel_ChkKey        0x04 

//** Service $31: Rountin define ** 
#define  Srv31_CheckAppValidation     0xDA04 
#define  Srv31_EraseFlash             0xFF00 
#define  Srv31_CheckComplete          0xFF01 
#define  Srv31_CheckBootCondition     0xFF02 
#define  Srv31_IndependCheck          0xFF03 

//** SubFunc of Routine control type $31 Routine Ctrl **
#define  Srv31_ROUTINE_START          0x01
#define  Srv31_ROUTINE_STOP           0x02
#define  Srv31_ROUTINE_REQRESULT      0x03

//** SubFunc of Diag Srv 28  CommunicationCtrl **
#define  Srv28_COMM_EN_RX_AND_TX      0x0
#define  Srv28_COMM_EN_RX_ONLY        0x1
#define  Srv28_COMM_EN_TX_ONLY        0x2
#define  Srv28_COMM_DISABLE_RX_AND_TX 0x3

//** SubFunc of Diag Srv 85  CtrlDTCSetting **
#define  Srv85_CTRL_DTC_ON            0x01
#define  Srv85_CTRL_DTC_OFF           0x02

///
#define DIAG_SESSION_CTRL_10   0x10
#define DIAG_ECUREST_11        0x11
#define DIAG_CLEARIFO_14       0x14 
#define DIAG_READ_DTC_INFO_19  0x19
#define DIAG_READDATABYID_22   0x22
#define DIAG_WRITEDATABYID_2E  0x2E
#define DIAG_SECURITY_27       0x27
#define DIAG_COMMCTRL_28       0x28
#define DIAG_ROUTINECTRL_31    0x31
#define DIAG_REQ_DOWNLOAD_34   0x34
#define DIAG_TRANS_DATA_36     0x36
#define DIAG_REQ_TRANS_EXIT_37 0x37
#define DIAG_TESTERPRESENT_3E  0x3E
#define DIAG_CTRL_DTC_SET_85   0x85
#define DIAG_NAGETIVE_7F       0x7F

bool xSYS_CANAppService(uint8_t* buf,uint32_t size)
{
	bool respRlt = false;
	uint8_t serviceType = buf[0];
	
	switch(serviceType)
	{
		case DIAG_SESSION_CTRL_10:
		{
			respRlt = xCAN_App10Service(&buf[1],size);		
		}
		break;
		
		case DIAG_ECUREST_11:
		{
			respRlt = xCAN_App11Service(&buf[1],size);		
		}
		break;
		
		case DIAG_CLEARIFO_14:
		{
			respRlt = xCAN_App14Service(&buf[1],size);		
		}
		break;
		
		case DIAG_READ_DTC_INFO_19:
		{
			respRlt = xCAN_App19Service(&buf[1],size);		
		}
		break;
		
		case DIAG_READDATABYID_22:
		{
			respRlt = xCAN_App22Service(&buf[1],size);		
		}
		break;

		case DIAG_WRITEDATABYID_2E:
		{
			respRlt = xCAN_App2EService(&buf[1],size);		
		}
		break;

		case DIAG_SECURITY_27:
		{
			respRlt = xCAN_App27Service(&buf[1],size);
		}
		break;

		case DIAG_COMMCTRL_28:
		{
			respRlt = xCAN_App28Service(&buf[1],size);		
		}
		break;

		case DIAG_ROUTINECTRL_31:
		{
			respRlt = xCAN_App31Service(&buf[1],size);		
		}
		break;

		case DIAG_REQ_DOWNLOAD_34:
		{
			respRlt = xCAN_App34Service(&buf[1],size);		
		}
		break;

		case DIAG_TRANS_DATA_36:
		{
			respRlt = xCAN_App36Service(&buf[1],size);		
		}
		break;

		case DIAG_REQ_TRANS_EXIT_37:
		{
			respRlt = xCAN_App37Service(&buf[1],size);		
		}
		break;

		case DIAG_TESTERPRESENT_3E:
		{
			respRlt = xCAN_App3EService(&buf[1],size);		
		}
		break;

		case DIAG_CTRL_DTC_SET_85:
		{
			respRlt = xCAN_App85Service(&buf[1],size);
		}
		break;

        default:
        {
			respRlt = xCAN_AppOthersService(&buf[1],size);		
		}
        break;		
	}
	
	return respRlt;
}

6.bootloader跳转app
笔者这里只是简单做个demo,所以跳转要求没做那么苛刻,具体如下:

            AREA    JUMP, CODE, READONLY	
JMP_APP     PROC
			EXPORT  JMP_APP 
			LDR R0, =0x60010000
			LDR R0, [R0]
			MOV SP, R0
			LDR R0, =0x60010004
			LDR R0, [R0]
			BX  R0
			ENDP

  END
int main(void)
{
	//该模式表示,固件跳转区域是否有固件	
	readJumpFlag = *(uint32_t*)(0x60000000+FLASH_ADDR_JUMP_FIRM_FLAG);  	
	
	xBSP_Config();
    xBSP_GPIO_Init();

	if(readJumpFlag == 0x04030201 && Read_WAKEUP())
	{
		JMP_APP();	
	}

	xBSP_MPUInit();

	/*Create Start Task*/	
	if(xTaskCreate(xTask_Start, "Task_Start", TASK_STACK_SIZE_StartTask, NULL, TASK_PRIORITY_StartTask, &Handle_StartTask) != pdPASS)
	{
		_Error_Handler(__FILE__, __LINE__);
	}		
	
	vTaskStartScheduler();	//开始系统调度			
		
	while(1)
	{  

	}
}
  • 5
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值