refactor(device): 重构SPI命令处理为模块化架构

- 将原有的 monolithic app_spi_command.cpp 拆分为多个专门的命令处理器文件
- 新增 spi_command_handler.h 定义命令处理器接口和上下文结构
- 新增 app_spi_command_defs.h 提取公共协议常量定义
- 新增多个具体命令处理器实现文件:
  - app_spi_cmd_boot_request.cpp (进入Bootloader命令)
  - app_spi_cmd_ota_wait.cpp (OTA等待响应命令)
  - app_spi_cmd_ping.cpp (APP心跳ping命令)
  - app_spi_cmd_can_bridge.cpp (SPI到CAN桥接命令)

- 修改 app_spi_command.cpp 仅负责SPI链路管理和命令路由逻辑
- 更新配置文件添加新源文件到项目编译列表
- 调整全局实例为对象实例(AppSpiCommandBus),移除静态方法依赖
- 更新README文档说明新的SPI命令总线拆分架构
- 微调版本号(patch: 0 -> 1, build: 2 -> 0)
This commit is contained in:
cbb
2026-06-23 01:49:33 +08:00
parent 01382a3fc1
commit dc91883f1e
14 changed files with 580 additions and 273 deletions
+62 -34
View File
@@ -1,59 +1,87 @@
#ifndef DEVICE_APP_SPI_COMMAND_H
#define DEVICE_APP_SPI_COMMAND_H
#include <stdint.h>
#include <cstdint>
class ISpiCommandHandler;
/**
* @brief APP 运行期 SPI 命令从机模块。
* @brief APP 运行期 SPI 链路与命令路由模块。
*
* 复用 BOOT 的 SpiOta 协议帧格式(SOF+TYPE+LEN+PAYLOAD+CRC):
* - TYPE=0xB0 → 请求进入 Bootloader,回复 ACK 帧
* - TYPE=0x01/0x02/0x03(OTA 命令)→ 回复 WAIT 帧(不在 BOOT 模式)
* - 旧式单字节 0xB0 命令仍兼容
* 本类现在主要承担两件事:
* - 维护 SPI0 从机的硬件链路(GPIO/SPI/DMA/EXTI)
* - 将接收到的合法帧按 TYPE 注册式分发给 ISpiCommandHandler
*
* 调试日志采用延迟输出:processCommand() 只写缓冲+置标志,
* poll() 中再 printf,避免在帧处理路径上耗时。
*
* 发送路径使用 DMA0_CH2 推 s_txBuf → SPI0_DATA,
* 与 BOOT 的 SpiOta 实现保持一致;接收仍用 RBNE 字节中断。
* 具体命令处理逻辑不再放在这里,而是拆到 `device/app_spi_cmd_*.cpp`,
* 通过 `registerHandler()` 注册进来,保持本模块可扩展。
*/
class AppSpiCommand {
public:
/** @brief 初始化 SPI0 从机命令通道。 */
static void init();
/** @brief 初始化 SPI0 从机命令通道并注册默认命令处理器。 */
void init();
/** @brief 主循环轮询:打印延迟日志。 */
static void poll();
void poll();
/** @brief SPI0 中断处理入口。 */
static void onInterrupt();
void onInterrupt();
/** @brief CS 边沿中断处理入口。 */
static void onCsExtiInterrupt();
void onCsExtiInterrupt();
/**
* @brief 注册一个 SPI 命令处理器,用于 TYPE 路由。
* @param[in] handler 命令处理器指针,调用方需保证其生命周期有效。
*/
void registerHandler(ISpiCommandHandler *handler);
/** @brief 记录一条待输出日志,主循环统一打印。 */
void postLog(const char *msg);
/** @brief 记录一条带 1 字节参数的待输出日志。 */
void postLogFmt(const char *fmt, uint8_t val);
/**
* @brief 将应答帧构建到指定发送缓冲区。
* @param[out] tx_buf 发送缓冲区。
* @param[in] tx_buf_size 发送缓冲区大小。
* @param[out] tx_len 构建完成后的帧长度。
* @param[in] rsp_type 响应类型。
* @param[in] payload 响应载荷。
* @param[in] payload_len 响应载荷长度。
*/
void buildResponse(uint8_t *tx_buf, uint16_t tx_buf_size,
uint16_t *tx_len, uint8_t rsp_type,
const uint8_t *payload, uint16_t payload_len);
private:
static constexpr uint16_t kRxBufSize = 128U;
static constexpr uint16_t kTxBufSize = 64U;
static constexpr uint16_t kLogBufSize = 128U;
static volatile bool s_csActive;
static volatile uint8_t s_rxLen;
static volatile uint8_t s_txLen;
static volatile uint8_t s_txPos;
static volatile bool s_logPending;
static uint8_t s_rxBuf[kRxBufSize];
static uint8_t s_txBuf[kTxBufSize];
static char s_logBuf[kLogBufSize];
private:
static constexpr uint8_t kMaxCommands = 8U;
static void configureGpio();
static void configureSpi0();
static void configureDma();
static void configureExti();
static void onCsAssert();
static void onCsRelease();
static void processCommand();
static void logDeferred(const char *msg);
static void logDeferredFmt(const char *fmt, uint8_t val);
volatile bool m_csActive = false;
volatile uint8_t m_rxLen = 0U;
uint8_t m_txLen = 0U;
volatile uint8_t m_txPos = 0U;
volatile bool m_logPending = false;
uint8_t m_rxBuf[kRxBufSize] = {};
uint8_t m_txBuf[kTxBufSize] = {};
char m_logBuf[kLogBufSize] = {};
uint8_t m_commandCount = 0U;
ISpiCommandHandler *m_commands[kMaxCommands] = {};
void configureGpio();
void configureSpi0();
void configureDma();
void configureExti();
void onCsAssert();
void onCsRelease();
void processCommand();
};
/** @brief 全局 SPI 命令总线实例。 */
extern AppSpiCommand AppSpiCommandBus;
#endif /* DEVICE_APP_SPI_COMMAND_H */