can收发程序分析

来源:互联网 发布:比特彗星端口开放 编辑:程序博客网 时间:2024/05/01 09:07

  对于发送程序,首先需要按照can协议,根据报文的格式,将can控制器的发送缓冲区填充完毕。然后再启动发送命令,具体的发送程序如下所示:

/*
***********************************************************************************************************
**函数原型  :   UINT32 CANSendData(eCANNUM CanNum,UINT32 Cmd,P_stcTxBUF Buf)
**参数说明  :   CanNum -->> CAN控制器,值不能大于CAN_MAX_NUM 规定的值
     Cmd --> 发送命令字
     Buf --> 要发送的数据
**返回值  : 无 
**说 明  : 本函数用于将数据发送到CAN总线
************************************************************************************************************/

UINT32  CANSendData(eCANNUM CanNum,UINT32 Cmd,P_stcTxBUF Buf)
{
 UINT32 i,status=0;
 if(0 != CANSR(CanNum).Bits.TBS1_BIT)//检测第一发送缓冲区是否可以写入数据,是否发送完毕,只有发送完毕
 {
  i=SEND_TX_BUF1;
 }
 else if(0 != CANSR(CanNum).Bits.TBS2_BIT)//检测第二发送缓冲区是否可以写入数据,是否发送完毕,只有发送完毕
 {
  i=SEND_TX_BUF2;
 }
 else if(0 != CANSR(CanNum).Bits.TBS3_BIT)//检测第三发送缓冲区是否可以写入数据,是否发送完毕,只有发送完毕
 {
  i=SEND_TX_BUF3;
 }
 else
 {
  i=0xFF;
 }
 status=WriteCanTxBuf(CanNum,i,  USE_TPM_CAN[CanNum],  Buf);//写入发送缓冲区,对于WriteCanTxBuf函数会在后面有详细分析的。
 if(status == 0)//‘0’表示前面的写入缓冲区成功。
 {
  #if 1
  if(CANMOD(CanNum).Bits.SM_BIT != 0)//这里检测canmod寄存器的SM位,来检测can控制器是否进入休眠了。   
  {
   CanQuitSM(CanNum);//函数用于使can控制器退出休眠的,对于CanQuitSM函数的分析请看下面。
  }
  #endif
  CanSendCmd(CanNum,Cmd,i);//启动发送命令。
 }
 return (status);
}

/*
***********************************************************************************************************
**函数原型 :   UINT32 WriteCanTxBuf(eCANNUM CanNum,UINT32 TxBufNum,
          UINT32 TPM,P_stcTxBUF Buf) ;
**参数说明 :   CanNum  -->> CAN控制器,值不能大于CAN_MAX_NUM 规定的值
    TxBufNum --> 选择发送缓冲区
    TPM   --> 发送优先级模式
    Buf   --> 发送到总线的数据
**返回值  : =0;成功将数据写入发送缓冲区
     !=0;写发送缓冲区操作失败 
**说 明  : 本函数用于向CAN发送缓冲区TxBUF写入发送数据
***********************************************************************************************************
*/
UINT32 WriteCanTxBuf(eCANNUM CanNum,UINT32 TxBufNum,UINT32 TPM,P_stcTxBUF Buf)
{
 UINT32 status=0;
 switch(TxBufNum)
 {

  case SEND_TX_BUF1://这里选择了缓冲区1
   if((0== CANSR(CanNum).Bits.TS1_BIT)&&(0 != CANSR(CanNum).Bits.TBS1_BIT))//在往里面写入之前,还是要判断can模块的相应缓冲区内是否有数据正在发送,是否可以往cantfi,cantid,cantda,cantdb寄存器内写入数据。
   {
    CANTFI1(CanNum)=Buf->TxFrameInfo;
    CANTID1(CanNum)=Buf->TxCANID;
    CANTDA1(CanNum)=Buf->CANTDA;
    CANTDB1(CanNum)=Buf->CANTDB;
   }
   else
   {
    status = 0x01;
   }
   break;
  case SEND_TX_BUF2:
   if((0== CANSR(CanNum).Bits.TS2_BIT)&&(0 !=CANSR(CanNum).Bits.TBS2_BIT))
   {
    CANTFI2(CanNum)=Buf->TxFrameInfo;
    CANTID2(CanNum)=Buf->TxCANID;
    CANTDA3(CanNum)=Buf->CANTDA;//这里好像有点错误????
    CANTDB2(CanNum)=Buf->CANTDB;
   }
   else
   {
    status = 0x01;
   }
   break;
  case SEND_TX_BUF3:
   if((0== CANSR(CanNum).Bits.TS3_BIT)&&(0 != CANSR(CanNum).Bits.TBS3_BIT))
   {
    CANTFI3(CanNum)=Buf->TxFrameInfo;
    CANTID3(CanNum)=Buf->TxCANID;
    CANTDA3(CanNum)=Buf->CANTDA;
    CANTDB3(CanNum)=Buf->CANTDB;
   }
   else
   {
    status = 0x01;
   }
   break;
  default:
   status = 0x01;//会发现,如果没有缓冲区写入的话,我们在这里就设定status标志为非‘0’值,表示我们写入缓冲区失败。
   break;
 }
 if(TPM == 1)
 {
  CANMOD(CanNum).Bits.TPM_BIT =1;//这里是一个优先级根据的问题,如果这里TPM不设置为1的时候,说明优先级是依靠各自的CAN ID决定的,如果为‘1’的话,说明有各个缓冲区tx的优先域决定的。
 }
 return(status);
}

/*
***********************************************************************************************************
**函数原型  :   UINT32 CanQuitSM(eCANNUM CanNum)
**参数说明  :   CanNum  -->> CAN控制器,值不能大于CAN_MAX_NUM 规定的值
**返回值  : =0;成功退出睡眠状态;
     !=0;不能退出睡眠状态; 
**说 明  : 本函数用于使CAN控制器退出休眠模式
***********************************************************************************************************
*/
UINT32 CanQuitSM(eCANNUM CanNum) 
{
 uCANMod  i;
 i=CANMOD(CanNum);
 CANMOD(CanNum).Bits.RM_BIT=1; //有些寄存器的写入前提是canmod寄存器的rm位先置为1.
 CANMOD(CanNum).Bits.SM_BIT = 0; //这里置为‘0’后就把can控制器唤醒了.
 CANMOD(CanNum).Bits.RM_BIT=i.Bits.RM_BIT; //恢复原来canmod的rm位的初始值.
 return(CANMOD(CanNum).Bits.SM_BIT);//这里起到一个检测的功能,如果返回的值为‘0’,说明我们已经成功唤醒了can控制器了。
}

/*
***********************************************************************************************************
**函数原型  :   void CanSendCmd(eCANNUM CanNum,UINT32 Cmd,UINT32 TxBuf)
**参数说明  :   CanNum  -->> CAN控制器,值不能大于CAN_MAX_NUM 规定的值
     Cmd  --> 发送命令字
     TxBuf --> 选择发送缓冲区
**返回值  :  
**说 明  : 本函数用于发送命令处理
************************************************************************************************************/
  其中,第一发送缓冲区为主要的发送区。发送缓冲区内是否还有数据,是否可以写入新的数据,都是通过寄存器的标志位来查看。
void CanSendCmd(eCANNUM CanNum,UINT32 Cmd,UINT32 TxBuf)
{
 uCANCMR Temp;
 Temp.Word =0;
 Temp.Bits.STB1_BIT = TxBuf&0x00000001;//这里是选择发送第一个缓冲区的内容.
 Temp.Bits.STB2_BIT = (TxBuf&0x00000002)>>1;//这里是选择发送第二个缓冲区的内容.
 Temp.Bits.STB3_BIT = (TxBuf&0x00000004)>>2;//这里是选择发送第3个缓冲区的内容.
 switch(Cmd)
 {
  case 1://单次发送
   Temp.Bits.AT_BIT =1;//表示取消等待发送的帧信息,只是发送此次。
   Temp.Bits.TR_BIT =1;//置位后,表示cantfi,cantid,cantda,cantdb寄存器里面的内容等待发送.
   break;
  case 2://自发自收
  case 3://单次自发自收
   if(CANMOD(CanNum).Bits.STM_BIT == 0)
   {
    CANMOD(CanNum).Word =1;
    CANMOD(CanNum).Bits.STM_BIT =1;//STM位被设置为1时---自测试模式——控制器认可没有应答的Tx 信息传输。此状态和CANCMR 的SRR 位一起使用,可以达到自发自收的效果。
    CANMOD(CanNum).Bits.RM_BIT =0;//恢复正常工作.
   }
   Temp.Bits.SRR_BIT =1;//如上所说,这里把CANCMR寄存器的SRR位设置为‘1’.
   if(Cmd == 3)
   {
    Temp.Bits.AT_BIT =1;//这里设置使我们是单次的。
   }
   break;
  case  0:
  default:
   Temp.Bits.TR_BIT =1;//如果为其他值的话,说明只是正常的发送。
   break;
 }
 CANCMR(CanNum) = Temp;//写入寄存器后就开始发送了。
}

  由上述程序可知,发送方式有四种:单次发送,自发自收,单次自发自收,正常发送。其中的自发自收,可以很好的用于调试程序。当只有一个can节点时,不需要外接can总线(需要收发器PCA82C250),就可以进行自发自收,以测试发送程序以及硬件电路的正确性。而正常发送,它需要can收发器和120欧姆的电阻,共同组成的can网。

2.can总线接收程序

  接收数据有两种方式,一种是查询,一种是中断。查询方式,比较简单,也就是不停的查询接收数据的标志位,当标志位为‘1’时,则表明接收缓冲区内有新的数据可读,然后读出接收到的can数据。不过,这种方法虽然实现起来比较简单,但是cpu要一直处于查询状态,这对cpu资源是很大的浪费。因此,我们采用第二种方式,中断。一定要先初始化好lpc2294(根据你的mcu型号来说)相应的中断设置寄存器以后,当接收缓冲区内有可读数据时,程序就会进入中断,只有我们在中断程序中,调用合适的读缓冲区函数,就达到了接收can数据的目的。

/*
***********************************************************************************************************
**函数原型  :   void CANIntPrg(void)
**参数说明  :   无
**返回值  : 无 
**说 明  : CAN控制器中断处理函数
************************************************************************************************************/
__irq  void  CANIntPrg(void)
{
 UINT32  j;
 uCANICR k;
 if(CANLUTerr.Word != 0 ) //验收滤波器遇到表格错误
 {
  //add or modify code
  j=CANLUTerrAd.Word; 
 }
 for(j=0;j<CAN_MAX_NUM;j++)
 {
  k=CANICR(j);//CANICR是中断捕获寄存器。
  if(k.Bits.RI_BIT != 0)//接收中断,表示接收到一条有用的帧信息。
  {
   //add code

   WriteCANRcvCyBuf(j);//这个函数实现的是先把数据从相应的CANRFS寄存器中的数据写到一个数组(CANRcvBufApp.RcvBuf)当中。
   ReadCANRcvCyBuf(j,&CRBuf);//再从数组中拿出数据,放在用户空间中。
  }
  if(k.Bits.TI1_BIT != 0)//第一发送缓冲区发送完成中断。
  {
   //add code
  }
  if(k.Bits.TI2_BIT != 0)//第二发送缓冲区发送完成中断。
  {
   //add code
  }
  if(k.Bits.TI3_BIT != 0)//第三发送缓冲区发送完成中断。
  {
   //add code
  }
  if(k.Bits.BEI_BIT != 0)//1:总线错误中断,CAN 控制器检测到总线错误时,该位置位。
  {
   //add code
   CanBufOffLinePrg(j);
  }
  if(k.Bits.ALI_BIT != 0)//仲裁丢失中断。
  {
   //add code
  }
  if(k.Bits.EPI_BIT != 0)//错误认可中断。
  {
   //add code
  }
  if(k.Bits.WUI_BIT != 0)//唤醒中断
  {
   //add code
  }
  if(k.Bits.DOI_BIT != 0)//缓冲区溢出中断
  {
   //add code
   ClrCanDataOver(j);//释放接收缓冲区
  }
 }
 VICVectAddr = 0;
}

其中,ReadCanRxBuf就是读取缓冲区数据函数,而其他的一些则是保留扩展添加使用,当发生相应的错误的时候,会进入到相应的函数。另外,需要额外注意的是,在读完数据以后,要把相应的标志位清零,否则整个程序会一直处于中断接收状态。

void WriteCANRcvCyBuf(eCANNUM CanNum)
{
 switch(CanNum)
 {
  case CAN1:
   if((0 == CANRcvBufApp.FullFlag1))
   {
    CANRcvBufApp.RcvBuf[CAN1][CANRcvBufApp.WritePoint1] = RxBUF(CAN1);//这里就是上面所说的写入数组的过程。
    if(++CANRcvBufApp.WritePoint1 >= USE_CAN_RCV_BUF_SIZE)
    {
     CANRcvBufApp.WritePoint1=0;
    }
    if(CANRcvBufApp.WritePoint1 == CANRcvBufApp.ReadPoint1)
    {
     CANRcvBufApp.FullFlag1 =1;//这里表明数组已经写满了,为什么可以表示写满,就是因为要符合这里这个IF条件只有writepoint自增到超过数组定义大小后,赋值为‘0’.
    }
   }
   break;
  case CAN2:
   if((0 == CANRcvBufApp.FullFlag2))
   {
    CANRcvBufApp.RcvBuf[CAN2][CANRcvBufApp.WritePoint2] = RxBUF(CAN2);
    if(++CANRcvBufApp.WritePoint2 >= USE_CAN_RCV_BUF_SIZE)
    {
     CANRcvBufApp.WritePoint2=0;
    }
    if(CANRcvBufApp.WritePoint2 == CANRcvBufApp.ReadPoint2)
    {
     CANRcvBufApp.FullFlag2 =1;
    }
   }
   break;

  default:
   break;
 }
 RelCanRecBuf(CanNum);
}

UINT32 ReadCANRcvCyBuf(eCANNUM CanNum,stcRxBUF *Buf)
{
 UINT32 status=0;
 switch(CanNum)
 {
  case CAN1:
   if((0 != CANRcvBufApp.FullFlag1) ||
    (CANRcvBufApp.ReadPoint1 != CANRcvBufApp.WritePoint1))//第一个判断是表示对因的一维数组已经放满了。第二个可能是指针不一致,说明还有数据没有读完。
   {
    *Buf=CANRcvBufApp.RcvBuf[CAN1][CANRcvBufApp.ReadPoint1];//从数组中读出数据放在用户空间中。
    if(++CANRcvBufApp.ReadPoint1 >= USE_CAN_RCV_BUF_SIZE)
    {
     CANRcvBufApp.ReadPoint1 =0;
    }
    CANRcvBufApp.FullFlag1=0;
   }
   else
   {
    status=1;
   }
   break;
  case CAN2:
   if((0 != CANRcvBufApp.FullFlag2) ||
    (CANRcvBufApp.ReadPoint2 != CANRcvBufApp.WritePoint2))
   {
    *Buf=CANRcvBufApp.RcvBuf[CAN2][CANRcvBufApp.ReadPoint2];
    if(++CANRcvBufApp.ReadPoint2 >= USE_CAN_RCV_BUF_SIZE)
    {
     CANRcvBufApp.ReadPoint2 =0;
    }
    CANRcvBufApp.FullFlag2=0;
   }
   case CAN3:
   if((0 != CANRcvBufApp.FullFlag3) ||
    (CANRcvBufApp.ReadPoint3 != CANRcvBufApp.WritePoint3))
   {
    *Buf=CANRcvBufApp.RcvBuf[CAN3][CANRcvBufApp.ReadPoint3];
    if(++CANRcvBufApp.ReadPoint3 >= USE_CAN_RCV_BUF_SIZE)
    {
     CANRcvBufApp.ReadPoint3 =0;
    }
    CANRcvBufApp.FullFlag3=0;
   }
   else
   {
    status=1;
   }
   break;
  case CAN4:
   if((0 != CANRcvBufApp.FullFlag4) ||
    (CANRcvBufApp.ReadPoint4 != CANRcvBufApp.WritePoint4))
   {
    *Buf=CANRcvBufApp.RcvBuf[CAN4][CANRcvBufApp.ReadPoint4];
    if(++CANRcvBufApp.ReadPoint4 >= USE_CAN_RCV_BUF_SIZE)
    {
     CANRcvBufApp.ReadPoint4 =0;
    }
    CANRcvBufApp.FullFlag4=0;
   }
   else
   {
    status=1;
   }
   break;
  break;
  default:
   status=1;
   break;
 }
 return status;
}

到此为止,我们算是分析完了,对于滤波器的设置,我会在以后的文章中阐明的。

原创粉丝点击