CCP下位机驱动程序解析

便于理解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里面发送出去

你可能感兴趣的:(CCP下位机驱动程序解析)