You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

MCP2515 CAN控制器多帧传输始终超时触发ABORT求助

ISO11783/J1939多帧传输失败排查(MCP2515+简易处理器)

问题现象

  • 单帧CAN消息收发完全正常,但使用全局BAM通信或ISO11783/J1939传输协议进行多帧数据传输时,数据无法成功送达接收端
  • 多帧传输流程:
    1. 发送PDU 236格式RTS宣告消息,包含所需字节数与数据包数量(如10个数据包)
    2. 收到接收端返回的PDU 236格式CTS消息,确认允许发送全部10个数据包
    3. 通过PDU 235帧发送每个数据包,已尝试0至200毫秒的不同延时
    4. 短暂无响应后,收到带超时原因的PDU 236格式ABORT消息
  • 已尝试仅使用MCP2515的buffer[0],等待缓冲区标记为空后再加载下一帧以确保帧顺序正确,但问题仍未解决
  • 目标:让多款厂商显示系统读取数据并返回EOF响应,目前未达成

相关代码

//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;
}
//---------------------------------------------------------------------

排查建议

  1. CTS参数解析校验:检查ID_CAN_MESSAGEinloop函数对CTS消息的解析逻辑,确认cts.next2Send(起始包序号)和cts.no2Send(允许发送的包数量)是否与接收端要求完全匹配,避免出现起始序号错误或发送数量超出接收端允许范围的情况。
  2. PDU 235帧格式合规性检查:
    • 验证ISO_TP_DATA生成的数据包序号是否从1开始严格递增,且总数与RTS中声明的一致
    • 确认数据段长度:除最后一帧外,每帧必须携带7字节数据;最后一帧携带剩余字节数,不能出现长度错误
    • 检查PGN、源地址、目标地址是否与RTS/CTS消息完全一致,避免地址或协议组不匹配导致接收端丢弃数据
  3. MCP2515发送时序优化:
    • 当前sendCANmessage仅在发送前检查缓冲区状态,发送后直接调用RTSbuffer触发发送,需确认是否等待缓冲区真正发送完成再继续下一帧。可在发送后增加对TX缓冲区发送完成标志的轮询,确保前一帧完全发送后再加载下一帧
    • 确认CAN总线波特率与接收端严格一致,波特率偏差会导致多帧传输时出现丢包或帧错误
  4. 超时逻辑调整:
    • 主循环超时设置为3000ms,但发送多个数据包时,每个包发送完成后应重置超时计时器,避免因发送多包耗时过长误触发ABORT
    • 收到ABORT消息时,打印消息中的具体原因码(ISO11783规范定义的超时、格式错误、缓冲区溢出等),精准定位问题根源
  5. 数据字节序验证:检查encode_CAN_TxBufferExtended函数中数据字节的填充顺序是否符合CAN帧要求,避免因字节序颠倒导致接收端解析数据失败

内容的提问来源于stack exchange,提问作者josie hinton

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 19:37:09