Compare commits
2
Commits
3377a2e58a
...
005072b1df
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
005072b1df | ||
|
|
62eab6fcdf |
@@ -167,6 +167,14 @@ void CAN_transmiter::communication_check(void) {
|
|||||||
if (can0_receive_count > 0) {
|
if (can0_receive_count > 0) {
|
||||||
can0_receive_count--;
|
can0_receive_count--;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* SPI OTA 进行中:丢弃 CAN OTA 帧,禁止 ota_bind_can 抢走 SPI 回包出口。
|
||||||
|
* (纯 CAN OTA 时 isSpiTransport()==false,仍走下方绑定与处理。)
|
||||||
|
*/
|
||||||
|
if (ota.isActive() && ota.isSpiTransport()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/** 走 CAN 收到的帧时,显式把 OTA 回包通道切回 CAN。 */
|
/** 走 CAN 收到的帧时,显式把 OTA 回包通道切回 CAN。 */
|
||||||
ota_bind_can_transport();
|
ota_bind_can_transport();
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/* 主动按铃超时时间(毫秒) */
|
/* 主动按铃超时时间(毫秒) */
|
||||||
static constexpr u32 kRingTimeoutMs = 500U;
|
static constexpr u32 kRingTimeoutMs = CONFIG_SPI_RING_TIMEOUT_MS;
|
||||||
#define SPI_TX_DMA_PERIPH DMA0
|
#define SPI_TX_DMA_PERIPH DMA0
|
||||||
#define SPI_TX_DMA_CH DMA_CH2
|
#define SPI_TX_DMA_CH DMA_CH2
|
||||||
#define SPI_RX_DMA_PERIPH DMA0
|
#define SPI_RX_DMA_PERIPH DMA0
|
||||||
@@ -68,9 +68,9 @@ static volatile u8 s_expectedRxSeq = 0U;
|
|||||||
static volatile bool s_v2Active = false;
|
static volatile bool s_v2Active = false;
|
||||||
|
|
||||||
/* 响应缓存:用于重复帧重发(dist 128~255 分支)。
|
/* 响应缓存:用于重复帧重发(dist 128~255 分支)。
|
||||||
* Boot 的 TX buffer 最多 128 字节,缓存长度对齐 kTxBufSize。 */
|
* 长度对齐 SpiOta::kTxBufSize(128);payload 上限 = kTxBufSize - V2_OVERHEAD = 108。 */
|
||||||
static u8 s_lastReqSeq = 0xFFU;
|
static u8 s_lastReqSeq = 0xFFU;
|
||||||
static u8 s_lastRespBuf[128] = {}; /* kTxBufSize=128 */
|
static u8 s_lastRespBuf[SpiOta::kTxBufSize] = {};
|
||||||
static u16 s_lastRespLen = 0U;
|
static u16 s_lastRespLen = 0U;
|
||||||
static bool s_hasCachedResp = false;
|
static bool s_hasCachedResp = false;
|
||||||
|
|
||||||
@@ -96,7 +96,7 @@ static volatile u8 s_pendingAckSeq = 0U;
|
|||||||
* ESP32 下一笔 CS 到达时 DMA 尚未 re-arm → 中后期(约几十~上百包后)卡死。
|
* ESP32 下一笔 CS 到达时 DMA 尚未 re-arm → 中后期(约几十~上百包后)卡死。
|
||||||
* ISR 只入队;主循环 SpiOta::poll() 调 handleLinkMessage 再回 ACK。
|
* ISR 只入队;主循环 SpiOta::poll() 调 handleLinkMessage 再回 ACK。
|
||||||
*/
|
*/
|
||||||
static constexpr u8 kOtaDataSlots = 4U;
|
static constexpr u8 kOtaDataSlots = CONFIG_OTA_ISR_DATA_SLOTS;
|
||||||
static u8 s_otaDataSlots[kOtaDataSlots][64];
|
static u8 s_otaDataSlots[kOtaDataSlots][64];
|
||||||
static volatile u8 s_otaDataWi = 0U;
|
static volatile u8 s_otaDataWi = 0U;
|
||||||
static volatile u8 s_otaDataRi = 0U;
|
static volatile u8 s_otaDataRi = 0U;
|
||||||
@@ -354,9 +354,11 @@ void SpiOta::isrProcessFrame() {
|
|||||||
s_v2Active = true;
|
s_v2Active = true;
|
||||||
u8 hello_ack[kTxBufSize] = {};
|
u8 hello_ack[kTxBufSize] = {};
|
||||||
size_t built = 0U;
|
size_t built = 0U;
|
||||||
|
/* BOOT 仅 6B 链路 ACK,无 APP 身份块 */
|
||||||
if (v2_build_hello_ack_frame((uint32_t)s_sessionId, V2_ROLE_BOOT,
|
if (v2_build_hello_ack_frame((uint32_t)s_sessionId, V2_ROLE_BOOT,
|
||||||
(uint8_t)g_localCredit,
|
(uint8_t)g_localCredit,
|
||||||
(uint8_t)s_txSeq, hdr.tx_seq,
|
(uint8_t)s_txSeq, hdr.tx_seq,
|
||||||
|
nullptr, 0U,
|
||||||
hello_ack, &built)) {
|
hello_ack, &built)) {
|
||||||
submitResponse(hello_ack, (u16)built);
|
submitResponse(hello_ack, (u16)built);
|
||||||
/* 缓存 HELLO_ACK 以应对 ESP32 重试同一 HELLO。 */
|
/* 缓存 HELLO_ACK 以应对 ESP32 重试同一 HELLO。 */
|
||||||
@@ -453,7 +455,7 @@ void SpiOta::isrProcessFrame() {
|
|||||||
/* 仅转发 BOOT 实际支持的 OTA 协议帧(REQ/DATA/CANCEL)给 OTA 状态机。
|
/* 仅转发 BOOT 实际支持的 OTA 协议帧(REQ/DATA/CANCEL)给 OTA 状态机。
|
||||||
* 其它帧类型(如 0xC0~0xC3 CAN bridge 命令,由 DCU_APP 注册处理器处理,
|
* 其它帧类型(如 0xC0~0xC3 CAN bridge 命令,由 DCU_APP 注册处理器处理,
|
||||||
* BOOT 无此功能)直接返回 ERROR,避免 mapMsgType 的 default 分支把
|
* BOOT 无此功能)直接返回 ERROR,避免 mapMsgType 的 default 分支把
|
||||||
* 未知类型误当成 CANCEL,导致 ESP32 待机时 1Hz CAN 反馈轮询(0xC1)
|
* 未知类型误当成 CANCEL,导致 ESP32 待机时 CAN 反馈推送(0xC1)
|
||||||
* 不断重置 OTA 状态机,干扰正常 OTA 握手。 */
|
* 不断重置 OTA 状态机,干扰正常 OTA 握手。 */
|
||||||
if (frame_type != SPI_OTA_CMD_REQ &&
|
if (frame_type != SPI_OTA_CMD_REQ &&
|
||||||
frame_type != SPI_OTA_CMD_DATA &&
|
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) {
|
if (s_ringPending) {
|
||||||
return false;
|
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,
|
/* 已有 OTA ACK/WAIT 待发时禁止 CREDIT_UPDATE 等按铃覆盖 s_txBuf,
|
||||||
* 否则 ESP32 读到 EVENT 而非 ACK → 超时重发 → DUP-SEQ 断续。
|
* 否则 ESP32 读到 EVENT 而非 ACK → 超时重发 → DUP-SEQ 断续。
|
||||||
* CREDIT 仍随下一笔 RESPONSE 帧头更新。 */
|
* CREDIT 仍随下一笔 RESPONSE 帧头更新。 */
|
||||||
@@ -766,10 +776,10 @@ void SpiOta::init() {
|
|||||||
configureGpio();
|
configureGpio();
|
||||||
configureDma();
|
configureDma();
|
||||||
configureSpi0();
|
configureSpi0();
|
||||||
configureExti();
|
|
||||||
s_linkState.reset();
|
s_linkState.reset();
|
||||||
/* 首笔 CS 到来前预装 DMA,避免 EXTI 晚于首 SCK */
|
/* 必须先预装 DMA,再开 CS EXTI(与 APP 一致,避免首笔 CS 赶在 arm 前) */
|
||||||
armSpiDmasForNextCs();
|
armSpiDmasForNextCs();
|
||||||
|
configureExti();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ============================================================
|
/* ============================================================
|
||||||
@@ -812,9 +822,8 @@ void SpiOta::onCsRelease() {
|
|||||||
|
|
||||||
/* ★ 关键:本 CS 已时钟输出预装的 TX 帧(延迟一拍响应)。
|
/* ★ 关键:本 CS 已时钟输出预装的 TX 帧(延迟一拍响应)。
|
||||||
* armSpiDmasForNextCs() 装载后并不清除 s_txPending,若此处不标记交付,
|
* armSpiDmasForNextCs() 装载后并不清除 s_txPending,若此处不标记交付,
|
||||||
* pending 会永远为 true → updateDataReady() 把 PB6 永久拉低 →
|
* pending 会永远为 true → updateDataReady() 把 PB6 永久拉低,
|
||||||
* ESP32 waitSpiReady(等 HIGH) 每包空等 SPI_READY_TIMEOUT_MS,
|
* ESP32 侧 DATA_READY 常低(收路径依赖 waitSpiDataReady/盲 poll)。
|
||||||
* 1116 包 OTA 表现为"卡在 1/1116 十几分钟"。
|
|
||||||
* 先清交付态;随后 isrProcessFrame/submitResponse 可再置 pending。
|
* 先清交付态;随后 isrProcessFrame/submitResponse 可再置 pending。
|
||||||
* 重复帧缓存在 s_lastRespBuf,不依赖 s_txPending 保活。
|
* 重复帧缓存在 s_lastRespBuf,不依赖 s_txPending 保活。
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
#ifndef __SPI_OTA_H__
|
#ifndef __SPI_OTA_H__
|
||||||
#define __SPI_OTA_H__
|
#define __SPI_OTA_H__
|
||||||
|
|
||||||
#include "OTA.h"
|
#include "OTA.h" /* 间接 config.h */
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @file SpiOta.h
|
* @file SpiOta.h
|
||||||
@@ -43,8 +44,8 @@ private:
|
|||||||
|
|
||||||
class SpiOta {
|
class SpiOta {
|
||||||
public:
|
public:
|
||||||
static constexpr u16 kRxBufSize = 256U;
|
static constexpr u16 kRxBufSize = CONFIG_SPI_RX_BUF_SIZE; /* 主机可打满 256 */
|
||||||
static constexpr u16 kTxBufSize = 128U;
|
static constexpr u16 kTxBufSize = CONFIG_SPI_TX_BUF_SIZE; /* BOOT 响应满缓冲 128 */
|
||||||
static constexpr u16 kIsrLogBufSize = 128U;
|
static constexpr u16 kIsrLogBufSize = 128U;
|
||||||
|
|
||||||
static void init();
|
static void init();
|
||||||
|
|||||||
@@ -126,16 +126,12 @@ static constexpr uint32_t kHeartbeatPrintIntervalMs = 2000U;
|
|||||||
* poll() 只负责消费 ISR 延迟日志和推进 OTA 后台任务(延迟擦除/跳转)。
|
* poll() 只负责消费 ISR 延迟日志和推进 OTA 后台任务(延迟擦除/跳转)。
|
||||||
*/
|
*/
|
||||||
static void pollAllLinks(void) {
|
static void pollAllLinks(void) {
|
||||||
/* 关键修正:先轮询 SPI,再轮询 CAN。
|
/* SPI 优先:尽快推进 OTA 状态机 / 消费 ISR 日志。 */
|
||||||
*
|
|
||||||
* 之前顺序是:CAN0_transmiter.communication_check() -> SpiOta::poll()
|
|
||||||
* 但 communication_check() 内部可能执行阻塞等待/超时处理,
|
|
||||||
* 导致 SPI ISR 延迟日志长时间不被消费,且更重要的是:
|
|
||||||
* OTA 状态机中的 process() 也被延后,进而影响 SPI 事务后的响应准备/推进。
|
|
||||||
*
|
|
||||||
* 当前我们正排查 SPI BOOT 通路,应让 SPI 的 poll() 优先执行,
|
|
||||||
* 尽快消费 ISR 结果、推进 OTA 状态机,并减少 CAN 侧对 SPI 调试的干扰。 */
|
|
||||||
SpiOta::poll();
|
SpiOta::poll();
|
||||||
|
/*
|
||||||
|
* SPI OTA 会话中 communication_check 会丢弃 CAN OTA 帧并拒绝 bind_can;
|
||||||
|
* 仍调用一次以排空 can0_receive_count,避免计数堆积。
|
||||||
|
*/
|
||||||
CAN0_transmiter.communication_check();
|
CAN0_transmiter.communication_check();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -269,6 +269,10 @@ void OTAHandler::init() {
|
|||||||
* @return 无。
|
* @return 无。
|
||||||
*/
|
*/
|
||||||
void ota_bind_can_transport(void) {
|
void ota_bind_can_transport(void) {
|
||||||
|
/* SPI OTA 会话进行中禁止 CAN 抢回包出口(否则 ACK 发到错误通道)。 */
|
||||||
|
if (ota.isActive() && ota.isSpiTransport()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
ota.setTransportSender(ota_can_sender);
|
ota.setTransportSender(ota_can_sender);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -281,6 +285,10 @@ void ota_bind_spi_transport(void) {
|
|||||||
ota.setTransportSender(ota_spi_sender);
|
ota.setTransportSender(ota_spi_sender);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool OTAHandler::isSpiTransport() const {
|
||||||
|
return transport_send == ota_spi_sender;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 推进 OTA 状态机后台逻辑。
|
* @brief 推进 OTA 状态机后台逻辑。
|
||||||
*
|
*
|
||||||
@@ -362,7 +370,9 @@ void OTAHandler::process() {
|
|||||||
|
|
||||||
if (pending_app_jump) {
|
if (pending_app_jump) {
|
||||||
pending_app_jump = false;
|
pending_app_jump = false;
|
||||||
delay_1ms(20);
|
/* 给 ESP32 FINISH 后安静窗口留时间:本侧再稍等再 jump,
|
||||||
|
* 避免 jump 瞬间与主机 HELLO 时钟重叠打坏 APP SPI 初始化。 */
|
||||||
|
delay_1ms(80);
|
||||||
jumpToApplication();
|
jumpToApplication();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -73,6 +73,11 @@ class OTAHandler {
|
|||||||
*/
|
*/
|
||||||
bool isActive() const { return ota_state != OTA_STATE_IDLE; }
|
bool isActive() const { return ota_state != OTA_STATE_IDLE; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 当前回包出口是否绑定到 SPI(用于禁止 CAN 抢 transport)。
|
||||||
|
*/
|
||||||
|
bool isSpiTransport() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 获取当前 OTA 状态值(调试用)。
|
* @brief 获取当前 OTA 状态值(调试用)。
|
||||||
*/
|
*/
|
||||||
@@ -172,8 +177,8 @@ class OTAHandler {
|
|||||||
bool pending_app_jump = false; /**< 是否存在待处理的应用跳转请求。 */
|
bool pending_app_jump = false; /**< 是否存在待处理的应用跳转请求。 */
|
||||||
ota_send_fn_t transport_send = nullptr; /**< 当前底层回包发送函数。 */
|
ota_send_fn_t transport_send = nullptr; /**< 当前底层回包发送函数。 */
|
||||||
|
|
||||||
/* Flash 写入环形缓冲(ISR 写入,主循环 drain 写 Flash) */
|
/* Flash 写入环形缓冲(ISR 写入,主循环 drain 写 Flash);槽位数见 config.h */
|
||||||
static constexpr u8 kFlashBufSlots = 8U;
|
static constexpr u8 kFlashBufSlots = CONFIG_OTA_FLASH_BUF_SLOTS;
|
||||||
struct FlashSlot {
|
struct FlashSlot {
|
||||||
u32 offset;
|
u32 offset;
|
||||||
u8 data[OTA_PACKET_SIZE];
|
u8 data[OTA_PACKET_SIZE];
|
||||||
|
|||||||
@@ -79,10 +79,8 @@ inline constexpr u32 CANFD_OTA_WAIT_ID = 0x00000103U;
|
|||||||
inline constexpr u32 CANFD_OTA_CANCEL_ID = 0x00000000U;
|
inline constexpr u32 CANFD_OTA_CANCEL_ID = 0x00000000U;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SPI Ready 握手输出引脚(PB6 复用为 SPI Ready)。
|
* SPI DATA_READY 输出引脚(PB6 → ESP32 GPIO10,开漏)。
|
||||||
* 与 DCU_APP sys/config.h 保持一致,适配 ESP32 waitSpiReady 硬件握手:
|
* v2:低 = 有待读数据,高 = 队列空;帧同步靠 SEQ/ACK/CREDIT,非「下一笔许可」。
|
||||||
* - CS 下降沿拉低 PB6,表示从机忙碌
|
|
||||||
* - 响应帧就绪后拉高 PB6,通知主机可发起下一笔事务
|
|
||||||
*/
|
*/
|
||||||
inline constexpr u32 CONFIG_SPI_READY_PORT = GPIOB;
|
inline constexpr u32 CONFIG_SPI_READY_PORT = GPIOB;
|
||||||
inline constexpr u32 CONFIG_SPI_READY_PIN = GPIO_PIN_6;
|
inline constexpr u32 CONFIG_SPI_READY_PIN = GPIO_PIN_6;
|
||||||
@@ -123,6 +121,17 @@ inline constexpr uint8_t K_master[16] = {
|
|||||||
inline constexpr u32 OTA_TIMEOUT = 50000U;
|
inline constexpr u32 OTA_TIMEOUT = 50000U;
|
||||||
inline constexpr u32 APP_DELAY_BOOT_TIME = 10000U;
|
inline constexpr u32 APP_DELAY_BOOT_TIME = 10000U;
|
||||||
|
|
||||||
|
/** @brief SPI 主动按铃超时(ms):ESP32 未在窗口内来读则放弃本次按铃。 */
|
||||||
|
inline constexpr u32 CONFIG_SPI_RING_TIMEOUT_MS = 500U;
|
||||||
|
/** @brief BOOT SPI TX DMA 满缓冲(字节)。payload 上限 = 本值 − V2_OVERHEAD(20) = 108。 */
|
||||||
|
inline constexpr u16 CONFIG_SPI_TX_BUF_SIZE = 128U;
|
||||||
|
/** @brief BOOT SPI RX 缓冲(主机可按 APP 预算打 256 时钟)。 */
|
||||||
|
inline constexpr u16 CONFIG_SPI_RX_BUF_SIZE = 256U;
|
||||||
|
/** @brief ISR→主循环 OTA DATA 队列槽位数(每槽 64B 明文包)。 */
|
||||||
|
inline constexpr u8 CONFIG_OTA_ISR_DATA_SLOTS = 4U;
|
||||||
|
/** @brief flash 写入缓冲槽位数(绑定 g_localCredit / kFlashBufSlots)。 */
|
||||||
|
inline constexpr u8 CONFIG_OTA_FLASH_BUF_SLOTS = 8U;
|
||||||
|
|
||||||
/** 常用尺寸常量。 */
|
/** 常用尺寸常量。 */
|
||||||
static const u8 SIZE_U32 = sizeof(uint32_t);
|
static const u8 SIZE_U32 = sizeof(uint32_t);
|
||||||
static const u8 SIZE_FLOAT = sizeof(float);
|
static const u8 SIZE_FLOAT = sizeof(float);
|
||||||
|
|||||||
Reference in New Issue
Block a user