便于理解CAN标定的工作原理
main_function
ccpInitCalPage //初始化标定页,将标定ROM数据(16K)读取到RAM(初始化标定RAM)
while(1)
if (CAN_bNewData(8))//如果有CAN消息
CAN_vGetMsgObj(8,&o); //接收CAN消息
ccpCommand(&o.ubData[0]); //处理接收到的CAN消息
if (cmd==CC_CONNECT
//会话连接指令
if (stationAddr==CCP_STATION_ADDR //地址匹配
ccp.SessionStatus |= SS_CONNECTED;
//置会话连接状态
ccp.SessionStatus &= ~SS_TMP_DISCONNECTED; //清会话临时断开状态
else if (ccp.SessionStatus&SS_CONNECTED) //如果已连接,则执行其他指令
switch (cmd)
处理各种命令
ccpSendCrm();
ccpSend(ccp.Crm); //应答给主控器
ccpSendCallBack //主要负责连续发送DAQ队列数据给主控器
ccp.SendStatus &= ~CCP_SEND_PENDING;
ccp.SendStatus |= CCP_DTM_PENDING;
ccpSend((CCP_BYTEPTR)&ccp.Queue.msg[ccp.Queue.rp]);
ccpBackground(); //循环处理该函数,主要用于校验计算
--------------------------------------------------------------------
//10ms定时中断处理
void timer_function( void ) {
// ms Timer
gTimer += 10;
// ECU Simulation
ecuCyclic();
// 10 ms
// Data Acquisition on Channel 2
ccpDaq(2);
}
--------------------------------------------------------------------
处理主控器发来的各种命令:
--------------------------------------
case CC_DISCONNECT:
//断开会话
ccp.SessionStatus &= ~SS_CONNECTED;
#define disconnectCmd com[2]
if (disconnectCmd==0x00) //如果只是临时断开
ccp.SessionStatus |= SS_TMP_DISCONNECTED; //置临时断开标志
else //否者是结束会话(disconnectCmd==0x01)
ccpStopAllDaq(); //清除所有会话的状态
ccp.ProtectionStatus = 0; //加锁状态也清除
//到此会话结束
--------------------------------------
case CC_EXCHANGE_ID:
//交换一些认证码信息(主要是把一些加密信息告诉主控器)
#define masterId com[2] //CAN主设备(标定工具)的认证码
for(i=0;ccpStationId[i]!=0;i++) ; //计算(自己的)认证码的长度
ccp.Crm[3] = i; //填充长度到发送缓存
ccp.Crm[5] = PL_CAL; //告诉主设备允许标定,PL_CAL表示允许标定 Calibration available
ccp.Crm[6] |= PL_CAL; //告诉主设备标定需要解锁
ccp.Crm[5] |= PL_DAQ; //告诉主设备可数据采集,PL_DAQ表示允许数据采集
ccp.Crm[6] |= PL_DAQ; //告诉主设备数据采集需要解锁
ccp.Crm[5] |= PL_PGM; //告诉主设备可下载flash程序
ccp.Crm[6] |= PL_PGM; //告诉主设备下载flash程序需要解锁
ccp.Crm[7] = CCP_DRIVER_VERSION; //告诉主设备该驱动版本号
ccpSetMTA(0,(CCP_MTABYTEPTR)ccpStationId); //
ccp.MTA[0] = (CCP_MTABYTEPTR)ccpStationId; //把自己的认证码的地址先放在传输缓存,便于MTA指令发送这个地址的内容
--------------------------------------
case CC_GET_SEED:
//获取种子秘钥
#define privilegeLevel com[2] //秘钥要求 PL_CAL,PL_DAQ,PL_PGM
switch (privilegeLevel)
case PL_CAL: //主设备想知道从设备的标定的加密状态与种子秘钥
ccp.Crm[3] = (0==(ccp.ProtectionStatus&PL_CAL)) //标定的加密状态告诉主设备,该状态值决定主设备是否要发送CC_UNLOCK解锁
*(CCP_DWORD*)&ccp.Crm[4] = ccpGetSeed(PL_CAL); //种子秘钥(字节)告诉主设备,如果加密了,则下一步从设备通过CC_UNLOCK指令发送这个秘钥来解锁
case PL_PGM:
ccp.Crm[3] = (0==(ccp.ProtectionStatus&PL_PGM)); //编程的加密状态告诉主设备
*(CCP_DWORD*)&ccp.Crm[4] = ccpGetSeed(PL_PGM);
case PL_DAQ: //主设备想知道从设备的数据采集的加密状态与种子秘钥
ccp.Crm[3] = (0==(ccp.ProtectionStatus&PL_DAQ)); //数据采集的加密状态告诉主设备
*(CCP_DWORD*)&ccp.Crm[4] = ccpGetSeed(PL_DAQ);
--------------------------------------
case CC_UNLOCK: //解锁(该指令要与CC_GET_SEED一起完成解密动作)
#define key com[2] //key就是CC_GET_SEED指令获取到的种子秘钥
ccp.ProtectionStatus |= ccpUnlock(&com[2]); //通过解密算法进行密码核对,如果成功则置ProtectionStatus标志,表示已解密,ccpUnlock与ccpGetSeed对应
ccp.Crm[3] = ccp.ProtectionStatus; //把密码匹配状态传回给主设备
--------------------------------------
case CC_SET_MTA: //设置MTA,设置要传输的数据的在从设备数据存储器的地址
#define mta com[2] //mta编号【0/1】:MTA0用于DNLOAD,UPLOAD,DNLOAD_6,SELECT_CAL_PAGE,CLEAR_MEMORY,PROGRAM,PROGRAM_6
#define addrExt com[3]
#define addr (*(CCP_DWORD*)&com[4])
ccpSetMTA(mta,ccpGetPointer(addrExt,addr));
ccp.MTA[mta] = ccpGetPointer(addrExt,addr);
--------------------------------------
case CC_DNLOAD: //下载数据(主控器写数据),给CC_SET_MTA命令设置好的MTA地址存放数据
#define size com[2] //要传输的数据长度【0-5】
if (!(ccp.ProtectionStatus&PL_CAL)) //如果是标定加密状态(ccp.ProtectionStatus==0)则不能下载数据
ccp.Crm[1] = CRC_ACCESS_DENIED; //告诉主设备拒绝访问
else
r = ccpWriteMTA(0,size,&com[3]); //数据写入存储器(EEPROM/RAM),com[3]为要下载的数据起始地址
//下面作为EEPROM地址操作(下面函数判断ccp.MTA[0]是不是EEPROM地址)
r = ccpCheckWriteEEPROM(ccp.MTA[0],size,&com[3]); //函数判断ccp.MTA[0]地址属性,如果是EEPROM地址,数据写入EEPROM,否者返回0作为RAM地址
if (r) ccp.MTA[n] += size ; return r; //如果这个地址是EEPROM地址区域,而且写成功了,则MTA地址要累加写入数据的长度,以便继续写入
//下面作为RAM地址操作
ccpCheckWriteAccess(ccp.MTA[n],size) //如果这个地址是RAM地址区域,那么防止ccp结构体被写入覆盖了,要先用这个函数过滤不允许覆盖ccp结构体数据段
while (size-->0) { //数据赋值给RAM地址区域
*(ccp.MTA[n]) = *d;
ccp.MTA[n]++;
d++;
}
--------------------------------------
case CC_DNLOAD6: //下载固定大小为6字节的数据,功能同CC_DNLOAD
r = ccpWriteMTA(0,6,&com[2]); //下载固定长度为6的数据,因为CC_DNLOAD最多一次只能下载5个字节数据,因为size占了一个字节,而这个没有size,所以可以多传一个字节
//其他同CC_DNLOAD
--------------------------------------
case CC_UPLOAD: //上传数据(主控器读取数据),读取CC_SET_MTA命令设置好的MTA地址的数据
#define size com[2]
ccpReadMTA(0,size,&ccp.Crm[3]); //ccpWriteMTA函数的反向操作,把ccp.MTA[0]地址的值读到ccp.Crm[3]发送给主控器
--------------------------------------
case CC_SHORT_UPLOAD:
//上传指定地址的数据,这个地址存放在ccp.MTA[1]中
#define size com[2]
#define addrExt com[3]
ccpSetMTA(CCP_INTERNAL_MTA,ccpGetPointer(addrExt,com[4])); //CCP_INTERNAL_MTA==1,将要读取的地址com[4]存放在ccp.MTA[1]中
ccpReadMTA(CCP_INTERNAL_MTA,size,&ccp.Crm[3]); //与CC_UPLOAD功能一样
--------------------------------------
case CC_GET_DAQ_SIZE: //获取DAQ表的大小(即一个DAQ有多少张ODT表)
#define daqList com[2] //DAQ表号
#define daqId (*(CCP_DWORD*)&com[4]) //DAQ消息的CAN ID标识符
ccp.Crm[3] = ccpClearDaqList(daqList); //最大ODT数量CCP_MAX_ODT也就是DAQ表的大小,同时清除DAQ表
ccp.Crm[4] = daqList*CCP_MAX_ODT; //计算第一个PID的包号,根据daqList来确定
--------------------------------------
case CC_SET_DAQ_PTR:
//设置对应DTO的指针
#define comDaq com[2] //DAQ表号
#define comOdt com[3]
//ODT表号(在DAQ表内)
#define comIdx com[4]
//DTO编号(在ODT表内)
ccp.DaqListPtr = &ccp.DaqList[comDaq].odt[comOdt][comIdx]; //根据数组表索引获取这个地址
--------------------------------------
case CC_WRITE_DAQ:
//填充DTO内数据的地址与数据大小(与CC_SET_DAQ_PTR配合使用)
#define writeDaqSize com[2] //存放DTO内数据的大小(只能是1,2,4,分别对应char short int/long 三种类型)
#define writeDaqAddrExt com[3]
#define writeDaqAddr (*(CCP_DWORD*)&com[4]) //DTO内数据的地址
ccp.DaqListPtr->ptr = ccpGetDaqPointer(writeDaqAddrExt,writeDaqAddr); // ccp.DaqListPtr->ptr =writeDaqAddr;
ccp.DaqListPtr->siz = writeDaqSize;
--------------------------------------
case CC_START_STOP:
// DAQ数据传输的启停指令
#define ssCmd com[2] //0:停止 1:开始 2:准备
#define ssDaq com[3] //DAQ表号
#define ssLast com[4] //每个DAQ的最后一个ODT的编号
#define ssEventChannel com[5] /*事件触发通道号*/
#define ssPrescaler (*(CCP_WORD*)&com[6]) /* 事件触发定时器 */
if (!(ccp.ProtectionStatus&PL_DAQ)) //如果DAQ是加密的
ccp.Crm[1] = CRC_ACCESS_DENIED; //则提示拒绝访问
switch (ssCmd)
case 0: //停止传输
ccpStopDaq(ssDaq);
ccp.DaqList[daq].flags = 0; //置DAQ状态为0
ccp.SessionStatus &= ~SS_RUN; //会话状态运行标识清除
case 1:
//开始传输
ccpPrepareDaq(ssDaq,ssLast,ssEventChannel,ssPrescaler); //一些参数,存到ccp结构体
ccp.DaqList[daq].eventChannel = eventChannel; //传输事件触发的通道号
if (prescaler==0) prescaler = 1;
ccp.DaqList[daq].prescaler = prescaler; //每次传输的延时周期
ccp.DaqList[daq].cycle = 1;
//cycle是一个定时周期减一,为0时prescaler赋值给cycle,再才开始传输,相当于延时prescaler个定时周期再开始传输
ccp.DaqList[daq].last = last; //每个DAQ的最后一个ODT的编号
ccp.DaqList[daq].flags = DAQ_FLAG_PREPARED;
ccpStartDaq(ssDaq);
ccp.DaqList[daq].flags = DAQ_FLAG_START; //置DAQ状态为DAQ_FLAG_START
ccp.SessionStatus |= SS_RUN; //会话状态运行标识置位
case 2: //准备传输
ccpPrepareDaq(ssDaq,ssLast,ssEventChannel,ssPrescaler);
--------------------------------------
case CC_START_STOP_ALL:
//功能和CC_START_STOP相同,只是这里不是指定的DAQ来的,所有DAQ列表都传输
--------------------------------------
case CC_BUILD_CHKSUM: //对flash存储区的和校验
*(CCP_DWORD*)&ccp.Crm[4] = 0xFFFFFFFF;
ccp.MTA[CCP_INTERNAL_MTA] = ccp.MTA[0];
s = (*(CCP_WORD*)&com[4]) | ((*(CCP_WORD*)&com[2])<<16); //存储区域大小
ccp.CheckSumSize = s;
return; //这里直接返回函数,不计算校验和,校验和的计算将由ccpBackground()执行再发送给主控器
--------------------------------------
case CC_CLEAR_MEMORY: //格式化存储区
if (!(ccp.ProtectionStatus&PL_PGM)) //如果PGM加密了
ccp.Crm[1] = CRC_ACCESS_DENIED;
//提示拒绝访问
s = (*(CCP_WORD*)&com[4]) | ((*(CCP_WORD*)&com[2])<<16); //存储区域大小
ccpFlashClear(ccp.MTA[0],s); //格式化flash,起始地址ccp.MTA[0](事先会用CC_SET_MTA指令设置好起始地址)
--------------------------------------
case CC_PROGRAM: //flash编程
#define size com[2] //当前帧传输的数据的大小【0-5】
if (!(ccp.ProtectionStatus&PL_PGM)) //如果PGM加密了
ccp.Crm[1] = CRC_ACCESS_DENIED;
//提示拒绝访问
r = ccpFlashProgramm(&com[3],ccp.MTA[0],size); //&com[3]:待写入数据起始 ccp.MTA[0]:数据写入该地址
ccp.MTA[0] += size; //编程地址往后移
--------------------------------------
case CC_PROGRAM6: //功能和CC_PROGRAM一样,该指令以固定大小6字节编程
r = ccpFlashProgramm(&com[2],ccp.MTA[0],6);
ccp.MTA[0] += 6;
--------------------------------------
case CC_SET_CAL_PAGE: //设置标定页(标定页就是在RAM里面的一段内存,用来做标定用的,这样不需要频繁读写ROM)
ccpSetCalPage((CCP_DWORD)ccp.MTA[0]); //把之前通过CC_SET_MTA指令传输的ccp.MTA[0]地址所在页设置为标定页
ccpCalPage = (CCP_BYTE)ccp.MTA[0];
--------------------------------------
case CC_GET_CAL_PAGE: //获取标定页
*(CCP_DWORD*)&ccp.Crm[4] = ccpGetCalPage(); //主控制器获取标定页
*(CCP_DWORD*)&ccp.Crm[4] = ccpCalPage;
--------------------------------------
case CC_GET_CCP_VERSION:
ccp.Crm[3] = CCP_VERSION_MAJOR;
ccp.Crm[4] = CCP_VERSION_MINOR;
----------------------------------------------------------------------------------------
ccpDaq函数:用于DAQ-DTO连续的数据传输
ccpDaq
for (o=0,q=0; q
for (j=0;j<=ccp.DaqList[q].last;j++) //ccp.DaqList[q].last为每个DAQ的最后一个ODT的编号
ccpSampleAndTransmitDtm(o+j,q,j); //pid=o+j,daq=q,odt=j
dtm[0] = pid; //odt[odt][0]填pid
e = &ccp.DaqList[daq].odt[odt][0];
for (i=1;i<8;i++) //填好一条DTO
p = (e++)->ptr; dtm[i] = *p;
ccpQueueWrite((ccpMsg_t*)dtm)
ccp.Queue.msg[(ccp.Queue.rp+ccp.Queue.len)%CCP_SEND_QUEUE_SIZE] = *msg; //该消息在ccpSendCallBack里面发送出去