fix: 修复:改进OTA错误处理和BLE OTA性能
- 重构app.js中的OTA错误处理,以准确报告获取失败而不假设重启,从而实现正确的状态轮询。 - 通过将握手状态封装在SpiLinkState类中来增强SPI主状态管理,提高清晰度并减少直接的状态操作。 引入otaSendAndPoll方法,以更好地处理SPI通信中的命令发送和响应轮询。 - 调整BLE OTA数据包确认逻辑,防止导致固件录变慢的虚假确认,确保仅在实际取得进展时才发送确认。 - 修改确认超时设置,以更好地适应闪存写入速度,减少不重传。
This commit is contained in:
@@ -85,6 +85,26 @@ public:
|
||||
esp_err_t otaPollResponse(uint8_t *resp_type, uint8_t *resp_payload,
|
||||
size_t resp_max, size_t *resp_len, uint32_t timeout_ms);
|
||||
|
||||
/** @brief ESP32→GD32 发送命令 + 轮询读取真实响应(两步法,原子持锁)。
|
||||
*
|
||||
* 解决 GD32 "延迟一拍"协议:otaTransfer 首笔 CS 读到过期响应,
|
||||
* otaPollResponse 读取本笔命令的真实响应。命令只发送一次,不重发。
|
||||
* 适用于所有 ESP32→GD32 方向的命令(CAN 发送/模式切换/ID 快照等)。
|
||||
*
|
||||
* @param type 请求帧类型(如 0xC0/0xC2/0xC3)。
|
||||
* @param payload 请求负载数据。
|
||||
* @param payload_len 请求负载长度。
|
||||
* @param resp_type 输出响应类型(ACK/WAIT/ERROR)。
|
||||
* @param resp_payload 输出响应负载缓冲区。
|
||||
* @param resp_max 响应负载缓冲区最大长度。
|
||||
* @param resp_len 输出实际响应负载长度。
|
||||
* @param timeout_ms 总超时(发送 + 轮询),内部分配。
|
||||
* @return ESP_OK 成功读到响应;其他为 SPI 错误或超时。
|
||||
*/
|
||||
esp_err_t otaSendAndPoll(uint8_t type, const uint8_t *payload, size_t payload_len,
|
||||
uint8_t *resp_type, uint8_t *resp_payload,
|
||||
size_t resp_max, size_t *resp_len, uint32_t timeout_ms);
|
||||
|
||||
/** @brief 读取对端最新上报的 CREDIT(0 表示对端忙,应 POLL 等待而非重发)。 */
|
||||
uint8_t remoteCredit() const { return m_remoteCredit; }
|
||||
|
||||
@@ -160,7 +180,39 @@ private:
|
||||
SERVING_GD32_RING, ///< 响应 GD32 按铃,正在读 GD32 请求帧
|
||||
WAITING_BOOT_READY, ///< 方案A: 等待 Bootloader 就绪(LOW→HIGH 序列)
|
||||
};
|
||||
volatile HandshakeState m_hsState = HandshakeState::IDLE;
|
||||
|
||||
/**
|
||||
* @brief SPI 链路状态机:封装握手状态 + 完成信号逻辑。
|
||||
*
|
||||
* 线状态语义(对齐 OTA):
|
||||
* - 线 HIGH = 空闲(双方释放),可发起新事务
|
||||
* - 线 LOW = 忙碌(某方下拉占用),禁止发起新事务
|
||||
*
|
||||
* ESP32 作为主动方(otaTransfer/appPing):enterBusy() 占用线 → transferClocked → exitBusy() 释放线
|
||||
* ESP32 作为按铃响应方(checkGd32Request):同上,releaseReady 上升沿通知 GD32 完成
|
||||
*/
|
||||
class SpiLinkState {
|
||||
public:
|
||||
/** 进入忙碌态(driveReadyLow 前/后调用)。 */
|
||||
void enterBusy(HandshakeState hs) { m_hsState = hs; }
|
||||
|
||||
/** 退出忙碌态(releaseReady 后调用),回到 IDLE。 */
|
||||
void exitBusy() { m_hsState = HandshakeState::IDLE; }
|
||||
|
||||
/** 当前是否空闲(可接受新按铃下降沿)。 */
|
||||
bool isIdle() const { return m_hsState == HandshakeState::IDLE; }
|
||||
|
||||
/** 当前握手状态。 */
|
||||
HandshakeState state() const { return m_hsState; }
|
||||
|
||||
/** ISR 检测下降沿时调用:仅 IDLE 态接受按铃。 */
|
||||
bool acceptRing() const { return isIdle(); }
|
||||
|
||||
private:
|
||||
volatile HandshakeState m_hsState = HandshakeState::IDLE;
|
||||
};
|
||||
|
||||
SpiLinkState m_linkState;
|
||||
/** GD32 主动按铃标志:GPIO 下降沿 ISR 置位,SPI 任务消费。 */
|
||||
volatile bool m_gd32RequestPending = false;
|
||||
/** SPI 任务句柄(用于 ISR 唤醒)。 */
|
||||
@@ -196,7 +248,7 @@ private:
|
||||
/**
|
||||
* @brief 等 GD32 释放 SPIready(上升沿),中断驱动非忙等。
|
||||
*
|
||||
* 调用前须将 m_hsState 置为 WAITING_RESPONSE,ISR 上升沿检测到该状态
|
||||
* 调用前须将 m_linkState 置为 WAITING_RESPONSE(enterBusy),ISR 上升沿检测到该状态
|
||||
* 后发 0x02 通知唤醒本任务。GD32 写 Flash 期间保持 PB6 低,本函数阻塞,
|
||||
* 写完拉高 → 唤醒 → 继续,自然避免 overrun。
|
||||
*
|
||||
@@ -233,6 +285,13 @@ private:
|
||||
*/
|
||||
esp_err_t otaPollResponseUnlocked(uint8_t *resp_type, uint8_t *resp_payload,
|
||||
size_t resp_max, size_t *resp_len, uint32_t timeout_ms);
|
||||
/** @brief 无锁版本的 otaTransfer(调用者须已持有锁)。
|
||||
* @param expect_stale true 表示调用方预期读到过期/空响应(如 otaSendAndPoll 步骤1),
|
||||
* 找不到帧时降级为 DBG 日志,不打 WARN。 */
|
||||
esp_err_t otaTransferUnlocked(uint8_t type, const uint8_t *payload, size_t payload_len,
|
||||
uint8_t *resp_type, uint8_t *resp_payload,
|
||||
size_t resp_max, size_t *resp_len, uint32_t timeout_ms,
|
||||
bool expect_stale = false);
|
||||
// OTA 帧工具(静态,无状态)
|
||||
static uint32_t otaCrc32(const uint8_t *data, size_t len);
|
||||
static bool otaBuildFrame(uint8_t type, const uint8_t *payload, size_t payload_len,
|
||||
|
||||
Reference in New Issue
Block a user