1

I'm attempting to use a simple processor and a MCP2515 CAN controller over SPI communication. I have no problems sending or receiving single frame CAN messages. However, when I try either global BAM messaging or using the Transport Protocol, I cannot get the multiframe data through to the other side.

Example:

  • Transport Protocol J1939 / ISO11783, send TP announcement message PDU 236 (RTS)- with number bytes and number packets required, say 10 packets.
  • Receive TP clear to send (CTS) PDU 236 from receiver with OK and flagged to send all 10 packets.
  • I then send each packet in PDU 235 frames, (tried with varying time delays from 0 to 200 msec).
  • Then a short time with no response, then I get PDU 236 with ABORT code and reason timeout.

Now I thought about the MCP2515 having 3 send buffers. Perhaps as I loaded them they were sent out of sequence. So I modified the code to use buffer[0] on the MCP2515 only by waiting until that buffer is flagged as empty before loading the next frame. This should make frame sequence a certainty.

Any leads here would be appreciated. The goal is simply to get the receiving end (which is any number of manufacturer display systems) to read the data and send a EOF response showing the data was received. This is not happening as of now.

//function to organize and send multi-packets with flow control protocol
bool ISO11783DATA::sendTPCM(byte reqADDR, byte* buff, uint16_t noBytes, uint32_t PGN, byte priority)        
{
    if(busONstate == true)
    {
        RECEIVE_CTS     cts;
        MFMESSOUT       outmess;
        //load frames
        uint8_t noFrames = outmess.loadBuffers(buff, noBytes);

        uint16_t byteCounter = 0;

        cts.PGN = PGN;  //set to compare with rx messages

        ISO_TPCM_RTS(reqADDR, noFrames, noBytes, PGN, priority);    //announce multiframe header
        if(*diagnostics == true)
        {
            informln(F("sent TCPM announcement"));
            inform(F("noFrames= ")); inform(noFrames); inform(F(" noBytes= ")); informln(noBytes);
        }
        bool clearToSend = false;
        bool messageComplete = false;
        unsigned long timeOUT = millis();   //break timer

        while(messageComplete == false)
        {
            if( millis()-timeOUT > 3000UL)//1250UL)  //time out break
            {
                ISO_TPCM_ABORT(reqADDR, priority, PGN);
                if(*diagnostics == true){informln(F("tcpm send timeout error"));}
                return false;
            }

            if(clearToSend == true) //load next frame and send 
            {
                clearToSend = false;
                uint8_t frameIndex = cts.next2Send-1;//if says send#1 send index 0

                for(uint8_t cntout = 0; cntout<cts.no2Send; cntout++)
                {
                    byte bytesInMessage = 7;
                    if( frameIndex == cts.no2Send-1 ){bytesInMessage = outmess.noInLastFrame;}


                    if(*diagnostics == true)
                    {
                        inform(F("sending multipacket data number ")); informln(frameIndex+1);
                        inform("no in this frame ="); informln(bytesInMessage);
                        //inform("no in last frame ="); informln(outmess.noInLastFrame);
                        byteCounter += bytesInMessage;
                        inform(F("sent bytes= ")); informln(byteCounter);
                    }
                    ISO_TP_DATA(reqADDR, frameIndex+1, bytesInMessage+1, outmess.data[frameIndex], priority);  //send packet
                    frameIndex++;   //send number it allowed then wait for CTS again
                    timeOUT = millis();
                    //if(cts.no2Send > 1 && cntout<cts.no2Send){delay(10);} //only delay till next send time unless done
                }
            }//end CST true

            //listen for CTS EOF or ABORT
            if(pCAN0->available() > 0 ){pCAN0->readCANbus();}//checks int pin reads in and flags can buffers
            for(int n=0; n<numberCanBuffers; n++)
            {
                if(pCAN0->isoFrame[n].newData == true)
                {
                    pCAN0->isoFrame[n].newData= false;
                    int action = ID_CAN_MESSAGEinloop( pCAN0->isoFrame[n], cts );   //checks data in frame now, returns new cts info if multiframe response, since waiting must not initiate another multiframe
                    if(action == CTS && cts.PGN == PGN) //filters so response is for this node and tx message
                    {
                        timeOUT = millis(); //reset timeout since new rx message
                        clearToSend = true; //ready to send more
                    }
                    if(action == EOF && cts.PGN == PGN)
                    {
                        timeOUT = millis();//reset timeout since new rx message
                        messageComplete = true;
                        //inform("noByes "); inform(cts.bytesReceived); inform("\tnoPackets "); inform(cts.packetsReceived); inform("\PGN "); informln(cts.PGN);
                        return true;
                    }
                    if(action == ABORT && cts.PGN == PGN)
                    {
                        inform("ordered Abort   "); informln(cts.PGN);
                        return false;
                    }
                }
            }//end for check rx buffers


        }//end while mess complete false loop
    }//end if bus on
    return false;
}//end TCPM
//---------------------------------------------------------------------
//returns to state ability to load tx buffer mcp2515
bool ISO11783DATA::send_singleFrame_Buff(byte* buf, byte priority, byte PDUF, byte PDUS, byte datalength, byte datapage, int TXindex)
{
    unsigned long timeout = millis();
    CAN_ID_t out;
    out.PRIORITY     = priority;
    out.extDATAPAGE  = 0;
    out.DATAPAGE     = datapage;
    out.PDU_FORMAT   = PDUF;
    out.PDU_SPECIFIC = PDUS;
    if(nodeADDRresolved == true){out.SRC_ADDR = nodeADDR;}
    else{out.SRC_ADDR = 254;}
    out.DATA_LENGTH  = datalength;
    out.RTR          = 0;
    out.IDExt        = 1; //true
    out.buffer[0] = buf[0];
    out.buffer[1] = buf[1];
    out.buffer[2] = buf[2];
    out.buffer[3] = buf[3];
    out.buffer[4] = buf[4];
    out.buffer[5] = buf[5];
    out.buffer[6] = buf[6];
    out.buffer[7] = buf[7];
    //check if buffer ready
    while(pCAN0->sendFrame(out, TXindex) == false)
    {
        if(millis() - timeout >= 1250UL){return false;}
    }
    return true;
}//end send single frame from selected mcp2515 txbuff
//-----------------------------------------------------------------------
    bool MPC2515CANBUS::sendFrame(CAN_ID_t struc, int index)
{
    byte buffer[14];
    if(struc.IDExt > 0)
    {
        encode_CAN_TxBufferExtended(buffer, (byte)struc.PRIORITY, (byte)struc.extDATAPAGE,  (byte)struc.DATAPAGE, (byte)struc.PDU_FORMAT, (byte)struc.PDU_SPECIFIC,
                                                        (byte)struc.SRC_ADDR, struc.RTR, (byte)struc.DATA_LENGTH,
                                                        struc.buffer[7], struc.buffer[6],struc.buffer[5],struc.buffer[4],struc.buffer[3],struc.buffer[2],struc.buffer[1],struc.buffer[0]);
    }
    if(struc.IDExt == 0)
    {
        encode_CAN_TxBufferStandard(buffer, (unsigned int)struc.PGN,  (byte)struc.SRC_ADDR, struc.RTR, (byte)struc.DATA_LENGTH,
                                    struc.buffer[7], struc.buffer[6],struc.buffer[5],struc.buffer[4],struc.buffer[3],struc.buffer[2],struc.buffer[1],struc.buffer[0]);

    }
    return sendCANmessage(buffer, index);
}
//------------------------------------------------------
bool MPC2515CANBUS::sendCANmessage(byte* buffer, int index)
{
      if(checkCANTXbuf(index) != 0) //buffer not yet sent // function checks flag bit to see if buffer is empty or not
      {
          informln("txbuff not ready");
          return false;
      }
      byte ADDR;
      if(index == 0){ADDR = 0x31;}
      if(index == 1){ADDR = 0x41;}
      if(index == 2){ADDR = 0x51;}
      digitalWrite(CS_can, LOW);
      SPI.transfer(WRITE);
      SPI.transfer(ADDR);
      for(int n=0; n<13; n++){SPI.transfer(buffer[n]);}
      digitalWrite(CS_can, HIGH);
      RTSbuffer(index);
      return true;
}
//---------------------------------------------------------------------

  • I don't know this particular controller (it's been out of fashion for 20 years) but I think you are onto something regarding the buffers. These old CAN controllers often had a stupid "priority" feature between tx buffers, separated from CAN id which you'd expect to set priority. If you don't set this separate tx priority explicitly, it will act as a "LIFO". While the programmer expects it to act as a FIFO. I don't see where in the code you poll the CAN controller before transmission to see if the previous transmission is done? – Lundin May 02 '23 at 07:41
  • @Lundin I posted the additional functions up to the one that polls the controller. The one that actually polls the flag bits is 'checkCANTXbuf(index)' in the last "sendCANmessage" function. The CAN controller IC is just really commonly available and I'm just using it with an old but extremely available micro controller that does not have built in CAN controller functions. – josie hinton May 03 '23 at 13:43
  • `for(int n=0; n<13;` looks fishy, this should be 8 or some `sizeof`, not a "magic number". When you say "old but extremely available" do you mean some PIC or AVR? As in no struct padding? – Lundin May 03 '23 at 14:21
  • `if(index == 0){ADDR = 0x31;} if(index == 1){ADDR = 0x41;}` looks fishy, shouldn't it be `else if`? – Lundin May 03 '23 at 14:21
  • Stack allocating buffers such as `byte buffer[14];` or `CAN_ID_t out;` will be very dangerous assuming one of the mentioned dinosaur 8 bit archtectures. PIC in particular is like actively looking for a reason to cause stack overflow at any given time. But any 8-bitter will have poor RAM. Allocate all such buffers `static` not to waste precious stack space. – Lundin May 03 '23 at 14:29
  • @Lundin, AVR ATMEGA2560. Yes RAM is limited but using globally defined eats all the RAM on the chip. It functions much better only using the need amount in each function. As it is single threaded only. – josie hinton May 04 '23 at 15:05
  • @Lundin, Oh, I see your point of using static declarations. Yes the MCP2515 has priority based TX buffers. First is message CAN priority and secondly is the higher index number buffer. So index 2 of the 3 total TX buffers. The defined buffer sizes are specific to match the flag bits and message bytes that the RX buffer and TX buffers use on the MCP2515. I have had similar code working before on a SAM processor but that had FIFO built onboard CAN controllers. I might pull one of those out and play with the code on it. This seems to be something specific to using the stand alone CAN controller. – josie hinton May 04 '23 at 15:17
  • These buffers take the same amount of RAM no matter where you allocate them, except in case of the stack they silently contribute to the eventual stack overflow. Placing them in `.data`/`.bss` means that you can easier keep track of how much memory you are actually using. You are going to want at least one of these buffers allocated at all times. Default init values can be placed in flash though, by using `const` plus whatever strange non-standard keyword AVR needs because of the Harvard architecture. – Lundin May 04 '23 at 15:42
  • (And why swap from ATSAM to MCP2515... that's like trading in your brand new Porsche for a rusty VW Beetle from 1950 :) SAMC has a great CAN controller with DMA-like features, non-strange FIFO, no strange priorities, mailboxes and CAN FD support.) – Lundin May 04 '23 at 15:46
  • @Lundin no on the SAM I had similar messaging code but not using the MCP2515. On the SAM I had used the onboard CAN and it worked quite well. I am trying to get basic working multiframe with the mega2560 right now to avoid laying out a new board. It may turn out better to redesign using a SAM and just modify as needed the prior SAM code. – josie hinton May 05 '23 at 16:28

0 Answers0