feat(cproject): 重构项目文件结构并添加EtherCAT通信支持
- 将设备相关文件重新组织为device/spi和comm分组 - 添加EtherCAT从站通信模块(esc_exmc_bus、esc_lan9252、ethercat_slave等) - 添加EXMC外设组件支持 - 统一文件命名规范为小写 refactor(canfd): 优化CANFD模块中断安全性和配置方式 - 替换自定义临界区函数为RT_CRITICAL_SAVE/RESTORE宏 - 使用配置常量CONFIG_IRQ_PREEMPT_CAN替代硬编码值 - 简化邮箱满时的处理逻辑,移除不必要的SysTick等待 - 移除废弃的中断保存头文件引用 refactor(timer): 更新定时器配置以使用配置常量 - 修复头文件包含路径大小写问题 - 使用CONFIG_NVIC_PRIORITY_GROUP配置项替代硬编码值
This commit is contained in:
@@ -0,0 +1,40 @@
|
||||
#include "device/spi/spi_command_handler.h"
|
||||
#include "device/spi/app_spi_command.h"
|
||||
#include "device/spi/app_spi_command_defs.h"
|
||||
#include "sys/ota_boot_request.h"
|
||||
#include "sys/config.h"
|
||||
#include "sys/task_manager.h"
|
||||
|
||||
namespace {
|
||||
|
||||
class SpiBootRequestHandler final : public ISpiCommandHandler {
|
||||
public:
|
||||
bool canHandle(uint8_t frame_type) const override
|
||||
{
|
||||
return frame_type == SpiDefs::kTypeBootReq;
|
||||
}
|
||||
|
||||
void handle(AppSpiCommand &bus, const SpiCommandContext &ctx) override
|
||||
{
|
||||
bus.postLog("BOOT_REQ → 进BOOT");
|
||||
|
||||
const bool ok = OtaBootRequest::requestEnterBoot();
|
||||
uint8_t ack = ok ? CONFIG_SPI_APP_OTA_BOOT_ACK_OK
|
||||
: CONFIG_SPI_APP_OTA_BOOT_ACK_ERROR;
|
||||
bus.buildResponse(ctx.tx_buf, ctx.tx_buf_size, ctx.tx_len,
|
||||
SpiDefs::kRspAck, &ack, 1U);
|
||||
|
||||
if (ok) {
|
||||
TaskManager::set(1UL << TASK_HEARTBEAT);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static SpiBootRequestHandler s_handler;
|
||||
|
||||
} // namespace
|
||||
|
||||
void registerSpiBootRequestCommand(AppSpiCommand &bus)
|
||||
{
|
||||
bus.registerHandler(&s_handler);
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
#include "device/spi/spi_command_handler.h"
|
||||
#include "device/spi/app_spi_command.h"
|
||||
#include "device/spi/app_spi_command_defs.h"
|
||||
#include "device/canfd.h"
|
||||
#include "sys/config.h"
|
||||
#include <cstdio>
|
||||
|
||||
namespace {
|
||||
|
||||
#if (CONFIG_SPI_CAN_SEND_VERBOSE_LOG != 0)
|
||||
constexpr uint8_t kCanLogMaxDlc = 64U;
|
||||
#endif
|
||||
|
||||
void postCanSendLog(AppSpiCommand &bus, const char *result,
|
||||
uint8_t can_ch, uint32_t can_id,
|
||||
const uint8_t *can_data, uint8_t can_dlc)
|
||||
{
|
||||
#if (CONFIG_SPI_CAN_SEND_VERBOSE_LOG != 0)
|
||||
const uint8_t logged_dlc = (can_dlc <= kCanLogMaxDlc) ? can_dlc : kCanLogMaxDlc;
|
||||
char data_hex[(kCanLogMaxDlc * 2U) + 1U] = {};
|
||||
for (uint8_t i = 0U; i < logged_dlc; ++i) {
|
||||
(void)snprintf(&data_hex[i * 2U], sizeof(data_hex) - (i * 2U), "%02X", can_data[i]);
|
||||
}
|
||||
|
||||
char log_msg[AppSpiCommand::kLogBufSize] = {};
|
||||
(void)snprintf(log_msg, sizeof(log_msg),
|
||||
"CAN_SEND CH%u ID=0x%08lX DLC=%u DATA=%s -> %s",
|
||||
static_cast<unsigned>(can_ch),
|
||||
static_cast<unsigned long>(can_id),
|
||||
static_cast<unsigned>(can_dlc),
|
||||
(logged_dlc > 0U) ? data_hex : "-",
|
||||
result ? result : "UNKNOWN");
|
||||
bus.postLog(log_msg);
|
||||
#else
|
||||
(void)bus;
|
||||
(void)result;
|
||||
(void)can_ch;
|
||||
(void)can_id;
|
||||
(void)can_data;
|
||||
(void)can_dlc;
|
||||
#endif
|
||||
}
|
||||
|
||||
class SpiCanBridgeHandler final : public ISpiCommandHandler {
|
||||
public:
|
||||
bool canHandle(uint8_t frame_type) const override
|
||||
{
|
||||
return frame_type == SpiDefs::kTypeCanSend;
|
||||
}
|
||||
|
||||
void handle(AppSpiCommand &bus, const SpiCommandContext &ctx) override
|
||||
{
|
||||
if (ctx.payload_len < 6U) {
|
||||
#if (CONFIG_SPI_APP_LOG_ENABLE != 0)
|
||||
char log_msg[AppSpiCommand::kLogBufSize] = {};
|
||||
(void)snprintf(log_msg, sizeof(log_msg),
|
||||
"CAN_SEND payload too short: len=%u -> ERROR",
|
||||
static_cast<unsigned>(ctx.payload_len));
|
||||
bus.postLog(log_msg);
|
||||
#endif
|
||||
uint8_t err = 0xE0U;
|
||||
bus.buildResponse(ctx.tx_buf, ctx.tx_buf_size, ctx.tx_len,
|
||||
SpiDefs::kRspError, &err, 1U);
|
||||
return;
|
||||
}
|
||||
|
||||
const uint8_t can_ch = ctx.payload[0];
|
||||
const uint32_t can_id = (static_cast<uint32_t>(ctx.payload[1]) << 24)
|
||||
| (static_cast<uint32_t>(ctx.payload[2]) << 16)
|
||||
| (static_cast<uint32_t>(ctx.payload[3]) << 8)
|
||||
| static_cast<uint32_t>(ctx.payload[4]);
|
||||
const uint8_t *can_data = &ctx.payload[5];
|
||||
const uint8_t can_dlc = static_cast<uint8_t>(ctx.payload_len - 5U);
|
||||
|
||||
if (can_ch >= 3U) {
|
||||
postCanSendLog(bus, "ERROR(channel)", can_ch, can_id, can_data, can_dlc);
|
||||
uint8_t err = 0xE1U;
|
||||
bus.buildResponse(ctx.tx_buf, ctx.tx_buf_size, ctx.tx_len,
|
||||
SpiDefs::kRspError, &err, 1U);
|
||||
return;
|
||||
}
|
||||
|
||||
const bool ok = CanFd::transmitFrame(can_ch, can_id, can_data, can_dlc);
|
||||
if (ok) {
|
||||
postCanSendLog(bus, "OK", can_ch, can_id, can_data, can_dlc);
|
||||
const uint8_t ack = 0x00U;
|
||||
bus.buildResponse(ctx.tx_buf, ctx.tx_buf_size, ctx.tx_len,
|
||||
SpiDefs::kRspAck, &ack, 1U);
|
||||
} else {
|
||||
postCanSendLog(bus, "BUSY", can_ch, can_id, can_data, can_dlc);
|
||||
uint8_t err = 0xE2U;
|
||||
bus.buildResponse(ctx.tx_buf, ctx.tx_buf_size, ctx.tx_len,
|
||||
SpiDefs::kRspError, &err, 1U);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static SpiCanBridgeHandler s_handler;
|
||||
|
||||
} // namespace
|
||||
|
||||
void registerSpiCanBridgeCommand(AppSpiCommand &bus)
|
||||
{
|
||||
bus.registerHandler(&s_handler);
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
#include "device/spi/spi_command_handler.h"
|
||||
#include "device/spi/app_spi_command.h"
|
||||
#include "device/spi/app_spi_command_defs.h"
|
||||
|
||||
namespace {
|
||||
|
||||
class SpiOtaWaitHandler final : public ISpiCommandHandler {
|
||||
public:
|
||||
bool canHandle(uint8_t frame_type) const override
|
||||
{
|
||||
switch (frame_type) {
|
||||
case SpiDefs::kTypeOtaReq:
|
||||
case SpiDefs::kTypeOtaData:
|
||||
case SpiDefs::kTypeOtaCancel:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void handle(AppSpiCommand &bus, const SpiCommandContext &ctx) override
|
||||
{
|
||||
bus.postLogFmt("[SPI-APP] OTA type=0x%02X → WAIT", ctx.frame_type);
|
||||
|
||||
const uint8_t hint = 0x01U;
|
||||
bus.buildResponse(ctx.tx_buf, ctx.tx_buf_size, ctx.tx_len,
|
||||
SpiDefs::kRspWait, &hint, 1U);
|
||||
}
|
||||
};
|
||||
|
||||
static SpiOtaWaitHandler s_handler;
|
||||
|
||||
} // namespace
|
||||
|
||||
void registerSpiOtaWaitCommand(AppSpiCommand &bus)
|
||||
{
|
||||
bus.registerHandler(&s_handler);
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
#include "device/spi/spi_command_handler.h"
|
||||
#include "device/spi/app_spi_command.h"
|
||||
#include "device/spi/app_spi_command_defs.h"
|
||||
#include "sys/config.h"
|
||||
#include "main.h"
|
||||
#include <cstring>
|
||||
|
||||
namespace {
|
||||
|
||||
class SpiAppPingHandler final : public ISpiCommandHandler {
|
||||
public:
|
||||
bool canHandle(uint8_t frame_type) const override
|
||||
{
|
||||
return frame_type == SpiDefs::kTypeAppPing;
|
||||
}
|
||||
|
||||
void handle(AppSpiCommand &bus, const SpiCommandContext &ctx) override
|
||||
{
|
||||
uint8_t alive_payload[19];
|
||||
std::memset(alive_payload, 0, sizeof(alive_payload));
|
||||
alive_payload[0] = 0xA1U; /* magic */
|
||||
alive_payload[1] = 0x55U; /* APP version tag */
|
||||
alive_payload[2] = (uint8_t)APP_FW_VERSION_MAJOR;
|
||||
alive_payload[3] = (uint8_t)APP_FW_VERSION_MINOR;
|
||||
alive_payload[4] = (uint8_t)APP_FW_VERSION_PATCH;
|
||||
alive_payload[5] = (uint8_t)APP_FW_VERSION_BUILD;
|
||||
alive_payload[6] = 0xAAU; /* BOOT version tag */
|
||||
|
||||
const volatile uint32_t *bv0 = (const volatile uint32_t *)CONFIG_BOOT_VERSION_ADDR0;
|
||||
const volatile uint32_t *bv1 = (const volatile uint32_t *)CONFIG_BOOT_VERSION_ADDR1;
|
||||
uint32_t bw0 = *bv0;
|
||||
uint32_t bw1 = *bv1;
|
||||
alive_payload[7] = (uint8_t)(bw0 & 0xFFU);
|
||||
alive_payload[8] = (uint8_t)((bw0 >> 16) & 0xFFU);
|
||||
alive_payload[9] = (uint8_t)(bw1 & 0xFFU);
|
||||
alive_payload[10] = (uint8_t)((bw1 >> 16) & 0xFFU);
|
||||
|
||||
alive_payload[11] = (uint8_t)(g_app_uptime_sec & 0xFFU);
|
||||
alive_payload[12] = (uint8_t)((g_app_uptime_sec >> 8) & 0xFFU);
|
||||
alive_payload[13] = (uint8_t)((g_app_uptime_sec >> 16) & 0xFFU);
|
||||
alive_payload[14] = (uint8_t)((g_app_uptime_sec >> 24) & 0xFFU);
|
||||
|
||||
bus.buildResponse(ctx.tx_buf, ctx.tx_buf_size, ctx.tx_len,
|
||||
SpiDefs::kRspAck, alive_payload, sizeof(alive_payload));
|
||||
}
|
||||
};
|
||||
|
||||
static SpiAppPingHandler s_handler;
|
||||
|
||||
} // namespace
|
||||
|
||||
void registerSpiAppPingCommand(AppSpiCommand &bus)
|
||||
{
|
||||
bus.registerHandler(&s_handler);
|
||||
}
|
||||
@@ -0,0 +1,598 @@
|
||||
/**
|
||||
* @file app_spi_command.cpp
|
||||
* @brief APP 运行期 SPI 链路与命令路由实现。
|
||||
*
|
||||
* 本模块现在只负责:
|
||||
* - SPI0 从机链路初始化(GPIO/SPI/DMA/EXTI)
|
||||
* - CS、DMA、中断驱动的帧接收与发送
|
||||
* - 公共帧格式解析与应答构建
|
||||
* - 业务命令的注册与分发
|
||||
*
|
||||
* 具体命令处理逻辑已经拆到独立的 `app_spi_cmd_*.cpp`,
|
||||
* 通过 `registerHandler()` 注册进来,后续新增 SPI 业务时
|
||||
* 只需要新增命令处理器,不再继续膨胀本文件。
|
||||
*/
|
||||
|
||||
#include "device/spi/app_spi_command.h"
|
||||
#include "device/spi/spi_command_handler.h"
|
||||
#include "device/spi/app_spi_command_defs.h"
|
||||
|
||||
extern "C" {
|
||||
#include "gd32e50x_exti.h"
|
||||
}
|
||||
#include "gd32e50x_gpio.h"
|
||||
#include "gd32e50x_misc.h"
|
||||
#include "gd32e50x_rcu.h"
|
||||
#include "gd32e50x_dma.h"
|
||||
#include "gd32e50x_spi.h"
|
||||
#include "sys/config.h"
|
||||
#include "sys/critical.h"
|
||||
#include "sys/task_manager.h"
|
||||
#include "sys/ota_boot_request.h"
|
||||
#include "sys/systick.h"
|
||||
#include "main.h"
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
|
||||
/* ============================================================
|
||||
* 本地别名:保持当前实现文件内部风格不变
|
||||
* ============================================================ */
|
||||
static constexpr uint8_t kSof = SpiDefs::kSof;
|
||||
static constexpr uint8_t kTypeBootReq = SpiDefs::kTypeBootReq;
|
||||
static constexpr uint8_t kTypeCanSend = SpiDefs::kTypeCanSend;
|
||||
static constexpr uint8_t kTypeOtaReq = SpiDefs::kTypeOtaReq;
|
||||
static constexpr uint8_t kTypeAppPing = SpiDefs::kTypeAppPing;
|
||||
static constexpr uint8_t kTypeOtaData = SpiDefs::kTypeOtaData;
|
||||
static constexpr uint8_t kTypeOtaCancel = SpiDefs::kTypeOtaCancel;
|
||||
static constexpr uint8_t kRspAck = SpiDefs::kRspAck;
|
||||
static constexpr uint8_t kRspWait = SpiDefs::kRspWait;
|
||||
static constexpr uint8_t kRspError = SpiDefs::kRspError;
|
||||
static constexpr uint16_t kFrameOverhead = SpiDefs::kFrameOverhead;
|
||||
|
||||
/* DMA 配置(与 BOOT SpiOta 一致:DMA0_CH2 用于 SPI0 TX) */
|
||||
#define SPI_TX_DMA_PERIPH DMA0
|
||||
#define SPI_TX_DMA_CH DMA_CH2
|
||||
|
||||
static void startSpiTxDma(const uint8_t *buf, uint16_t len)
|
||||
{
|
||||
DMA_CHCTL(SPI_TX_DMA_PERIPH, SPI_TX_DMA_CH) &= ~DMA_CHXCTL_CHEN;
|
||||
DMA_INTC(SPI_TX_DMA_PERIPH) |= DMA_FLAG_ADD(DMA_CHINTF_RESET_VALUE, SPI_TX_DMA_CH);
|
||||
DMA_CHMADDR(SPI_TX_DMA_PERIPH, SPI_TX_DMA_CH) = (uint32_t)buf;
|
||||
DMA_CHCNT(SPI_TX_DMA_PERIPH, SPI_TX_DMA_CH) = len;
|
||||
DMA_CHCTL(SPI_TX_DMA_PERIPH, SPI_TX_DMA_CH) |= DMA_CHXCTL_CHEN;
|
||||
}
|
||||
|
||||
static bool isAllBytesValue(const uint8_t *data, uint8_t len, uint8_t value)
|
||||
{
|
||||
if (!data || len == 0U) {
|
||||
return false;
|
||||
}
|
||||
for (uint8_t i = 0U; i < len; ++i) {
|
||||
if (data[i] != value) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool shouldIgnoreTransaction(const uint8_t *data, uint8_t len)
|
||||
{
|
||||
if (!data || len == 0U) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (isAllBytesValue(data, len, 0xFFU) || isAllBytesValue(data, len, 0x00U)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/* APP 侧只接受两类事务:
|
||||
* 1) 旧式单字节 0xB0
|
||||
* 2) 完整协议帧(至少 5 字节)
|
||||
* 短于 5 字节且不是 0xB0 的事务只能是启动切换期的残包,直接忽略。 */
|
||||
return (len < kFrameOverhead) &&
|
||||
!(len == 1U && data[0] == CONFIG_SPI_APP_OTA_BOOT_CMD);
|
||||
}
|
||||
|
||||
static bool isFlashRelatedFrame(uint8_t frame_type)
|
||||
{
|
||||
switch (frame_type) {
|
||||
case kTypeOtaReq:
|
||||
case kTypeOtaData:
|
||||
case kTypeOtaCancel:
|
||||
case kTypeBootReq:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* CRC8 / 帧构建 / 帧解析(与 BOOT SpiOta 一致)
|
||||
* ============================================================ */
|
||||
|
||||
static uint8_t spi_crc8(const uint8_t *data, uint16_t len)
|
||||
{
|
||||
uint8_t crc = 0x00U;
|
||||
for (uint16_t i = 0U; i < len; ++i) {
|
||||
crc ^= data[i];
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
|
||||
static bool spi_build_frame(uint8_t type, const uint8_t *payload, uint16_t payload_len,
|
||||
uint8_t *out, uint16_t *out_len)
|
||||
{
|
||||
if (!out || !out_len) { return false; }
|
||||
uint16_t total = (uint16_t)(kFrameOverhead + payload_len);
|
||||
if (total > AppSpiCommand::kTxBufSize) { return false; }
|
||||
out[0] = kSof;
|
||||
out[1] = type;
|
||||
out[2] = (uint8_t)(payload_len & 0xFFU);
|
||||
out[3] = (uint8_t)((payload_len >> 8) & 0xFFU);
|
||||
if (payload && payload_len > 0U) {
|
||||
memcpy(&out[4], payload, payload_len);
|
||||
}
|
||||
out[4 + payload_len] = spi_crc8(out, (uint16_t)(4U + payload_len));
|
||||
*out_len = total;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool spi_try_parse_frame(const uint8_t *frame, uint16_t frame_len,
|
||||
uint8_t *type, const uint8_t **payload, uint16_t *payload_len)
|
||||
{
|
||||
if (!frame || !type || !payload || !payload_len) { return false; }
|
||||
|
||||
/* 兼容首字节延迟:在帧头前面可能出现若干 0xFF / 0x00 残留字节,
|
||||
* 搜索到第一个 kSof 后尝试按合法帧长度校验 CRC。 */
|
||||
uint16_t s = 0U;
|
||||
while (s < frame_len && frame[s] != kSof) { ++s; }
|
||||
if (s > 0U && s < frame_len) {
|
||||
const uint16_t shifted_len = (uint16_t)(frame_len - s);
|
||||
if (shifted_len >= kFrameOverhead) {
|
||||
const uint16_t len_field = (uint16_t)frame[s + 2U] | ((uint16_t)frame[s + 3U] << 8);
|
||||
const uint16_t expected = (uint16_t)(kFrameOverhead + len_field);
|
||||
if (shifted_len >= expected &&
|
||||
spi_crc8(&frame[s], (uint16_t)(4U + len_field)) == frame[s + 4U + len_field]) {
|
||||
*type = frame[s + 1U];
|
||||
*payload = &frame[s + 4U];
|
||||
*payload_len = len_field;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (frame_len < kFrameOverhead) { return false; }
|
||||
if (frame[0] != kSof) { return false; }
|
||||
|
||||
uint16_t len = (uint16_t)frame[2] | ((uint16_t)frame[3] << 8);
|
||||
uint16_t expected = (uint16_t)(kFrameOverhead + len);
|
||||
if (frame_len < expected) { return false; }
|
||||
if (spi_crc8(frame, (uint16_t)(4U + len)) != frame[4U + len]) { return false; }
|
||||
|
||||
*type = frame[1];
|
||||
*payload = &frame[4];
|
||||
*payload_len = len;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* 调试日志辅助
|
||||
* ============================================================ */
|
||||
|
||||
void AppSpiCommand::postLog(const char *msg)
|
||||
{
|
||||
#if (CONFIG_SPI_APP_LOG_ENABLE != 0)
|
||||
if (!msg) return;
|
||||
snprintf(m_logBuf, kLogBufSize, "[SPI-APP] %s", msg);
|
||||
m_logPending = true;
|
||||
#else
|
||||
(void)msg;
|
||||
#endif
|
||||
}
|
||||
|
||||
void AppSpiCommand::postLogFmt(const char *fmt, uint8_t val)
|
||||
{
|
||||
#if (CONFIG_SPI_APP_LOG_ENABLE != 0)
|
||||
if (!fmt) return;
|
||||
snprintf(m_logBuf, kLogBufSize, fmt, val);
|
||||
m_logPending = true;
|
||||
#else
|
||||
(void)fmt;
|
||||
(void)val;
|
||||
#endif
|
||||
}
|
||||
|
||||
void AppSpiCommand::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)
|
||||
{
|
||||
if (!tx_buf || !tx_len || tx_buf_size == 0U) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint16_t built_len = 0U;
|
||||
const bool ok = spi_build_frame(rsp_type, payload, payload_len, tx_buf, &built_len);
|
||||
if (ok) {
|
||||
*tx_len = built_len;
|
||||
} else {
|
||||
uint8_t err = 0xEEU;
|
||||
(void)spi_build_frame(kRspError, &err, 1U, tx_buf, &built_len);
|
||||
*tx_len = built_len;
|
||||
}
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* 硬件配置(与 BOOT 完全一致)
|
||||
* ============================================================ */
|
||||
|
||||
void AppSpiCommand::configureGpio()
|
||||
{
|
||||
rcu_periph_clock_enable(RCU_GPIOA);
|
||||
rcu_periph_clock_enable(RCU_GPIOB);
|
||||
rcu_periph_clock_enable(RCU_AF);
|
||||
|
||||
gpio_pin_remap_config(GPIO_SPI0_REMAP, ENABLE);
|
||||
gpio_pin_remap_config(GPIO_SWJ_SWDPENABLE_REMAP, ENABLE);
|
||||
|
||||
gpio_init(GPIOB, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, GPIO_PIN_3 | GPIO_PIN_5);
|
||||
gpio_init(GPIOB, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_4);
|
||||
gpio_init(GPIOA, GPIO_MODE_IPU, GPIO_OSPEED_50MHZ, GPIO_PIN_15);
|
||||
}
|
||||
|
||||
void AppSpiCommand::configureSpi0()
|
||||
{
|
||||
rcu_periph_clock_enable(RCU_SPI0);
|
||||
spi_i2s_deinit(SPI0);
|
||||
|
||||
spi_parameter_struct spi_init_struct;
|
||||
spi_struct_para_init(&spi_init_struct);
|
||||
spi_init_struct.device_mode = SPI_SLAVE;
|
||||
spi_init_struct.trans_mode = SPI_TRANSMODE_FULLDUPLEX;
|
||||
spi_init_struct.frame_size = SPI_FRAMESIZE_8BIT;
|
||||
spi_init_struct.nss = SPI_NSS_SOFT;
|
||||
spi_init_struct.endian = SPI_ENDIAN_MSB;
|
||||
spi_init_struct.clock_polarity_phase = SPI_CK_PL_LOW_PH_1EDGE;
|
||||
spi_init_struct.prescale = SPI_PSC_8;
|
||||
spi_init(SPI0, &spi_init_struct);
|
||||
|
||||
spi_nss_internal_low(SPI0);
|
||||
(void)spi_i2s_data_receive(SPI0);
|
||||
spi_i2s_data_transmit(SPI0, 0xFFU);
|
||||
spi_i2s_interrupt_enable(SPI0, SPI_I2S_INT_RBNE);
|
||||
spi_i2s_interrupt_enable(SPI0, SPI_I2S_INT_ERR);
|
||||
spi_i2s_interrupt_disable(SPI0, SPI_I2S_INT_TBE);
|
||||
nvic_irq_enable(SPI0_IRQn, CONFIG_IRQ_PREEMPT_SPI_USART, 0U);
|
||||
spi_enable(SPI0);
|
||||
}
|
||||
|
||||
void AppSpiCommand::configureDma()
|
||||
{
|
||||
rcu_periph_clock_enable(RCU_DMA0);
|
||||
dma_deinit(SPI_TX_DMA_PERIPH, SPI_TX_DMA_CH);
|
||||
|
||||
dma_parameter_struct dma_cfg;
|
||||
dma_struct_para_init(&dma_cfg);
|
||||
dma_cfg.periph_addr = (uint32_t)(SPI0 + 0x0CU); /* SPI0_DATA 寄存器 */
|
||||
dma_cfg.periph_width = DMA_PERIPHERAL_WIDTH_8BIT;
|
||||
dma_cfg.memory_addr = (uint32_t)m_txBuf;
|
||||
dma_cfg.memory_width = DMA_MEMORY_WIDTH_8BIT;
|
||||
dma_cfg.number = 0U;
|
||||
dma_cfg.priority = DMA_PRIORITY_HIGH;
|
||||
dma_cfg.periph_inc = DMA_PERIPH_INCREASE_DISABLE;
|
||||
dma_cfg.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
|
||||
dma_cfg.direction = DMA_MEMORY_TO_PERIPHERAL;
|
||||
dma_init(SPI_TX_DMA_PERIPH, SPI_TX_DMA_CH, &dma_cfg);
|
||||
dma_circulation_disable(SPI_TX_DMA_PERIPH, SPI_TX_DMA_CH);
|
||||
dma_channel_disable(SPI_TX_DMA_PERIPH, SPI_TX_DMA_CH);
|
||||
}
|
||||
|
||||
void AppSpiCommand::configureExti()
|
||||
{
|
||||
gpio_exti_source_select(GPIO_PORT_SOURCE_GPIOA, GPIO_PIN_SOURCE_15);
|
||||
exti_init(EXTI_15, EXTI_INTERRUPT, EXTI_TRIG_BOTH);
|
||||
exti_interrupt_enable(EXTI_15);
|
||||
exti_interrupt_flag_clear(EXTI_15);
|
||||
nvic_irq_enable(EXTI10_15_IRQn, CONFIG_IRQ_PREEMPT_SPI_USART, 1U);
|
||||
}
|
||||
|
||||
|
||||
/* ============================================================
|
||||
* 默认命令注册入口(由各 app_spi_cmd_*.cpp 提供)
|
||||
* ============================================================ */
|
||||
extern void registerSpiBootRequestCommand(AppSpiCommand &bus);
|
||||
extern void registerSpiOtaWaitCommand(AppSpiCommand &bus);
|
||||
extern void registerSpiAppPingCommand(AppSpiCommand &bus);
|
||||
extern void registerSpiCanBridgeCommand(AppSpiCommand &bus);
|
||||
AppSpiCommand AppSpiCommandBus;
|
||||
void AppSpiCommand::init()
|
||||
{
|
||||
m_csActive = false;
|
||||
m_rxLen = 0U;
|
||||
m_txLen = 0U;
|
||||
m_txPos = 0U;
|
||||
m_logPending = false;
|
||||
m_commandCount = 0U;
|
||||
memset(m_txBuf, 0xFF, sizeof(m_txBuf));
|
||||
memset(m_logBuf, 0, sizeof(m_logBuf));
|
||||
memset(m_commands, 0, sizeof(m_commands));
|
||||
|
||||
registerSpiBootRequestCommand(AppSpiCommandBus);
|
||||
registerSpiOtaWaitCommand(AppSpiCommandBus);
|
||||
registerSpiAppPingCommand(AppSpiCommandBus);
|
||||
registerSpiCanBridgeCommand(AppSpiCommandBus);
|
||||
|
||||
configureGpio();
|
||||
configureSpi0();
|
||||
configureDma();
|
||||
configureExti();
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* 命令路由
|
||||
* ============================================================ */
|
||||
|
||||
void AppSpiCommand::registerHandler(ISpiCommandHandler *handler)
|
||||
{
|
||||
if (!handler || m_commandCount >= kMaxCommands) {
|
||||
return;
|
||||
}
|
||||
m_commands[m_commandCount++] = handler;
|
||||
}
|
||||
|
||||
void AppSpiCommand::processCommand()
|
||||
{
|
||||
if (m_rxLen == 0U) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* 尝试按协议帧解析 */
|
||||
uint8_t frame_type = 0U;
|
||||
const uint8_t *payload = nullptr;
|
||||
uint16_t payload_len = 0U;
|
||||
|
||||
if (spi_try_parse_frame(m_rxBuf, m_rxLen, &frame_type, &payload, &payload_len)) {
|
||||
if (CONFIG_SPI_APP_LOG_ENABLE != 0 && !isFlashRelatedFrame(frame_type)) {
|
||||
postLogFmt("[SPI-APP] 收到非刷写 SPI type=0x%02X", frame_type);
|
||||
}
|
||||
|
||||
/* 合法帧:按 TYPE 分发给已注册命令处理器 */
|
||||
uint16_t tx_len = 0U;
|
||||
bool handled = false;
|
||||
|
||||
SpiCommandContext ctx{};
|
||||
ctx.frame_type = frame_type;
|
||||
ctx.payload = payload;
|
||||
ctx.payload_len = payload_len;
|
||||
ctx.tx_buf = m_txBuf;
|
||||
ctx.tx_buf_size = kTxBufSize;
|
||||
ctx.tx_len = &tx_len;
|
||||
|
||||
for (uint8_t i = 0U; i < m_commandCount; ++i) {
|
||||
ISpiCommandHandler *handler = m_commands[i];
|
||||
if (handler && handler->canHandle(frame_type)) {
|
||||
handler->handle(AppSpiCommandBus, ctx);
|
||||
handled = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!handled) {
|
||||
postLogFmt("[SPI-APP] 未知 type=0x%02X → ERROR", frame_type);
|
||||
uint8_t err = 0xEEU;
|
||||
spi_build_frame(kRspError, &err, 1U, m_txBuf, &tx_len);
|
||||
}
|
||||
|
||||
if (tx_len == 0U) {
|
||||
/* 兜底:避免残留上一次应答帧 */
|
||||
uint8_t err = 0xEEU;
|
||||
spi_build_frame(kRspError, &err, 1U, m_txBuf, &tx_len);
|
||||
}
|
||||
|
||||
m_txLen = (uint8_t)tx_len;
|
||||
m_txPos = 0U;
|
||||
} else {
|
||||
/* 非标准帧:保留旧式单字节 0xB0 通道,后续新命令建议走注册式帧协议 */
|
||||
const uint8_t first_valid = m_rxBuf[0U];
|
||||
if (first_valid == CONFIG_SPI_APP_OTA_BOOT_CMD) {
|
||||
postLog("旧式0xB0 → 进BOOT");
|
||||
|
||||
uint16_t tx_len_legacy = 0U;
|
||||
SpiCommandContext ctx{};
|
||||
ctx.frame_type = first_valid;
|
||||
ctx.payload = nullptr;
|
||||
ctx.payload_len = 0U;
|
||||
ctx.tx_buf = m_txBuf;
|
||||
ctx.tx_buf_size = kTxBufSize;
|
||||
ctx.tx_len = &tx_len_legacy;
|
||||
|
||||
bool handled = false;
|
||||
for (uint8_t i = 0U; i < m_commandCount; ++i) {
|
||||
ISpiCommandHandler *handler = m_commands[i];
|
||||
if (handler && handler->canHandle(first_valid)) {
|
||||
handler->handle(AppSpiCommandBus, ctx);
|
||||
handled = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!handled) {
|
||||
const bool ok = OtaBootRequest::requestEnterBoot();
|
||||
uint8_t ack = ok ? CONFIG_SPI_APP_OTA_BOOT_ACK_OK
|
||||
: CONFIG_SPI_APP_OTA_BOOT_ACK_ERROR;
|
||||
m_txBuf[0] = CONFIG_SPI_APP_OTA_BOOT_CMD;
|
||||
m_txBuf[1] = ack;
|
||||
m_txBuf[2] = 0x4FU;
|
||||
m_txBuf[3] = 0x54U;
|
||||
tx_len_legacy = 4U;
|
||||
if (ok) {
|
||||
TaskManager::set(1UL << TASK_HEARTBEAT);
|
||||
}
|
||||
}
|
||||
|
||||
m_txLen = (uint8_t)tx_len_legacy;
|
||||
m_txPos = 0U;
|
||||
} else {
|
||||
/* 无法识别 */
|
||||
postLogFmt("[SPI-APP] 非协议 0x%02X → ERROR", first_valid);
|
||||
uint16_t tx_len = 0U;
|
||||
uint8_t err = 0xEEU;
|
||||
spi_build_frame(kRspError, &err, 1U, m_txBuf, &tx_len);
|
||||
m_txLen = (uint8_t)tx_len;
|
||||
m_txPos = 0U;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_logPending) {
|
||||
/* 置位 TASK_SPI_LOG,唤醒主循环立刻打印日志 */
|
||||
TaskManager::set(1UL << TASK_SPI_LOG);
|
||||
}
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* 主循环轮询:打印延迟日志
|
||||
* ============================================================ */
|
||||
|
||||
void AppSpiCommand::poll()
|
||||
{
|
||||
char log_copy[kLogBufSize] = {};
|
||||
bool has_log = false;
|
||||
|
||||
uint32_t save = RT_CRITICAL_SAVE();
|
||||
if (m_logPending) {
|
||||
memcpy(log_copy, m_logBuf, sizeof(log_copy));
|
||||
m_logPending = false;
|
||||
has_log = true;
|
||||
}
|
||||
RT_CRITICAL_RESTORE(save);
|
||||
|
||||
if (has_log) {
|
||||
printf("%s\r\n", log_copy);
|
||||
}
|
||||
}
|
||||
|
||||
void AppSpiCommand::processPendingCommand()
|
||||
{
|
||||
if (!m_cmdPending) {
|
||||
return;
|
||||
}
|
||||
m_cmdPending = false;
|
||||
processCommand();
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* CS 边沿处理
|
||||
* ============================================================ */
|
||||
|
||||
void AppSpiCommand::onCsAssert()
|
||||
{
|
||||
if (m_csActive) {
|
||||
return;
|
||||
}
|
||||
m_csActive = true;
|
||||
m_rxLen = 0U;
|
||||
m_txPos = 0U;
|
||||
|
||||
/* 停止 DMA 并清理旧状态 */
|
||||
DMA_CHCTL(SPI_TX_DMA_PERIPH, SPI_TX_DMA_CH) &= ~DMA_CHXCTL_CHEN;
|
||||
spi_dma_disable(SPI0, SPI_DMA_TRANSMIT);
|
||||
|
||||
/* 与 BOOT 一致:清除 SPI 双缓冲中的残留数据 */
|
||||
spi_i2s_data_transmit(SPI0, 0xFFU);
|
||||
if (spi_i2s_flag_get(SPI0, SPI_FLAG_TBE) != RESET) {
|
||||
spi_i2s_data_transmit(SPI0, 0xFFU);
|
||||
}
|
||||
|
||||
/* 清除错误标志 */
|
||||
if (spi_i2s_interrupt_flag_get(SPI0, SPI_I2S_INT_FLAG_RXORERR) != RESET) {
|
||||
(void)spi_i2s_data_receive(SPI0);
|
||||
}
|
||||
if (spi_i2s_interrupt_flag_get(SPI0, SPI_I2S_INT_FLAG_FERR) != RESET) {
|
||||
spi_i2s_format_error_clear(SPI0, SPI_FLAG_FERR);
|
||||
}
|
||||
|
||||
/* 启动 DMA 发送响应帧(与 BOOT 完全一致) */
|
||||
if (m_txLen > 0U) {
|
||||
spi_dma_enable(SPI0, SPI_DMA_TRANSMIT);
|
||||
startSpiTxDma(m_txBuf, m_txLen);
|
||||
} else {
|
||||
/* 无响应时发送全 0xFF 填充 */
|
||||
spi_dma_enable(SPI0, SPI_DMA_TRANSMIT);
|
||||
startSpiTxDma(m_txBuf, kTxBufSize);
|
||||
}
|
||||
}
|
||||
|
||||
void AppSpiCommand::onCsRelease()
|
||||
{
|
||||
if (!m_csActive) {
|
||||
return;
|
||||
}
|
||||
m_csActive = false;
|
||||
|
||||
/* 停止 DMA */
|
||||
DMA_CHCTL(SPI_TX_DMA_PERIPH, SPI_TX_DMA_CH) &= ~DMA_CHXCTL_CHEN;
|
||||
spi_dma_disable(SPI0, SPI_DMA_TRANSMIT);
|
||||
|
||||
if (spi_i2s_interrupt_flag_get(SPI0, SPI_I2S_INT_FLAG_RBNE) != RESET) {
|
||||
uint8_t last_byte = (uint8_t)(spi_i2s_data_receive(SPI0) & 0xFFU);
|
||||
if (m_rxLen < sizeof(m_rxBuf)) {
|
||||
m_rxBuf[m_rxLen++] = last_byte;
|
||||
}
|
||||
}
|
||||
|
||||
if (shouldIgnoreTransaction(m_rxBuf, m_rxLen)) {
|
||||
m_rxLen = 0U;
|
||||
return;
|
||||
}
|
||||
m_cmdPending = true;
|
||||
TaskManager::set(1UL << TASK_SPI_CMD_PROCESS);
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* SPI0 / EXTI 中断处理
|
||||
* ============================================================ */
|
||||
|
||||
void AppSpiCommand::onInterrupt()
|
||||
{
|
||||
if (spi_i2s_interrupt_flag_get(SPI0, SPI_I2S_INT_FLAG_RBNE) != RESET) {
|
||||
if (!m_csActive && gpio_input_bit_get(GPIOA, GPIO_PIN_15) == RESET) {
|
||||
onCsAssert();
|
||||
}
|
||||
uint8_t byte = (uint8_t)(spi_i2s_data_receive(SPI0) & 0xFFU);
|
||||
if (m_csActive && m_rxLen < sizeof(m_rxBuf)) {
|
||||
m_rxBuf[m_rxLen++] = byte;
|
||||
}
|
||||
}
|
||||
|
||||
/* TBE 已由 DMA 替代,不再处理 */
|
||||
|
||||
if (spi_i2s_interrupt_flag_get(SPI0, SPI_I2S_INT_FLAG_RXORERR) != RESET) {
|
||||
(void)spi_i2s_data_receive(SPI0);
|
||||
m_rxLen = 0U;
|
||||
}
|
||||
|
||||
if (spi_i2s_interrupt_flag_get(SPI0, SPI_I2S_INT_FLAG_FERR) != RESET) {
|
||||
spi_i2s_format_error_clear(SPI0, SPI_FLAG_FERR);
|
||||
}
|
||||
}
|
||||
|
||||
void AppSpiCommand::onCsExtiInterrupt()
|
||||
{
|
||||
if (exti_interrupt_flag_get(EXTI_15) != RESET) {
|
||||
exti_interrupt_flag_clear(EXTI_15);
|
||||
if (gpio_input_bit_get(GPIOA, GPIO_PIN_15) == RESET) {
|
||||
onCsAssert();
|
||||
} else {
|
||||
onCsRelease();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" void SPI0_IRQHandler(void)
|
||||
{
|
||||
AppSpiCommandBus.onInterrupt();
|
||||
}
|
||||
|
||||
extern "C" void EXTI10_15_IRQHandler(void)
|
||||
{
|
||||
AppSpiCommandBus.onCsExtiInterrupt();
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
#ifndef DEVICE_APP_SPI_COMMAND_H
|
||||
#define DEVICE_APP_SPI_COMMAND_H
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
class ISpiCommandHandler;
|
||||
|
||||
/**
|
||||
* @brief APP 运行期 SPI 链路与命令路由模块。
|
||||
*
|
||||
* 本类现在主要承担两件事:
|
||||
* - 维护 SPI0 从机的硬件链路(GPIO/SPI/DMA/EXTI)
|
||||
* - 将接收到的合法帧按 TYPE 注册式分发给 ISpiCommandHandler
|
||||
*
|
||||
* 具体命令处理逻辑不再放在这里,而是拆到 `device/spi/app_spi_cmd_*.cpp`,
|
||||
* 通过 `registerHandler()` 注册进来,保持本模块可扩展。
|
||||
*/
|
||||
class AppSpiCommand {
|
||||
public:
|
||||
/** @brief 初始化 SPI0 从机命令通道并注册默认命令处理器。 */
|
||||
void init();
|
||||
|
||||
/** @brief 主循环轮询:打印延迟日志。 */
|
||||
void poll();
|
||||
|
||||
/** @brief 主循环调用:执行 ISR 中推迟的 SPI 命令解析(从 onCsRelease 移出)。 */
|
||||
void processPendingCommand();
|
||||
|
||||
/** @brief SPI0 中断处理入口。 */
|
||||
void onInterrupt();
|
||||
|
||||
/** @brief CS 边沿中断处理入口。 */
|
||||
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);
|
||||
|
||||
static constexpr uint16_t kRxBufSize = 128U;
|
||||
static constexpr uint16_t kTxBufSize = 64U;
|
||||
static constexpr uint16_t kLogBufSize = 256U;
|
||||
|
||||
private:
|
||||
static constexpr uint8_t kMaxCommands = 8U;
|
||||
|
||||
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;
|
||||
volatile bool m_cmdPending = false; /**< ISR 置位,标记有待主循环解析的 SPI 命令。 */
|
||||
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 */
|
||||
@@ -0,0 +1,36 @@
|
||||
#ifndef DEVICE_APP_SPI_COMMAND_DEFS_H
|
||||
#define DEVICE_APP_SPI_COMMAND_DEFS_H
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
/**
|
||||
* @file app_spi_command_defs.h
|
||||
* @brief SPI 命令总线公共协议常量。
|
||||
*
|
||||
* 将原来散落在 `app_spi_command.cpp` 中的帧类型与响应类型抽到公共头文件,
|
||||
* 方便后续在不同命令处理器之间复用;当前仅服务于 APP 运行期 SPI 命令通道,
|
||||
* 未来若 BOOT 与 APP 需要继续共享协议常量,也可从这里统一收敛。
|
||||
*/
|
||||
|
||||
namespace SpiDefs {
|
||||
|
||||
static constexpr uint8_t kSof = 0xA5U;
|
||||
|
||||
/* 帧类型 */
|
||||
static constexpr uint8_t kTypeOtaReq = 0x01U;
|
||||
static constexpr uint8_t kTypeOtaData = 0x02U;
|
||||
static constexpr uint8_t kTypeOtaCancel = 0x03U;
|
||||
static constexpr uint8_t kTypeAppPing = 0xA1U;
|
||||
static constexpr uint8_t kTypeBootReq = 0xB0U;
|
||||
static constexpr uint8_t kTypeCanSend = 0xC0U;
|
||||
|
||||
/* 响应类型 */
|
||||
static constexpr uint8_t kRspAck = 0x80U;
|
||||
static constexpr uint8_t kRspWait = 0x81U;
|
||||
static constexpr uint8_t kRspError = 0x82U;
|
||||
|
||||
static constexpr uint16_t kFrameOverhead = 5U;
|
||||
|
||||
} // namespace SpiDefs
|
||||
|
||||
#endif /* DEVICE_APP_SPI_COMMAND_DEFS_H */
|
||||
@@ -0,0 +1,25 @@
|
||||
#ifndef DEVICE_SPI_COMMAND_HANDLER_H
|
||||
#define DEVICE_SPI_COMMAND_HANDLER_H
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
class AppSpiCommand;
|
||||
|
||||
struct SpiCommandContext {
|
||||
uint8_t frame_type;
|
||||
const uint8_t *payload;
|
||||
uint16_t payload_len;
|
||||
uint8_t *tx_buf;
|
||||
uint16_t tx_buf_size;
|
||||
uint16_t *tx_len;
|
||||
};
|
||||
|
||||
class ISpiCommandHandler {
|
||||
public:
|
||||
~ISpiCommandHandler() {}
|
||||
|
||||
virtual bool canHandle(uint8_t frame_type) const = 0;
|
||||
virtual void handle(AppSpiCommand &bus, const SpiCommandContext &ctx) = 0;
|
||||
};
|
||||
|
||||
#endif /* DEVICE_SPI_COMMAND_HANDLER_H */
|
||||
Reference in New Issue
Block a user