【经验分享】飞思卡尔单片机在CAN总线上模块化应用代码...

2020-02-11 09:56发布

本帖最后由 FSL_TICS_ZJJ 于 2014-5-5 14:59 编辑

CAN是Controller Area Network的缩写,即“局域网控制器”的意思,可以归属于工业现场总线的范畴,通常称为CAN BUS,即CAN总线,是目前国际上应用最为广泛的开放式现场总线之一。CAN总线规范从CAN1.2规范发展为兼容CAN1.2规范的CAN2.0规范(CAN 2.0A为标准格式,CAN2.0B为扩展格式),目前应用的CAN器件大多符合CAN2.0规范。

CAN模块它的速率有:83.33K,125K,250K,500K等常用速率.其中83.33K速率是针对单线CAN驱动IC的(不感兴趣).其它的都是针对双线CAN驱动IC的 .CAN总线是没有主从之分的,它所有CAN器件挂于一点.要接终端电阻.这种网络结构类似星形结构 . CAN的驱动IC有普通(1040,1041)和容错(1053,1054)之分,它们的区分主要在于数字1,0的电压表示方式不一样, 所以一个总线上不能同时存在这两种驱动IC。举例 : CAN总线收发器(TJA1040) .

CAN总线协议属于多主控制 , 不分主从设备 . 所以任何一个CAN模块都可以发出数据,那么哪个发送的数据最终能发出去呢,这就涉及到总线的仲裁。同时这个数据是发给哪个器件的呢,这就涉及到数据报文的区分,它是通过设备ID来区别的.CAN 以两种形式存在,一种是具有11位ID标识符的BasicCAN,另一种是带有扩展成29位ID标识符的高级形式PeliCAN。Philips、Intel、飞思卡尔均支持BasicCAN 和PeliCAN。同时,PeliCAN 协议允许两段长度的标识符:A部分使用11位报文标识符,能够识别出2032个不同的标识符(保留十六位),此部分兼容BasicCAN;而PeliCAN (B部分)有29位,能够产生536870912个不同的标识符。

飞思卡尔MC9S08DZ96在CAN总线上模块化应用代码的编写如下:
//CAN_Device_IC  CAN总线收发器是 TJA1040  速度配置
#define CAN_Boude_125k  0x49c3
#define CAN_Boude_250k  0x49c1
#define CAN_Boude_500k  0x23c1

//CAN报文格式,标准11位ID和扩展29位ID
typedef enum {
  CAN_OUT_STD_MSG,   //标准      
  CAN_OUT_XTD_MSG    //扩展
} CAN_OUT_MODE;

//CAN模块的运行方式
enum  CAN_Operation_Mode
{
  CAN_Mode_LoopB,    //自环测试模式
  CAN_Mode_Listen,   //监听模式
  CAN_Mode_Operation, //正常运行模式
};
//存在CAN报文的数据结构
typedef struct CANMsgBase{
  word CycleTime;
  word TimeCount;
  byte cCANID[4];
  byte cDataLength;
  byte cData[8];
} CANMsg;

CANMsg CANMsgTx[10];
CANMsg CANMsgRx[10];

//初始化CAN模块
void MSCAN1_Init(word Boude,enum CAN_Operation_Mode type)
{
INT16U wTemp;

TRANSCEIVER_STB_DIR = 1;
cMSCANTxBufCANIDIndex = 0xff;

CAN_TX_PIN_DIR = 1;      //将TX设为输出口
TRANSCEIVER_STB_PIN = 1; //先复位1040IC,此处是对相应的CAN驱动IC来设置的

CANCTL1 = 0x80;          //打开CAN模块
wTemp = 0x1000;
while(wTemp){
  wTemp --;  
}
  
TRANSCEIVER_STB_PIN = 0;  //使能1040IC
wTemp = 0x1000;
   
CANCTL0 |=CANCTL0_INITRQ_MASK;  //请求配置模式         
while(!CANCTL1_INITAK){
  wTemp --;
  if(wTemp == 0x00) {
   break;
  }   
}

if(wTemp == 0){
//配置CAN模块失败
  cCANDrvInitFailed = TRUE;
  TRANSCEIVER_STB_PIN = 0;
} else {
//成功进入配置
  CANBTR1 = (byte)(Boude>>8);                              
  CANBTR0 =(byte)Boude;  

//以下为对接收到的报文进行过滤的ID寄存器设置,即不符合设置的报文不
//进入中断。此处设置为接收所有的报文                              
  CANIDAC = 0x00;  //two 32-bit acceptance filters
//below is the set of acceptance filter  and its mask register,recieve all message
  CANIDAR0 = 0xff;//0xff;
  CANIDAR1 = 0xff;//0xff;
  CANIDAR2 = 0xff;
  CANIDAR3 = 0xff;
  CANIDAR4 = 0xff;//0xff;
  CANIDAR5 = 0xff;//0xf7;
  CANIDAR6 = 0xff;
  CANIDAR7 = 0xff;
  CANIDMR0 = 0xff;//0xff;
  CANIDMR1 = 0xff;//0xff;
  CANIDMR2 = 0xff;
  CANIDMR3 = 0xff;
  CANIDMR4 = 0xff;//0xff;
  CANIDMR5 = 0xff;//0xff;
  CANIDMR6 = 0xff;
  CANIDMR7 = 0xff;

  cCANDrvInitFailed = FALSE;  
  wTemp = 0x1000;
  switch(type) {
  //选择工作模式
      case  CAN_Mode_LoopB:CANCTL1 |=CANCTL1_LOOPB_MASK;break; //进入自环模式
        case  CAN_Mode_Listen:CANCTL1 |=CANCTL1_LISTEN_MASK;break;//监听模式
        case  CAN_Mode_Operation:CANCTL1&=0xcf;break;  //正常工作模式
  }
           
  CANCTL0 &=(~CANCTL0_INITRQ_MASK);  //请求退出配置模式 ,并进入正常运行模式               
  while(CANCTL1_INITAK){
   wTemp --;     
   if(wTemp == 0x00) {
    break;
   }  
  }
  if(wTemp == 0x00){
  //配置失败
   cCANDrvInitFailed = TRUE;     
  }
  CANRIER = 0x01; //接收中断
  CANTIER = 0;    //发送不允许中断           
}
}


//发送数据
byte MSCANTxMsg(CANMsg msg)
{
INT8U cDataLen;
INT8U *pData;
  byte emptyindex=0;
  byte returndata=0;
  
  if(CANTFLG_TXE) {
    CANTBSEL_TX=CANTFLG_TXE;
    emptyindex=1;
  }

if(emptyindex)
{//the buffer 0 is empty   
    pData = CANTIDR_ARR;
    if(msg.cCANID[0]&0x80) {
    //extended
      *pData = msg.cCANID[0]<<3;  //IDR0 high 5bits
      *pData|= msg.cCANID[1]>>5;  //IDR0 low  3bits  
      pData++;
    *pData = msg.cCANID[1]<<3;
    *pData &=0xe0;    //IDR1 high 5bits SRR=1;IDE=1
     *pData |=0x18;    //IDE=1;extended
     *pData |= (msg.cCANID[1]<<1)&0x06; //IDR1 bit 1,2
     *pData |= msg.cCANID[2]>>7;  //IDR1 bit0
    pData++;
    *pData |= msg.cCANID[2]<<1;  //IDR2 high 7bits
     *pData |= msg.cCANID[3]>>7;  //IDR2 low  1bits
    pData ++;
    *pData |= msg.cCANID[3]<<1;  //IDR3 high 7bits
      *pData &=0xfe;  //RTR=0
    } else{
    //standard
      *pData = (msg.cCANID[2]<<5);  //IDR0 high 3bits
      *pData|= (msg.cCANID[3]>>3);  //IDR0 low  5bits  
      pData++;
    *pData = (msg.cCANID[3]<<5);  //IDR1 high 3bits
     *pData &=0xe0;  //RTR=0;IDE=0;
    pData++;
    *pData = 0x00;
    pData ++;
    *pData = 0x00;  
    }  
   
   pData = CANTDSR_ARR;
   for(cDataLen = 0; cDataLen < 8; cDataLen ++)
   {
    *pData = msg.cData[cDataLen];
    pData++;
   }
   CANTDLR_DLC =0x08;
      CANTFLG_TXE= CANTBSEL_TX;
   CANTIER_TXEIE=CANTBSEL_TX; //发送空中断
   returndata=1;      //成功写入则返回1
}

return  returndata;
}


//只接收数据,不处理数据,中断方式
__interrupt void MSCANRxFullInt(void)            //0xFFC6 0xFFC7      
{
INT8U      cDataLength;
INT8U     *pData;

if(CANRDLR){
   pData = CANRIDR_ARR;
   for(cDataLength = 0; cDataLength < 4; cDataLength ++){
     CANMsgRx[CANRxIndex].cCANID[cDataLength]=0;//先清0
     CANMsgRx[CANRxIndex].cCANID[cDataLength] = *pData;
    pData++;
   }
   
   if(CANMsgRx[CANRxIndex].cCANID[1]&0x08) {
   //extended  扩展格式
     CANMsgRx[CANRxIndex].cCANID[3]>>=1;   //去掉RTR
     CANMsgRx[CANRxIndex].cCANID[3]|=(CANMsgRx[CANRxIndex].cCANID[2]&0x01)<<7;
     CANMsgRx[CANRxIndex].cCANID[2]>>=1;
     CANMsgRx[CANRxIndex].cCANID[2]|=(CANMsgRx[CANRxIndex].cCANID[1]&0x01)<<7;
     CANMsgRx[CANRxIndex].cCANID[1]>>=1;
     CANMsgRx[CANRxIndex].cCANID[1]|=(CANMsgRx[CANRxIndex].cCANID[1]&0x70)>>2; //去掉SRR,IDE
     CANMsgRx[CANRxIndex].cCANID[1]|=CANMsgRx[CANRxIndex].cCANID[0]<<=5;
     CANMsgRx[CANRxIndex].cCANID[0]>>=3;
   
   }else{
   //standard  标准格式
     CANMsgRx[CANRxIndex].cCANID[3]=CANMsgRx[CANRxIndex].cCANID[1]>>5;
      CANMsgRx[CANRxIndex].cCANID[3]|=CANMsgRx[CANRxIndex].cCANID[0]<<3;
      CANMsgRx[CANRxIndex].cCANID[2]= CANMsgRx[CANRxIndex].cCANID[0]>>5;
      CANMsgRx[CANRxIndex].cCANID[0]=0;
      CANMsgRx[CANRxIndex].cCANID[1]=0;
   }
   CANMsgRx[CANRxIndex].cDataLength = CANRDLR;
   
   pData = CANRDSR_ARR;
   for(cDataLength = 0; cDataLength < 8; cDataLength ++){
     CANMsgRx[CANRxIndex].cData[cDataLength] =0;//先清0
     CANMsgRx[CANRxIndex].cData[cDataLength] = *pData;
    pData++;
   }
  }
CANRxIndex++;
CANRFLG_RXF = 1;
}

CAN协议有五种错误检测的方法:三个是报文级的,而两个是位级的。如果一个报文出错,那么错误检测的任何一个方法使节点不接收这个报文,并产生一个出错帧,使所有的帧都忽略它,并使发送节点重新发送这个报文。在报文级检查中,有CRC检查和应答隙。CRC检查是一个15位CRC,它计算描述符场和数据字节的CRC。应答场有两位,包括一个应答位和一个应答界定符。这个发送器将会把一个隐性位放在应答场。任何一个正确接收报文的节点在应答场写一个显性位,如果发送器在应答场没有读回一个显形位,它将产生一个出错帧,并重新传送报文。最后在报文级还有一个形式检查,它检查那些总是隐性位的报文场,如果检测到显形位就会产生错误,它检查帧起始、帧结束、应答界定符以及CRC界定符位。 在位级检查中,每一个位都由发送器监控,如果一个位被写进总线但读到的是它的反,错误就会产生。只有标识符场用于仲裁和应答隙是除外的,它要求显性位覆盖隐性位 . 最后的一种错误检测方法是通过位填充规则,当一个报文没有被填充,即如果在逻辑电平相同的连续5位后下一位不是前面的反,则产生一个错误。
      活动错误帧包括六个显形位,它们违背了位填充规则。所有的CAN节点都认为它是一个错误并产生自己的错误帧,所以错误帧的长度可以在6位和12位之间,错误帧后是8位隐性位界定符场,而总线在重发被破坏的报文前是空闲的。要注意报文在被成功接收之前仍要争取仲裁。
友情提示: 此问题已得到解决,问题已经关闭,关闭后问题禁止继续编辑,回答。
该问题目前已经被作者或者管理员关闭, 无法添加新回复
7条回答
FSL_TICS_ZJJ
1楼-- · 2020-02-11 10:03
楼主这样的经验分享非常值得推荐!
楼主以后资料分享,请直接在你的标题开始处加上【经验分享】。
365026266
2楼-- · 2020-02-11 12:44
值得一看,非常不错
glenclh
3楼-- · 2020-02-11 15:44
写的很好,受教了
jiang887786
4楼-- · 2020-02-11 18:51
谢谢楼主分享,楼主辛苦了。收藏!
bbs137
5楼-- · 2020-02-11 23:14
 精彩回答 2  元偷偷看……
jiwx2011
6楼-- · 2020-02-12 03:26
谢谢分享!

一周热门 更多>