Electronic – Microchip PIC18F CAN message reception problem

canpic

I am attempting CAN communication between two nodes and can not get the receiver to transfer a message from the MAB to a readable buffer. The nodes are identical, physically and in configuration. I am transmitting at 40kbps. The receiver will send the acknowledge bit and I have confirmed that a valid message is being sent with a Saleae Logic connected to PC. Also, when connected to MPLABX with PICKit3 for debugging I am able to see that neither the TXERRCNT of the trasmiter nor the RXERRCNT of the receiver are logging any errors. I have RXB0CON, RXM1 set which means the receiver should be transferring all messages, including those with errors, to RXB0 ignoring filters and masks. I have tried the same setting with RXB1CON and nothing is being transferred to that register either. I am strictly using Assembly, no canned routines. I have also tried starting with a "bootup message" of SID set to all 0's and 0 DLC prior to sending a normal message but it does not make any difference.

Hardware is PIC18F25K80 connected to MCP2561. Bus is properly terminated with 120 ohm resistors at each node and meter shows 60 ohms on bus when nodes are not powered so no shorted or open terminators.

Thanks in advance to anyone that may offer suggestions.

Best Answer

Here is the remainder of the file that didn't fit into the previous answer. This part performs the actual sending and receiving at run time.

;*******************************************************************************
;
;   CAN receiving task.
;
can_task unbank              ;task start address
;
;   Wait for the software receive buffer to be empty.  Another task may be
;   reading the last received frame from the buffer.  When done with the data
;   in the receive buffer, FLAG_CANIN will be cleared.
;
wait_swbuf unbank            ;back here until software buffer ready for next frame
         gcall   task_yield  ;give other tasks a chance to run
         dbankif gbankadr
         btfsc   flag_canin  ;software CAN frame receive buffer is empty ?
         jump    wait_swbuf  ;no, back and check again
;
;   Clear the software receive buffer to all zeros.  This makes it easier later
;   to assemble the fields in the buffer from the hardware registers.
;
         dbankif gbankadr
         bcf     flag_canin_ext ;init to not extended frame
         bcf     flag_canin_rtr ;init to not remote request frame

         loadk32 canin_id, 0 ;set ID to 0
         clrf    canin_ndat  ;init number of data bytes
         clrf    canin_dat+0 ;init the data bytes
         clrf    canin_dat+1
         clrf    canin_dat+2
         clrf    canin_dat+3
         clrf    canin_dat+4
         clrf    canin_dat+5
         clrf    canin_dat+6
         clrf    canin_dat+7

wait_recv unbank             ;back here until a new CAN frame is received
         gcall   task_yield  ;give other tasks a chance to run
         dbankif comstat
         btfss   comstat, 7  ;at least one message in receive FIFO ?
         jump    wait_recv   ;no, go back and check again
;
;   Map the current receive buffer to the access bank region F60-F6Dh.  This is
;   where the fixed receive buffer 0 is located in legacy mode.  The symbols
;   RXB0xxx will therefore be used to access the buffer, even though it could
;   be any of the 8 receive buffers.
;
         dbankif cancon
         movf    cancon, w
         andlw   b'00001111' ;mask in only ID of the current receive buffer
         addlw   ecanconr0   ;merge with other control bits
         dbankif ecancon
         movwf   ecancon     ;map the current receiver buffer to access window
;
;   A new CAN frame has been received and hardware receive buffer containing it
;   has been mapped into the access bank where the fixed receive buffer 0
;   normally is.
;
         dbankif gbankadr
         btfsc   rxb0con, rtrro ;this is data frame, not remote request ?
         bsf     flag_canin_rtr ;is remote request frame
         btfsc   rxb0sidl, exid ;standard frame, not extended ?
         jump    recv_ext    ;extended frame
         ;
         ;   Standard frame.  Get the 11 bit frame ID.
         ;
         swapf   rxb0sidl, w
         rrncf   wreg
         andlw   b'00000111'
         iorwf   canin_id+0  ;set ID bits 

         rlncf   rxb0sidh, w
         rlncf   wreg
         rlncf   wreg
         andlw   b'11111000'
         iorwf   canin_id+0  ;set ID bits 

         swapf   rxb0sidh, w
         rrncf   wreg
         andlw   b'00000111'
         iorwf   canin_id+1  ;set ID bits 
         jump    done_id     ;done assembling ID
         ;
         ;   Extended frame.  Get the 29 bit frame ID.  In this case, the
         ;   extended ID bits form the low 18 bits of the ID and the standard
         ;   ID bits the high 11.
         ;
recv_ext dbankis gbankadr
         bsf     flag_canin_ext ;indicate extended frame

         movf    rxb0eidl, w
         iorwf   canin_id+0  ;set ID bits 

         movf    rxb0eidh, w
         iorwf   canin_id+1  ;set ID bits 

         movf    rxb0sidl, w
         andlw   b'00000011'
         iorwf   canin_id+2  ;set ID bits 

         swapf   rxb0sidl, w
         rlncf   wreg
         andlw   b'00011100'
         iorwf   canin_id+2  ;set ID bits 

         swapf   rxb0sidh, w
         rlncf   wreg
         andlw   b'11100000'
         iorwf   canin_id+2  ;set ID bits 

         rrncf   rxb0sidh, w
         rrncf   wreg
         rrncf   wreg
         andlw   b'00011111'
         iorwf   canin_id+3  ;set ID bits 

done_id  dbankis gbankadr    ;done assembling ID bits into CANIN_ID
;
;   Get the data bytes.
;
         movf    rxb0dlc, w
         andlw   b'00001111' ;mask in only the number of data bytes
         bz      done_dat    ;no data bytes ?
         movwf   reg2        ;save number data bytes in REG2
         sublw   8           ;compare to max valid number
         skip_wle            ;number of data bytes is within range ?
         jump    done_dat    ;skip the data bytes, something is wrong
         movf    reg2, w
         movwf   canin_ndat  ;set 1-8 number of data bytes in frame
         ;
         ;   Copy the data bytes in a unrolled loop.  It would be faster for
         ;   large frames to just copy all 8 bytes all the time.  Only the
         ;   actual data bytes are copied mostly to aid in debugging.  The
         ;   unused data bytes in the software buffer will therefore be zero.
         ;
         movff   rxb0d0, canin_dat+0
         dcfsnz  reg2
         jump    done_dat

         movff   rxb0d1, canin_dat+1
         dcfsnz  reg2
         jump    done_dat

         movff   rxb0d2, canin_dat+2
         dcfsnz  reg2
         jump    done_dat

         movff   rxb0d3, canin_dat+3
         dcfsnz  reg2
         jump    done_dat

         movff   rxb0d4, canin_dat+4
         dcfsnz  reg2
         jump    done_dat

         movff   rxb0d5, canin_dat+5
         dcfsnz  reg2
         jump    done_dat

         movff   rxb0d6, canin_dat+6
         dcfsnz  reg2
         jump    done_dat

         movff   rxb0d7, canin_dat+7

done_dat dbankis gbankadr    ;done copying all data bytes into the software buffer
         bcf     rxb0con, rxful ;mark HW buffer as empty, allow it to be re-used
;
;   Tell the rest of the system that a new CAN frame has been received.
;   FLAG_CANIN is always set.  This can be used by other parts of the system as
;   a event flag to read the CAN frame in the CANIN buffer.  This other code
;   must clear FLAG_CANIN when done so that this task can read the next received
;   CAN frame into the buffer.
;
;   For some systems, waiting for FLAG_CANIN to be noticed may be too slow.  For
;   such cases there is a callback mechnism.  When the preprocessor string
;   constant CANIN_CALLBACK is not the empty string, it is taken as the name of
;   a subroutine to call.  This routine can perform immediate action from this
;   task.  In any case, whatever code in the rest of the system handles the
;   received CAN frame must also clear FLAG_CANIN when it is done reading the
;   CANIN buffer.
;
         dbankif gbankadr
         bsf     flag_canin  ;indicate a new received frame is in the CANIN buffer

/if [> [slen canin_callback] 0] then ;callback routine defined ?
  /if callback_extern then
         extern  [chars canin_callback]
    /endif
         call    [chars canin_callback]
  /endif

         jump    wait_swbuf  ;back to wait for done with this frame

;*******************************************************************************
;
;   Subroutine CAN_SEND_INIT
;
;   Init the transmit frame state.  REG0 contains flag bits indicating the type
;   of frame:
;
;     Bit 0  -  0 = standard frame, 11 bit ID
;               1 = extended frame, 29 bit ID
;
;     Bit 1  -  0 = data frame
;               1 = remote request frame
;
;   The transmit state frame has a interlock so that only one task at a time can
;   attempt to send a frame.  This routine waits for the transmit frame state to
;   be available, then locks it.  Since this routine must always be called each
;   new CAN frame transmitted, the caller has exclusive access to the transmit
;   state until it is released by CAN_SEND.
;
         glbsub  can_send_init, noregs
;
;   Wait for the transmit frame state to not be in use, then lock it for our
;   use.
;
sendi_wait unbank
         dbankif gbankadr
         btfss   flag_cansend ;transmit frame still in use ?
         jump    sendi_avail ;no, it is available
         gcall   task_yield_save ;give other tasks a chance to run
         jump    sendi_wait  ;back to check again
sendi_avail dbankis gbankadr ;transmit frame state is not in use

         bsf     flag_cansend ;indicate it is now in use
         movff   currtask, canwtask ;save ID of task that has transmit state locked
;
;   Initialize the transmit frame state.
;
         dbankif lbankadr
         movf    reg0, w     ;get the flags byte
         andlw   b'00000011' ;mask in only the valid flags
         movwf   wr_flags    ;init transmit frame flags byte

         loadk32 wr_id, 0    ;init all the ID bits to 0
         loadk8  wr_ndat, 0  ;init number of data bytes to 0

         leaverest

;*******************************************************************************
;
;   Subroutine CAN_SEND_ID
;
;   Set the ID of the transmit frame state.  If this is a standard frame, then
;   the ID is in the low 11 bits of REG1:REG0.  If this is a extended frame,
;   then the ID is in the low 29 bits of REG3:REG2:REG1:REG0.
;
         glbsub  can_send_id, noregs

         dbankif lbankadr
         movff   reg0, wr_id+0 ;set low 8 bits of ID
         btfsc   wr_flags, 0 ;this is a standard frame ?
         jump    sid_ext     ;extended frame
;
;   Standard frame.  ID is 11 bits.
;
         movf    reg1, w
         andlw   b'00000111' ;mask in valid ID bits only
         movwf   wr_id+1     ;save ID high byte
         clrf    wr_id+2     ;clear unused ID bytes
         clrf    wr_id+3
         jump    sid_leave
;
;   Extended frame.  ID is 29 bits.
;
sid_ext  dbankif lbankadr
         movff   reg1, wr_id+1
         movff   reg2, wr_id+2
         movf    reg3, w
         andlw   b'00011111' ;mask in valid ID bits of high byte only
         movwf   wr_id+3

sid_leave unbank             ;common exit point
         leaverest

;*******************************************************************************
;
;   Subroutine CAN_SEND_DAT
;
;   Add the byte in REG0 as the next data byte in the transmit frame state.
;   Data bytes beyond what the CAN frame can contain are ignored.
;
         glbsub  can_send_dat, noregs

         dbankif lbankadr
         btfsc   wr_flags, 1 ;normal, not remote request frame
         jump    sdat_leave  ;remote request frames don't have data bytes

         movf    wr_ndat, w  ;get number of data bytes already stored
         sublw   7           ;compare to max with any room left
         skip_wle            ;still room for another data byte ?
         jump    sdat_leave  ;no
         lfsr    0, wr_dat   ;init pointer to first data byte
         movf    wr_ndat, w
         addwf   fsr0l       ;add offset to new data byte to write
         movff   reg0, indf0 ;stuff the data byte into the transmit frame buffer
         incf    wr_ndat     ;update number of data bytes stored

sdat_leave unbank            ;common exit point
         leaverest

;*******************************************************************************
;
;   Subroutine CAN_SEND
;
;   Send the frame stored in the current transmit frame state.  This routine
;   returns when the transmission has been initiated.  The actual transmission
;   may not occur until later, and may fail.  However, after this call the
;   information about the frame will have been transferred into the hardware and
;   the lock on the software transmit frame state released.
;
         glbsub  can_send, noregs
;
;   Wait for any previous frame to finish transmission.
;
snd_wait unbank
         dbankif txb0con
         btfss   txb0con, txreq ;previous transmission still in progress ?
         jump    snd_ready   ;no
         gcall   task_yield_save ;give other tasks a chance to run
         jump    snd_wait
snd_ready unbank             ;HW is ready for next transmission
;
;   Load the transmit buffer control registers sequentially starting with
;   TXB0SIDH.  The registers that will be loaded, in order, are:
;
;     SIDH
;     SIDL
;     EIDH
;     EIDL
;     DLC
;
         dbankif lbankadr
         lfsr    0, txb0sidh ;init pointer to first sequential register
         btfsc   wr_flags, 0
         jump    snd_ext
         ;
         ;   This is a standard frame, 11 bit ID.
         ;
         rrncf   wr_id+0, w
         rrncf   wreg
         rrncf   wreg
         andlw   b'00011111'
         movwf   indf0       ;set standard address bits 
         swapf   wr_id+1, w
         rlncf   wreg
         andlw   b'11100000'
         iorwf   postinc0    ;set standard address bits 

         swapf   wr_id+0, w
         rlncf   wreg
         andlw   b'11100000'
         movwf   postinc0    ;set standard address bits 

         clrf    postinc0    ;EIDH not used in standard address mode
         clrf    postinc0    ;EIDL not used in standard address mode
         jump    snd_doneadr ;done setting address bits
         ;
         ;   This is a extended frame, 29 bit ID.
         ;
snd_ext  dbankis lbankadr
         rlncf   wr_id+3, w
         rlncf   wreg
         rlncf   wreg
         andlw   b'11111000'
         movwf   indf0       ;set ID bits 
         swapf   wr_id+2, w
         rrncf   wreg
         andlw   b'00000111'
         iorwf   postinc0    ;set ID bits 

         rlncf   wr_id+2, w
         rlncf   wreg
         rlncf   wreg
         andlw   b'11100000'
         movwf   indf0       ;set ID bits 
         bsf     indf0, exide ;indicate this is a extended frame
         movf    wr_id+2, w
         andlw   b'00000011'
         iorwf   postinc0    ;set ID bits 

         movff   wr_id+1, postinc0 ;set ID bits 
         movff   wr_id+0, postinc0 ;set ID bits 
snd_doneadr dbankis lbankadr ;done setting address bits

         movf    wr_ndat, w  ;get number of data bytes
         btfsc   wr_flags, 1 ;data frame ?
         iorlw   b'01000000' ;no, remote request frame
         movwf   postinc0
;
;   Load the data bytes.
;
         movff   wr_dat+0, postinc0
         movff   wr_dat+1, postinc0
         movff   wr_dat+2, postinc0
         movff   wr_dat+3, postinc0
         movff   wr_dat+4, postinc0
         movff   wr_dat+5, postinc0
         movff   wr_dat+6, postinc0
         movff   wr_dat+7, postinc0
;
;   Write the transmit buffer control byte.  The TXREQ bit will be set to one,
;   which starts the transmission.
;
         setreg  b'00001000', txb0con
                 ; 0-------  clear transmit completed flag
                 ; -XXX----  read-only status bits
                 ; ----1---  request transmission
                 ; -----X--  unused
                 ; ------00  priority level

         dbankif gbankadr
         bcf     flag_cansend ;release lock on transmit frame state

         leaverest