MCP2515 CAN控制器多帧传输始终超时触发ABORT求助
ISO11783/J1939多帧传输失败排查(MCP2515+简易处理器)
问题现象
- 单帧CAN消息收发完全正常,但使用全局BAM通信或ISO11783/J1939传输协议进行多帧数据传输时,数据无法成功送达接收端
- 多帧传输流程:
- 发送PDU 236格式RTS宣告消息,包含所需字节数与数据包数量(如10个数据包)
- 收到接收端返回的PDU 236格式CTS消息,确认允许发送全部10个数据包
- 通过PDU 235帧发送每个数据包,已尝试0至200毫秒的不同延时
- 短暂无响应后,收到带超时原因的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; } //---------------------------------------------------------------------
排查建议
- CTS参数解析校验:检查
ID_CAN_MESSAGEinloop函数对CTS消息的解析逻辑,确认cts.next2Send(起始包序号)和cts.no2Send(允许发送的包数量)是否与接收端要求完全匹配,避免出现起始序号错误或发送数量超出接收端允许范围的情况。 - PDU 235帧格式合规性检查:
- 验证
ISO_TP_DATA生成的数据包序号是否从1开始严格递增,且总数与RTS中声明的一致 - 确认数据段长度:除最后一帧外,每帧必须携带7字节数据;最后一帧携带剩余字节数,不能出现长度错误
- 检查PGN、源地址、目标地址是否与RTS/CTS消息完全一致,避免地址或协议组不匹配导致接收端丢弃数据
- 验证
- MCP2515发送时序优化:
- 当前
sendCANmessage仅在发送前检查缓冲区状态,发送后直接调用RTSbuffer触发发送,需确认是否等待缓冲区真正发送完成再继续下一帧。可在发送后增加对TX缓冲区发送完成标志的轮询,确保前一帧完全发送后再加载下一帧 - 确认CAN总线波特率与接收端严格一致,波特率偏差会导致多帧传输时出现丢包或帧错误
- 当前
- 超时逻辑调整:
- 主循环超时设置为3000ms,但发送多个数据包时,每个包发送完成后应重置超时计时器,避免因发送多包耗时过长误触发ABORT
- 收到ABORT消息时,打印消息中的具体原因码(ISO11783规范定义的超时、格式错误、缓冲区溢出等),精准定位问题根源
- 数据字节序验证:检查
encode_CAN_TxBufferExtended函数中数据字节的填充顺序是否符合CAN帧要求,避免因字节序颠倒导致接收端解析数据失败
内容的提问来源于stack exchange,提问作者josie hinton
相关产品推荐
相关产品推荐

