Modifying Cory Fowler's CAN bus MCP2515 library for use at slow data rates

I'm running a CAN bus at 20k instead of the the usual 125k or 500k found in cars.

At this slow data rate I get transmit timeout errors due to the presence of a hard coded value in a retry loop where it checks the chip 50 times to see if the data has been sent or else returns CAN_SENDMSGTIMEOUT. The data does actually get sent from the chip, it's just that the Arduino code returns too early and indicates an error when there isn't one.

At high bus speeds this isn't a problem as by the time it has gone through the loop 50 times the data frame has left the chip.
At 20k I found by trial and error that I have to change this hard coded value to 300 in order to give the chip enough time to push out a data frame.

In the code the retry count is is labelled TIMEOUTVALUE, which is a bit confusing as it isn't a time value, it's just a retry count, which by virtue of the SPI and chip speed indirectly results in a time delay.

A couple of questions:

  1. Is it really necessary to block waiting for the chip to push the data frame out. Why not return immediately?
    Wouldn't a Tx failure be evident when it fails to return a Tx buffer the next time you try to send a message. Alternatively you could periodically check the Tx status in the Arduino main loop rather than having it block.

  2. If blocking is necessary, how would you modify the code to dynamically choose a sensible timeout value depending on the data rate, bearing in mind that congestion on the CAN bus will also influence how long it takes to send a frame?

The "#define TIMEOUTVALUE 50" is defined in

The sendMsg() function is defined in

INT8U MCP_CAN::sendMsg()
{
    INT8U res, res1, txbuf_n;
    uint16_t uiTimeOut = 0;

    do {
        res = mcp2515_getNextFreeTXBuf(&txbuf_n);                       /* info = addr.                 */
        uiTimeOut++;
    } while (res == MCP_ALLTXBUSY && (uiTimeOut < TIMEOUTVALUE));

    if(uiTimeOut == TIMEOUTVALUE) 
    {   
        return CAN_GETTXBFTIMEOUT;                                      /* get tx buff time out         */
    }
    uiTimeOut = 0;
    mcp2515_write_canMsg( txbuf_n);
    mcp2515_modifyRegister( txbuf_n-1 , MCP_TXB_TXREQ_M, MCP_TXB_TXREQ_M );
    
    do
    {
        uiTimeOut++;        
        res1 = mcp2515_readRegister(txbuf_n-1);                         /* read send buff ctrl reg 	*/
        res1 = res1 & 0x08;                               		
    } while (res1 && (uiTimeOut < TIMEOUTVALUE));   
    
    if(uiTimeOut == TIMEOUTVALUE)                                       /* send msg timeout             */	
        return CAN_SENDMSGTIMEOUT;
    
    return CAN_OK;
}

I seem to be having some issues with the transmit function as well. So I created A receive and Transmit FIFO in my program. However, I am looking for a way to determine that a transmit buffer is available before sending a message just so i can do other things in my code without having to wait for a buffer to become available.
Any one know how I can do this?

johnfarrugia:
I seem to be having some issues with the transmit function as well. So I created A receive and Transmit FIFO in my program. However, I am looking for a way to determine that a transmit buffer is available before sending a message just so i can do other things in my code without having to wait for a buffer to become available.
Any one know how I can do this?

something like this maybe (snippet below)

  digitalWrite(CS, LOW);
	  
  SPI.transfer(0xA0); //SPI_READ_STATUS command
  
  uint8_t r_status = SPI.transfer(0xff);
	  
  digitalWrite(CS, HIGH);
  
  /* Statusbyte:
   *
   * Bit  Function
   *  2 TXB0CNTRL.TXREQ
   *  4 TXB1CNTRL.TXREQ
   *  6 TXB2CNTRL.TXREQ
   */
  
  if (!(r_status & (1<<2))) {
    addr = 0x00;
  }
  else if (!(r_status & (1<<4))) {
    addr = 0x02;
  } 
  else if (!(r_status & (1<<6))) {
    addr = 0x04;
  }
  else {
    // all transmit buffer used => could not send message
    return 0;
  }