STM32G473CET6 CAN总线多设备响应异常问题排查求助
我基于STM32G473CET6开发CAN总线项目,STM32负责发送消息、处理接收消息并响应。例如发送10170001查询设备01的IO状态,设备会返回8条包含IO状态、设备状态、PWM值等的响应消息。但总线上存在多台设备时,发送10170002查询设备02,会出现部分响应是设备01的旧回复(比如8条里有2条),直到第三次查询才能得到设备02的正确IO状态。怀疑是STM32的溢出或缓冲问题,但FIFO状态监测正常,终端电阻配置也正确。
已完成的排查工作
- 总线两端终端电阻配置正确
- 已实现FIFO状态监测机制
- 使用分析仪追踪总线上的消息
- 两台设备均出现相同问题,排除硬件故障
- CAN总线速率为500kbps,即使发送少量测试消息仍会出现该问题
相关代码
/* main can line fifo0 callback */ void HAL_FDCAN_RxFifo0Callback(FDCAN_HandleTypeDef *hfdcan, uint32_t RxFifo0ITs) { FDCAN_RxHeaderTypeDef bufRxHdrCan; uint8_t payloadCan[8]; if ((RxFifo0ITs & FDCAN_IT_RX_FIFO0_NEW_MESSAGE) != RESET) { if (HAL_FDCAN_GetRxMessage(hfdcan, FDCAN_RX_FIFO0,&bufRxHdrCan,payloadCan) == HAL_OK) { dbgPrintf("Can1 new message in RxFifo0\n"); dbgPrintf("RxHeader.Identifier: %x\n", bufRxHdrCan.Identifier); dbgPrintf("RxHeader.DataLength: %x\n", bufRxHdrCan.DataLength); CanMessage canMsg; memcpy(canMsg.data,payloadCan,bufRxHdrCan.DataLength); canMsg.id = bufRxHdrCan.Identifier; canMsg.len = bufRxHdrCan.DataLength; ParsedID id = canMessageParseId(canMsg.id); dbgTrace("ParsedID.pgn : %d\n", id.pgn); dbgTrace("ParsedID.sourceAddr: %d\n", id.sourceAddr); dbgTrace("ParsedID.targetAddr: %d\n", id.targetAddr); if((id.targetAddr == gAppSettings.devAddress) || (id.targetAddr == BROADCAST_ADDRESS)) { gSysMsg sysMsg; sysMsg.dataDesc.msgId = canMsg.id; sysMsg.dataDesc.msgSrc = SRC_CANA; sysMsg.dataLen = canMsg.len; memcpy(sysMsg.dataBuffer,payloadCan,sysMsg.dataLen); gRingBufPush(&sysMsgRingBuf,(void*)(&sysMsg)); } return; } dbgPrintf("ERROR: Fifo0Callback HAL_FDCAN_GetRxMessage\n"); Error_Handler(1); // id 1 } } /* can message send */ bool canMessageSend(CanLine cLine,CanMessage *pCanMsg) { FDCAN_HandleTypeDef *phfdcan = phfdcanMain; FDCAN_TxHeaderTypeDef TxHeader; TxHeader.Identifier = pCanMsg->id; TxHeader.DataLength = pCanMsg->len; TxHeader.IdType = FDCAN_EXTENDED_ID; if(TxHeader.DataLength > 0) { TxHeader.TxFrameType = FDCAN_DATA_FRAME; } else { TxHeader.TxFrameType = FDCAN_REMOTE_FRAME; } TxHeader.ErrorStateIndicator = FDCAN_ESI_PASSIVE; TxHeader.BitRateSwitch = FDCAN_BRS_OFF; TxHeader.FDFormat = FDCAN_CLASSIC_CAN; TxHeader.TxEventFifoControl = FDCAN_NO_TX_EVENTS; TxHeader.MessageMarker = 0; dbgTrace("canMessageSend\n"); dbgTrace("id = %d\n", pCanMsg->id); dbgTrace("len = %d\n",pCanMsg->len); while (HAL_FDCAN_GetTxFifoFreeLevel(phfdcan) == 0); if (HAL_FDCAN_AddMessageToTxFifoQ(phfdcan,&TxHeader,pCanMsg->data)!= HAL_OK) { dbgPrintf("ERROR:HAL_FDCAN_AddMessageToTxFifoQ-MIO\n"); return false; } return true; }
可能的原因分析
环形缓冲区未清空
切换查询目标设备时,sysMsgRingBuf环形缓冲区中可能残留上一次查询设备01的响应消息。如果后续处理逻辑未先清空缓冲区,会将旧消息与新消息混合返回,导致异常。需检查查询新设备前是否有清空缓冲区的操作。响应消息与请求无关联
当前代码仅通过targetAddr判断是否接收消息,但未将响应与对应的查询请求关联。若设备01的响应延迟到达,会被误当作设备02的响应处理。需给每个查询请求添加唯一标识(如序列号),响应消息携带对应标识,处理时仅匹配当前查询的序列号。CAN过滤器配置不严谨
即使FIFO状态正常,若CAN过滤器未正确过滤非目标设备的消息,可能导致设备接收其他设备的旧响应。检查FDCAN过滤器配置,确保仅接收目标地址匹配的消息,或在接收后严格校验sourceAddr是否为当前查询的设备地址。局部变量生命周期问题
回调函数中定义的sysMsg是局部变量,调用gRingBufPush时传入的是局部变量指针。若环形缓冲区存储的是指针而非复制完整数据,回调返回后局部变量被销毁,后续访问会出现数据错误。需确认gRingBufPush是否复制整个sysMsg数据,而非存储指针。消息处理时序问题
设备02的响应可能被延迟处理,而旧的设备01消息仍在处理队列中。检查消息处理线程的优先级和调度逻辑,确保新查询请求发出后,立即暂停处理旧响应消息,优先处理新设备的响应。
内容的提问来源于stack exchange,提问作者MKT12

