nnimo 发表于 2014-5-5 14:32:59

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

本帖最后由 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_ICCAN总线收发器是 TJA1040速度配置
#define CAN_Boude_125k0x49c3
#define CAN_Boude_250k0x49c1
#define CAN_Boude_500k0x23c1

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

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

CANMsg CANMsgTx;
CANMsg CANMsgRx;

//初始化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 filterand 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) {
//选择工作模式
      caseCAN_Mode_LoopB:CANCTL1 |=CANCTL1_LOOPB_MASK;break; //进入自环模式
      caseCAN_Mode_Listen:CANCTL1 |=CANCTL1_LISTEN_MASK;break;//监听模式
      caseCAN_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&0x80) {
    //extended
      *pData = msg.cCANID<<3;//IDR0 high 5bits
      *pData|= msg.cCANID>>5;//IDR0 low3bits
      pData++;
    *pData = msg.cCANID<<3;
    *pData &=0xe0;    //IDR1 high 5bits SRR=1;IDE=1
   *pData |=0x18;    //IDE=1;extended
   *pData |= (msg.cCANID<<1)&0x06; //IDR1 bit 1,2
   *pData |= msg.cCANID>>7;//IDR1 bit0
    pData++;
    *pData |= msg.cCANID<<1;//IDR2 high 7bits
   *pData |= msg.cCANID>>7;//IDR2 low1bits
    pData ++;
    *pData |= msg.cCANID<<1;//IDR3 high 7bits
      *pData &=0xfe;//RTR=0
    } else{
    //standard
      *pData = (msg.cCANID<<5);//IDR0 high 3bits
      *pData|= (msg.cCANID>>3);//IDR0 low5bits
      pData++;
    *pData = (msg.cCANID<<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;
    pData++;
   }
   CANTDLR_DLC =0x08;
      CANTFLG_TXE= CANTBSEL_TX;
   CANTIER_TXEIE=CANTBSEL_TX; //发送空中断
   returndata=1;      //成功写入则返回1
}

returnreturndata;
}


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

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

CAN协议有五种错误检测的方法:三个是报文级的,而两个是位级的。如果一个报文出错,那么错误检测的任何一个方法使节点不接收这个报文,并产生一个出错帧,使所有的帧都忽略它,并使发送节点重新发送这个报文。在报文级检查中,有CRC检查和应答隙。CRC检查是一个15位CRC,它计算描述符场和数据字节的CRC。应答场有两位,包括一个应答位和一个应答界定符。这个发送器将会把一个隐性位放在应答场。任何一个正确接收报文的节点在应答场写一个显性位,如果发送器在应答场没有读回一个显形位,它将产生一个出错帧,并重新传送报文。最后在报文级还有一个形式检查,它检查那些总是隐性位的报文场,如果检测到显形位就会产生错误,它检查帧起始、帧结束、应答界定符以及CRC界定符位。 在位级检查中,每一个位都由发送器监控,如果一个位被写进总线但读到的是它的反,错误就会产生。只有标识符场用于仲裁和应答隙是除外的,它要求显性位覆盖隐性位 . 最后的一种错误检测方法是通过位填充规则,当一个报文没有被填充,即如果在逻辑电平相同的连续5位后下一位不是前面的反,则产生一个错误。
      活动错误帧包括六个显形位,它们违背了位填充规则。所有的CAN节点都认为它是一个错误并产生自己的错误帧,所以错误帧的长度可以在6位和12位之间,错误帧后是8位隐性位界定符场,而总线在重发被破坏的报文前是空闲的。要注意报文在被成功接收之前仍要争取仲裁。

FSL_TICS_ZJJ 发表于 2014-5-5 14:59:34

楼主这样的经验分享非常值得推荐!
楼主以后资料分享,请直接在你的标题开始处加上【经验分享】。

365026266 发表于 2014-5-5 15:02:47

值得一看,非常不错

glenclh 发表于 2015-6-18 10:58:23

写的很好,受教了

jiang887786 发表于 2015-6-23 09:04:54

谢谢楼主分享,楼主辛苦了。收藏!

longlong105 发表于 2015-8-16 14:26:11

不错, 学习了!!

jiwx2011 发表于 2015-8-22 18:01:47

谢谢分享!

fiaanull 发表于 2016-10-21 10:18:19

谢谢楼主,感慨啊。飞思卡尔的资料太难找了。。。。
页: [1]
查看完整版本: 【经验分享】飞思卡尔单片机在CAN总线上模块化应用代码...