CanBus

CAN BUS module available for Swordfish.

Test equipment : 2 Home Made boards, MCU PIC18F458, MCP2551 CAN transceiver.

Available functions

{
****************************************************************************
* Name    : CANInitialize                                                  *
* Purpose : Initialize CAN module, Filters, Mask                           *
* Input   : SJW     - SJW value as defined in 18CXX8 datasheet             *
*                     (Must be between 1 thru 4)                           *
*           BRP     - BRP value as defined in 18CXX8 datasheet             *
*                     (Must be between 1 thru 64)                          *
*           PHSEG1  - PHSEG1 value as defined in 18CXX8 datasheet          *                 
*                     (Must be between 1 thru 8)                           *
*           PHSEG2  - PHSEG2 value as defined in 18CXX8 datasheet          *                 
*                     (Must be between 1 thru 8)                           *
*           PROPSEG - PROPSEG value as defined in 18CXX8 datasheet         *                   
*                     (Must be between 1 thru 8)                           *
*           flags   - Value of CAN_CONFIG_FLAGS                            *
****************************************************************************
}
Public Sub CANInitialize(SJW, BRP, PHSEG1, PHSEG2, PROPSEG, flags As Byte)
{
****************************************************************************
* Name    : CANSetFilter                                                   *
* Purpose : Given value is bit adjusted to appropriate buffer              *
*           filter registers.                                              *
****************************************************************************
}
Public Sub CANSetFilter(filter As Byte, value As LongWord, flags As Byte)
{
****************************************************************************
* Name    : CANSetMask                                                     *
* Purpose : Given value is bit adjusted to appropriate buffer              *
*           mask registers.                                                *
****************************************************************************
}
Public Sub CANSetMask(mask As Byte, value As LongWord, flags As Byte) 
{
****************************************************************************
* Name    : CANSetOperationMode                                            *
* Purpose : Given mode byte is copied to CANCON                            *
*           and made sure that requested mode is set.                      *
****************************************************************************
}
Public Sub CANSetOperationMode(mode As Byte) 
{
****************************************************************************
* Name    : CANSendMessage                                                 *
* Purpose : If at least one empty transmit buffer is found,                *
*           given message is queued to be transmitted.                     *
*           If none found FALSE value is returned.                         *
****************************************************************************
} 
Public Function CANSendMessage(id As LongWord, ByRef Data() As Byte, DataLen, flags As Byte) As Boolean
{
****************************************************************************
* Name    : CANReceiveMessage                                              *
* Purpose : If at least one full receive buffer is found,                  *
*           it is extrated and returned.                                   *
*           If none found FALSE value is returned.                         *
****************************************************************************
}
Public Function CANReceiveMessage(ByRef id As LongWord, ByRef Data(), DataLen, flags As Byte) As Boolean
{
****************************************************************************
* Name    : String2ID                                                      *
* Purpose : Transform 3 Char String into LongWord id                       *
****************************************************************************
}
Public Function String2ID(str_in As String) As LongWord

You can download source code from : http://www.microelemente.ro/Swordfish/CAN.zip

In ZIP file, you have 4 examples and additional SYS_lib module.

Example Code

{
*****************************************************************************
*  Name    : CAN_Echo.BAS                                                   *
*  Author  : Florin Andrei Medrea                                           *
*  Notice  : Copyright (c) 2007 - YO2LIO -                                  *
*          : All Rights Reserved                                            *
*  Date    : 10/26/2007                                                     *
*  Version : 1.0                                                            *
*  Notes   : Receive CAN message for XTD id "S01",                          *
*          : send message back to sender with XTD id "S01"                  *
*          : and send message to Usart                                      *
*****************************************************************************
}
Device = 18F458
Clock = 40
Config OSC = HSPLL

Include "CAN_lib"
Include "usart"
Include "SYS_lib"

Dim Can_Init_Flags, Can_Send_Flags, Can_Rcv_Flags, Rx_Data_Len, i As Byte,
    RxTx_Data(8) As Byte,
    Tx_ID, Rx_ID As LongWord,
    Msg_Rcvd, Msg_Sent As Boolean


  ADCON1 = %11000110      // AD off
  CMCON = $07             // Comparator off
  WDTCON = 1

  ClrWDT
  SetBaudrate(br115200)

  Can_Init_Flags = 0
  Can_Send_Flags = 0
  Can_Rcv_Flags  = 0

  Can_Send_Flags = CAN_TX_PRIORITY_0 And                    // Form value to be used
                   CAN_TX_XTD_FRAME  And                    //  with CANSendMessage
                   CAN_TX_NO_RTR_FRAME

  Can_Init_Flags = CAN_CONFIG_SAMPLE_THRICE And             // Form value to be used
                   CAN_CONFIG_PHSEG2_PRG_ON And             //  with CANInitialize
                   CAN_CONFIG_XTD_MSG       And            
                   CAN_CONFIG_DBL_BUFFER_ON And
                   CAN_CONFIG_ALL_VALID_MSG

  CANInitialize(1, 10, 7, 6, 2, Can_Init_Flags)             // initialize CAN at 250 Kbps

  CANSetOperationMode(CAN_OP_MODE_CONFIG)                   // set CONFIGURATION mode
  Rx_ID = $FFFFFFFF
  CANSetMask(CAN_MASK_B1, Rx_ID, CAN_CONFIG_XTD_MSG)        // set all mask1 bits to ones
  CANSetMask(CAN_MASK_B2, Rx_ID, CAN_CONFIG_XTD_MSG)        // set all mask2 bits to ones  
  Rx_ID = String2ID("S01")
  Tx_ID = String2ID("S01")
  CANSetFilter(CAN_FILTER_B1_F1, Rx_ID, CAN_CONFIG_XTD_MSG) // set id of filter B1_F1 to "S01"
  CANSetOperationMode(CAN_OP_MODE_NORMAL)                   // set NORMAL mode

  While true
    ClrWDT
    Msg_Rcvd = CANReceiveMessage(Rx_ID , RxTx_Data , Rx_Data_Len, Can_Rcv_Flags)
    If Msg_Rcvd Then
      WriteByte(Rx_ID.Byte2)
      WriteByte(Rx_ID.Byte1)
      WriteByte(Rx_ID.Byte0)
      WriteByte(" ")
      i = 0
      While i < Rx_Data_Len
        WriteByte(RxTx_Data(i))
        Inc(i)
      Wend
      Write($0D, $0A)
      Msg_Sent = CANSendMessage(Tx_ID, RxTx_Data, Rx_Data_Len, Can_Send_Flags) // send data back
      If Msg_Sent Then
        Write("CANSendMessage Success", $0D, $0A)
      Else
        Write("CANSendMessage Error", $0D, $0A)
      End If
    End If
  Wend 

Module Code

{
*****************************************************************************
*  Name    : CAN_lib.BAS                                                    *
*  Author  : Florin Andrei Medrea                                           *
*  Notice  : Copyright (c) 2007 - YO2LIO -                                  *
*          : All Rights Reserved                                            *
*  Date    : 10/26/2007                                                     *
*  Version : 1.0                                                            *
*  Notes   : This module is based on Microchip sample                       *
*          : CAN Application Note AN738                                     *
*****************************************************************************
}
Module CAN_lib

Include "SYS_lib"

Public Const
  CAN_TX_PRIORITY_BITS= %00000011,
  CAN_TX_PRIORITY_0   = %11111100,            // XXXXXX00
  CAN_TX_PRIORITY_1   = %11111101,            // XXXXXX01
  CAN_TX_PRIORITY_2   = %11111110,            // XXXXXX10
  CAN_TX_PRIORITY_3   = %11111111,            // XXXXXX11

  CAN_TX_FRAME_BIT    = %00001000,
  CAN_TX_STD_FRAME    = %11111111,            // XXXXX1XX
  CAN_TX_XTD_FRAME    = %11110111,            // XXXXX0XX

  CAN_TX_RTR_BIT      = %01000000,
  CAN_TX_NO_RTR_FRAME = %11111111,            // X1XXXXXX
  CAN_TX_RTR_FRAME    = %10111111,            // X0XXXXXX

  CAN_RX_FILTER_BITS  = %00000111,
  CAN_RX_FILTER_1     = %00000000,
  CAN_RX_FILTER_2     = %00000001,
  CAN_RX_FILTER_3     = %00000010,
  CAN_RX_FILTER_4     = %00000011,
  CAN_RX_FILTER_5     = %00000100,
  CAN_RX_FILTER_6     = %00000101,

  CAN_RX_OVERFLOW     = %00001000,            // Set if Overflowed else cleared 
  CAN_RX_INVALID_MSG  = %00010000,            // Set if invalid else cleared
  CAN_RX_XTD_FRAME    = %00100000,            // Set if XTD message else cleared
  CAN_RX_RTR_FRAME    = %01000000,            // Set if RTR message else cleared
  CAN_RX_DBL_BUFFERED = %10000000,            // Set if this message was hardware double-buffered

  CAN_OP_MODE_BITS    = %11100000,
  CAN_OP_MODE_NORMAL  = %00000000,
  CAN_OP_MODE_SLEEP   = %00100000,
  CAN_OP_MODE_LOOP    = %01000000,
  CAN_OP_MODE_LISTEN  = %01100000,
  CAN_OP_MODE_CONFIG  = %10000000,

  CAN_CONFIG_DEFAULT          = %11111111,    // 11111111
  CAN_CONFIG_PHSEG2_PRG_BIT   = %00000001,
  CAN_CONFIG_PHSEG2_PRG_ON    = %11111111,    // XXXXXXX1
  CAN_CONFIG_PHSEG2_PRG_OFF   = %11111110,    // XXXXXXX0

  CAN_CONFIG_LINE_FILTER_BIT  = %00000010,
  CAN_CONFIG_LINE_FILTER_ON   = %11111111,    // XXXXXX1X
  CAN_CONFIG_LINE_FILTER_OFF  = %11111101,    // XXXXXX0X

  CAN_CONFIG_SAMPLE_BIT       = %00000100,
  CAN_CONFIG_SAMPLE_ONCE      = %11111111,    // XXXXX1XX
  CAN_CONFIG_SAMPLE_THRICE    = %11111011,    // XXXXX0XX

  CAN_CONFIG_MSG_TYPE_BIT     = %00001000,
  CAN_CONFIG_STD_MSG          = %11111111,    // XXXX1XXX
  CAN_CONFIG_XTD_MSG          = %11110111,    // XXXX0XXX

  CAN_CONFIG_DBL_BUFFER_BIT   = %00010000,
  CAN_CONFIG_DBL_BUFFER_ON    = %11111111,    // XXX1XXXX
  CAN_CONFIG_DBL_BUFFER_OFF   = %11101111,    // XXX0XXXX

  CAN_CONFIG_MSG_BITS         = %01100000,
  CAN_CONFIG_ALL_MSG          = %11111111,    // X11XXXXX
  CAN_CONFIG_VALID_XTD_MSG    = %11011111,    // X10XXXXX
  CAN_CONFIG_VALID_STD_MSG    = %10111111,    // X01XXXXX
  CAN_CONFIG_ALL_VALID_MSG    = %10011111,    // X00XXXXX

  CAN_MASK_B1           = 1,
  CAN_MASK_B2           = 2,

  CAN_FILTER_B1_F1      = 1,
  CAN_FILTER_B1_F2      = 2,
  CAN_FILTER_B2_F1      = 3,
  CAN_FILTER_B2_F2      = 4,
  CAN_FILTER_B2_F3      = 5,
  CAN_FILTER_B2_F4      = 6

Const
                                              // BRGCON3
  WAKFIL = 6,
                                              // BRGCON2
  SEG2PHTS = 7,
  SAM = 6,
                                              // COMSTAT
  RX1OVFL = 7,
  RXB0OVFL = 7,
  TXBO = 5,
  TXBP = 4,
  RXBP = 3,
                                              // PIR3
  IRXIF = 7,
  RXB1IF = 1,
  RXB0IF = 0,
                                              // CANCON
  ABAT = 4,
                                              // RXBnCON
  RXFUL = 7,
  RXB0DBEN = 2,
                                              // RXBnDLC
  RXRTR = 6,
                                              // RXBnSIDL
  EXID = 3,
                                              // TXBnCON
  TXREQ = 3

Dim 
  RXB0CON_RX0DBEN  As RXB0CON.Bits(RXB0DBEN),
  BRGCON2_SAM      As BRGCON2.Bits(SAM),
  BRGCON2_SEG2PHTS As BRGCON2.Bits(SEG2PHTS),
  BRGCON3_WAKFIL   As BRGCON3.Bits(WAKFIL),
  PIR3_RXB0IF      As PIR3.Bits(RXB0IF),
  COMSTAT_RX0OVFL  As COMSTAT.Bits(RXB0OVFL),
  PIR3_RXB1IF      As PIR3.Bits(RXB1IF),
  COMSTAT_RX1OVFL  As COMSTAT.Bits(RX1OVFL),
  RXB0DLC_RTR      As RXB0DLC.Bits(RXRTR),
  RXB0SIDL_EXID    As RXB0SIDL.Bits(EXID),
  PIR3_IRXIF       As PIR3.Bits(IRXIF),

  COMSTAT_TXB0     As COMSTAT.Bits(TXBO),
  COMSTAT_TXBP     As COMSTAT.Bits(TXBP),
  COMSTAT_RXBP     As COMSTAT.Bits(RXBP),
  CANCON_ABAT      As CANCON.Bits(ABAT),
  RXB0CON_RXFUL    As RXB0CON.Bits(RXFUL)
//  RXB1CON_RXFUL    As RXB1CON.Bits(RXFUL),
//  TXB0CON_TXREQ    As TXB0CON.Bits(TXREQ),
//  TXB1CON_TXREQ    As TXB1CON.Bits(TXREQ),
//  TXB2CON_TXREQ    As TXB2CON.Bits(TXREQ)

{
****************************************************************************
* Name    : CANSetOperationMode                                            *
* Purpose : Given mode byte is copied to CANCON                            *
*           and made sure that requested mode is set.                      *
****************************************************************************
}
Public Sub CANSetOperationMode(mode As Byte)  
  CANCON = mode   // Request desired mode.  
  While((CANSTAT And CAN_OP_MODE_BITS) <> mode)
    ClrWDT
  Wend            // Wait till desired mode is set.
End Sub
{
****************************************************************************
* Name    : CANSetBaudRate                                                 *
* Purpose : Given values are bit adjusted to fit in 18CXX8                 *
*           BRGCONx registers and copied.                                  *
* Input   : SJW     - SJW value as defined in 18CXX8 datasheet             *
*                     (Must be between 1 thru 4)                           *
*           BRP     - BRP value as defined in 18CXX8 datasheet             *
*                     (Must be between 1 thru 64)                          *
*           PHSEG1  - PHSEG1 value as defined in 18CXX8 datasheet          *                 
*                     (Must be between 1 thru 8)                           *
*           PHSEG2  - PHSEG2 value as defined in 18CXX8 datasheet          *                 
*                     (Must be between 1 thru 8)                           *
*           PROPSEG - PROPSEG value as defined in 18CXX8 datasheet         *                   
*                     (Must be between 1 thru 8)                           *
*           flags   - Value of CAN_CONFIG_FLAGS                            *
****************************************************************************
}
Sub CANSetBaudRate(SJW, BRP, PHSEG1, PHSEG2, PROPSEG, flags As Byte)
  Dec(SJW)
  Dec(BRP)
  Dec(PHSEG1)
  Dec(PHSEG2)
  Dec(PROPSEG)
  BRGCON1 = SJW << 6
  BRGCON1 = BRGCON1 Or BRP
  BRGCON2 = PHSEG1 << 3
  BRGCON2 = BRGCON2 Or PROPSEG
  If (flags And CAN_CONFIG_SAMPLE_BIT) = 0 Then
    BRGCON2_SAM = 1
  End If
  If (flags And CAN_CONFIG_PHSEG2_PRG_BIT) <> 0 Then
    BRGCON2_SEG2PHTS = 1
  End If
  BRGCON3 = PHSEG2
  If (flags And CAN_CONFIG_LINE_FILTER_BIT) <> 0 Then 
    BRGCON3_WAKFIL = 1
  End If   
End Sub
{
****************************************************************************
* Name    : CANIDToRegs                                                    *
* Purpose : If given id is standard identifier,                            *
*           only SIDH and SIDL are updated                                 *
*           If given id is extended identifier,                            *
*           bits value<17:0> is copied to EIDH, EIDL and SIDH<1:0>         *
*           bits value<28:18> is copied to SIDH and SIDL                   *
****************************************************************************
}
Sub CANIDToRegs(CANreg As Word, value As LongWord, flags As Byte)
  FSR2 = CANreg
  If (flags And CAN_CONFIG_MSG_TYPE_BIT) <> 0 Then  // Standard Identifier  
    INDF2 = value.Byte0 >> 3                  // Copy SID<7:3> to SIDH<4:0>
    POSTINC2 = INDF2 Or (value.Byte1 << 5)    // Copy SID<10:8> to SIDH<7:5>
    INDF2 = value.Byte0 << 5                  // Copy SID<2:0> to SIDL<7:5>
  Else                                              // Extended Identifier
    INDF2 = value.Byte2 >> 5                  // Copy EID<23:21> to SIDH<2:0>
    POSTINC2 = INDF2 Or (value.Byte3 << 3)    // Copy EID<28:24> to SIDH<7:3>
    INDF2 = (value.Byte2 << 3) And $E0        // Copy EID<20:18> to SIDL<7:5>
    INDF2 = INDF2 Or %00001000                // Set EXIDEN bit  to SIDL<3>
    POSTINC2 = INDF2 Or (value.Byte2 And $03) // Copy EID<17:16> to SIDL<1:0>
    POSTINC2 = value.Byte1                    // Copy EID<15:8> to EIDH<7:0>
    INDF2 = value.Byte0                       // Copy EID<7:0> to EIDL<7:0>
  End If
End Sub
{
****************************************************************************
* Name    : RegsToCANID                                                    *
* Purpose : If given id is standard identifier,                            *
*           only SIDH and SIDL are used                                    *
*           If given id is extended identifier,                            *
*           EIDH, EIDL And SIDH<1:0> is copied To bits value<17:0>         *
*           SIDH And SIDL is copied To bits value<28:18>                   *
****************************************************************************
}
Function RegsToCANID(CANreg As Word, flags As Byte) As LongWord
  FSR2 = CANreg 
  If (flags And CAN_CONFIG_MSG_TYPE_BIT) <> 0 Then  // Standard Identifier
    result.Byte0 = INDF2 << 3                   // Copy SIDH<4:0> to SID<7:3>
    result.Byte1 = POSTINC2 >> 5                // Copy SIDH<7:5> to SID<10:8>
    result.Byte0 = result.Byte0 Or (INDF2 >> 5) // Copy SIDL<7:6> to SID<2:0>
    result.Byte2 = $00
    result.Byte3 = $00                               
  Else                                              // Extended Identifier
    result.Byte2 = INDF2 << 5                   // Copy SIDH<2:0> to EID<23:21>
    result.Byte3 = POSTINC2 >> 3                // Copy SIDH<7:3> to EID<29:25>
    result.Byte2 = result.Byte2 Or (INDF2 And $03) // Copy SIDH<1:0> to EID<17:16>
    result.Byte2 = result.Byte2 Or ((POSTINC2 And $E0) >> 3) // Copy SIDL<7:6> to EID<20:18>
    result.Byte1 = POSTINC2                     // Copy EIDH<15:8> to EID<15:8>
    result.Byte0 = INDF2                        // Copy EIDH<7:0> to EID<7:0>
  End If  
End Function 
{
****************************************************************************
* Name    : CANSetMask                                                     *
* Purpose : Given value is bit adjusted to appropriate buffer              *
*           mask registers.                                                *
****************************************************************************
} 
Public Sub CANSetMask(mask As Byte, value As LongWord, flags As Byte)
Dim buf As Word
  Save(FSR1, FSR2)
  If mask = CAN_MASK_B1 Then // Select appropriate starting address based on given CAN_MASK value.
    buf = AddressOf(RXM0SIDH)
  Else
    buf = AddressOf(RXM1SIDH)
  End If
  CANIDToRegs(buf, value, flags)
  Restore
End Sub
{
****************************************************************************
* Name    : CANSetFilter                                                   *
* Purpose : Given value is bit adjusted to appropriate buffer              *
*           filter registers.                                              *
****************************************************************************
}
Public Sub CANSetFilter(filter As Byte, value As LongWord, flags As Byte)
Dim buf As Word
  Save(FSR1, FSR2)
  Select filter
    Case CAN_FILTER_B1_F1
      buf = AddressOf(RXF0SIDH)
    Case CAN_FILTER_B1_F2
      buf = AddressOf(RXF1SIDH)
    Case CAN_FILTER_B2_F1
      buf = AddressOf(RXF2SIDH)
    Case CAN_FILTER_B2_F2
      buf = AddressOf(RXF3SIDH)
    Case CAN_FILTER_B2_F3
      buf = AddressOf(RXF4SIDH)
    Else
      buf = AddressOf(RXF5SIDH)
  End Select
  CANIDToRegs(buf, value, flags)
  Restore
End Sub 
{
****************************************************************************
* Name    : CANInitialize                                                  *
* Purpose : Initialize CAN module, Filters, Mask                           *
* Input   : SJW     - SJW value as defined in 18CXX8 datasheet             *
*                     (Must be between 1 thru 4)                           *
*           BRP     - BRP value as defined in 18CXX8 datasheet             *
*                     (Must be between 1 thru 64)                          *
*           PHSEG1  - PHSEG1 value as defined in 18CXX8 datasheet          *                 
*                     (Must be between 1 thru 8)                           *
*           PHSEG2  - PHSEG2 value as defined in 18CXX8 datasheet          *                 
*                     (Must be between 1 thru 8)                           *
*           PROPSEG - PROPSEG value as defined in 18CXX8 datasheet         *                   
*                     (Must be between 1 thru 8)                           *
*           flags   - Value of CAN_CONFIG_FLAGS                            *
****************************************************************************
}
Public Sub CANInitialize(SJW, BRP, PHSEG1, PHSEG2, PROPSEG, flags As Byte)
Dim FilterConfig1 As Byte,
    FilterConfig2 As Byte
  Save(FSR1, FSR2)
  #if _device in (18F248, 18F258, 18F448, 18F458, 18F2480, 18F2580, 18F4480, 18F4580, 18F2682, 18F2685, 18F4682, 18F4685)
  TRISB.2 = 0 'TX CAN
  TRISB.3 = 1 'RX CAN
  #else
  TRISG.0 = 0 'TX CAN
  TRISG.2 = 1 'RX CAN
  #endif
  CIOCON = 0
  CANSetOperationMode(CAN_OP_MODE_CONFIG) // In order to setup necessary config parameters of CAN module, it must be in Config mode. 
  #if _device in (18F248, 18F258, 18F448, 18F458)
  Nop
  #else
  ECANCON = ECANCON And %00111111
  #endif    
  CANSetBaudRate(SJW, BRP, PHSEG1, PHSEG2, PROPSEG, flags) // Now set the baud rate.
  RXB0CON = flags And CAN_CONFIG_MSG_BITS
  If (flags And CAN_CONFIG_DBL_BUFFER_BIT) = CAN_CONFIG_DBL_BUFFER_ON Then
    RXB0CON_RX0DBEN = 1
  End If
  RXB1CON = RXB0CON  
  CANSetMask(CAN_MASK_B1, 0, CAN_CONFIG_XTD_MSG)  // Set default filter and mask registers for all receive buffers.
  CANSetMask(CAN_MASK_B2, 0, CAN_CONFIG_XTD_MSG)
  Select ((flags And CAN_CONFIG_MSG_BITS) Or (Not(CAN_CONFIG_MSG_BITS)))
    Case CAN_CONFIG_VALID_XTD_MSG
      FilterConfig1 = CAN_CONFIG_XTD_MSG
      FilterConfig2 = CAN_CONFIG_XTD_MSG
    Case CAN_CONFIG_VALID_STD_MSG
      FilterConfig1 = CAN_CONFIG_STD_MSG
      FilterConfig2 = CAN_CONFIG_STD_MSG
    Else
      FilterConfig1 = CAN_CONFIG_STD_MSG
      FilterConfig2 = CAN_CONFIG_XTD_MSG
  End Select 
  CANSetFilter(CAN_FILTER_B1_F1, 0, FilterConfig1) // By default, there will be no mask on any receive filters, hence filter value of '0' will be ignored.
  CANSetFilter(CAN_FILTER_B1_F2, 0, FilterConfig1)
  CANSetFilter(CAN_FILTER_B2_F1, 0, FilterConfig2)
  CANSetFilter(CAN_FILTER_B2_F2, 0, FilterConfig2)
  CANSetFilter(CAN_FILTER_B2_F3, 0, FilterConfig2)
  CANSetFilter(CAN_FILTER_B2_F4, 0, FilterConfig2)  
  CANSetOperationMode(CAN_OP_MODE_NORMAL)         // Restore to Normal mode.
  Restore
End Sub
{
****************************************************************************
* Name    : CANSendMessage                                                 *
* Purpose : If at least one empty transmit buffer is found,                *
*           given message is queued to be transmitted.                     *
*           If none found FALSE value is returned.                         *
****************************************************************************
} 
Public Function CANSendMessage(id As LongWord, ByRef Data() As Byte, DataLen, flags As Byte) As Boolean
Dim i As Byte  
  Save(FSR1, FSR2)
  FSR2 = AddressOf(TXB0CON)           // For compatibility with SwordfishSE
  If INDF2.bits(TXREQ) = 0 Then       // Find the first empty transmitter.
//  If TXB0CON_TXREQ = 0 Then    
    CANCON = CANCON And %11110001     // TxBuffer0 is empty.  Set WIN bits to point to TXB0
    CANCON = CANCON Or %00001000
  Else
    FSR2 = AddressOf(TXB1CON)         // For compatibility with SwordfishSE
    If INDF2.bits(TXREQ) = 0 Then  
//    If TXB1CON_TXREQ = 0 Then
      CANCON = CANCON And %11110001   // TxBuffer1 is empty. Set WIN bits to point to TXB1
      CANCON = CANCON Or %00000110
    Else
      FSR2 = AddressOf(TXB2CON)       // For compatibility with SwordfishSE
      If INDF2.bits(TXREQ) = 0 Then
//      If TXB2CON_TXREQ = 0 Then
        CANCON = CANCON And %11110001 // TxBuffer2 is empty. Set WIN bits to point to TXB2
        CANCON = CANCON Or %00000100
      Else
        result = false                // None of the transmit buffers were empty.
        Exit
      End If
    End If
  End If      
  RXB0CON = flags And CAN_TX_PRIORITY_BITS  // Set transmit priority.
  If (flags And CAN_TX_FRAME_BIT) = 0 Then  // Populate Extended identifier information only if it is desired.
    CANIDToRegs(AddressOf(RXB0SIDH), id, CAN_CONFIG_XTD_MSG)
  Else
    CANIDToRegs(AddressOf(RXB0SIDH), id, CAN_CONFIG_STD_MSG)
  End If
  RXB0DLC = DataLen
  If (flags And CAN_TX_RTR_BIT) = 0 Then
    RXB0DLC = RXB0DLC Or %01000000
  End If
  i = 0
  FSR2 = AddressOf(RXB0D0)            // Populate data values.
  FSR1 = AddressOf(Data)
  While i < DataLen 
    POSTINC2 = POSTINC1
    Inc(i)
  Wend
  ASM
    bsf RXB0CON, 3
  End ASM
  CANCON = CANCON And %11110001
  result = true
  Restore
End Function
{
****************************************************************************
* Name    : CANReceiveMessage                                              *
* Purpose : If at least one full receive buffer is found,                  *
*           it is extrated and returned.                                   *
*           If none found FALSE value is returned.                         *
****************************************************************************
} 
Public Function CANReceiveMessage(ByRef id As LongWord, ByRef Data(), DataLen, flags As Byte) As Boolean
Dim i, buf As Byte,
    lbIsItBuffer0 As Boolean  
  Save(FSR1, FSR2)
  flags = 0                         // Start with no error or flags set.
  If RXB0CON_RXFUL = 1 Then         // Find which buffer is ready.    
    CANCON = CANCON And %11110001   // RXBuffer0 is full.
    lbIsItBuffer0 = true
    PIR3_RXB0IF = 0                 // Clear the received flag.    
    If COMSTAT_RX0OVFL = 1 Then     // Record and forget any previous overflow
      flags = flags Or CAN_RX_OVERFLOW
      COMSTAT_RX0OVFL = 0
    End If
    If RXB0CON_RX0DBEN = 1 Then
      flags = (flags Or RXB0CON) And CAN_RX_FILTER_BITS
      flags = flags And $01
    End If
  Else
    FSR2 = AddressOf(RXB1CON)       // For compatibility with SwordfishSE
    If INDF2.bits(RXFUL) = 1 Then
//    If RXB1CON_RXFUL = 1 Then
      CANCON = CANCON And %11110001 // RXBuffer1 is full
      CANCON = CANCON Or %00001010
      lbIsItBuffer0 = false      
      PIR3_RXB1IF = 0               // Clear the received flag.
      If COMSTAT_RX1OVFL = 1 Then   // Record and forget any previous overflow
        flags = flags Or CAN_RX_OVERFLOW
        COMSTAT_RX1OVFL = 0
      End If
      FSR2 = AddressOf(RXB1CON)      // For compatibility with SwordfishSE
      flags = (flags Or INDF2) And CAN_RX_FILTER_BITS
//      flags = (flags Or RXB1CON) And CAN_RX_FILTER_BITS
      If flags < $02 Then
        flags = flags Or CAN_RX_DBL_BUFFERED
      End If
    Else
      result = false
      Exit
    End If
  End If 
  DataLen = RXB0DLC And %00001111   // Retrieve message length.  
  If RXB0DLC_RTR = 1 Then           // Determine whether this was RTR or not.
    flags = flags Or CAN_RX_RTR_FRAME
  End If 
  If RXB0SIDL_EXID = 1 Then         // Retrieve EIDX bytes only if this is extended message
    flags = flags Or CAN_RX_XTD_FRAME
    id = RegsToCANID(AddressOf(RXB0SIDH), CAN_CONFIG_XTD_MSG)
  Else
    id = RegsToCANID(AddressOf(RXB0SIDH), CAN_CONFIG_STD_MSG)
  End If  
  i = 0
  buf = DataLen                     // Get message data itself
  FSR1 = AddressOf(RXB0D0)
  FSR2 = AddressOf(Data)
  While i < buf 
    POSTINC2 = POSTINC1
    Inc(i)
  Wend  
  CANCON = CANCON And %11110001     // Restore default RXB0 mapping.  
  If PIR3_IRXIF = 1 Then            // Record and Clear any previous invalid message bit flag.
    flags = flags Or CAN_RX_INVALID_MSG
    PIR3_IRXIF = 0
  End If
  If lbIsItBuffer0 = true Then 
    RXB0CON_RXFUL = 0
  Else
    FSR2 = AddressOf(RXB1CON)       // For compatibility with SwordfishSE
    INDF2.bits(RXFUL) = 0
//    RXB1CON_RXFUL = 0
  End If
  result = true
  Restore
End Function
{
****************************************************************************
* Name    : String2ID                                                      *
* Purpose : Transform 3 Char String into LongWord id                       *
****************************************************************************
}
Public Function String2ID(str_in As String) As LongWord
  Save(FSR2)
  FSR2 = AddressOf(str_in)
  result.Byte3 = 0
  result.Byte2 = POSTINC2
  result.Byte1 = POSTINC2
  result.Byte0 = INDF2
  Restore
End Function