基于PIC32开发CAN监视器



引言:CANController AreaNetwork)是目前使用非常广泛的一种通信方式,被大量应用于汽车、船泊、数控加工设备、机器人以及各种自动化设备。与之相关的工作人员——工程师、设备师、维修师、开发人员都将频繁与其打交道,不管是开发人员的初期调试,还是维修师检测设备的状况,对CAN总线的数据量都有监视的渴望,对两根差分的信号线,相通过示波器来监视总线上的数据,恐怕让人神精。当然,市面上也有商用CAN监视器,本人也用过,使用也还算方便,但不知道为啥,已经坏掉了好几个(与我无关),因为本人现在跳到一家资金捉襟见肘的小公司,咖啡都不给买,工资也发不起的扣老板,自制一台CAN监视器的想法诞生了。

 

1. 框架图

 

        PC <<---->>USB接口<<---->>MCP2200<<---->>PIC32<<---->>CAN Transceiver<<---->>CAN 总线

 


 


关于USB接口,在这里就是与上位机PC通信的方式,是笔者惯用方式,一根USB线就可以,并且在PIC32里只需要做UART的驱动就可以了,十分简单。读者也可自行选择其他方式,比如使用PIC32自带的USB功能,HIDCDC都可以,用232芯片转换也行吧。

 

 

 

2. PIC32 关于 CAN 模块的配置

请将CAN模块配置为监听模式(Listen-Onlymode)。

以下引自Microchip公司手册DS61154C_CN

监听模式是正常工作模式的一种变化形式。如果监听模式被激活,则CAN模块会出现在CAN总线上,但处于被动状态。它会接收报文,但不会发送报文。CAN模块不会产生错误标志,也不会应答信号。在该状态下,错误计数器不再工作。监听模式可用来检测CAN总线上的波特率。要使用此功能,必须至少有另外两个互相通信的节点。波特率可以通过测试一些不同值以实证方式检测。该模式也可用作总线监视器,因为CAN总线不会影响数据通信。

 

    

 

 

配置代码:

void CAN1Init(void)

{

           CAN_BIT_CONFIGcanBitConfig;

           UINT baudPrescalar;

 

           CANEnableModule(CAN1,TRUE);

 

           CANSetOperatingMode(CAN1,CAN_CONFIGURATION);

           while(CANGetOperatingMode(CAN1)!= CAN_CONFIGURATION);

 

          CANSetTimeStampPrescalar(CAN1, 80 );

          CANSetCap(CAN1, TRUE );

 

          canBitConfig.phaseSeg2Tq           = CAN_BIT_2TQ;

          canBitConfig.phaseSeg1Tq           = CAN_BIT_3TQ;

          canBitConfig.propagationSegTq      = CAN_BIT_4TQ;

           canBitConfig.phaseSeg2TimeSelect   = TRUE;

           canBitConfig.sample3Time           = TRUE;

          canBitConfig.syncJumpWidth         = CAN_BIT_2TQ;

           CANSetSpeed( CAN1, &canBitConfig,SYSTEM_FREQ, CAN_SPD_1MHZ );

     

           CANAssignMemoryBuffer(CAN1,CAN1MessageFifoArea,(24* 16) );

           CANConfigureChannelForRx(CAN1,CAN_CHANNEL1, 24, CAN_RX_FULL_RECEIVE);

          CANConfigureFilterMask  (CAN1, CAN_FILTER_MASK0, 0, CAN_SID,CAN_FILTER_MASK_IDE_TYPE);

           CANConfigureFilter     (CAN1, CAN_FILTER1, 0x00, CAN_SID);

           CANLinkFilterToChannel (CAN1, CAN_FILTER1, CAN_FILTER_MASK0,CAN_CHANNEL1);

           CANEnableFilter        (CAN1, CAN_FILTER1, TRUE);

   

           CANEnableChannelEvent(CAN1, CAN_CHANNEL1,CAN_RX_CHANNEL_NOT_EMPTY, TRUE);

           CANEnableModuleEvent (CAN1, CAN_RX_EVENT,TRUE);

 

           /* These functions are from interruptperipheral

          * library. */   

 

           /*Step 7: Switch the CAN mode

           * to normal mode. */

 

           CANSetOperatingMode(CAN1,CAN_LISTEN_ONLY);

           while(CANGetOperatingMode(CAN1)!= CAN_LISTEN_ONLY);         

}

 

代码说明:以上代码为笔者自制CAN监视器正在使用的源代码,使用通道1来监听总线的消息。此代码参考了MPLAB X自带的关于CAN模块的示例代码,如果有相关函数不理解,请自行参考MPLAB X自带的示例代码。

        

3. 应用层代码的准备

          CAN自定义数据结构

typedef struct _CAN_MSG_TYPE

{

       struct

       {

                unsigned short sid;                                // 标准ID

                unsigned short timer;                          // 接收时间

       } ;

       long size;                                                                               //数据个数

                                  unsigned char data[8];                                      // 数据buffer

} CAN_MSG_TYPE;

 

        CAN消息检测函数

long can1_rx_chk( long channel )

{

      long ret;

      

      if ( !C1INTbits.RBIF )

      {

                    return 0;

      }

      if((CANGetModuleEvent(CAN1)& CAN_RX_EVENT) == 0)

      {

                    return 0;

      }            

      

                    

      if(CANGetChannelEvent(CAN1, channel) & CAN_RX_CHANNEL_NOT_EMPTY )

      {

                    ret        = 1;

      }                          

      else

      {

                    ret        = 0;

      }

      

      return ret;

}

 

CAN消息读取函数

void can1_rx_read( long channel, CAN_MSG_TYPE*msg )

{

      CANRxMessageBuffer* message;

      longi;

      

      message        = CANGetRxMessage(CAN1,channel);         

      msg->sid          = message->msgSID.SID;

      msg->size        = message->msgEID.DLC;

       msg->timer     =message->msgSID.CMSGTS;

 

      

      for( i = 0; i < 8; i++ )

      {

                    msg->data[i]= message->data[i];

      }

      

      CANUpdateChannel(CAN1,channel);

}

USB打印函数

void usb_printf( char*str, long dat );

此函数稍有复杂,并不在这里贴出代码,请读者自行编写或是采用更为简单的方法。

此函数的功能为与标准C语言里的printf函数类似,目的是通过UART模块将字符打印到PC的终端应用程序。

 

4. 应用层代码

for ( ;; )

{

if ( can1_rx_chk( CAN_CHANNEL1 ) )

{

can1_rx_read( CAN_CHANNEL1, &can_msg );

usb_printf( "\r\nID:%h\r\n", can_msg.sid);

usb_printf( "Timer:%dus\r\n", can_msg.timer);

usb_printf( "Size:%d\r\n", can_msg.size);

usb_printf( "Data:", 0 );

for ( index = 0; index < 8; index++ )

{

                            usb_printf( "%d", can_msg.data[index] );

                   }

}

}

 

5.敲完代码编译,小伙伴们就可以了在PC的超级终端上看到CAN总线上的数据,当然也可以使用其它能够接收串口数据的其他应用软件。有个地方要请小伙伴们引起注意,就是CAN总线上的数据量与UART的通信速率的问题,请通过计算总线上的数据量来设置合理的UART速率,否则会得不到正确的总线数据。希望能够帮到一些正在为CAN通信纠结的小伙伴们,但也请小伙伴们尊重原创。

你可能感兴趣的:(can,PIC32)