refactor(spi): 优化CAN反馈推送的节流机制
将CAN反馈推送改为1Hz节流,由taskHeartbeat每秒调用 canFeedbackStartRound激活新轮次,避免与CANFD刷新频率不匹配。 添加s_roundActive标志控制推送节奏,确保每轮完成后等待下一秒 再开始新的推送循环。 --- feat(spi): 实现SPI就绪状态检测和链路状态机 新增SpiLinkState类管理SPI链路状态,通过EXTI6检测PB6引脚 的上升沿来判断ESP32是否完成上一帧处理。 配置SPI Ready引脚的外部中断,实现可靠的ESP32完成信号检测, 避免在ESP32未就绪时过早发起新的SPI通信。 修复SPI接收溢出处理,改为丢弃溢出字节而非重置整个接收缓冲区, 提高SPI通信稳定性。
This commit is contained in:
@@ -5,6 +5,39 @@
|
||||
|
||||
class ISpiCommandHandler;
|
||||
|
||||
/**
|
||||
* @brief GD32 侧 SPI 链路状态机:封装按铃状态 + ESP32 完成信号。
|
||||
*
|
||||
* 线状态语义(对齐 OTA):
|
||||
* - 线 HIGH = 空闲(双方释放),GD32 可发起新按铃
|
||||
* - 线 LOW = 忙碌(GD32 按铃下拉 或 ESP32 checkGd32Request 期间下拉)
|
||||
*
|
||||
* 状态流转:
|
||||
* - canRingNow()=true(初始/ESP32 完成后)→ requestEsp32Read() → canRingNow()=false
|
||||
* - ESP32 releaseReady 上升沿 → onEsp32Complete() → canRingNow()=true
|
||||
* - 按铃超时 → onRingTimeout() → canRingNow()=true(允许重试)
|
||||
*/
|
||||
class SpiLinkState {
|
||||
public:
|
||||
/** 是否允许发起新按铃(ESP32 已完成上一帧 或 初始态)。 */
|
||||
bool canRingNow() const { return m_esp32Ready; }
|
||||
|
||||
/** 发起按铃时调用(消费完成 flag)。 */
|
||||
void onRingStart() { m_esp32Ready = false; }
|
||||
|
||||
/** ESP32 releaseReady 上升沿触发(EXTI6 ISR 调用)。 */
|
||||
void onEsp32Complete() { m_esp32Ready = true; }
|
||||
|
||||
/** 按铃超时取消时调用(允许重试,不依赖 EXTI)。 */
|
||||
void onRingTimeout() { m_esp32Ready = true; }
|
||||
|
||||
/** 初始化(首帧允许)。 */
|
||||
void reset() { m_esp32Ready = true; }
|
||||
|
||||
private:
|
||||
volatile bool m_esp32Ready = true;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief APP 运行期 SPI 链路与命令路由模块。
|
||||
*
|
||||
@@ -38,6 +71,9 @@ public:
|
||||
/** @brief CS 边沿中断处理入口。 */
|
||||
void onCsExtiInterrupt();
|
||||
|
||||
/** @brief SPI Ready(PB6) EXTI 上升沿中断处理入口(ESP32 完成信号)。 */
|
||||
void onSpiReadyExti();
|
||||
|
||||
/**
|
||||
* @brief 注册一个 SPI 命令处理器,用于 TYPE 路由。
|
||||
* @param[in] handler 命令处理器指针,调用方需保证其生命周期有效。
|
||||
@@ -84,6 +120,11 @@ public:
|
||||
/** @brief 查询当前是否有未读按铃(ESP32 尚未来读)。 */
|
||||
bool isRingPending() const { return m_ringPending; }
|
||||
|
||||
/** @brief 查询 ESP32 是否已完成上一帧处理(SPIready 上升沿触发)。
|
||||
* 初始为 true 允许第一次按铃;每次 requestEsp32Read 后置 false,
|
||||
* ESP32 releaseReady 产生上升沿后由 EXTI 置 true。 */
|
||||
bool canRingNow() const { return m_linkState.canRingNow(); }
|
||||
|
||||
static constexpr uint16_t kRxBufSize = 256U;
|
||||
static constexpr uint16_t kTxBufSize = 256U;
|
||||
static constexpr uint16_t kLogBufSize = 256U;
|
||||
@@ -112,10 +153,14 @@ private:
|
||||
/** 按铃期间暂存的请求帧长度(供 onCsAssert 装入 DMA)。 */
|
||||
volatile uint16_t m_ringTxLen = 0U;
|
||||
|
||||
/** SPI 链路状态机:封装 ESP32 完成信号。 */
|
||||
SpiLinkState m_linkState;
|
||||
|
||||
void configureGpio();
|
||||
void configureSpi0();
|
||||
void configureDma();
|
||||
void configureExti();
|
||||
void configureSpiReadyExti();
|
||||
void onCsAssert();
|
||||
void onCsRelease();
|
||||
void processCommand();
|
||||
@@ -138,9 +183,15 @@ private:
|
||||
* 主动轮询。每次调用推进一帧:若上一帧尚未被 ESP32 读走则立即返回,
|
||||
* 否则构建当前 (channel, page) 的反馈 payload 并 requestEsp32Read() 推送,
|
||||
* 然后推进到下一页/下一通道。
|
||||
*
|
||||
* 1Hz 节流由 taskHeartbeat() 调用 canFeedbackStartRound() 激活每秒一轮,
|
||||
* 推完一轮后内部清除激活标志,等待下一次 taskHeartbeat() 触发。
|
||||
*/
|
||||
void canFeedbackPushStep();
|
||||
|
||||
/** @brief 激活新一轮 CAN 反馈推送(由 1Hz taskHeartbeat 调用)。 */
|
||||
void canFeedbackStartRound();
|
||||
|
||||
/** @brief 全局 SPI 命令总线实例。 */
|
||||
extern AppSpiCommand AppSpiCommandBus;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user