原创 PIC24HJ单片机自学笔记-ECAN远程帧通信程序解读

2014-1-17 06:44 3217 17 17 分类: MCU/ 嵌入式 文集: PIC24HJ单片机自学笔记

 

主函数操作流程:
  • 时钟设置
  • 清中断标志
  • ECAN1初始化,DMA0,2初始化,使能中断
  • ECAN2初始化,DMA1初始化 ,使能中断
  • ECAN2写信息,ECAN1写信息,发送请求
  • 主循环为空,等待中断
 
ECAN1初始化
void ecan1Init(void){
 C1CTRL1bits.REQOP=4;
 while(C1CTRL1bits.OPMODE!=4); //进入配置模式
 ecan1ClkInit(); //时钟初始化,配置TQ
 C1FCTRLbits.FSA=0b10000;//设置FIFO从buffer16开始
 C1FCTRLbits.DMABS=0b000;//DMA中开辟32个BUFFER
 
//FILTER设置
  //下面FILTER的设置参数含义为:设置FILTER1,ID为0X1FFFFFFF,拓展模式,指向缓存1,MASK选择0
ecan1WriteRxAcptFilter(1,0x1FFEFFFF,1,1,0);
//MASK设置
//下面MASK的设置参数含义为:设置MASK1,所有的位都要比较,模式看后面,模式为拓展模式
 
 ecan1WriteRxAcptMask(1,0x1FFFFFFF,1,1);
 
//设置完FILTER和MASK,回到常规模式
 C1CTRL1bits.REQOP=0;
 while(C1CTRL1bits.OPMODE!=0);
 
//定义BUFFER0和BUFFER1的方向,以及优先级
 
 C1RXFUL1=C1RXFUL2=C1RXOVF1=C1RXOVF2=0x0000;
 C1TR01CONbits.TXEN0=1; /* ECAN1, Buffer 0 is a Transmit Buffer */
 C1TR01CONbits.TXEN1=0; /* ECAN1, Buffer 1 is a Receive Buffer */
 C1TR01CONbits.TX0PRI=0b11; /* Message Buffer 0 Priority Level */
 C1TR01CONbits.TX1PRI=0b11; /* Message Buffer 1 Priority Level */
}
 
ECAN2初始化
void ecan2Init(void){
 C2CTRL1bits.REQOP=4; //进入配置模式
 while(C2CTRL1bits.OPMODE!=4);
 ecan2ClkInit(); //配置时钟TQ
 C2FCTRLbits.FSA=0b10000;//FIFO首地址
 C2FCTRLbits.DMABS=0b000; //缓存数量
 //下面FILTER的设置参数含义为:设置FILTER1,ID为0X1FFFFFFF,拓展模式,指向缓存0,MASK选择0
 ecan2WriteRxAcptFilter(1,0x1FFEFFFF,1,0,0);
//下面MASK的设置参数含义为:设置MASK1,所有的位都要比较,模式看后面,模式为拓展模式
 ecan2WriteRxAcptMask(1,0x1FFFFFFF,1,1);
 C2CTRL1bits.REQOP=0;
 while(C2CTRL1bits.OPMODE!=0);//进入常规模式
 C2RXFUL1=C2RXFUL2=C2RXOVF1=C2RXOVF2=0x0000;
 C2TR01CONbits.TXEN0=1; //ECAN2发送
 C2TR01CONbits.TX0PRI=0b11; //优先级设置
}

DMA初始化
void dma0init(void){
  DMACS0=0; //冲突检测清零
     DMA0CON=0x2020; //方向为发送,外设直接地址
  DMA0PAD=0x0442; /发送地址
   DMA0CNT=0x0007; //发送计数
  DMA0REQ=0x0046; //写入请求好
  DMA0STA= __builtin_dmaoffset(ecan1msgBuf); //地址指向
  DMA0CONbits.CHEN=1; //使能通道0
}
 
void dma1init(void){
 
 DMA1CON=0x2020; //方向为发送,外设直接地址
 DMA1PAD=(int)&C2TXD; //C2TXD为发送数据寄存器
 DMA1CNT=0x0007; //发送计数
 DMA1REQ=0x0047; //请求号
 DMA1STA= __builtin_dmaoffset(ecan2msgBuf); //DPRAM偏置,地址指向
 DMA1CONbits.CHEN=1; //使能CH1
   
}
void dma2init(void){
 
  DMACS0=0; //冲突检测清零
     DMA2CON=0x0020; //接收,外设直接地址
  DMA2PAD=0x0440; //接收地址
   DMA2CNT=0x0007; //接收计数
  DMA2REQ=0x0022; //请求号
  DMA2STA= __builtin_dmaoffset(ecan1msgBuf); //地址指向
  DMA2CONbits.CHEN=1; //使能通道2
}
 
void dma3init(void){
  DMACS0=0;
     DMA3CON=0x0020;
  DMA3PAD=(int)&C2RXD; /* ECAN 2 (C2RXD) */
   DMA3CNT=0x0007;
  DMA3REQ=0x0037; /* ECAN 2 Receive */
  DMA3STA=__builtin_dmaoffset(ecan2msgBuf);
  DMA3CONbits.CHEN=1;
}
 
ECAN1写消息
void ecan1WriteMessage(void){
 ecan1WriteTxMsgBufId(0,0x1FFEFFFF,1,1);
 ecan1WriteTxMsgBufData(0,8,0,0,0,0);
}
//第一个函数:第一个参数,BUFFER号,第二个参数ID号,第三个,拓展ID标志,第四个,是否是远程帧
//上面的调用,写到缓存0,ID号是0X1FFFFFFF,是拓展帧,是远程帧
void ecan1WriteTxMsgBufId(unsigned int buf, long txIdentifier, unsigned int ide, unsigned int remoteTransmit){
unsigned long word0=0, word1=0, word2=0;
unsigned long sid10_0=0, eid5_0=0, eid17_6=0,a;
 
 eid5_0 = (txIdentifier & 0x3F); //取得ID号
 eid17_6 = (txIdentifier>>6) & 0xFFF;
 sid10_0 = (txIdentifier>>18) & 0x7FF;
  word1 = eid17_6;
 
 if(remoteTransmit==1) { // 如果是远程帧,将远程帧的标志写入到WORD0和WORD2中
  word0 = ((sid10_0 << 2) | ide | 0x2);
  word2 = ((eid5_0 << 10)| 0x0200);}
 else {
  word0 = ((sid10_0 << 2) | ide);
  word2 = (eid5_0 << 10);
      }
 ecan1msgBuf[buf][0] = word0;//将合成的WORD写入到BUFFER中
 ecan1msgBuf[buf][1] = word1;
 ecan1msgBuf[buf][2] = word2;
}
如果是数据帧,则继续写入数据,如果是远程帧,也可以写入一些无效数据,比如:
ecan1WriteTxMsgBufId(0,0x1FFEFFFF,1,1);
 ecan1WriteTxMsgBufData(0,8,0,0,0,0);:
 
void ecan1WriteTxMsgBufData(unsigned int buf, unsigned int dataLength, unsigned int data1, unsigned int data2, unsigned int data3, unsigned int data4){
 ecan1msgBuf[buf][2] = ((ecan1msgBuf[buf][2] & 0xFFF0) + dataLength) ;
 ecan1msgBuf[buf][3] = data1;
 ecan1msgBuf[buf][4] = data2;
 ecan1msgBuf[buf][5] = data3;
 ecan1msgBuf[buf][6] = data4;
}
 
中断函数的处理
 
上面的程序中,中断有6个,两个为ECAN1和ECAN2的中断,另外四个为DMA0~DMA3的四个中断
ECAN1和ECAN2的中断处理是一样的,具体接收子程序不同。
void __attribute__((interrupt, no_auto_psv))_C1Interrupt(void)
{
 IFS2bits.C1IF = 0; //清零ECAN1中断标志
 if(C1INTFbits.TBIF)
    {
     C1INTFbits.TBIF = 0; //发送中断?清空就行
    }
    if(C1INTFbits.RBIF)
    {
     if(C1RXFUL1bits.RXFUL1==1) //接收?则需要接收程序
     {
      rx_ecan1message.buffer=1; //设置一个缓存中有数据的标志
      C1RXFUL1bits.RXFUL1=0; //清零接收中断标志
     }
     rxECAN1(&rx_ecan1message); //缓存有数据,则开始接收,然后清零标志
  C1INTFbits.RBIF = 0;
 }
}
 
ECAN1的接收程序:
void rxECAN1(mID *message)
{
 unsigned int ide=0;
 unsigned int srr=0;
 unsigned long id=0,d;
 ide=ecan1msgBuf[message->buffer][0] & 0x0001; //将收到的帧的IDE和SRR读出来
 srr=ecan1msgBuf[message->buffer][0] & 0x0002; //
 if(ide==0)
 {
  message->id=(ecan1msgBuf[message->buffer][0] & 0x1FFC) >> 2; //如果是标准帧,则将ID读出来,并且标记为标准帧
  message->frame_type=CAN_FRAME_STD;
 }
 else //如果是扩展帧,将ID也读出来,放到BUFFER 0/1/2中
 {
  id=ecan1msgBuf[message->buffer][0] & 0x1FFC;
  message->id=id << 16;
  id=ecan1msgBuf[message->buffer][1] & 0x0FFF;
  message->id=message->id+(id << 6);
  id=(ecan1msgBuf[message->buffer][2] & 0xFC00) >> 10;
  message->id=message->id+id;
  message->frame_type=CAN_FRAME_EXT;
 }
//判断是不是远程帧,如果是远程帧,则将远程帧标志置位
 if(srr==1)
 {
  message->message_type=CAN_MSG_RTR;
 }
//如果为常规数据帧,则先标志帧类型为数据帧,然后将数据读入DMA的缓存,并且将数据长度也读入到BUFFER2中。
 else
 {
  message->message_type=CAN_MSG_DATA;
  message->data[0]=(unsigned char)ecan1msgBuf[message->buffer][3];
  message->data[1]=(unsigned char)((ecan1msgBuf[message->buffer][3] & 0xFF00) >> 8);
  message->data[2]=(unsigned char)ecan1msgBuf[message->buffer][4];
  message->data[3]=(unsigned char)((ecan1msgBuf[message->buffer][4] & 0xFF00) >> 8);
  message->data[4]=(unsigned char)ecan1msgBuf[message->buffer][5];
  message->data[5]=(unsigned char)((ecan1msgBuf[message->buffer][5] & 0xFF00) >> 8);
  message->data[6]=(unsigned char)ecan1msgBuf[message->buffer][6];
  message->data[7]=(unsigned char)((ecan1msgBuf[message->buffer][6] & 0xFF00) >> 8);
  message->data_length=(unsigned char)(ecan1msgBuf[message->buffer][2] & 0x000F);
 }
}
 
四个DMA的中断程序都很简单,就是单纯的清标志。
 
/***************************************************/

2013年是移动互联网的元年,移动互联网大大提高了我们工作和生活的效率。 
罗辑思维用互联网思维网络了大量的会员,最近又在召集用互联网思维来吃霸王餐。 
我们不需要那么牛气哄哄,互联网思维既然能够解决这样的大事,那小事当然更没问题。 

利用互联网思维做一个晨型人,也应该不是一件难事吧。 
操作模式如下:申请一个微信公众号,分享作为晨型人一天的收获。 
以及在遇到困难的时候,如果克服自己,解决问题。 
如果有兴趣,扫一扫下面的公众号 吧,让我们一起轻松的做一个快乐的晨型人。 

/***************************************************/
 
 
 
 
 
 
 
 
 
 
 
 
PARTNER CONTENT

文章评论0条评论)

登录后参与讨论
EE直播间
更多
我要评论
0
17
关闭 站长推荐上一条 /3 下一条