refactor(SPI): 实现全双工DMA传输以提升通信可靠性
- 配置DMA0_CH1用于SPI0_RX接收,替代原有的RBNE中断逐字节处理 - 添加startSpiRxDma、stopSpiDmas、drainSpiRxHw等DMA控制函数 - 在CS空闲期预装下一笔DMA传输,避免EXTI CS下降沿相对于SCK偏晚导致收0字节 - 与BOOT工程SpiOta的DMA配置对齐,确保通信稳定性 refactor(SPI): 优化CS边沿处理逻辑和命令解析时机 - 移除原有的EXTI5_9_IRQHandler用于SPI Ready引脚处理 - 将命令处理从异步任务调度改为在CS释放中断中同步完成 - 重构onCsAssert和onCsRelease逻辑,简化DMA启停流程 - 删除TASK_SPI_CMD_PROCESS任务类型,减少不必要的任务调度 feat(CANFD): 修改transmitFrame返回类型为枚举值以提供详细错误信息 - 将CanFd::transmitFrame返回类型从bool改为CanTxResult枚举 - 新增CanTxResult枚举定义,包含kOk、kErrBusOff、kErrTecHigh、kErrMailbox、kErrInvalid - 更新SPI桥接命令处理中对transmitFrame返回值的处理逻辑 - 枚举值设计与SPI错误响应码对齐,便于直接透传错误信息 refactor(SPI): 实现全双工DMA传输以提升通信可靠性 - 配置DMA0_CH1用于SPI0_RX接收,替代原有的RBNE中断逐字节处理 - 添加startSpiRxDma、stopSpiDmas、drainSpiRxHw等DMA控制函数 - 在CS空闲期预装下一笔DMA传输,避免EXTI CS下降沿相对于SCK偏晚导致收0字节 - 与BOOT工程SpiOta的DMA配置对齐,确保通信稳定性 refactor(SPI): 优化CS边沿处理逻辑和命令解析时机 - 移除原有的EXTI5_9_IRQHandler用于SPI Ready引脚处理 - 将命令处理从异步任务调度改为在CS释放中断中同步完成 - 重构onCsAssert和onCsRelease逻辑,简化DMA启停流程 - 删除TASK_SPI_CMD_PROCESS任务类型,减少不必要的任务调度 feat(OTA): 添加BOOT版本号读取功能并优化版本信息获取 - 在ota_boot_request模块中添加readBootVersion函数 - 从OTA配置页Flash读取BOOT版本号信息 - 在SPI PING命令响应中使用新的版本读取接口 - 统一版本号获取方式,避免裸指针访问Flash地址 refactor(task): 调整诊断打印任务调度周期为5秒间隔 - 新增div_diag_print调度分频计数器用于诊断打印 - 将诊断打印任务从心跳任务中分离,独立控制执行周期 - 修改诊断打印函数注释,明确其由5秒节拍触发 - 优化任务调度逻辑,提高系统运行效率
This commit is contained in:
@@ -812,16 +812,16 @@ bool CanFd::consumeRxFrame(uint32_t *id, uint8_t *data, uint8_t *length, uint8_t
|
||||
* @param[in] dlc 数据长度(1~64)。
|
||||
* @param[in] periodic 是否为周期性发送;true 时仅允许一个邮箱保持自动重发,
|
||||
* 不主动 abort 旧帧,且跳过 TEC 保护以匹配诊断行为。
|
||||
* @retval bool true 表示发送请求已进入硬件邮箱,false 表示参数无效或邮箱已满。
|
||||
* @retval CanTxResult kOk 表示发送请求已进入硬件邮箱;其它值表示具体失败原因。
|
||||
* @note 绕过软件发送队列,直接调用硬件发送;适用于 SPI 桥接等即时发送场景。
|
||||
* 网页发送数仅在 TX 中断确认 MTFNERR 后增加,不能用本函数返回值判断总线 ACK。
|
||||
*/
|
||||
bool CanFd::transmitFrame(uint8_t channelIndex, uint32_t ext_id,
|
||||
const uint8_t *data, uint8_t dlc, bool periodic)
|
||||
CanFd::CanTxResult CanFd::transmitFrame(uint8_t channelIndex, uint32_t ext_id,
|
||||
const uint8_t *data, uint8_t dlc, bool periodic)
|
||||
{
|
||||
if (channelIndex >= kChannelCount || !kChannelEnabled[channelIndex]
|
||||
|| data == nullptr || dlc == 0U || dlc > 64U) {
|
||||
return false;
|
||||
return CanTxResult::kErrInvalid;
|
||||
}
|
||||
|
||||
/* 总线健康检查:BUS_OFF 或 TEC 过高时跳过发送,防止测试帧把通道推向 BUS_OFF */
|
||||
@@ -840,7 +840,7 @@ bool CanFd::transmitFrame(uint8_t channelIndex, uint32_t ext_id,
|
||||
static_cast<unsigned long>(CAN_TSTAT(canPeriph)));
|
||||
s_lastBusOffLogSeq[channelIndex] = s_feedbackSeq;
|
||||
}
|
||||
return false; /* 通道处于 BUS_OFF,不发送 */
|
||||
return CanTxResult::kErrBusOff; /* 通道处于 BUS_OFF,不发送 */
|
||||
}
|
||||
const uint32_t tec = (errReg >> 16) & 0xFFU;
|
||||
if (tec > 96U && !periodic) {
|
||||
@@ -856,7 +856,7 @@ bool CanFd::transmitFrame(uint8_t channelIndex, uint32_t ext_id,
|
||||
static_cast<unsigned long>(CAN_TSTAT(canPeriph)));
|
||||
s_lastTecLogSeq[channelIndex] = s_feedbackSeq;
|
||||
}
|
||||
return false; /* TEC 过高(>75% 错误被动阈值),暂停发送保护通道 */
|
||||
return CanTxResult::kErrTecHigh; /* TEC 过高(>75% 错误被动阈值),暂停发送保护通道 */
|
||||
}
|
||||
|
||||
/* 周期发送只允许一个邮箱保持自动重发。无 ACK 时同一帧会持续产生
|
||||
@@ -865,7 +865,7 @@ bool CanFd::transmitFrame(uint8_t channelIndex, uint32_t ext_id,
|
||||
if (periodic) {
|
||||
for (uint8_t mb = CAN_MAILBOX0; mb <= CAN_MAILBOX2; ++mb) {
|
||||
if (can_transmit_states(canPeriph, mb) == CAN_TRANSMIT_PENDING) {
|
||||
return false;
|
||||
return CanTxResult::kErrMailbox;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -911,7 +911,7 @@ bool CanFd::transmitFrame(uint8_t channelIndex, uint32_t ext_id,
|
||||
s_diag.last_tx_id = ext_id;
|
||||
s_diag.last_tx_channel = channelIndex;
|
||||
s_diag.last_payload_length = dlc;
|
||||
return true;
|
||||
return CanTxResult::kOk;
|
||||
}
|
||||
s_diag.error_count++;
|
||||
s_channelStats[channelIndex].error_count++;
|
||||
@@ -928,7 +928,7 @@ bool CanFd::transmitFrame(uint8_t channelIndex, uint32_t ext_id,
|
||||
static_cast<unsigned long>(CAN_TSTAT(canPeriph)));
|
||||
s_lastMailboxLogSeq[channelIndex] = s_feedbackSeq;
|
||||
}
|
||||
return false;
|
||||
return CanTxResult::kErrMailbox;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -28,6 +28,19 @@ public:
|
||||
kModeBridge0To12 = 2U, /**< CAN0 桥接模式:硬件过滤等同 AllPass,CAN0 收到的每一帧按原帧格式自动转发到 CAN1 与 CAN2。 */
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief CAN 发送结果枚举,枚举值即 SPI 错误响应码,便于 handler 直接透传。
|
||||
* @note 0xE0/0xE1 保留给 handler 入口校验(payload 过短/通道非法),
|
||||
* 不经 transmitFrame 返回;0xE2~0xE5 由 transmitFrame 区分四种失败原因。
|
||||
*/
|
||||
enum class CanTxResult : uint8_t {
|
||||
kOk = 0x00U, /**< 发送成功,已进入硬件邮箱。 */
|
||||
kErrBusOff = 0xE2U, /**< 通道处于 BUS_OFF,不发送。 */
|
||||
kErrTecHigh = 0xE3U, /**< TEC > 96,暂停发送保护通道。 */
|
||||
kErrMailbox = 0xE4U, /**< 3 个硬件邮箱全 PENDING,abort+重投仍失败。 */
|
||||
kErrInvalid = 0xE5U, /**< 入口参数无效(channelIndex/dlc/data 等)。 */
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief CANFD 运行诊断统计信息。
|
||||
*/
|
||||
@@ -122,8 +135,8 @@ public:
|
||||
static void poll();
|
||||
static bool hasRxFrame();
|
||||
static bool consumeRxFrame(uint32_t *id, uint8_t *data, uint8_t *length, uint8_t *channel = nullptr);
|
||||
static bool transmitFrame(uint8_t channelIndex, uint32_t ext_id,
|
||||
const uint8_t *data, uint8_t dlc, bool periodic = false);
|
||||
static CanTxResult transmitFrame(uint8_t channelIndex, uint32_t ext_id,
|
||||
const uint8_t *data, uint8_t dlc, bool periodic = false);
|
||||
static const Diag *diag();
|
||||
|
||||
/**
|
||||
|
||||
@@ -80,15 +80,15 @@ public:
|
||||
return;
|
||||
}
|
||||
|
||||
const bool ok = CanFd::transmitFrame(can_ch, can_id, can_data, can_dlc);
|
||||
if (ok) {
|
||||
const CanFd::CanTxResult result = CanFd::transmitFrame(can_ch, can_id, can_data, can_dlc);
|
||||
if (result == CanFd::CanTxResult::kOk) {
|
||||
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;
|
||||
const uint8_t err = static_cast<uint8_t>(result); /* 枚举值即 SPI 错误码 */
|
||||
bus.buildResponse(ctx.tx_buf, ctx.tx_buf_size, ctx.tx_len,
|
||||
SpiDefs::kRspError, &err, 1U);
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include "device/spi/app_spi_command.h"
|
||||
#include "device/spi/app_spi_command_defs.h"
|
||||
#include "sys/config.h"
|
||||
#include "sys/ota_boot_request.h"
|
||||
#include "sys/systick.h"
|
||||
#include "main.h"
|
||||
#include <cstring>
|
||||
@@ -30,15 +31,12 @@ public:
|
||||
ping_payload[2] = (uint8_t)APP_FW_VERSION_PATCH;
|
||||
ping_payload[3] = (uint8_t)APP_FW_VERSION_BUILD;
|
||||
|
||||
/* BOOT 版本号从 OTA 配置页 Flash 读取 */
|
||||
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;
|
||||
ping_payload[4] = (uint8_t)(bw0 & 0xFFU);
|
||||
ping_payload[5] = (uint8_t)((bw0 >> 16) & 0xFFU);
|
||||
ping_payload[6] = (uint8_t)(bw1 & 0xFFU);
|
||||
ping_payload[7] = (uint8_t)((bw1 >> 16) & 0xFFU);
|
||||
/* BOOT 版本号从 OTA 配置页 Flash 读取(封装函数,避免裸指针散落) */
|
||||
const BootVersion boot_ver = OtaBootRequest::readBootVersion();
|
||||
ping_payload[4] = boot_ver.major;
|
||||
ping_payload[5] = boot_ver.minor;
|
||||
ping_payload[6] = boot_ver.patch;
|
||||
ping_payload[7] = boot_ver.build;
|
||||
|
||||
const uint32_t uptime_ms = SysTickTimer::getMs();
|
||||
ping_payload[8] = (uint8_t)(uptime_ms & 0xFFU);
|
||||
|
||||
@@ -55,9 +55,13 @@ static constexpr uint8_t kRspAck = SpiDefs::kRspAck;
|
||||
static constexpr uint8_t kRspWait = SpiDefs::kRspWait;
|
||||
static constexpr uint8_t kRspError = SpiDefs::kRspError;
|
||||
|
||||
/* DMA 配置(与 BOOT SpiOta 一致:DMA0_CH2 用于 SPI0 TX) */
|
||||
/* DMA 全双工配置(与 BOOT SpiOta 一致):
|
||||
* DMA0_CH1: SPI0_RX DMA0_CH2: SPI0_TX
|
||||
* RX 走 DMA 后禁用 RBNE 逐字节中断,避免 256B 事务 RX-OVR/SOF 丢失。 */
|
||||
#define SPI_TX_DMA_PERIPH DMA0
|
||||
#define SPI_TX_DMA_CH DMA_CH2
|
||||
#define SPI_RX_DMA_PERIPH DMA0
|
||||
#define SPI_RX_DMA_CH DMA_CH1
|
||||
|
||||
static void startSpiTxDma(const uint8_t *buf, uint16_t len)
|
||||
{
|
||||
@@ -68,6 +72,45 @@ static void startSpiTxDma(const uint8_t *buf, uint16_t len)
|
||||
DMA_CHCTL(SPI_TX_DMA_PERIPH, SPI_TX_DMA_CH) |= DMA_CHXCTL_CHEN;
|
||||
}
|
||||
|
||||
/** @brief 启动 DMA 接收 SPI0_DATA → m_rxBuf(容量 kRxBufSize)。
|
||||
* 每次 CS 空闲期预装,下一笔 CS 上升沿时由 stopSpiDmas() 读 CHCNT 反算实际长度。 */
|
||||
static void startSpiRxDma(uint8_t *rx_buf, uint16_t rx_cap)
|
||||
{
|
||||
DMA_CHCTL(SPI_RX_DMA_PERIPH, SPI_RX_DMA_CH) &= ~DMA_CHXCTL_CHEN;
|
||||
DMA_INTC(SPI_RX_DMA_PERIPH) |= DMA_FLAG_ADD(DMA_CHINTF_RESET_VALUE, SPI_RX_DMA_CH);
|
||||
DMA_CHMADDR(SPI_RX_DMA_PERIPH, SPI_RX_DMA_CH) = (uint32_t)rx_buf;
|
||||
DMA_CHCNT(SPI_RX_DMA_PERIPH, SPI_RX_DMA_CH) = rx_cap;
|
||||
DMA_CHCTL(SPI_RX_DMA_PERIPH, SPI_RX_DMA_CH) |= DMA_CHXCTL_CHEN;
|
||||
}
|
||||
|
||||
/** @brief 清空 SPI 接收通路残留(含 OVR),对齐 BOOT drainSpiRxHw。 */
|
||||
static void drainSpiRxHw(void)
|
||||
{
|
||||
while (spi_i2s_flag_get(SPI0, SPI_FLAG_RBNE) != RESET) {
|
||||
(void)spi_i2s_data_receive(SPI0);
|
||||
}
|
||||
if (spi_i2s_interrupt_flag_get(SPI0, SPI_I2S_INT_FLAG_RXORERR) != RESET) {
|
||||
(void)spi_i2s_data_receive(SPI0);
|
||||
(void)SPI_STAT(SPI0);
|
||||
}
|
||||
}
|
||||
|
||||
/** @brief 停止双 DMA;返回本 CS 实际 RX 字节数(必须在 disable 前读 CHCNT)。
|
||||
* 对齐 BOOT stopSpiDmas:先读 remaining 再 disable,否则计数器被清。 */
|
||||
static uint16_t stopSpiDmas(uint16_t rx_cap)
|
||||
{
|
||||
const uint16_t remaining = (uint16_t)DMA_CHCNT(SPI_RX_DMA_PERIPH, SPI_RX_DMA_CH);
|
||||
DMA_CHCTL(SPI_TX_DMA_PERIPH, SPI_TX_DMA_CH) &= ~DMA_CHXCTL_CHEN;
|
||||
DMA_CHCTL(SPI_RX_DMA_PERIPH, SPI_RX_DMA_CH) &= ~DMA_CHXCTL_CHEN;
|
||||
spi_dma_disable(SPI0, SPI_DMA_TRANSMIT);
|
||||
spi_dma_disable(SPI0, SPI_DMA_RECEIVE);
|
||||
uint16_t got = 0U;
|
||||
if (remaining <= rx_cap) {
|
||||
got = (uint16_t)(rx_cap - remaining);
|
||||
}
|
||||
return got;
|
||||
}
|
||||
|
||||
static bool isAllBytesValue(const uint8_t *data, uint16_t len, uint8_t value)
|
||||
{
|
||||
if (!data || len == 0U) {
|
||||
@@ -264,6 +307,7 @@ void AppSpiCommand::configureSpi0()
|
||||
{
|
||||
rcu_periph_clock_enable(RCU_SPI0);
|
||||
spi_i2s_deinit(SPI0);
|
||||
(void)spi_i2s_data_receive(SPI0); /* 清空 SPI_DATA 残留值 */
|
||||
|
||||
spi_parameter_struct spi_init_struct;
|
||||
spi_struct_para_init(&spi_init_struct);
|
||||
@@ -276,11 +320,14 @@ void AppSpiCommand::configureSpi0()
|
||||
spi_init_struct.prescale = SPI_PSC_8;
|
||||
spi_init(SPI0, &spi_init_struct);
|
||||
|
||||
/* 内部 NSS 常低:SPI 从机始终就绪,不依赖 EXTI ISR 切换 internal NSS。
|
||||
* PA15 仅作 EXTI 事务边界信号,用于 onCsAssert/onCsRelease 控制 DMA 装载。 */
|
||||
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);
|
||||
/* RX 改走 DMA0_CH1:禁用 RBNE 逐字节中断,避免 256B 事务 RX-OVR/SOF 丢失。 */
|
||||
spi_i2s_interrupt_disable(SPI0, SPI_I2S_INT_RBNE);
|
||||
spi_i2s_interrupt_disable(SPI0, SPI_I2S_INT_ERR);
|
||||
spi_i2s_interrupt_disable(SPI0, SPI_I2S_INT_TBE);
|
||||
nvic_irq_enable(SPI0_IRQn, 2U, 0U);
|
||||
spi_enable(SPI0);
|
||||
@@ -289,8 +336,9 @@ void AppSpiCommand::configureSpi0()
|
||||
void AppSpiCommand::configureDma()
|
||||
{
|
||||
rcu_periph_clock_enable(RCU_DMA0);
|
||||
dma_deinit(SPI_TX_DMA_PERIPH, SPI_TX_DMA_CH);
|
||||
|
||||
/* TX: memory → SPI0_DATA */
|
||||
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 寄存器 */
|
||||
@@ -305,6 +353,23 @@ void AppSpiCommand::configureDma()
|
||||
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);
|
||||
|
||||
/* RX: SPI0_DATA → m_rxBuf(替代旧 RBNE 逐字节累积)。
|
||||
* 与 BOOT SpiOta 对齐:DMA0_CH1 固定映射 SPI0_RX。 */
|
||||
dma_deinit(SPI_RX_DMA_PERIPH, SPI_RX_DMA_CH);
|
||||
dma_struct_para_init(&dma_cfg);
|
||||
dma_cfg.periph_addr = (uint32_t)(SPI0 + 0x0CU);
|
||||
dma_cfg.periph_width = DMA_PERIPHERAL_WIDTH_8BIT;
|
||||
dma_cfg.memory_addr = (uint32_t)m_rxBuf;
|
||||
dma_cfg.memory_width = DMA_MEMORY_WIDTH_8BIT;
|
||||
dma_cfg.number = kRxBufSize;
|
||||
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_PERIPHERAL_TO_MEMORY;
|
||||
dma_init(SPI_RX_DMA_PERIPH, SPI_RX_DMA_CH, &dma_cfg);
|
||||
dma_circulation_disable(SPI_RX_DMA_PERIPH, SPI_RX_DMA_CH);
|
||||
dma_channel_disable(SPI_RX_DMA_PERIPH, SPI_RX_DMA_CH);
|
||||
}
|
||||
|
||||
void AppSpiCommand::configureExti()
|
||||
@@ -316,26 +381,6 @@ void AppSpiCommand::configureExti()
|
||||
nvic_irq_enable(EXTI10_15_IRQn, 1U, 1U);
|
||||
}
|
||||
|
||||
void AppSpiCommand::configureSpiReadyExti()
|
||||
{
|
||||
/* PB6 已在 configureGpio() 配置为开漏输出,此处保留 EXTI6 中断使能。
|
||||
* v2 DATA_READY: ESP32 不再操作 PB6,EXTI6 上升沿无业务含义,
|
||||
* onSpiReadyExti() 仅清标志避免反复触发。 */
|
||||
gpio_exti_source_select(GPIO_PORT_SOURCE_GPIOB, GPIO_PIN_SOURCE_6);
|
||||
exti_init(EXTI_6, EXTI_INTERRUPT, EXTI_TRIG_RISING);
|
||||
exti_interrupt_enable(EXTI_6);
|
||||
exti_interrupt_flag_clear(EXTI_6);
|
||||
nvic_irq_enable(EXTI5_9_IRQn, CONFIG_IRQ_PREEMPT_SPI_USART, 1U);
|
||||
}
|
||||
|
||||
void AppSpiCommand::onSpiReadyExti()
|
||||
{
|
||||
/* v2 DATA_READY: ESP32 不再操作 PB6,EXTI6 上升沿不再有意义。
|
||||
* 清除中断标志避免反复触发。 */
|
||||
if (exti_interrupt_flag_get(EXTI_6) != RESET) {
|
||||
exti_interrupt_flag_clear(EXTI_6);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* ============================================================
|
||||
@@ -373,10 +418,55 @@ void AppSpiCommand::init()
|
||||
registerSpiIdSnapshotCommand(AppSpiCommandBus);
|
||||
|
||||
configureGpio();
|
||||
configureSpi0();
|
||||
configureDma();
|
||||
configureSpi0();
|
||||
configureExti();
|
||||
configureSpiReadyExti();
|
||||
/* 首笔 CS 到来前预装 DMA,避免 EXTI 晚于首 SCK。
|
||||
* 必须在 configureSpi0/configureDma 之后调用。 */
|
||||
armSpiDmasForNextCs();
|
||||
}
|
||||
|
||||
/** @brief 在 CS 空闲期预装下一笔全双工 DMA。
|
||||
*
|
||||
* 必须在首个 SCK 之前完成:EXTI CS 下降相对 ESP32 时钟经常偏晚,
|
||||
* 若在 onCsAssert 里才 start DMA,整笔事务会收 0 字节 → 永不 HELLO_ACK → MISO 全 0xFF。
|
||||
*
|
||||
* TX 始终按 kTxBufSize 装载:响应帧在头部,其余 0xFF。
|
||||
* 若只 DMA 实际帧长(~26B),主机打 96 时钟时 TX underrun,后续 MISO 不定。
|
||||
*
|
||||
* 对齐 BOOT SpiOta::armSpiDmasForNextCs。
|
||||
*/
|
||||
void AppSpiCommand::armSpiDmasForNextCs()
|
||||
{
|
||||
DMA_CHCTL(SPI_TX_DMA_PERIPH, SPI_TX_DMA_CH) &= ~DMA_CHXCTL_CHEN;
|
||||
DMA_CHCTL(SPI_RX_DMA_PERIPH, SPI_RX_DMA_CH) &= ~DMA_CHXCTL_CHEN;
|
||||
spi_dma_disable(SPI0, SPI_DMA_TRANSMIT);
|
||||
spi_dma_disable(SPI0, SPI_DMA_RECEIVE);
|
||||
drainSpiRxHw();
|
||||
|
||||
/* 预填 TX:有挂起响应用响应帧(头部)+ 0xFF 填充到满缓冲 */
|
||||
if (m_txLen > 0U && m_txLen <= kTxBufSize) {
|
||||
if (m_txLen < kTxBufSize) {
|
||||
memset(&m_txBuf[m_txLen], 0xFF, (size_t)(kTxBufSize - m_txLen));
|
||||
}
|
||||
} else {
|
||||
m_txLen = 0U;
|
||||
memset(m_txBuf, 0xFF, kTxBufSize);
|
||||
}
|
||||
|
||||
/* RX 缓冲清零(便于诊断时区分有效字节与残留) */
|
||||
memset(m_rxBuf, 0x00, kRxBufSize);
|
||||
m_rxLen = 0U;
|
||||
|
||||
/* 预装 SPI_DATA,避免 DMA 启动前首拍 TBE 吐旧值 */
|
||||
spi_i2s_data_transmit(SPI0, m_txBuf[0]);
|
||||
|
||||
/* 先使能 SPI DMA 请求,再开 DMA 通道,保证首字节即可搬运 */
|
||||
spi_dma_enable(SPI0, SPI_DMA_RECEIVE);
|
||||
spi_dma_enable(SPI0, SPI_DMA_TRANSMIT);
|
||||
startSpiRxDma(m_rxBuf, kRxBufSize);
|
||||
/* TX 永远满缓冲长度,与主机 96~256B 事务匹配,避免 underrun */
|
||||
startSpiTxDma(m_txBuf, kTxBufSize);
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
@@ -715,59 +805,36 @@ void AppSpiCommand::onCsAssert()
|
||||
return;
|
||||
}
|
||||
m_csActive = true;
|
||||
/* v2 DATA_READY: CS 本身即互斥,PB6 不再表示「忙碌」,不在 CS 下降沿拉低。
|
||||
* PB6 仅由 updateDataReady() 依据待发数据驱动,避免与队列语义冲突。 */
|
||||
m_rxLen = 0U;
|
||||
/* CS 下降:DMA 已在空闲期由 armSpiDmasForNextCs() 预装,此处只标记忙碌。
|
||||
* v2 DATA_READY: CS 本身即互斥,PB6 不再表示「忙碌」,不在 CS 下降沿拉低。
|
||||
* 切勿在这里 stop/restart DMA,否则赶不上 ESP32 已发出的 SCK(对齐 BOOT)。 */
|
||||
m_txPos = 0U;
|
||||
m_txActiveThisCs = false;
|
||||
|
||||
/* 停止 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) {
|
||||
m_txActiveThisCs = true;
|
||||
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) {
|
||||
/* EXTI 丢失下降沿时:仍尝试收尾并 re-arm,避免永久失步(对齐 BOOT) */
|
||||
m_rxLen = stopSpiDmas(kRxBufSize);
|
||||
drainSpiRxHw();
|
||||
if (m_txLen > 0U) {
|
||||
m_txLen = 0U;
|
||||
m_txPos = 0U;
|
||||
memset(m_txBuf, 0xFF, sizeof(m_txBuf));
|
||||
}
|
||||
if (m_rxLen >= V2_OVERHEAD) {
|
||||
processCommand();
|
||||
}
|
||||
armSpiDmasForNextCs();
|
||||
updateDataReady();
|
||||
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;
|
||||
}
|
||||
}
|
||||
/* 停 DMA 并取本笔 RX 长度(先读 CHCNT 再 disable,否则计数器被清) */
|
||||
m_rxLen = stopSpiDmas(kRxBufSize);
|
||||
drainSpiRxHw();
|
||||
|
||||
/* 若本次 CS 事务是 ESP32 响应 GD32 按铃来读请求帧,
|
||||
* 则 GD32 的请求帧已通过 DMA 输出到 MISO,取消按铃状态。
|
||||
@@ -810,8 +877,9 @@ void AppSpiCommand::onCsRelease()
|
||||
/* v2 DATA_READY: 已清 m_txLen=0 与 m_ringPending=false,
|
||||
* updateDataReady() 释放为高;ESP32 完成 CS 即视为已读走确认。 */
|
||||
m_linkState.onEsp32Complete();
|
||||
/* 重新预装下一笔 DMA,保证下一 CS 首字节即可收发 */
|
||||
armSpiDmasForNextCs();
|
||||
updateDataReady();
|
||||
/* 上层若需要 ESP32 处理该请求帧,由 ESP32 侧读到的帧 TYPE 决定后续动作 */
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -826,6 +894,7 @@ void AppSpiCommand::onCsRelease()
|
||||
m_txActiveThisCs = false;
|
||||
/* v2 DATA_READY: shouldIgnoreTransaction 路径已清 m_txLen=0,
|
||||
* updateDataReady() 释放为高,避免 PB6 卡 LOW。 */
|
||||
armSpiDmasForNextCs();
|
||||
updateDataReady();
|
||||
return;
|
||||
}
|
||||
@@ -836,8 +905,12 @@ void AppSpiCommand::onCsRelease()
|
||||
memset(m_txBuf, 0xFF, sizeof(m_txBuf));
|
||||
|
||||
/* 命令处理在 EXTI ISR 内同步完成(对齐 BOOT 的 isrProcessFrame 模式),
|
||||
* 消除主循环延迟。处理完成后 processCommand() 末尾释放 SPIready。 */
|
||||
* 消除主循环延迟。处理完成后 processCommand() 末尾再 re-arm DMA + 释放 SPIready。 */
|
||||
processCommand();
|
||||
|
||||
/* 处理完立即预装下一笔,保证下一 CS 首字节即可收发 */
|
||||
armSpiDmasForNextCs();
|
||||
updateDataReady();
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
@@ -846,26 +919,14 @@ void AppSpiCommand::onCsRelease()
|
||||
|
||||
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 替代,不再处理 */
|
||||
|
||||
/* RX 已改走 DMA0_CH1:RBNE 逐字节中断已禁用,本 ISR 只保留错误标志清理。
|
||||
* CS 边沿处理全部在 onCsExtiInterrupt 的 EXTI15 ISR 里完成。 */
|
||||
if (spi_i2s_interrupt_flag_get(SPI0, SPI_I2S_INT_FLAG_RXORERR) != RESET) {
|
||||
/* 读取并丢弃 overrun 字节以清除标志,但保留已收到的帧数据。
|
||||
* 丢失的字节会导致 CRC 校验失败,由 spi_try_parse_frame 优雅处理
|
||||
* (返回 false -> processCommand 构建 ERROR 响应),
|
||||
* 而不是重置 m_rxLen 导致整帧丢失。 */
|
||||
/* RXORERR 理论上不应再出现(RX 走 DMA),但兜底清理避免标志卡死。
|
||||
* 读 SPI_DATA + SPI_STAT 清除 OVR 标志。 */
|
||||
(void)spi_i2s_data_receive(SPI0);
|
||||
(void)SPI_STAT(SPI0);
|
||||
}
|
||||
|
||||
if (spi_i2s_interrupt_flag_get(SPI0, SPI_I2S_INT_FLAG_FERR) != RESET) {
|
||||
spi_i2s_format_error_clear(SPI0, SPI_FLAG_FERR);
|
||||
}
|
||||
@@ -892,8 +953,3 @@ extern "C" void EXTI10_15_IRQHandler(void)
|
||||
{
|
||||
AppSpiCommandBus.onCsExtiInterrupt();
|
||||
}
|
||||
|
||||
extern "C" void EXTI5_9_IRQHandler(void)
|
||||
{
|
||||
AppSpiCommandBus.onSpiReadyExti();
|
||||
}
|
||||
|
||||
@@ -71,10 +71,6 @@ public:
|
||||
/** @brief CS 边沿中断处理入口。 */
|
||||
void onCsExtiInterrupt();
|
||||
|
||||
/** @brief SPI Ready(PB6) EXTI6 中断入口。
|
||||
* v2 DATA_READY: ESP32 不再操作 PB6,EXTI6 仅清标志。 */
|
||||
void onSpiReadyExti();
|
||||
|
||||
/**
|
||||
* @brief 注册一个 SPI 命令处理器,用于 TYPE 路由。
|
||||
* @param[in] handler 命令处理器指针,调用方需保证其生命周期有效。
|
||||
@@ -145,11 +141,14 @@ private:
|
||||
static constexpr uint32_t kRingTimeoutMs = 50U;
|
||||
|
||||
volatile bool m_csActive = false;
|
||||
/** 本笔 CS 实际收到的字节数(CS 上升沿时由 RX DMA CHCNT 反算得到)。 */
|
||||
volatile uint16_t m_rxLen = 0U;
|
||||
uint16_t m_txLen = 0U;
|
||||
volatile uint8_t m_txPos = 0U;
|
||||
volatile bool m_logPending = false;
|
||||
bool m_txActiveThisCs = false;
|
||||
/** RX DMA 接收缓冲(替代旧 RBNE 逐字节累积的 m_rxBuf)。
|
||||
* CS 上升沿时 m_rxLen 为本笔实际收到的字节数,本缓冲前 m_rxLen 字节有效。 */
|
||||
uint8_t m_rxBuf[kRxBufSize] = {};
|
||||
uint8_t m_txBuf[kTxBufSize] = {};
|
||||
char m_logBuf[kLogBufSize] = {};
|
||||
@@ -170,11 +169,15 @@ private:
|
||||
void configureSpi0();
|
||||
void configureDma();
|
||||
void configureExti();
|
||||
void configureSpiReadyExti();
|
||||
void onCsAssert();
|
||||
void onCsRelease();
|
||||
void processCommand();
|
||||
|
||||
/** @brief 在 CS 空闲期预装下一笔全双工 DMA(TX + RX)。
|
||||
* 必须在首个 SCK 之前完成,否则 EXTI CS 下降相对 ESP32 时钟偏晚,
|
||||
* 整笔事务会收 0 字节。对齐 BOOT SpiOta::armSpiDmasForNextCs。 */
|
||||
void armSpiDmasForNextCs();
|
||||
|
||||
/** @brief 翻转 SPI Ready 握手引脚(PB6)电平(开漏线与)。
|
||||
* @param[in] ready true=释放(高阻,靠上拉拉高), false=下拉(驱动低)。
|
||||
* @note GPIO BRR 写入原子,无需临界区;可由 ISR 与主循环共同调用。
|
||||
|
||||
@@ -23,7 +23,6 @@ enum system_task_t {
|
||||
TASK_DIAGNOSTIC_PRINT, /**< 诊断信息打印任务。 */
|
||||
TASK_SPI_LOG, /**< SPI 延迟日志打印任务。 */
|
||||
TASK_ETHERCAT_PROCESS, /**< EtherCAT 周期/事件处理任务(非实时应用层 PDO 映射)。 */
|
||||
TASK_SPI_CMD_PROCESS, /**< SPI CS release 后的命令解析任务(从 ISR 移出)。 */
|
||||
TASK_COUNT /**< 任务总数,用于边界或统计场景。 */
|
||||
};
|
||||
|
||||
@@ -49,8 +48,10 @@ struct task_scheduler_t {
|
||||
volatile uint16_t div_can_tx;
|
||||
/** @brief 调度分频计数:状态灯(100ms)。 */
|
||||
volatile uint16_t div_status_led;
|
||||
/** @brief 调度分频计数:心跳与诊断打印。 */
|
||||
/** @brief 调度分频计数:心跳(1s)。 */
|
||||
volatile uint16_t div_heartbeat;
|
||||
/** @brief 调度分频计数:APP 状态/诊断打印(5s)。 */
|
||||
volatile uint16_t div_diag_print;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/**
|
||||
/**
|
||||
* @file config.h
|
||||
* @brief 应用级集中配置项定义。
|
||||
* @note 用于统一管理当前项目中可调的外设、时序、通信与算法常量。
|
||||
@@ -161,8 +161,10 @@ constexpr uint32_t CONFIG_TASK_PERIOD_CAN_RX_TICKS = 100U;
|
||||
constexpr uint32_t CONFIG_TASK_PERIOD_CAN_TX_TICKS = 10U;
|
||||
/** @brief 调度基准节拍:状态灯任务周期,1000 tick = 100ms。 */
|
||||
constexpr uint32_t CONFIG_TASK_PERIOD_STATUS_LED_TICKS = 1000U;
|
||||
/** @brief 调度基准节拍:心跳/诊断打印周期,单位 100us tick。 */
|
||||
/** @brief 调度基准节拍:心跳周期,单位 100us tick。 */
|
||||
constexpr uint32_t CONFIG_TASK_PERIOD_HEARTBEAT_TICKS = 10000U;
|
||||
/** @brief 调度基准节拍:APP 状态/诊断打印周期,50000 tick = 5s。 */
|
||||
constexpr uint32_t CONFIG_TASK_PERIOD_DIAG_PRINT_TICKS = 50000U;
|
||||
|
||||
/** @brief 周期温度/电压 UART 打印开关。@note 保留 #define:task_manager.cpp #if 使用。 */
|
||||
#define CONFIG_UART_TEMP_PERIODIC_PRINT 1
|
||||
|
||||
@@ -15,6 +15,9 @@ constexpr uint32_t kOtaConfigPageAddr = 0x0807E000U;
|
||||
constexpr uint32_t kAppLatestAvalAddr = kOtaConfigPageAddr + 0x00U;
|
||||
constexpr uint32_t kAppLatestEnAddr = kOtaConfigPageAddr + 0x08U;
|
||||
constexpr uint32_t kAppLatestCheckAddr = kOtaConfigPageAddr + 0x10U;
|
||||
/** BOOT 版本号在 OTA 配置页中的存储地址:word6=major|minor, word7=patch|build。 */
|
||||
constexpr uint32_t kBootVersionAddr0 = kOtaConfigPageAddr + 0x18U;
|
||||
constexpr uint32_t kBootVersionAddr1 = kOtaConfigPageAddr + 0x1CU;
|
||||
constexpr uint32_t kOtaAval = 0x00000000U;
|
||||
constexpr uint32_t kOtaNotAval = 0xFFFFFFFFU;
|
||||
constexpr uint32_t kFmcPageSize = 0x00000800U;
|
||||
@@ -102,6 +105,23 @@ void OtaBootRequest::init()
|
||||
s_spiFrozenForBoot = false;
|
||||
}
|
||||
|
||||
BootVersion OtaBootRequest::readBootVersion()
|
||||
{
|
||||
/* BOOT 启动时将自身版本写入 OTA 配置页 word6/word7,
|
||||
* 字布局:word6=[15:0]major|[31:16]minor, word7=[15:0]patch|[31:16]build。
|
||||
* Flash 未写入(擦除态 0xFFFFFFFF)时各字段返回 0xFF,调用方可据此判断有效性。 */
|
||||
const volatile uint32_t *bv0 = (const volatile uint32_t *)kBootVersionAddr0;
|
||||
const volatile uint32_t *bv1 = (const volatile uint32_t *)kBootVersionAddr1;
|
||||
const uint32_t w0 = *bv0;
|
||||
const uint32_t w1 = *bv1;
|
||||
return BootVersion{
|
||||
(uint8_t)(w0 & 0xFFU),
|
||||
(uint8_t)((w0 >> 16) & 0xFFU),
|
||||
(uint8_t)(w1 & 0xFFU),
|
||||
(uint8_t)((w1 >> 16) & 0xFFU)
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 写入 Bootloader OTA 等待标志,并安排稍后软复位。
|
||||
* @return true 表示标志写入成功;false 表示 Flash 配置页写入失败。
|
||||
@@ -172,14 +192,12 @@ void OtaBootRequest::process()
|
||||
s_spiFrozenForBoot = true;
|
||||
nvic_irq_disable(SPI0_IRQn);
|
||||
nvic_irq_disable(EXTI10_15_IRQn);
|
||||
nvic_irq_disable(EXTI5_9_IRQn);
|
||||
|
||||
const bool ok = requestEnterBoot();
|
||||
if(!ok) {
|
||||
s_spiFrozenForBoot = false;
|
||||
nvic_irq_enable(SPI0_IRQn, 2U, 0U);
|
||||
nvic_irq_enable(EXTI10_15_IRQn, 1U, 1U);
|
||||
nvic_irq_enable(EXTI5_9_IRQn, CONFIG_IRQ_PREEMPT_SPI_USART, 1U);
|
||||
/* requestEnterBoot 失败时 s_rebootPending 仍为 false */
|
||||
return;
|
||||
}
|
||||
@@ -194,7 +212,6 @@ void OtaBootRequest::process()
|
||||
s_spiFrozenForBoot = true;
|
||||
nvic_irq_disable(SPI0_IRQn);
|
||||
nvic_irq_disable(EXTI10_15_IRQn);
|
||||
nvic_irq_disable(EXTI5_9_IRQn);
|
||||
|
||||
/* v2 DATA_READY: Boot 切换不再用 PB6 通知对端,
|
||||
* BOOT 起机后再由 HELLO role=boot 确认。 */
|
||||
|
||||
@@ -3,6 +3,20 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/**
|
||||
* @brief BOOT 版本号结构体(从 Flash OTA 配置页读取)。
|
||||
*
|
||||
* Flash 字布局(与 BOOT 工程 sys/config.h 对称):
|
||||
* word6 (ADDR0): [15:0]=major [31:16]=minor
|
||||
* word7 (ADDR1): [15:0]=patch [31:16]=build
|
||||
*/
|
||||
struct BootVersion {
|
||||
uint8_t major;
|
||||
uint8_t minor;
|
||||
uint8_t patch;
|
||||
uint8_t build;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief DCU_APP 进入 Bootloader OTA 窗口控制模块。
|
||||
* @note ESP32 通过 CAN-FD 发送受保护命令,APP 写 OTA 标志后软复位,
|
||||
@@ -13,6 +27,19 @@ public:
|
||||
/** @brief 初始化内部状态。 */
|
||||
static void init();
|
||||
|
||||
/**
|
||||
* @brief 从 OTA 配置页 Flash 读取 BOOT 版本号。
|
||||
*
|
||||
* BOOT 启动时将自身版本写入 0x0807E018/0x0807E01C,
|
||||
* APP 运行期通过本函数读出,用于 SPI PING 响应、日志等场景。
|
||||
*
|
||||
* @return BootVersion 结构体,字段已按 Flash 字布局拆包。
|
||||
*
|
||||
* @note 若 Flash 未写入(擦除态 0xFFFFFFFF),返回值各字段为 0xFF,
|
||||
* 调用方可据此判断 BOOT 版本是否有效。
|
||||
*/
|
||||
static BootVersion readBootVersion();
|
||||
|
||||
/**
|
||||
* @brief 尝试处理一帧 CAN 接收报文。
|
||||
* @param[in] channel 报文来源 CAN 通道。
|
||||
|
||||
@@ -33,6 +33,7 @@ void TaskManager::init()
|
||||
g_scheduler.div_can_tx = 0U;
|
||||
g_scheduler.div_status_led = 0U;
|
||||
g_scheduler.div_heartbeat = 0U;
|
||||
g_scheduler.div_diag_print = 0U;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -109,12 +110,15 @@ void TaskManager::tick100us()
|
||||
g_scheduler.div_heartbeat = 0U;
|
||||
g_scheduler.tick_1s++;
|
||||
g_app_uptime_sec++;
|
||||
#if (CONFIG_UART_TEMP_PERIODIC_PRINT != 0)
|
||||
set((1UL << TASK_HEARTBEAT) | (1UL << TASK_DIAGNOSTIC_PRINT));
|
||||
#else
|
||||
set(1UL << TASK_HEARTBEAT);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if (CONFIG_UART_TEMP_PERIODIC_PRINT != 0)
|
||||
if(++g_scheduler.div_diag_print >= CONFIG_TASK_PERIOD_DIAG_PRINT_TICKS) {
|
||||
g_scheduler.div_diag_print = 0U;
|
||||
set(1UL << TASK_DIAGNOSTIC_PRINT);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -182,7 +182,7 @@ void TaskRunner::taskCanTxSend()
|
||||
}
|
||||
|
||||
const uint32_t extId = 0x18FF5000U | static_cast<uint32_t>(ch);
|
||||
if (CanFd::transmitFrame(ch, extId, data, sizeof(data), true)) {
|
||||
if (CanFd::transmitFrame(ch, extId, data, sizeof(data), true) == CanFd::CanTxResult::kOk) {
|
||||
seq++;
|
||||
}
|
||||
}
|
||||
@@ -240,7 +240,7 @@ void TaskRunner::taskHeartbeat() { OtaBootRequest::process(); CanFd::refres
|
||||
* @param[in] 无。
|
||||
* @param[out] 无。
|
||||
* @retval 无返回值。
|
||||
* @note 由 1 秒节拍触发,通过 UART 打印当前温度、电压;采用定点整数格式,避免依赖浮点 `printf` 支持。
|
||||
* @note 由 5 秒节拍触发,通过 UART 打印当前温度、电压;采用定点整数格式,避免依赖浮点 `printf` 支持。
|
||||
*/
|
||||
void TaskRunner::taskDiagnosticPrint()
|
||||
{
|
||||
@@ -304,18 +304,6 @@ void TaskRunner::taskDiagnosticPrint()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 执行 SPI CS release 后推迟的命令解析(从 ISR 移出)。
|
||||
* @param[in] 无。
|
||||
* @param[out] 无。
|
||||
* @retval 无返回值。
|
||||
* @note ISR 瘦身:onCsRelease() 仅置位 TASK_SPI_CMD_PROCESS,实际解析在此执行。
|
||||
*/
|
||||
void TaskRunner::taskSpiCmdProcess()
|
||||
{
|
||||
AppSpiCommandBus.poll(); // 命令处理已移入 ISR,此处仅日志打印+按铃看护
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 执行 EtherCAT 周期/事件处理(非实时应用层 PDO 映射)。
|
||||
* @param[in] 无。
|
||||
@@ -401,6 +389,5 @@ void TaskRunner::runPending()
|
||||
if(pending & (1UL << TASK_HEARTBEAT)) taskHeartbeat();
|
||||
if(pending & (1UL << TASK_DIAGNOSTIC_PRINT)) taskDiagnosticPrint();
|
||||
/* TASK_SPI_LOG 的 poll() 已移至 runPending() 开头无条件调用(含按铃看门狗) */
|
||||
if(pending & (1UL << TASK_SPI_CMD_PROCESS)) taskSpiCmdProcess();
|
||||
if(pending & (1UL << TASK_ETHERCAT_PROCESS)) taskEthercatProcess();
|
||||
}
|
||||
|
||||
@@ -47,8 +47,6 @@ private:
|
||||
static void taskHeartbeat();
|
||||
/** @brief 诊断打印任务。 */
|
||||
static void taskDiagnosticPrint();
|
||||
/** @brief SPI CS release 后推迟的命令解析(从 ISR 移出)。 */
|
||||
static void taskSpiCmdProcess();
|
||||
/** @brief EtherCAT 周期/事件处理(非实时应用层 PDO 映射)。 */
|
||||
static void taskEthercatProcess();
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user