Files
DCU_APP/GD32E508_DCU_APP/device/app_spi_command.cpp
T
cbb 86c0ec730b feat(canfd): 添加CANFD发送邮箱占用处理机制
当CAN控制器因总线无ACK导致邮箱卡死时,实现两段式恢复策略:
1) 短自旋轮询邮箱状态,大多数场景下可避免触发abort而不丢帧
2) 若仍全部PENDING则只中止第一个占用邮箱,限制最坏丢帧数量为1帧

添加了tx_abort_count诊断计数器用于监控该情况,并引入SysTick延时功能

refactor(spi-cmd): 优化SPI命令日志输出格式

将CAN发送相关日志改为统一的格式化函数输出,增加数据字段十六进制显示功能,
并扩大日志缓冲区大小从128到256字节以支持更详细的日志信息

chore(spi-command): 添加SPI帧类型分类和非刷写帧日志记录

新增isFlashRelatedFrame函数区分刷写相关帧类型,在处理命令时对非刷写帧进行日志记录

perf(diagnostic): 在诊断打印中添加发送中止计数统计

更新诊断信息打印格式,增加tx_abort计数项,便于监控CAN发送异常情况
2026-06-25 01:11:31 +08:00

577 lines
18 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* @file app_spi_command.cpp
* @brief APP 运行期 SPI 链路与命令路由实现。
*
* 本模块现在只负责:
* - SPI0 从机链路初始化(GPIO/SPI/DMA/EXTI)
* - CS、DMA、中断驱动的帧接收与发送
* - 公共帧格式解析与应答构建
* - 业务命令的注册与分发
*
* 具体命令处理逻辑已经拆到独立的 `app_spi_cmd_*.cpp`,
* 通过 `registerHandler()` 注册进来,后续新增 SPI 业务时
* 只需要新增命令处理器,不再继续膨胀本文件。
*/
#include "device/app_spi_command.h"
#include "device/spi_command_handler.h"
#include "device/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/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 (!msg) return;
snprintf(m_logBuf, kLogBufSize, "[SPI-APP] %s", msg);
m_logPending = true;
}
void AppSpiCommand::postLogFmt(const char *fmt, uint8_t val)
{
if (!fmt) return;
snprintf(m_logBuf, kLogBufSize, fmt, val);
m_logPending = true;
}
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, 2U, 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, 1U, 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 (!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;
}
}
/* 置位 TASK_SPI_LOG,唤醒主循环立刻打印日志 */
TaskManager::set(1UL << TASK_SPI_LOG);
}
/* ============================================================
* 主循环轮询:打印延迟日志
* ============================================================ */
void AppSpiCommand::poll()
{
char log_copy[kLogBufSize] = {};
bool has_log = false;
__disable_irq();
if (m_logPending) {
memcpy(log_copy, m_logBuf, sizeof(log_copy));
m_logPending = false;
has_log = true;
}
__enable_irq();
if (has_log) {
printf("%s\r\n", log_copy);
}
}
/* ============================================================
* 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;
}
processCommand();
}
/* ============================================================
* 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();
}