feat(ota): 添加SPI OTA传输模式下的CAN帧处理保护机制
当SPI OTA会话进行时,禁止CAN抢夺回包出口,防止ACK发送到错误通道。 同时在SPI OTA期间丢弃CAN OTA帧,避免ota_bind_can抢占SPI回包出口。 BREAKING CHANGE: 修改了CAN传输器的通信检查逻辑,优先处理SPI OTA。
This commit is contained in:
@@ -167,6 +167,14 @@ void CAN_transmiter::communication_check(void) {
|
||||
if (can0_receive_count > 0) {
|
||||
can0_receive_count--;
|
||||
|
||||
/*
|
||||
* SPI OTA 进行中:丢弃 CAN OTA 帧,禁止 ota_bind_can 抢走 SPI 回包出口。
|
||||
* (纯 CAN OTA 时 isSpiTransport()==false,仍走下方绑定与处理。)
|
||||
*/
|
||||
if (ota.isActive() && ota.isSpiTransport()) {
|
||||
return;
|
||||
}
|
||||
|
||||
/** 走 CAN 收到的帧时,显式把 OTA 回包通道切回 CAN。 */
|
||||
ota_bind_can_transport();
|
||||
|
||||
|
||||
@@ -354,9 +354,11 @@ void SpiOta::isrProcessFrame() {
|
||||
s_v2Active = true;
|
||||
u8 hello_ack[kTxBufSize] = {};
|
||||
size_t built = 0U;
|
||||
/* BOOT 仅 6B 链路 ACK,无 APP 身份块 */
|
||||
if (v2_build_hello_ack_frame((uint32_t)s_sessionId, V2_ROLE_BOOT,
|
||||
(uint8_t)g_localCredit,
|
||||
(uint8_t)s_txSeq, hdr.tx_seq,
|
||||
nullptr, 0U,
|
||||
hello_ack, &built)) {
|
||||
submitResponse(hello_ack, (u16)built);
|
||||
/* 缓存 HELLO_ACK 以应对 ESP32 重试同一 HELLO。 */
|
||||
@@ -453,7 +455,7 @@ void SpiOta::isrProcessFrame() {
|
||||
/* 仅转发 BOOT 实际支持的 OTA 协议帧(REQ/DATA/CANCEL)给 OTA 状态机。
|
||||
* 其它帧类型(如 0xC0~0xC3 CAN bridge 命令,由 DCU_APP 注册处理器处理,
|
||||
* BOOT 无此功能)直接返回 ERROR,避免 mapMsgType 的 default 分支把
|
||||
* 未知类型误当成 CANCEL,导致 ESP32 待机时 1Hz CAN 反馈轮询(0xC1)
|
||||
* 未知类型误当成 CANCEL,导致 ESP32 待机时 CAN 反馈推送(0xC1)
|
||||
* 不断重置 OTA 状态机,干扰正常 OTA 握手。 */
|
||||
if (frame_type != SPI_OTA_CMD_REQ &&
|
||||
frame_type != SPI_OTA_CMD_DATA &&
|
||||
@@ -582,6 +584,14 @@ bool SpiOta::requestEsp32Read(u8 frame_type, const u8 *payload, u16 payload_len)
|
||||
if (s_ringPending) {
|
||||
return false;
|
||||
}
|
||||
/*
|
||||
* OTA 状态机活跃时禁止主动按铃:s_txBuf 专供 ACK/WAIT/ERROR RESPONSE,
|
||||
* 按铃 EVENT(含 CREDIT_UPDATE)会覆盖待发响应,导致 ESP32 丢 ACK。
|
||||
* CREDIT 改由 RESPONSE 帧头携带。
|
||||
*/
|
||||
if (s_otaHandler != nullptr && s_otaHandler->isActive()) {
|
||||
return false;
|
||||
}
|
||||
/* 已有 OTA ACK/WAIT 待发时禁止 CREDIT_UPDATE 等按铃覆盖 s_txBuf,
|
||||
* 否则 ESP32 读到 EVENT 而非 ACK → 超时重发 → DUP-SEQ 断续。
|
||||
* CREDIT 仍随下一笔 RESPONSE 帧头更新。 */
|
||||
|
||||
@@ -126,16 +126,12 @@ static constexpr uint32_t kHeartbeatPrintIntervalMs = 2000U;
|
||||
* poll() 只负责消费 ISR 延迟日志和推进 OTA 后台任务(延迟擦除/跳转)。
|
||||
*/
|
||||
static void pollAllLinks(void) {
|
||||
/* 关键修正:先轮询 SPI,再轮询 CAN。
|
||||
*
|
||||
* 之前顺序是:CAN0_transmiter.communication_check() -> SpiOta::poll()
|
||||
* 但 communication_check() 内部可能执行阻塞等待/超时处理,
|
||||
* 导致 SPI ISR 延迟日志长时间不被消费,且更重要的是:
|
||||
* OTA 状态机中的 process() 也被延后,进而影响 SPI 事务后的响应准备/推进。
|
||||
*
|
||||
* 当前我们正排查 SPI BOOT 通路,应让 SPI 的 poll() 优先执行,
|
||||
* 尽快消费 ISR 结果、推进 OTA 状态机,并减少 CAN 侧对 SPI 调试的干扰。 */
|
||||
/* SPI 优先:尽快推进 OTA 状态机 / 消费 ISR 日志。 */
|
||||
SpiOta::poll();
|
||||
/*
|
||||
* SPI OTA 会话中 communication_check 会丢弃 CAN OTA 帧并拒绝 bind_can;
|
||||
* 仍调用一次以排空 can0_receive_count,避免计数堆积。
|
||||
*/
|
||||
CAN0_transmiter.communication_check();
|
||||
}
|
||||
|
||||
|
||||
@@ -269,6 +269,10 @@ void OTAHandler::init() {
|
||||
* @return 无。
|
||||
*/
|
||||
void ota_bind_can_transport(void) {
|
||||
/* SPI OTA 会话进行中禁止 CAN 抢回包出口(否则 ACK 发到错误通道)。 */
|
||||
if (ota.isActive() && ota.isSpiTransport()) {
|
||||
return;
|
||||
}
|
||||
ota.setTransportSender(ota_can_sender);
|
||||
}
|
||||
|
||||
@@ -281,6 +285,10 @@ void ota_bind_spi_transport(void) {
|
||||
ota.setTransportSender(ota_spi_sender);
|
||||
}
|
||||
|
||||
bool OTAHandler::isSpiTransport() const {
|
||||
return transport_send == ota_spi_sender;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 推进 OTA 状态机后台逻辑。
|
||||
*
|
||||
|
||||
@@ -73,6 +73,11 @@ class OTAHandler {
|
||||
*/
|
||||
bool isActive() const { return ota_state != OTA_STATE_IDLE; }
|
||||
|
||||
/**
|
||||
* @brief 当前回包出口是否绑定到 SPI(用于禁止 CAN 抢 transport)。
|
||||
*/
|
||||
bool isSpiTransport() const;
|
||||
|
||||
/**
|
||||
* @brief 获取当前 OTA 状态值(调试用)。
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user