前言
CCP协议在新能源汽车电子领域发挥着重要作用,CCP观测和标定作用对开发工程师起着重要作用。
疫情宅在家无聊,把这块的知识重新梳理下。
一、CCP是什么?
CCP(CAN Calibration Protocol)是一种基于CAN总线的ECU(Electronic Control Unit)标定协议,已经在许多欧美汽车厂商得到应用,采用CCP协议可以快速而有效地实现对汽车电控单元的标定。
二、移植步骤
1.准备工作
准备S32K144的CAN驱动
2.移植
CAN驱动部分移植头文件的配置:
配置CCP的ID
配置DAQ
配置中断
/* CCPPAR.H */
#ifndef CCPPAR_H_
#define CCPPAR_H_
#include "Cpu.h"
#include "APPL_CCP_HAL.H"
#include "ccp.h"
#define C_ENABLE_CCP
#define C_MINI_CAN_DRIVER
#define CCP_INTEL
#define CCP_RAM
#define CCP_ROM const
#define CCP_BYTE unsigned char
#define CCP_WORD unsigned short
#define CCP_DWORD unsigned long
#define CCP_BYTEPTR unsigned char*
#define CCP_DISABLE_INTERRUPT ccpDisableInterrupt()
#define CCP_ENABLE_INTERRUPT ccpEnableInterrupt()
#define CCP_MTABYTEPTR unsigned char*
#define CCP_DAQBYTEPTR unsigned char*
#define CCP_STATION_ID "CCPtest"
#define CCP_STATION_ADDR 0x1
#define CCP_DTO_ID 0x101
#define CCP_CRO_ID 0x100
#define CCP_DAQ
#define CCP_MAX_ODT 40
#define CCP_MAX_DAQ 1
//#define CCP_SEND_QUEUE_OVERRUN_INDICATION
//#define CCP_SEND_QUEUE
//#define CCP_SEND_QUEUE_SIZE 6
//#define CCP_CHECKSUM
//#define CCP_CHECKSUM_TYPE CCP_WORD
//#define CCP_BOOTLOADER_DOWNLOAD
//#define CCP_CALPAGE
#endif
将CCP需要移植的部分抽取到一个文件中方便移植:
ccpSend CAN发送的驱动函数
ccpDisableInterrupt 关全局中断
ccpEnableInterrupt 开全局中断
#include "Cpu.h"
#include "APPL_CCP_HAL.H"
#include "ccp.h"
extern can_message_t TxMsg;
extern volatile uint32_t CanRecvFlag;
extern can_message_t RxMsg;
//------------------------------------------------------------------------------
// Global
unsigned long ccpSendFlag = 0;
unsigned long gCounter;
// Current Calibration page
CCP_BYTE ccpCalPage = 0; // Default ROM
/*----------------------------------------------------------------------------*/
/* Transmit the CCP message */
/* Id is CCP_DTO_ID, which is configured at compile time in CAN.C */
void ccpSend( CCP_BYTEPTR msg )
{
TxMsg.id = CCP_DTO_ID;
TxMsg.length = 8;
TxMsg.data[0] = msg[0];
TxMsg.data[1] = msg[1];
TxMsg.data[2] = msg[2];
TxMsg.data[3] = msg[3];
TxMsg.data[4] = msg[4];
TxMsg.data[5] = msg[5];
TxMsg.data[6] = msg[6];
TxMsg.data[7] = msg[7];
CAN_Send(&can_pal1_instance, 0, &TxMsg);
ccpSendFlag = 1;
}
/*----------------------------------------------------------------------------*/
/* Convert a memory address from CCP 8/32bit into a C pointer */
CCP_MTABYTEPTR ccpGetPointer( CCP_BYTE addr_ext, CCP_DWORD addr )
{
//if (ccpCalPage==1 && addr>=0x14000 && addr<0x18000) { /* CALRAM */
//return (CCP_MTABYTEPTR) ( addr + 0x30000UL );
//}
return (CCP_MTABYTEPTR) addr;
}
/*----------------------------------------------------------------------------*/
// CCP Callbacks
void ccpUserBackground( void )
{
}
CCP_BYTE ccpDisableNormalOperation( CCP_MTABYTEPTR a, CCP_WORD s )
{
return 1;
}
/*----------------------------------------------------------------------------*/
/* Calibration RAM/ROM Selection */
CCP_DWORD ccpGetCalPage( void )
{
return (CCP_DWORD)ccpCalPage;
}
void ccpSetCalPage( CCP_DWORD a )
{
ccpCalPage = (CCP_BYTE)a;
if (ccpCalPage==1) { /* RAM */
//#pragma asm
//mov DPP1,#11h
//#pragma endasm
} else { /* ROM */
//#pragma asm
//mov DPP1,#05h
//#pragma endasm
}
}
void ccpInitCalPage( void ) {
#define CALROM_ADDR 0x14000
#define CALRAM_ADDR 0x44000
unsigned char *p1 = (unsigned char *)CALROM_ADDR;
unsigned char *p2 = (unsigned char *)CALRAM_ADDR;
unsigned int i;
for (i=0;i<0x4000;i++) {
// *p2++ = *p1++;
}
}
//------------------------------------------------------------------------------
// 10ms Timer Isr
void CCP_DAQ_Callback(void)
{
// 10 ms
// Data Acquisition on Channel 2
ccpDaq(1);
}
//------------------------------------------------------------------------------
void CCP_Init(void)
{
// initialize Calibration RAM
ccpInitCalPage();
// Initialize CCP driver
ccpInit();
}
void CCP_Main(void)
{
if(CanRecvFlag)
{
CanRecvFlag = 0;
ccpCommand(&RxMsg.data[0]);
}
// Check for pending CCP transmit messages (DTO)
//if (ccpSendFlag)
{
//ccpSendFlag = 0;
ccpSendCallBack();
}
ccpBackground();
}
void ccpDisableInterrupt(void)
{
DISABLE_INTERRUPTS();
}
void ccpEnableInterrupt(void)
{
ENABLE_INTERRUPTS();
}
在main函数中调用如下:
调用初始化
主循环中10ms定时器调用DAQ任务
int main()
{
...
CCP_Init();
...
for(;;)
{
...
if(ChkSoftTimerTout(AppTimer, TMR_APP_TEST_CNT))
{
SetSoftTimerTout(AppTimer, TMR_APP_TEST_CNT, 5);
*((uint32_t *)TestCnt) += 1;
*((uint32_t *)(TestCnt+3)) += 1;
}
CCP_Main();
if(ChkSoftTimerTout(AppTimer, TMR_APP_DAQ1_TEST))
{
SetSoftTimerTout(AppTimer, TMR_APP_DAQ1_TEST, 10);
CCP_DAQ_Callback();
}
}
3.测试验证
从测试工程中elf用ASAP2 Editor 工具制作A2L文件
将A2L文件导入CANape开启标定
测试效果如下:
总结
通过标定协议我们就可以更快的调试程序了。