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:
@@ -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();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user