利用CAN中断进行简单的数据接收

CAN是控制器局域网络(Controller Area Network, CAN)的简称,是国际上应用最广泛的现场总线之一。 在北美和西欧,CAN总线协议已经成为汽车计算机控制系统和嵌入式工业控制局域网的标准总线,并且拥有以CAN为底层协议专为大型货车和重工机械车辆设计的J1939协议。
利用CAN中断进行简单的数据接收_第1张图片
本例通过用MC9S12XS128MAA来实现CAN标准帧的接收。
以下为本例所用到的寄存器介绍(CAN初始化部分用到的寄存器此篇不再赘述,见“利用CAN进行简单的数据发送”)

CANRFLG寄存器
利用CAN中断进行简单的数据接收_第2张图片
WUPIF (唤醒中断标志)= 1 时,MSCAN在CAN总线上检测到活动并请求唤醒
= 0 时,睡眠模式下未观察到唤醒活动
CSCIF(CAN状态改变中断标志) = 1 时, MSCAN更改了当前CAN总线状态
= 0 时,CAN总线状态未发生变化
RSTAT[1:0] (接收状态位)= 00 时,RxOK :0 ≤ 接收错误计数器 ≤ 96
= 01 时,RxWRN :96 < 接收错误计数器 ≤ 127
= 10 时,RxERR :127 < 接收错误计数器 ≤ 255
= 11 时,Bus-off : 传输错误计数器 > 255

TSTAT[1:0] (发送状态位) = 00 时,TxOK :0 ≤ 发送错误计数器 ≤ 96
= 01 时,TxWRN :96 < 发送错误计数器 ≤ 127
= 10 时,RxERR :127 < 发送错误计数器 ≤ 255
= 11 时,Bus-off : 传输错误计数器 > 255

OVRIF(溢出中断标志) = 1 时,数据溢出
= 0 时,没有溢出

RXF(接收缓冲区满标志) = 1 时,RxFG中有一条新消息
= 0 时,RxFG无可用新消息

CANRXIDR:用来存放CAN报文的ID、远程帧或数据帧状态、标准帧或扩展帧
利用CAN中断进行简单的数据接收_第3张图片
在这里插入图片描述
CANRXDSR:用来存放数据长度用来存放数据(最多8个字节)
在这里插入图片描述
CAN0RXDLR:用来存放数据长度
在这里插入图片描述
利用CAN中断进行简单的数据接收_第4张图片
以下为完整的CAN接收报文程序


#include            
#include "derivative.h"      
#define LED PORTB_PB0         //定义连接发光二级管的PORTB_PB0口数据寄存
                              //器为LED,写'0'亮,写'1' 灭
#define LED_dir DDRB_DDRB0    //定义连接发光二级管的PORTB_PB0口方向寄存器
                              //为LED_dir,写'0'做输入口,写'1'做输出口
                             


struct can_msg     //定义接收报文的结构体
{
    unsigned int id;    //报文的ID号
    Bool RTR;
    unsigned char data[8];  //报文的数据
    unsigned char len;      //数据长度
  
};



struct can_msg  msg_get;   //定义结构体变量



void INIT_PLL(void)     //初始化锁相环
{   
   
   CLKSEL_PLLSEL=0;	    //内部总线时钟来源于晶振
           
   PLLCTL_PLLON=0;  //关闭PLL

   SYNR=0x40 | 0x03; 	
  
   REFDV=0x80 | 0x01; 
  
   POSTDIV=0x00;     //PLL为64MHz

   PLLCTL_PLLON=1;  //打开PLL

   _asm(nop);         
   _asm(nop);      //等待两个机器周期
   while(CRGFLG_LOCK==0); //根据CRGFLG寄存器的LOCK位,确定PLL是否稳定	LOCK==1 稳定,==0 不稳定  
   CLKSEL_PLLSEL =1;     //选择PLL作为时钟源		       
}



void INIT_CAN0(void)         //初始化CAN0
{
  if(CAN0CTL0_INITRQ==0)      // 查询是否进入初始化状态   
    CAN0CTL0_INITRQ =1;        // 进入初始化状态

  while (CAN0CTL1_INITAK==0);  //初始化握手标志

  CAN0BTR0_SJW = 0;            //设置同步
  CAN0BTR0_BRP = 7;            //设置波特率  
  CAN0BTR1 = 0x1c;       //设置时段1和时段2的Tq个数 ,总线频率为250kb/s
  
  
  /*
      Bit Time = [(Prescaler value)*(1+TimeSegment1+TimeSegment2)]/fCANCLK
      f = 1 / Bit Time
      Prescaler value = 8
      TimeSegment1 = 13
      TimeSegment2 = 2
      fcanclk = 32MHz
      f = 250kb/s
  */

  CAN0IDMR0 = 0xFF;
  CAN0IDMR1 = 0xFF;
  CAN0IDMR2 = 0xFF;
  CAN0IDMR3 = 0xFF;
  CAN0IDMR4 = 0xFF;
  CAN0IDMR5 = 0xFF;
  CAN0IDMR6 = 0xFF;
  CAN0IDMR7 = 0xFF;     // 关闭滤波器                                  

  CAN0CTL1 = 0xC0;             //使能MSCAN模块,设置为一般运行模式、使用总线时钟源 

  CAN0CTL0 = 0x00;             //返回一般模式运行

  while(CAN0CTL1_INITAK);      //等待回到一般运行模式

  while(CAN0CTL0_SYNCH==0);    //等待总线时钟同步

  CAN0RIER_RXFIE = 1;          //打开接收中断
}



Bool MSCAN0GetMsg(struct can_msg *msg)     //CAN0接收函数
{
  unsigned char sp2;    

  
  if(!(CAN0RFLG_RXF))    // 检测接收标志    0没有新消息,1有新消息
    return(FALSE);
  
  
  if(CAN0RXIDR1_IDE)   // 检测 CAN协议报文模式 (标准帧/扩展帧) 0标准帧  1扩展帧
    return(FALSE);             
  
  msg->id = (unsigned int)(CAN0RXIDR0<<3) | 
            (unsigned char)(CAN0RXIDR1>>5);   //将ID通过移位放入相应的寄存器位置
  
  if(CAN0RXIDR1&0x10)
    msg->RTR = TRUE;
  else
    msg->RTR = FALSE;   // 读标识符    RTR0数据帧  1远程帧
  
  
  msg->len = CAN0RXDLR;   // 读取数据长度 
  
 
  for(sp2 = 0; sp2 < msg->len; sp2++)
    msg->data[sp2] = *((&CAN0RXDSR0)+sp2);    // 读取数据

  
  CAN0RFLG = 0x01;       // 清 RXF 标志位 (缓冲器准备接收)

  return(TRUE);
}



#pragma CODE_SEG __NEAR_SEG NON_BANKED          //中断接收函数
void interrupt VectorNumber_Vcan0rx CAN_receive(void)   //通过中断向量VectorNumber_Vcan0rx来定义中断接收函数
{
      if(MSCAN0GetMsg(&msg_get))    //判断是否有合法的标准帧收到
      {
        
                 LED = ~LED;     //接收到合法标准帧LED反相
                        
        
      }
      else             //错误进入死循环
      {
       for(;;);
      }
}



void main(void) {
  DisableInterrupts;           //禁止打开所有中断
  INIT_PLL();                  //初始化PLL模块,设置busclock=32Mhz
  INIT_CAN0();                 //初始化can0模块
  LED_dir=1;                   //LED接口PB0设置为输出口 
  LED=0;                       //初始化LED初始状态为亮
  EnableInterrupts;            //允许打开所有中断  


  for(;;) 
  {
  } 
}

注意:1.本例CAN初始化中 CAN0RIER_RXFIE 一定要赋值为1,打开接收中断,否则将无法接收到数据
2.中断向量可以通过查看Project -> Includes -> MC9S12XS128.h文件寻找
3.由于本单片机只有一路CAN,所以无法实现自发自收,因而选择通过CANTest进行发送,通过指示灯闪烁来判断是否收到数据

接线实物图见下方:

通过CANTest发送数据,数据内容可以通过下图粉色椭圆框起来的部分填写,点击发送就可以将所填写的信息发送出来了,此时开发板的指示灯将会由亮变暗或者由暗变亮
利用CAN中断进行简单的数据接收_第5张图片

你可能感兴趣的:(单片机)